TimingWheels

This structure implements scheme 6.2 thom the http://www.cs.columbia.edu/~nahum/w6998/papers/sosp87-timing-wheels.pdf and supports several primitives:

  • schedule timer in the future.
  • cancel timer.
  • time step (advance) - all timers expired at current time tick are extracted from wheels.

Each operation take O(1) time.

Destructor

~this
~this()
Undocumented in source.

Members

Aliases

Ticks
alias Ticks = ulong
Undocumented in source.

Functions

advance
auto advance(ulong ticks)

Adnvance wheel and return all timers expired during wheel turn.

cancel
void cancel(T timer)

Cancel timer

currStdTime
auto currStdTime(Duration tick)

Return internal view on current time - it is time at the call to init plus total number of steps multiplied by tick duration.

init
void init()
Undocumented in source. Be warned that the author may not have intended to support it.
schedule
void schedule(T timer, ulong ticks)

Schedule timer to ticks ticks forward from internal 'now'.

ticksToCatchUp
int ticksToCatchUp(Duration tick, ulong realTime)

Number of ticks to rotate wheels until internal wheel 'now' catch up with real world realTime. Calculation based on time when wheels were stared and total numer of ticks pasded.

timeUntilNextEvent
Duration timeUntilNextEvent(Duration tick, ulong realNow)

Time until next scheduled timer event. You provide tick size and current real world time. This function find ticks until next event and use time of the start and total steps executed to calculate time delta from realNow to next event.

Examples

1 import std;
2 globalLogLevel = LogLevel.info;
3 auto rnd = Random(142);
4 auto Tick = getValue!Duration();
5 /// track execution
6 int  counter;
7 SysTime last;
8 
9 /// this is our Timer
10 class Timer
11 {
12     static ulong __id;
13     private ulong _id;
14     private string _name;
15     this(string name)
16     {
17         _id = __id++;
18         _name = name;
19     }
20     /// must provide id() method
21     ulong id()
22     {
23         return _id;
24     }
25 }
26 
27 enum IOWakeUpInterval = 100; // to simulate random IO wakeups in interval 0 - 100.msecs
28 
29 // each tick span 5 msecs - this is our link with time in reality
30 TimingWheels!Timer w;
31 w.init();
32 auto durationToTicks(Duration d)
33 {
34     // we have to adjust w.now and realtime 'now' before scheduling timer
35     auto real_now = Clock.currStdTime;
36     auto tw_now = w.currStdTime(Tick);
37     auto delay = (real_now - tw_now).hnsecs;
38     return (d + delay)/Tick;
39 }
40 void process_timer(Timer t)
41 {
42     switch(t._name)
43     {
44         case "periodic":
45             if ( last.stdTime == 0)
46             {
47                 // initialize tracking
48                 last = Clock.currTime - 50.msecs;
49             }
50             auto delta = Clock.currTime - last;
51             assert(delta - 50.msecs <= max(Tick + Tick/20, 5.msecs), "delta-50.msecs=%s".format(delta-50.msecs));
52             writefln("@ %s - delta: %sms (should be 50ms)", t._name, (Clock.currTime - last).split!"msecs".msecs);
53             last = Clock.currTime;
54             counter++;
55             w.schedule(t, durationToTicks(50.msecs)); // rearm
56             break;
57         default:
58             writefln("@ %s", t._name);
59             break;
60     }
61 }
62 // emulate some random initial delay
63 auto randomInitialDelay = uniform(0, 500, rnd).msecs;
64 Thread.sleep(randomInitialDelay);
65 //
66 // start one arbitrary timer and one periodic timer
67 //
68 auto some_timer = new Timer("some");
69 auto periodic_timer = new Timer("periodic");
70 w.schedule(some_timer, durationToTicks(32.msecs));
71 w.schedule(periodic_timer, durationToTicks(50.msecs));
72 
73 while(counter < 10)
74 {
75     auto realNow = Clock.currStdTime;
76     auto randomIoInterval = uniform(0, IOWakeUpInterval, rnd).msecs;
77     auto nextTimerEvent = max(w.timeUntilNextEvent(Tick, realNow), 0.msecs);
78     // wait for what should happen earlier
79     auto time_to_sleep = min(randomIoInterval, nextTimerEvent);
80     writefln("* sleep until timer event or random I/O for %s", time_to_sleep);
81     Thread.sleep(time_to_sleep);
82     // make steps if required
83     int ticks = w.ticksToCatchUp(Tick, Clock.currStdTime);
84     if (ticks > 0)
85     {
86         auto wr = w.advance(ticks);
87         foreach(t; wr.timers)
88         {
89             process_timer(t);
90         }
91     }
92     // emulate some random processing time
93     Thread.sleep(uniform(0, 5, rnd).msecs);
94 }

Meta