| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576 |
- Cascading timer design.
- Inspired by the Linux kernel approach, documented roughly at:
- https://lwn.net/Articles/152436/
- For easy description, we use whole seconds and powers of 10: in the
- implementation, we use powers of 2 (eg. 256 entries) and smaller
- granularities.
- We start with a simple data structure:
- struct timer_level {
- struct timer_level *next;
- /* Ten buckets: [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] */
- struct list_head bucket[10];
- };
- struct timers {
- /* We can never have a timer before this, aka "now". */
- time_t offset;
- struct timer_level *level;
- /* Anything too far in the future. */
- struct list_head far;
- }
- The first level of timers holds anything which will happen in the next
- 10 seconds. The next level holds things which will happen in the next
- 100 seconds. And so on.
- When we want to add a new timer into the structure, we need to figure
- out first what level it goes into, and second, which bucket. Say our
- offset is 500,000,001 (about Tue Nov 5, 1985 in Unix time). And our
- timer is set to go off in 5 seconds, ie. 500,000,006.
- The level is easy: the difference between the timer and the offset is
- 5, and that's less than 10, so it's in the first level. The position,
- however, depends on the absolute time, in this case the last digit 6,
- so it's in bucket 6.
- Adding a timer at 500,000,123? The difference is > 100 and < 1000, so
- it's in the third level. The bucket is 1. If there's no third level,
- we just add it to the 'far' list for stuff which is in the far future.
- Deleting a timer is as simple as removing it; there is no external
- bookkeeping in this scheme. This matters, since timers used for
- timeouts are almost always deleted before they expire.
- Now, when a second passes, we need to know if there are any timers
- which are due. We increment the offset to 500,000,002, and look in
- the first level, bucket 2 for any timers, so lookup is simple.
- We do this eight more times, and we increment the offset to
- 500,000,010. We've swept around back to bucket 0, though it may not
- be empty if we added more timers as we were going.
- But we need to look into the next level since a timer at 500,000,010
- added when the offset was 500,000,000 would have gone up there. We
- empty bucket 1 (due to the '1' in 500,000,010) into these buckets,
- which will contain timers between 500,000,010 and 500,000,019, which
- all now are less than 10 seconds away, so belong in the bottom level.
- Similarly, at 500,000,020 we will empty bucket 1 of the second level
- into the first level. And at 500,000,100 we will empty bucket 1 of
- the third level into the second level then bucket 0 of the second
- level into the first level. We do it in this order, since emptying
- bucket 1 on the third level (500,000,100 - 500,000,199) may put more
- entries (500,000,100 - 500,000,109) into bucket 0 on the second level.
- When we get to 500,001,000 we should empty the fourth level. If there
- is no fourth level, that's when we sort through the 'far' list and
- empty any which are less than 500,002,000. If there are many entries
- in the far list, we should add more levels to reduce the number, or at
- least the frequency we have to check it.
|