Commit 85dacab1 authored by Juho Snellman's avatar Juho Snellman

Add more unit tests specifically probing the special nature of wheel slot 0

parent 35bfedfa
......@@ -4,7 +4,37 @@
#include "../timer-wheel.h"
void test_single_timer_no_hierarchy() {
#define TEST(fun) \
do { \
if (fun()) { \
printf("[OK] %s\n", #fun); \
} else { \
ok = false; \
printf("[FAILED] %s\n", #fun); \
} \
} while (0)
#define EXPECT(expr) \
do { \
if (!(expr)) { \
printf("%s:%d: Expect failed: %s\n", \
__FILE__, __LINE__, #expr); \
return false; \
} \
} while (0)
#define EXPECT_INTEQ(actual, expect) \
do { \
if (expect != actual) { \
printf("%s:%d: Expect failed, wanted %d" \
" got %d\n", \
__FILE__, __LINE__, \
expect, actual); \
return false; \
} \
} while (0)
bool test_single_timer_no_hierarchy() {
typedef std::function<void()> Callback;
HierarchicalTimerWheel<Callback> timers;
int count = 0;
......@@ -12,61 +42,107 @@ void test_single_timer_no_hierarchy() {
&timers);
timers.advance(10);
assert(count == 0);
assert(!timer.active());
EXPECT_INTEQ(count, 0);
EXPECT(!timer.active());
timers.schedule(&timer, 5);
assert(timer.active());
EXPECT(timer.active());
timers.advance(10);
assert(count == 1);
EXPECT_INTEQ(count, 1);
timers.advance(10);
assert(count == 1);
EXPECT_INTEQ(count, 1);
timers.schedule(&timer, 5);
timers.advance(10);
assert(count == 2);
EXPECT_INTEQ(count, 2);
timers.schedule(&timer, 5);
timer.cancel();
assert(!timer.active());
EXPECT(!timer.active());
timers.advance(10);
assert(count == 2);
EXPECT_INTEQ(count, 2);
// Test wraparound
timers.advance(250);
timers.schedule(&timer, 5);
timers.advance(10);
assert(count == 3);
EXPECT_INTEQ(count, 3);
return true;
}
void test_single_timer_hierarchy() {
bool test_single_timer_hierarchy() {
typedef std::function<void()> Callback;
HierarchicalTimerWheel<Callback> timers;
int count = 0;
TimerEvent<Callback> timer([&count] () { ++count; },
&timers);
timers.advance(10);
assert(count == 0);
EXPECT_INTEQ(count, 0);
// Schedule timer one layer up.
timers.schedule(&timer, 261);
timers.advance(260);
assert(count == 0);
// Schedule timer one layer up (make sure timer ends up in slot 0 once
// promoted to the innermost wheel, since that's a special case).
timers.schedule(&timer, 256);
timers.advance(255);
EXPECT_INTEQ(count, 0);
timers.advance(1);
assert(count == 1);
EXPECT_INTEQ(count, 1);
timers.schedule(&timer, 256*4);
timers.advance(256*4 - 1);
assert(count == 1);
// Then schedule one that ends up in some other slot
timers.schedule(&timer, 257);
timers.advance(256);
EXPECT_INTEQ(count, 1);
timers.advance(1);
assert(count == 2);
EXPECT_INTEQ(count, 2);
// Schedule multiple rotations ahead in time, to slot 0.
timers.schedule(&timer, 256*4 - 1);
timers.advance(256*4 - 2);
EXPECT_INTEQ(count, 2);
timers.advance(1);
EXPECT_INTEQ(count, 3);
// Schedule multiple rotations ahead in time, to non-0 slot. (Do this
// twice, once starting from slot 0, once starting from slot 5);
for (int i = 0; i < 2; ++i) {
timers.schedule(&timer, 256*4 + 5);
timers.advance(256*4 + 4);
EXPECT_INTEQ(count, 3 + i);
timers.advance(1);
EXPECT_INTEQ(count, 4 + i);
}
return true;
}
bool test_single_timer_random() {
typedef std::function<void()> Callback;
HierarchicalTimerWheel<Callback> timers;
int count = 0;
TimerEvent<Callback> timer([&count] () { ++count; },
&timers);
for (int i = 0; i < 10000; ++i) {
int len = rand() % 20;
int r = 1 + rand() % ( 1 << len);
timers.schedule(&timer, r);
timers.advance(r - 1);
EXPECT_INTEQ(count, i);
timers.advance(1);
EXPECT_INTEQ(count, i + 1);
}
return true;
}
int main(void) {
test_single_timer_no_hierarchy();
test_single_timer_hierarchy();
bool ok = true;
TEST(test_single_timer_no_hierarchy);
TEST(test_single_timer_hierarchy);
TEST(test_single_timer_random);
// Test canceling timer from within timer
// Test rescheduling timer from within timer
return ok ? 0 : 1;
}
......@@ -131,12 +131,12 @@ public:
void advance(Timestamp delta) {
while (delta--) {
now_++;
size_t slot_index = now_ & (NUM_SLOTS - 1);
size_t slot_index = now_ & MASK;
auto slot = &slots_[slot_index];
while (slot->events()) {
auto event = slot->pop_event();
if (down_) {
assert((down_->now_ & (NUM_SLOTS - 1)) == 0);
assert((down_->now_ & MASK) == 0);
down_->schedule_absolute(event, event->scheduled_at());
} else {
event->execute();
......@@ -154,10 +154,10 @@ public:
}
if (delta >= NUM_SLOTS) {
return up_->schedule(event, delta >> WIDTH_BITS);
return up_->schedule(event, (delta + (now_ & MASK)) >> WIDTH_BITS);
}
size_t slot_index = (now_ + delta) & (NUM_SLOTS - 1);
size_t slot_index = (now_ + delta) & MASK;
auto slot = &slots_[slot_index];
slot->push_event(event);
}
......@@ -177,7 +177,7 @@ private:
void schedule_absolute(TimerEvent<CBType>* event, Timestamp absolute) {
Timestamp delta;
delta = absolute - now_;
assert(absolute > now_);
assert(absolute >= now_);
assert(delta < NUM_SLOTS);
if (delta == 0) {
if (!down_) {
......@@ -195,6 +195,7 @@ 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_;
......
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