Commit faaa3ee4 authored by Juho Snellman's avatar Juho Snellman

Redesign the API a bit to make HierarchicalTimerWheel non-template

- Allows using multiple different callback signatures in the same
  wheel.
- Unfortunately it means having to eat the vtable overhead.
- Add a MemberTimerEvent subclass which is initialized with a
  (dynamic) instance pointer and a (static, templated) member
  function pointer, instead of just a generic Callable.
parent 49338b29
...@@ -36,10 +36,9 @@ ...@@ -36,10 +36,9 @@
bool test_single_timer_no_hierarchy() { bool test_single_timer_no_hierarchy() {
typedef std::function<void()> Callback; typedef std::function<void()> Callback;
HierarchicalTimerWheel<Callback> timers; HierarchicalTimerWheel timers;
int count = 0; int count = 0;
TimerEvent<Callback> timer([&count] () { ++count; }, TimerEvent<Callback> timer([&count] () { ++count; });
&timers);
timers.advance(10); timers.advance(10);
EXPECT_INTEQ(count, 0); EXPECT_INTEQ(count, 0);
...@@ -74,10 +73,9 @@ bool test_single_timer_no_hierarchy() { ...@@ -74,10 +73,9 @@ bool test_single_timer_no_hierarchy() {
bool test_single_timer_hierarchy() { bool test_single_timer_hierarchy() {
typedef std::function<void()> Callback; typedef std::function<void()> Callback;
HierarchicalTimerWheel<Callback> timers; HierarchicalTimerWheel timers;
int count = 0; int count = 0;
TimerEvent<Callback> timer([&count] () { ++count; }, TimerEvent<Callback> timer([&count] () { ++count; });
&timers);
EXPECT_INTEQ(count, 0); EXPECT_INTEQ(count, 0);
...@@ -118,10 +116,9 @@ bool test_single_timer_hierarchy() { ...@@ -118,10 +116,9 @@ bool test_single_timer_hierarchy() {
bool test_single_timer_random() { bool test_single_timer_random() {
typedef std::function<void()> Callback; typedef std::function<void()> Callback;
HierarchicalTimerWheel<Callback> timers; HierarchicalTimerWheel timers;
int count = 0; int count = 0;
TimerEvent<Callback> timer([&count] () { ++count; }, TimerEvent<Callback> timer([&count] () { ++count; });
&timers);
for (int i = 0; i < 10000; ++i) { for (int i = 0; i < 10000; ++i) {
int len = rand() % 20; int len = rand() % 20;
...@@ -137,11 +134,53 @@ bool test_single_timer_random() { ...@@ -137,11 +134,53 @@ bool test_single_timer_random() {
return true; return true;
} }
class Test {
public:
Test()
: inc_timer_(this), reset_timer_(this) {
}
void start(HierarchicalTimerWheel* timers) {
timers->schedule(&inc_timer_, 10);
timers->schedule(&reset_timer_, 15);
}
void on_inc() {
count_++;
}
void on_reset() {
count_ = 0;
}
int count() { return count_; }
private:
MemberTimerEvent<Test, &Test::on_inc> inc_timer_;
MemberTimerEvent<Test, &Test::on_reset> reset_timer_;
int count_ = 0;
};
bool test_timeout_method() {
HierarchicalTimerWheel timers;
Test test;
test.start(&timers);
EXPECT_INTEQ(test.count(), 0);
timers.advance(10);
EXPECT_INTEQ(test.count(), 1);
timers.advance(5);
EXPECT_INTEQ(test.count(), 0);
return true;
}
int main(void) { 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_single_timer_random); TEST(test_single_timer_random);
TEST(test_timeout_method);
// Test canceling timer from within timer // Test canceling timer from within timer
// Test rescheduling timer from within timer // Test rescheduling timer from within timer
return ok ? 0 : 1; return ok ? 0 : 1;
......
...@@ -14,53 +14,21 @@ ...@@ -14,53 +14,21 @@
typedef uint64_t Tick; typedef uint64_t Tick;
template<typename CBType>
class TimerWheelSlot; class TimerWheelSlot;
template<typename CBType>
class HierarchicalTimerWheel; class HierarchicalTimerWheel;
template<typename CBType> class TimerEventInterface {
class TimerEvent {
public: public:
TimerEvent<CBType>(const CBType& callback, TimerEventInterface()
HierarchicalTimerWheel<CBType>* timers) : slot_(NULL),
: callback_(callback), next_(NULL),
prev_(NULL), prev_(NULL) {
next_(NULL) {
}
void cancel() {
if (this == slot_->events()) {
slot_->pop_event();
} else {
auto prev = prev_;
auto next = next_;
if (prev) {
prev->next_ = next;
}
if (next) {
next->prev_ = prev;
}
prev_ = NULL;
next_ = NULL;
}
slot_ = NULL;
} }
void relink(TimerWheelSlot<CBType>* slot) { void cancel();
assert(slot_); void relink(TimerWheelSlot* slot);
if (slot_ == slot) {
return;
}
cancel();
slot_ = slot;
slot_->push_event(this);
}
void execute() { virtual void execute() = 0;
callback_();
}
bool active() { bool active() {
return slot_ != NULL; return slot_ != NULL;
...@@ -68,32 +36,59 @@ public: ...@@ -68,32 +36,59 @@ public:
Tick scheduled_at() { return scheduled_at_; } Tick scheduled_at() { return scheduled_at_; }
void set_scheduled_at(Tick ts) { scheduled_at_ = ts; } void set_scheduled_at(Tick ts) { scheduled_at_ = ts; }
private: private:
TimerEvent(const TimerEvent& other) = delete; TimerEventInterface(const TimerEventInterface& other) = delete;
TimerEvent& operator=(const TimerEvent& other) = delete; TimerEventInterface& operator=(const TimerEventInterface& other) = delete;
friend TimerWheelSlot;
Tick scheduled_at_; Tick scheduled_at_;
CBType callback_;
// The slot this event is currently in (NULL if not currently scheduled). // The slot this event is currently in (NULL if not currently scheduled).
TimerWheelSlot<CBType>* slot_ = NULL; TimerWheelSlot* slot_ = NULL;
// The events are linked together in the slot using an internal // The events are linked together in the slot using an internal
// doubly-linked list; this iterator does double duty as the // doubly-linked list; this iterator does double duty as the
// linked list node for this event. // linked list node for this event.
// typename std::list<TimerEvent<CBType>*>::iterator it_; TimerEventInterface* next_;
TimerEvent<CBType>* prev_; TimerEventInterface* prev_;
TimerEvent<CBType>* next_;
friend TimerWheelSlot<CBType>;
}; };
template<typename CBType> template<typename CBType>
class TimerEvent : public TimerEventInterface {
public:
TimerEvent<CBType>(const CBType& callback)
: callback_(callback) {
}
void execute() {
callback_();
}
private:
TimerEvent<CBType>(const TimerEvent<CBType>& other) = delete;
TimerEvent<CBType>& operator=(const TimerEvent<CBType>& other) = delete;
CBType callback_;
};
template<typename T, void(T::*MFun)() >
class MemberTimerEvent : public TimerEventInterface {
public:
MemberTimerEvent(T* obj) : obj_(obj) {
}
virtual void execute () {
(obj_->*MFun)();
}
private:
T* obj_;
};
class TimerWheelSlot { class TimerWheelSlot {
public: public:
TimerWheelSlot() : events_(NULL) { TimerWheelSlot() {
} }
TimerEvent<CBType>* events() { return events_; } TimerEventInterface* events() { return events_; }
TimerEvent<CBType>* pop_event() { TimerEventInterface* pop_event() {
auto event = events_; auto event = events_;
events_ = event->next_; events_ = event->next_;
if (events_) { if (events_) {
...@@ -103,7 +98,7 @@ public: ...@@ -103,7 +98,7 @@ public:
event->slot_ = NULL; event->slot_ = NULL;
return event; return event;
} }
void push_event(TimerEvent<CBType>* event) { void push_event(TimerEventInterface* event) {
event->slot_ = this; event->slot_ = this;
event->next_ = events_; event->next_ = events_;
if (events_) { if (events_) {
...@@ -116,15 +111,14 @@ private: ...@@ -116,15 +111,14 @@ private:
TimerWheelSlot(const TimerWheelSlot& other) = delete; TimerWheelSlot(const TimerWheelSlot& other) = delete;
TimerWheelSlot& operator=(const TimerWheelSlot& other) = delete; TimerWheelSlot& operator=(const TimerWheelSlot& other) = delete;
TimerEvent<CBType>* events_; TimerEventInterface* events_ = NULL;
}; };
template<typename CBType>
class HierarchicalTimerWheel { class HierarchicalTimerWheel {
public: public:
HierarchicalTimerWheel() HierarchicalTimerWheel()
: now_(0), : now_(0),
up_(new HierarchicalTimerWheel<CBType>(WIDTH_BITS, this)), up_(new HierarchicalTimerWheel(WIDTH_BITS, this)),
down_(NULL) { down_(NULL) {
} }
...@@ -148,7 +142,7 @@ public: ...@@ -148,7 +142,7 @@ public:
} }
} }
void schedule(TimerEvent<CBType>* event, Tick delta) { void schedule(TimerEventInterface* event, Tick delta) {
if (!down_) { if (!down_) {
event->set_scheduled_at(now_ + delta); event->set_scheduled_at(now_ + delta);
} }
...@@ -170,11 +164,11 @@ private: ...@@ -170,11 +164,11 @@ private:
: now_(0), : now_(0),
down_(down) { down_(down) {
if (offset + WIDTH_BITS < 64) { if (offset + WIDTH_BITS < 64) {
up_ = new HierarchicalTimerWheel<CBType>(offset + WIDTH_BITS, this); up_ = new HierarchicalTimerWheel(offset + WIDTH_BITS, this);
} }
} }
void schedule_absolute(TimerEvent<CBType>* event, Tick absolute) { void schedule_absolute(TimerEventInterface* event, Tick absolute) {
Tick delta; Tick delta;
delta = absolute - now_; delta = absolute - now_;
assert(absolute >= now_); assert(absolute >= now_);
...@@ -196,9 +190,38 @@ private: ...@@ -196,9 +190,38 @@ private:
static const int WIDTH_BITS = 8; static const int WIDTH_BITS = 8;
static const int NUM_SLOTS = 1 << WIDTH_BITS; static const int NUM_SLOTS = 1 << WIDTH_BITS;
static const int MASK = (NUM_SLOTS - 1); static const int MASK = (NUM_SLOTS - 1);
TimerWheelSlot<CBType> slots_[NUM_SLOTS]; TimerWheelSlot slots_[NUM_SLOTS];
HierarchicalTimerWheel<CBType>* up_; HierarchicalTimerWheel* up_;
HierarchicalTimerWheel<CBType>* down_; HierarchicalTimerWheel* down_;
}; };
void TimerEventInterface::cancel() {
if (this == slot_->events()) {
slot_->pop_event();
} else {
auto prev = prev_;
auto next = next_;
if (prev) {
prev->next_ = next;
}
if (next) {
next->prev_ = prev;
}
prev_ = NULL;
next_ = NULL;
}
slot_ = NULL;
}
void TimerEventInterface::relink(TimerWheelSlot* slot) {
assert(slot_);
if (slot_ == slot) {
return;
}
cancel();
slot_ = slot;
slot_->push_event(this);
}
#endif // _TIMER_WHEEL_H #endif // _TIMER_WHEEL_H
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