Commit 4eb95a09 authored by Juho Snellman's avatar Juho Snellman

Add TimerWheel::ticks_to_next_event

- Add a max parameter (optional), since in my experience you'll never use
  the ticks of the next event as a raw value.
parent e5bee55a
......@@ -29,10 +29,10 @@
#define EXPECT_INTEQ(actual, expect) \
do { \
if (expect != actual) { \
printf("%s:%d: Expect failed, wanted %d" \
" got %d\n", \
printf("%s:%d: Expect failed, wanted %ld" \
" got %ld\n", \
__FILE__, __LINE__, \
expect, actual); \
(long) expect, (long) actual); \
return false; \
} \
} while (0)
......@@ -140,6 +140,66 @@ bool test_single_timer_hierarchy() {
return true;
}
bool test_ticks_to_next_event() {
typedef std::function<void()> Callback;
TimerWheel timers;
TimerEvent<Callback> timer([] () { });
TimerEvent<Callback> timer2([] () { });
// No timers scheduled, return the max value.
EXPECT_INTEQ(timers.ticks_to_next_event(100), 100);
EXPECT_INTEQ(timers.ticks_to_next_event(0), 0);
for (int i = 0; i < 10; ++i) {
// Just vanilla tests
timers.schedule(&timer, 1);
EXPECT_INTEQ(timers.ticks_to_next_event(100), 1);
timers.schedule(&timer, 20);
EXPECT_INTEQ(timers.ticks_to_next_event(100), 20);
// Check the the "max" parameters works.
timers.schedule(&timer, 150);
EXPECT_INTEQ(timers.ticks_to_next_event(100), 100);
// Check that a timer on the next layer can be found.
timers.schedule(&timer, 280);
EXPECT_INTEQ(timers.ticks_to_next_event(100), 100);
EXPECT_INTEQ(timers.ticks_to_next_event(1000), 280);
// Test having a timer on the next wheel (still remaining from
// the previous test), and another (earlier) timer on this
// wheel.
for (int i = 1; i < 256; ++i) {
timers.schedule(&timer2, i);
EXPECT_INTEQ(timers.ticks_to_next_event(1000), i);
}
timer.cancel();
timer2.cancel();
// And then run these same tests from a bunch of different
// wheel locations.
timers.advance(32);
}
// More thorough tests for cases where the next timer could be on
// either of two different wheels.
for (int i = 0; i < 20; ++i) {
timers.schedule(&timer, 270);
timers.advance(128);
EXPECT_INTEQ(timers.ticks_to_next_event(512), 270 - 128);
timers.schedule(&timer2, 250);
EXPECT_INTEQ(timers.ticks_to_next_event(512), 270 - 128);
timers.schedule(&timer2, 10);
EXPECT_INTEQ(timers.ticks_to_next_event(512), 10);
// Again, do this from a bunch of different locatoins.
timers.advance(32);
}
return true;
}
bool test_reschedule_from_timer() {
typedef std::function<void()> Callback;
TimerWheel timers;
......@@ -228,6 +288,7 @@ int main(void) {
bool ok = true;
TEST(test_single_timer_no_hierarchy);
TEST(test_single_timer_hierarchy);
TEST(test_ticks_to_next_event);
TEST(test_single_timer_random);
TEST(test_reschedule_from_timer);
TEST(test_timeout_method);
......
......@@ -10,7 +10,7 @@
#include <cstdlib>
#include <cstdint>
#include <cstdio>
#include <list>
#include <limits>
typedef uint64_t Tick;
......@@ -26,11 +26,11 @@ public:
virtual void execute() = 0;
bool active() {
bool active() const {
return slot_ != NULL;
}
Tick scheduled_at() { return scheduled_at_; }
Tick scheduled_at() const { return scheduled_at_; }
void set_scheduled_at(Tick ts) { scheduled_at_ = ts; }
private:
......@@ -87,7 +87,7 @@ public:
TimerWheelSlot() {
}
TimerEventInterface* events() { return events_; }
const TimerEventInterface* events() const { return events_; }
TimerEventInterface* pop_event() {
auto event = events_;
events_ = event->next_;
......@@ -96,7 +96,6 @@ public:
}
event->next_ = NULL;
event->slot_ = NULL;
lose_event();
return event;
}
......@@ -105,12 +104,6 @@ private:
TimerWheelSlot& operator=(const TimerWheelSlot& other) = delete;
friend TimerEventInterface;
void lose_event() {
}
void gain_event(TimerEventInterface *e) {
}
TimerEventInterface* events_ = NULL;
};
......@@ -166,7 +159,50 @@ public:
// multiple ticks during a single call to advance(), the value of now()
// will be the tick on which the timer was first run. Not the tick that
// the timer eventually will advance to.
const Tick& now() const { return now_; }
Tick now() const { return now_; }
Tick ticks_to_next_event(const Tick& max = 0) {
// The actual current time (not the bitshifted time)
Tick now = down_ ? down_->now() : now_;
// Smallest tick we've found.
Tick min = max ? now + max : std::numeric_limits<Tick>::max();
for (int i = 0; i < NUM_SLOTS; ++i) {
// Note: Unlike the uses of "now", slot index calculations really
// need to use now_.
auto slot_index = (now_ + 1 + i) & MASK;
// We've reached slot 0. In normal scheduling this would
// mean advancing the next wheel and promoting or running
// those timers. So we need to look in that slot too
// before proceeding with the rest of this wheel. But we
// can't just accept those results outright, we need to
// check the best result there against the next slot on
// this wheel.
if (slot_index == 0 && up_) {
const auto& slot = up_->slots_[(up_->now_ + 1) & MASK];
for (auto event = slot.events(); event != NULL;
event = event->next_) {
min = std::min(min, event->scheduled_at());
}
}
bool found = false;
const auto& slot = slots_[slot_index];
for (auto event = slot.events(); event != NULL;
event = event->next_) {
min = std::min(min, event->scheduled_at());
found = true;
}
if (found) {
return min - now;
}
}
// Nothind found on this wheel, try the next one.
if (up_) {
return up_->ticks_to_next_event(max);
}
return max;
}
private:
TimerWheel(const TimerWheel& other) = delete;
......@@ -178,7 +214,7 @@ private:
if (offset + WIDTH_BITS < 64) {
up_ = new TimerWheel(offset + WIDTH_BITS, down);
}
}
}
Tick now_;
......@@ -208,7 +244,6 @@ void TimerEventInterface::relink(TimerWheelSlot* new_slot) {
// Must be at head of slot. Move the next item to the head.
slot_->events_ = next;
}
slot_->lose_event();
}
// Insert in new slot.
......@@ -220,7 +255,6 @@ void TimerEventInterface::relink(TimerWheelSlot* new_slot) {
old->prev_ = this;
}
new_slot->events_ = this;
new_slot->gain_event(this);
} else {
next_ = NULL;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment