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 @@ ...@@ -29,10 +29,10 @@
#define EXPECT_INTEQ(actual, expect) \ #define EXPECT_INTEQ(actual, expect) \
do { \ do { \
if (expect != actual) { \ if (expect != actual) { \
printf("%s:%d: Expect failed, wanted %d" \ printf("%s:%d: Expect failed, wanted %ld" \
" got %d\n", \ " got %ld\n", \
__FILE__, __LINE__, \ __FILE__, __LINE__, \
expect, actual); \ (long) expect, (long) actual); \
return false; \ return false; \
} \ } \
} while (0) } while (0)
...@@ -140,6 +140,66 @@ bool test_single_timer_hierarchy() { ...@@ -140,6 +140,66 @@ bool test_single_timer_hierarchy() {
return true; 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() { bool test_reschedule_from_timer() {
typedef std::function<void()> Callback; typedef std::function<void()> Callback;
TimerWheel timers; TimerWheel timers;
...@@ -228,6 +288,7 @@ int main(void) { ...@@ -228,6 +288,7 @@ int main(void) {
bool ok = true; bool ok = true;
TEST(test_single_timer_no_hierarchy); TEST(test_single_timer_no_hierarchy);
TEST(test_single_timer_hierarchy); TEST(test_single_timer_hierarchy);
TEST(test_ticks_to_next_event);
TEST(test_single_timer_random); TEST(test_single_timer_random);
TEST(test_reschedule_from_timer); TEST(test_reschedule_from_timer);
TEST(test_timeout_method); TEST(test_timeout_method);
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
#include <cstdlib> #include <cstdlib>
#include <cstdint> #include <cstdint>
#include <cstdio> #include <cstdio>
#include <list> #include <limits>
typedef uint64_t Tick; typedef uint64_t Tick;
...@@ -26,11 +26,11 @@ public: ...@@ -26,11 +26,11 @@ public:
virtual void execute() = 0; virtual void execute() = 0;
bool active() { bool active() const {
return slot_ != NULL; 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; } void set_scheduled_at(Tick ts) { scheduled_at_ = ts; }
private: private:
...@@ -87,7 +87,7 @@ public: ...@@ -87,7 +87,7 @@ public:
TimerWheelSlot() { TimerWheelSlot() {
} }
TimerEventInterface* events() { return events_; } const TimerEventInterface* events() const { return events_; }
TimerEventInterface* pop_event() { TimerEventInterface* pop_event() {
auto event = events_; auto event = events_;
events_ = event->next_; events_ = event->next_;
...@@ -96,7 +96,6 @@ public: ...@@ -96,7 +96,6 @@ public:
} }
event->next_ = NULL; event->next_ = NULL;
event->slot_ = NULL; event->slot_ = NULL;
lose_event();
return event; return event;
} }
...@@ -105,12 +104,6 @@ private: ...@@ -105,12 +104,6 @@ private:
TimerWheelSlot& operator=(const TimerWheelSlot& other) = delete; TimerWheelSlot& operator=(const TimerWheelSlot& other) = delete;
friend TimerEventInterface; friend TimerEventInterface;
void lose_event() {
}
void gain_event(TimerEventInterface *e) {
}
TimerEventInterface* events_ = NULL; TimerEventInterface* events_ = NULL;
}; };
...@@ -166,7 +159,50 @@ public: ...@@ -166,7 +159,50 @@ public:
// multiple ticks during a single call to advance(), the value of now() // 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 // will be the tick on which the timer was first run. Not the tick that
// the timer eventually will advance to. // 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: private:
TimerWheel(const TimerWheel& other) = delete; TimerWheel(const TimerWheel& other) = delete;
...@@ -178,7 +214,7 @@ private: ...@@ -178,7 +214,7 @@ private:
if (offset + WIDTH_BITS < 64) { if (offset + WIDTH_BITS < 64) {
up_ = new TimerWheel(offset + WIDTH_BITS, down); up_ = new TimerWheel(offset + WIDTH_BITS, down);
} }
} }
Tick now_; Tick now_;
...@@ -208,7 +244,6 @@ void TimerEventInterface::relink(TimerWheelSlot* new_slot) { ...@@ -208,7 +244,6 @@ void TimerEventInterface::relink(TimerWheelSlot* new_slot) {
// Must be at head of slot. Move the next item to the head. // Must be at head of slot. Move the next item to the head.
slot_->events_ = next; slot_->events_ = next;
} }
slot_->lose_event();
} }
// Insert in new slot. // Insert in new slot.
...@@ -220,7 +255,6 @@ void TimerEventInterface::relink(TimerWheelSlot* new_slot) { ...@@ -220,7 +255,6 @@ void TimerEventInterface::relink(TimerWheelSlot* new_slot) {
old->prev_ = this; old->prev_ = this;
} }
new_slot->events_ = this; new_slot->events_ = this;
new_slot->gain_event(this);
} else { } else {
next_ = NULL; 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