three-level timer wheel implementation w/ overflow vector
[vpp.git] / src / vppinfra / tw_timer_template.c
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 /** @file
17  *  @brief TW timer implementation TEMPLATE ONLY, do not compile directly
18  *
19  *
20  */
21 static inline u32
22 TW (make_internal_timer_handle) (u32 pool_index, u32 timer_id)
23 {
24   u32 handle;
25
26   ASSERT (timer_id < TW_TIMERS_PER_OBJECT);
27 #if LOG2_TW_TIMERS_PER_OBJECT > 0
28   ASSERT (pool_index < (1 << (32 - LOG2_TW_TIMERS_PER_OBJECT)));
29
30   handle = (timer_id << (32 - LOG2_TW_TIMERS_PER_OBJECT)) | (pool_index);
31 #else
32   handle = pool_index;
33 #endif
34   return handle;
35 }
36
37 static inline void
38 timer_addhead (TWT (tw_timer) * pool, u32 head_index, u32 new_index)
39 {
40   TWT (tw_timer) * head = pool_elt_at_index (pool, head_index);
41   TWT (tw_timer) * old_first;
42   u32 old_first_index;
43   TWT (tw_timer) * new;
44
45   new = pool_elt_at_index (pool, new_index);
46
47   if (PREDICT_FALSE (head->next == head_index))
48     {
49       head->next = head->prev = new_index;
50       new->next = new->prev = head_index;
51       return;
52     }
53
54   old_first_index = head->next;
55   old_first = pool_elt_at_index (pool, old_first_index);
56
57   new->next = old_first_index;
58   new->prev = old_first->prev;
59   old_first->prev = new_index;
60   head->next = new_index;
61 }
62
63 static inline void
64 timer_remove (TWT (tw_timer) * pool, u32 index)
65 {
66   TWT (tw_timer) * elt = pool_elt_at_index (pool, index);
67   TWT (tw_timer) * next_elt, *prev_elt;
68
69   ASSERT (elt->user_handle != ~0);
70
71   next_elt = pool_elt_at_index (pool, elt->next);
72   prev_elt = pool_elt_at_index (pool, elt->prev);
73
74   next_elt->prev = elt->prev;
75   prev_elt->next = elt->next;
76
77   elt->prev = elt->next = ~0;
78 }
79
80 /**
81  * @brief Start a Tw Timer
82  * @param tw_timer_wheel_t * tw timer wheel object pointer
83  * @param u32 pool_index user pool index, presumably for a tw session
84  * @param u32 timer_id app-specific timer ID. 4 bits.
85  * @param u64 interval timer interval in ticks
86  * @returns handle needed to cancel the timer
87  */
88 u32
89 TW (tw_timer_start) (TWT (tw_timer_wheel) * tw, u32 pool_index, u32 timer_id,
90                      u64 interval)
91 {
92 #if TW_TIMER_WHEELS > 1
93   u16 slow_ring_offset;
94   u32 carry;
95 #endif
96 #if TW_TIMER_WHEELS > 2
97   u16 glacier_ring_offset;
98 #endif
99 #if TW_OVERFLOW_VECTOR > 0
100   u64 interval_plus_time_to_wrap, triple_wrap_mask;
101 #endif
102   u16 fast_ring_offset;
103   tw_timer_wheel_slot_t *ts;
104   TWT (tw_timer) * t;
105
106   ASSERT (interval);
107
108   pool_get (tw->timers, t);
109   memset (t, 0xff, sizeof (*t));
110
111   t->user_handle = TW (make_internal_timer_handle) (pool_index, timer_id);
112
113   /* Factor interval into 1..3 wheel offsets */
114 #if TW_TIMER_WHEELS > 2
115 #if TW_OVERFLOW_VECTOR > 0
116   /*
117    * This is tricky. Put a timer onto the overflow
118    * vector if the interval PLUS the time
119    * until the next triple-wrap exceeds one full revolution
120    * of all three wheels.
121    */
122   triple_wrap_mask = (1 << (3 * TW_RING_SHIFT)) - 1;
123   interval_plus_time_to_wrap =
124     interval + (tw->current_tick & triple_wrap_mask);
125   if ((interval_plus_time_to_wrap >= 1 << (3 * TW_RING_SHIFT)))
126     {
127       t->expiration_time = tw->current_tick + interval;
128       ts = &tw->overflow;
129       timer_addhead (tw->timers, ts->head_index, t - tw->timers);
130       return t - tw->timers;
131     }
132 #endif
133
134   glacier_ring_offset = interval >> (2 * TW_RING_SHIFT);
135   ASSERT (glacier_ring_offset < TW_SLOTS_PER_RING);
136   interval -= (glacier_ring_offset << (2 * TW_RING_SHIFT));
137 #endif
138 #if TW_TIMER_WHEELS > 1
139   slow_ring_offset = interval >> TW_RING_SHIFT;
140   ASSERT (slow_ring_offset < TW_SLOTS_PER_RING);
141   interval -= (slow_ring_offset << TW_RING_SHIFT);
142 #endif
143   fast_ring_offset = interval & TW_RING_MASK;
144
145   /*
146    * Account for the current wheel positions(s)
147    * This is made slightly complicated by the fact that the current
148    * index vector will contain (TW_SLOTS_PER_RING, ...) when
149    * the actual position is (0, ...)
150    */
151
152   fast_ring_offset += tw->current_index[TW_TIMER_RING_FAST] & TW_RING_MASK;
153
154 #if TW_TIMER_WHEELS > 1
155   carry = fast_ring_offset >= TW_SLOTS_PER_RING ? 1 : 0;
156   fast_ring_offset %= TW_SLOTS_PER_RING;
157   slow_ring_offset += (tw->current_index[TW_TIMER_RING_SLOW] & TW_RING_MASK)
158     + carry;
159   carry = slow_ring_offset >= TW_SLOTS_PER_RING ? 1 : 0;
160   slow_ring_offset %= TW_SLOTS_PER_RING;
161 #endif
162
163 #if TW_TIMER_WHEELS > 2
164   glacier_ring_offset +=
165     (tw->current_index[TW_TIMER_RING_GLACIER] & TW_RING_MASK) + carry;
166   glacier_ring_offset %= TW_SLOTS_PER_RING;
167 #endif
168
169 #if TW_TIMER_WHEELS > 2
170   if (glacier_ring_offset !=
171       (tw->current_index[TW_TIMER_RING_GLACIER] & TW_RING_MASK))
172     {
173       /* We'll need slow and fast ring offsets later */
174       t->slow_ring_offset = slow_ring_offset;
175       t->fast_ring_offset = fast_ring_offset;
176
177       ts = &tw->w[TW_TIMER_RING_GLACIER][glacier_ring_offset];
178
179       timer_addhead (tw->timers, ts->head_index, t - tw->timers);
180
181       return t - tw->timers;
182     }
183 #endif
184
185 #if TW_TIMER_WHEELS > 1
186   /* Timer expires more than 51.2 seconds from now? */
187   if (slow_ring_offset !=
188       (tw->current_index[TW_TIMER_RING_SLOW] & TW_RING_MASK))
189     {
190       /* We'll need the fast ring offset later... */
191       t->fast_ring_offset = fast_ring_offset;
192
193       ts = &tw->w[TW_TIMER_RING_SLOW][slow_ring_offset];
194
195       timer_addhead (tw->timers, ts->head_index, t - tw->timers);
196
197       return t - tw->timers;
198     }
199 #else
200   fast_ring_offset %= TW_SLOTS_PER_RING;
201 #endif
202
203   /* Timer expires less than one fast-ring revolution from now */
204   ts = &tw->w[TW_TIMER_RING_FAST][fast_ring_offset];
205
206   timer_addhead (tw->timers, ts->head_index, t - tw->timers);
207   return t - tw->timers;
208 }
209
210 #if TW_TIMER_SCAN_FOR_HANDLE > 0
211 int TW (scan_for_handle) (TWT (tw_timer_wheel) * tw, u32 handle)
212 {
213   int i, j;
214   tw_timer_wheel_slot_t *ts;
215   TWT (tw_timer) * t, *head;
216   u32 next_index;
217   int rv = 0;
218
219   for (i = 0; i < TW_TIMER_WHEELS; i++)
220     {
221       for (j = 0; j < TW_SLOTS_PER_RING; j++)
222         {
223           ts = &tw->w[i][j];
224           head = pool_elt_at_index (tw->timers, ts->head_index);
225           next_index = head->next;
226
227           while (next_index != ts->head_index)
228             {
229               t = pool_elt_at_index (tw->timers, next_index);
230               if (next_index == handle)
231                 {
232                   clib_warning ("handle %d found in ring %d slot %d",
233                                 handle, i, j);
234                   clib_warning ("user handle 0x%x", t->user_handle);
235                   rv = 1;
236                 }
237               next_index = t->next;
238             }
239         }
240     }
241   return rv;
242 }
243 #endif /* TW_TIMER_SCAN_FOR_HANDLE */
244
245 /**
246  * @brief Stop a tw timer
247  * @param tw_timer_wheel_t * tw timer wheel object pointer
248  * @param u32 handle timer cancellation returned by tw_timer_start
249  */
250 void TW (tw_timer_stop) (TWT (tw_timer_wheel) * tw, u32 handle)
251 {
252   TWT (tw_timer) * t;
253
254   t = pool_elt_at_index (tw->timers, handle);
255
256   /* in case of idiotic handle (e.g. passing a listhead index) */
257   ASSERT (t->user_handle != ~0);
258
259   timer_remove (tw->timers, handle);
260
261   pool_put_index (tw->timers, handle);
262 }
263
264 /**
265  * @brief Initialize a tw timer wheel template instance
266  * @param tw_timer_wheel_t * tw timer wheel object pointer
267  * @param void * expired_timer_callback. Passed a u32 * vector of
268  *   expired timer handles. The callback is optional.
269  * @param f64 timer_interval_in_seconds
270  */
271 void
272 TW (tw_timer_wheel_init) (TWT (tw_timer_wheel) * tw,
273                           void *expired_timer_callback,
274                           f64 timer_interval_in_seconds, u32 max_expirations)
275 {
276   int ring, slot;
277   tw_timer_wheel_slot_t *ts;
278   TWT (tw_timer) * t;
279   memset (tw, 0, sizeof (*tw));
280   tw->expired_timer_callback = expired_timer_callback;
281   tw->max_expirations = max_expirations;
282   if (timer_interval_in_seconds == 0.0)
283     {
284       clib_warning ("timer interval is zero");
285       abort ();
286     }
287   tw->timer_interval = timer_interval_in_seconds;
288   tw->ticks_per_second = 1.0 / timer_interval_in_seconds;
289   tw->first_expires_tick = ~0ULL;
290   vec_validate (tw->expired_timer_handles, 0);
291   _vec_len (tw->expired_timer_handles) = 0;
292
293   for (ring = 0; ring < TW_TIMER_WHEELS; ring++)
294     {
295       for (slot = 0; slot < TW_SLOTS_PER_RING; slot++)
296         {
297           ts = &tw->w[ring][slot];
298           pool_get (tw->timers, t);
299           memset (t, 0xff, sizeof (*t));
300           t->next = t->prev = t - tw->timers;
301           ts->head_index = t - tw->timers;
302         }
303     }
304
305 #if TW_OVERFLOW_VECTOR > 0
306   ts = &tw->overflow;
307   pool_get (tw->timers, t);
308   memset (t, 0xff, sizeof (*t));
309   t->next = t->prev = t - tw->timers;
310   ts->head_index = t - tw->timers;
311 #endif
312 }
313
314 /**
315  * @brief Free a tw timer wheel template instance
316  * @param tw_timer_wheel_t * tw timer wheel object pointer
317  */
318 void TW (tw_timer_wheel_free) (TWT (tw_timer_wheel) * tw)
319 {
320   int i, j;
321   tw_timer_wheel_slot_t *ts;
322   TWT (tw_timer) * head, *t;
323   u32 next_index;
324
325   for (i = 0; i < TW_TIMER_WHEELS; i++)
326     {
327       for (j = 0; j < TW_SLOTS_PER_RING; j++)
328         {
329           ts = &tw->w[i][j];
330           head = pool_elt_at_index (tw->timers, ts->head_index);
331           next_index = head->next;
332
333           while (next_index != ts->head_index)
334             {
335               t = pool_elt_at_index (tw->timers, next_index);
336               next_index = t->next;
337               pool_put (tw->timers, t);
338             }
339           pool_put (tw->timers, head);
340         }
341     }
342
343 #if TW_OVERFLOW_VECVOR > 0
344   ts = &tw->overflow;
345   head = pool_elt_at_index (tw->timers, ts->head_index);
346   next_index = head->next;
347
348   while (next_index != ts->head_index)
349     {
350       t = pool_elt_at_index (tw->timers, next_index);
351       next_index = t->next;
352       pool_put (tw->timers, t);
353     }
354   pool_put (tw->timers, head);
355 #endif
356
357   memset (tw, 0, sizeof (*tw));
358 }
359
360 /**
361  * @brief Advance a tw timer wheel. Calls the expired timer callback
362  * as needed. This routine should be called once every timer_interval seconds
363  * @param tw_timer_wheel_t * tw timer wheel template instance pointer
364  * @param f64 now the current time, e.g. from vlib_time_now(vm)
365  * @returns u32 * vector of expired user handles
366  */
367 static inline
368   u32 * TW (tw_timer_expire_timers_internal) (TWT (tw_timer_wheel) * tw,
369                                               f64 now,
370                                               u32 * callback_vector_arg)
371 {
372   u32 nticks, i;
373   tw_timer_wheel_slot_t *ts;
374   TWT (tw_timer) * t, *head;
375   u32 *callback_vector;
376   u32 fast_wheel_index;
377   u32 next_index;
378   u32 slow_wheel_index __attribute__ ((unused));
379   u32 glacier_wheel_index __attribute__ ((unused));
380
381   /* Shouldn't happen */
382   if (PREDICT_FALSE (now < tw->next_run_time))
383     return callback_vector_arg;
384
385   /* Number of ticks which have occurred */
386   nticks = tw->ticks_per_second * (now - tw->last_run_time);
387   if (nticks == 0)
388     return callback_vector_arg;
389
390   /* Remember when we ran, compute next runtime */
391   tw->next_run_time = (now + tw->timer_interval);
392
393   if (callback_vector_arg == 0)
394     {
395       _vec_len (tw->expired_timer_handles) = 0;
396       callback_vector = tw->expired_timer_handles;
397     }
398   else
399     callback_vector = callback_vector_arg;
400
401   for (i = 0; i < nticks; i++)
402     {
403       fast_wheel_index = tw->current_index[TW_TIMER_RING_FAST];
404       if (TW_TIMER_WHEELS > 1)
405         slow_wheel_index = tw->current_index[TW_TIMER_RING_SLOW];
406       if (TW_TIMER_WHEELS > 2)
407         glacier_wheel_index = tw->current_index[TW_TIMER_RING_GLACIER];
408
409 #if TW_OVERFLOW_VECTOR > 0
410       /* Triple odometer-click? Process the overflow vector... */
411       if (PREDICT_FALSE (fast_wheel_index == TW_SLOTS_PER_RING
412                          && slow_wheel_index == TW_SLOTS_PER_RING
413                          && glacier_wheel_index == TW_SLOTS_PER_RING))
414         {
415           u64 interval;
416           u32 new_glacier_ring_offset, new_slow_ring_offset;
417           u32 new_fast_ring_offset;
418
419           ts = &tw->overflow;
420           head = pool_elt_at_index (tw->timers, ts->head_index);
421           next_index = head->next;
422
423           /* Make slot empty */
424           head->next = head->prev = ts->head_index;
425
426           /* traverse slot, place timers wherever they go */
427           while (next_index != head - tw->timers)
428             {
429               t = pool_elt_at_index (tw->timers, next_index);
430               next_index = t->next;
431
432               /* Remove from the overflow vector (hammer) */
433               t->next = t->prev = ~0;
434
435               ASSERT (t->expiration_time >= tw->current_tick);
436
437               interval = t->expiration_time - tw->current_tick;
438
439               /* Right back onto the overflow vector? */
440               if (interval >= (1 << (3 * TW_RING_SHIFT)))
441                 {
442                   ts = &tw->overflow;
443                   timer_addhead (tw->timers, ts->head_index, t - tw->timers);
444                   continue;
445                 }
446               /* Compute ring offsets */
447               new_glacier_ring_offset = interval >> (2 * TW_RING_SHIFT);
448
449               interval -= (new_glacier_ring_offset << (2 * TW_RING_SHIFT));
450
451               /* Note: the wheels are at (0,0,0), no add-with-carry needed */
452               new_slow_ring_offset = interval >> TW_RING_SHIFT;
453               interval -= (new_slow_ring_offset << TW_RING_SHIFT);
454               new_fast_ring_offset = interval & TW_RING_MASK;
455               t->slow_ring_offset = new_slow_ring_offset;
456               t->fast_ring_offset = new_fast_ring_offset;
457
458               /* Timer expires Right Now */
459               if (PREDICT_FALSE (t->slow_ring_offset == 0 &&
460                                  t->fast_ring_offset == 0 &&
461                                  new_glacier_ring_offset == 0))
462                 {
463                   vec_add1 (callback_vector, t->user_handle);
464                   pool_put (tw->timers, t);
465                 }
466               /* Timer moves to the glacier ring */
467               else if (new_glacier_ring_offset)
468                 {
469                   ts = &tw->w[TW_TIMER_RING_GLACIER][new_glacier_ring_offset];
470                   timer_addhead (tw->timers, ts->head_index, t - tw->timers);
471                 }
472               /* Timer moves to the slow ring */
473               else if (t->slow_ring_offset)
474                 {
475                   /* Add to slow ring */
476                   ts = &tw->w[TW_TIMER_RING_SLOW][t->slow_ring_offset];
477                   timer_addhead (tw->timers, ts->head_index, t - tw->timers);
478                 }
479               /* Timer timer moves to the fast ring */
480               else
481                 {
482                   ts = &tw->w[TW_TIMER_RING_FAST][t->fast_ring_offset];
483                   timer_addhead (tw->timers, ts->head_index, t - tw->timers);
484                 }
485             }
486         }
487 #endif
488
489 #if TW_TIMER_WHEELS > 2
490       /*
491        * Double odometer-click? Process one slot in the glacier ring...
492        */
493       if (PREDICT_FALSE (fast_wheel_index == TW_SLOTS_PER_RING
494                          && slow_wheel_index == TW_SLOTS_PER_RING))
495         {
496           glacier_wheel_index %= TW_SLOTS_PER_RING;
497           ts = &tw->w[TW_TIMER_RING_GLACIER][glacier_wheel_index];
498
499           head = pool_elt_at_index (tw->timers, ts->head_index);
500           next_index = head->next;
501
502           /* Make slot empty */
503           head->next = head->prev = ts->head_index;
504
505           /* traverse slot, deal timers into slow ring */
506           while (next_index != head - tw->timers)
507             {
508               t = pool_elt_at_index (tw->timers, next_index);
509               next_index = t->next;
510
511               /* Remove from glacier ring slot (hammer) */
512               t->next = t->prev = ~0;
513
514               /* Timer expires Right Now */
515               if (PREDICT_FALSE (t->slow_ring_offset == 0 &&
516                                  t->fast_ring_offset == 0))
517                 {
518                   vec_add1 (callback_vector, t->user_handle);
519                   pool_put (tw->timers, t);
520                 }
521               /* Timer expires during slow-wheel tick 0 */
522               else if (PREDICT_FALSE (t->slow_ring_offset == 0))
523                 {
524                   ts = &tw->w[TW_TIMER_RING_FAST][t->fast_ring_offset];
525                   timer_addhead (tw->timers, ts->head_index, t - tw->timers);
526                 }
527               else              /* typical case */
528                 {
529                   /* Add to slow ring */
530                   ts = &tw->w[TW_TIMER_RING_SLOW][t->slow_ring_offset];
531                   timer_addhead (tw->timers, ts->head_index, t - tw->timers);
532                 }
533             }
534         }
535 #endif
536
537 #if TW_TIMER_WHEELS > 1
538       /*
539        * Single odometer-click? Process a slot in the slow ring,
540        */
541       if (PREDICT_FALSE (fast_wheel_index == TW_SLOTS_PER_RING))
542         {
543           slow_wheel_index %= TW_SLOTS_PER_RING;
544           ts = &tw->w[TW_TIMER_RING_SLOW][slow_wheel_index];
545
546           head = pool_elt_at_index (tw->timers, ts->head_index);
547           next_index = head->next;
548
549           /* Make slot empty */
550           head->next = head->prev = ts->head_index;
551
552           /* traverse slot, deal timers into fast ring */
553           while (next_index != head - tw->timers)
554             {
555               t = pool_elt_at_index (tw->timers, next_index);
556               next_index = t->next;
557
558               /* Remove from sloe ring slot (hammer) */
559               t->next = t->prev = ~0;
560
561               /* Timer expires Right Now */
562               if (PREDICT_FALSE (t->fast_ring_offset == 0))
563                 {
564                   vec_add1 (callback_vector, t->user_handle);
565                   pool_put (tw->timers, t);
566                 }
567               else              /* typical case */
568                 {
569                   /* Add to fast ring */
570                   ts = &tw->w[TW_TIMER_RING_FAST][t->fast_ring_offset];
571                   timer_addhead (tw->timers, ts->head_index, t - tw->timers);
572                 }
573             }
574         }
575 #endif
576
577       /* Handle the fast ring */
578       fast_wheel_index %= TW_SLOTS_PER_RING;
579       ts = &tw->w[TW_TIMER_RING_FAST][fast_wheel_index];
580
581       head = pool_elt_at_index (tw->timers, ts->head_index);
582       next_index = head->next;
583
584       /* Make slot empty */
585       head->next = head->prev = ts->head_index;
586
587       /* Construct vector of expired timer handles to give the user */
588       while (next_index != ts->head_index)
589         {
590           t = pool_elt_at_index (tw->timers, next_index);
591           next_index = t->next;
592           vec_add1 (callback_vector, t->user_handle);
593           pool_put (tw->timers, t);
594         }
595
596       /* If any timers expired, tell the user */
597       if (callback_vector_arg == 0 && vec_len (callback_vector))
598         {
599           /* The callback is optional. We return the u32 * handle vector */
600           if (tw->expired_timer_callback)
601             {
602               tw->expired_timer_callback (callback_vector);
603               _vec_len (callback_vector) = 0;
604             }
605           tw->expired_timer_handles = callback_vector;
606         }
607       tw->current_tick++;
608       fast_wheel_index++;
609       tw->current_index[TW_TIMER_RING_FAST] = fast_wheel_index;
610
611 #if TW_TIMER_WHEELS > 1
612       if (PREDICT_FALSE (fast_wheel_index == TW_SLOTS_PER_RING))
613         slow_wheel_index++;
614       tw->current_index[TW_TIMER_RING_SLOW] = slow_wheel_index;
615 #endif
616
617 #if TW_TIMER_WHEELS > 2
618       if (PREDICT_FALSE (slow_wheel_index == TW_SLOTS_PER_RING))
619         glacier_wheel_index++;
620       tw->current_index[TW_TIMER_RING_GLACIER] = glacier_wheel_index;
621 #endif
622
623       if (vec_len (callback_vector) >= tw->max_expirations)
624         break;
625     }
626
627   if (callback_vector_arg == 0)
628     tw->expired_timer_handles = callback_vector;
629
630   tw->last_run_time += i * tw->timer_interval;
631   return callback_vector;
632 }
633
634 u32 *TW (tw_timer_expire_timers) (TWT (tw_timer_wheel) * tw, f64 now)
635 {
636   return TW (tw_timer_expire_timers_internal) (tw, now, 0 /* no vector */ );
637 }
638
639 u32 *TW (tw_timer_expire_timers_vec) (TWT (tw_timer_wheel) * tw, f64 now,
640                                       u32 * vec)
641 {
642   return TW (tw_timer_expire_timers_internal) (tw, now, vec);
643 }
644
645 /*
646  * fd.io coding-style-patch-verification: ON
647  *
648  * Local Variables:
649  * eval: (c-set-style "gnu")
650  * End:
651  */