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 @@
bool test_single_timer_no_hierarchy() {
typedef std::function<void()> Callback;
HierarchicalTimerWheel<Callback> timers;
HierarchicalTimerWheel timers;
int count = 0;
TimerEvent<Callback> timer([&count] () { ++count; },
&timers);
TimerEvent<Callback> timer([&count] () { ++count; });
timers.advance(10);
EXPECT_INTEQ(count, 0);
......@@ -74,10 +73,9 @@ bool test_single_timer_no_hierarchy() {
bool test_single_timer_hierarchy() {
typedef std::function<void()> Callback;
HierarchicalTimerWheel<Callback> timers;
HierarchicalTimerWheel timers;
int count = 0;
TimerEvent<Callback> timer([&count] () { ++count; },
&timers);
TimerEvent<Callback> timer([&count] () { ++count; });
EXPECT_INTEQ(count, 0);
......@@ -118,10 +116,9 @@ bool test_single_timer_hierarchy() {
bool test_single_timer_random() {
typedef std::function<void()> Callback;
HierarchicalTimerWheel<Callback> timers;
HierarchicalTimerWheel timers;
int count = 0;
TimerEvent<Callback> timer([&count] () { ++count; },
&timers);
TimerEvent<Callback> timer([&count] () { ++count; });
for (int i = 0; i < 10000; ++i) {
int len = rand() % 20;
......@@ -137,11 +134,53 @@ bool test_single_timer_random() {
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) {
bool ok = true;
TEST(test_single_timer_no_hierarchy);
TEST(test_single_timer_hierarchy);
TEST(test_single_timer_random);
TEST(test_timeout_method);
// Test canceling timer from within timer
// Test rescheduling timer from within timer
return ok ? 0 : 1;
......
......@@ -14,53 +14,21 @@
typedef uint64_t Tick;
template<typename CBType>
class TimerWheelSlot;
template<typename CBType>
class HierarchicalTimerWheel;
template<typename CBType>
class TimerEvent {
class TimerEventInterface {
public:
TimerEvent<CBType>(const CBType& callback,
HierarchicalTimerWheel<CBType>* timers)
: callback_(callback),
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;
TimerEventInterface()
: slot_(NULL),
next_(NULL),
prev_(NULL) {
}
void relink(TimerWheelSlot<CBType>* slot) {
assert(slot_);
if (slot_ == slot) {
return;
}
cancel();
slot_ = slot;
slot_->push_event(this);
}
void cancel();
void relink(TimerWheelSlot* slot);
void execute() {
callback_();
}
virtual void execute() = 0;
bool active() {
return slot_ != NULL;
......@@ -68,32 +36,59 @@ public:
Tick scheduled_at() { return scheduled_at_; }
void set_scheduled_at(Tick ts) { scheduled_at_ = ts; }
private:
TimerEvent(const TimerEvent& other) = delete;
TimerEvent& operator=(const TimerEvent& other) = delete;
TimerEventInterface(const TimerEventInterface& other) = delete;
TimerEventInterface& operator=(const TimerEventInterface& other) = delete;
friend TimerWheelSlot;
Tick scheduled_at_;
CBType callback_;
// 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
// doubly-linked list; this iterator does double duty as the
// linked list node for this event.
// typename std::list<TimerEvent<CBType>*>::iterator it_;
TimerEvent<CBType>* prev_;
TimerEvent<CBType>* next_;
friend TimerWheelSlot<CBType>;
TimerEventInterface* next_;
TimerEventInterface* prev_;
};
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 {
public:
TimerWheelSlot() : events_(NULL) {
TimerWheelSlot() {
}
TimerEvent<CBType>* events() { return events_; }
TimerEvent<CBType>* pop_event() {
TimerEventInterface* events() { return events_; }
TimerEventInterface* pop_event() {
auto event = events_;
events_ = event->next_;
if (events_) {
......@@ -103,7 +98,7 @@ public:
event->slot_ = NULL;
return event;
}
void push_event(TimerEvent<CBType>* event) {
void push_event(TimerEventInterface* event) {
event->slot_ = this;
event->next_ = events_;
if (events_) {
......@@ -116,15 +111,14 @@ private:
TimerWheelSlot(const TimerWheelSlot& other) = delete;
TimerWheelSlot& operator=(const TimerWheelSlot& other) = delete;
TimerEvent<CBType>* events_;
TimerEventInterface* events_ = NULL;
};
template<typename CBType>
class HierarchicalTimerWheel {
public:
HierarchicalTimerWheel()
: now_(0),
up_(new HierarchicalTimerWheel<CBType>(WIDTH_BITS, this)),
up_(new HierarchicalTimerWheel(WIDTH_BITS, this)),
down_(NULL) {
}
......@@ -148,7 +142,7 @@ public:
}
}
void schedule(TimerEvent<CBType>* event, Tick delta) {
void schedule(TimerEventInterface* event, Tick delta) {
if (!down_) {
event->set_scheduled_at(now_ + delta);
}
......@@ -170,11 +164,11 @@ private:
: now_(0),
down_(down) {
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;
delta = absolute - now_;
assert(absolute >= now_);
......@@ -196,9 +190,38 @@ private:
static const int WIDTH_BITS = 8;
static const int NUM_SLOTS = 1 << WIDTH_BITS;
static const int MASK = (NUM_SLOTS - 1);
TimerWheelSlot<CBType> slots_[NUM_SLOTS];
HierarchicalTimerWheel<CBType>* up_;
HierarchicalTimerWheel<CBType>* down_;
TimerWheelSlot slots_[NUM_SLOTS];
HierarchicalTimerWheel* up_;
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
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