LCOV - code coverage report
Current view: top level - ext/timeouts - timeout.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 130 135 96.3 %
Date: 2021-11-24 03:28:48 Functions: 17 17 100.0 %

          Line data    Source code
       1             : /* ==========================================================================
       2             :  * timeout.c - Tickless hierarchical timing wheel.
       3             :  * --------------------------------------------------------------------------
       4             :  * Copyright (c) 2013, 2014  William Ahern
       5             :  *
       6             :  * Permission is hereby granted, free of charge, to any person obtaining a
       7             :  * copy of this software and associated documentation files (the
       8             :  * "Software"), to deal in the Software without restriction, including
       9             :  * without limitation the rights to use, copy, modify, merge, publish,
      10             :  * distribute, sublicense, and/or sell copies of the Software, and to permit
      11             :  * persons to whom the Software is furnished to do so, subject to the
      12             :  * following conditions:
      13             :  *
      14             :  * The above copyright notice and this permission notice shall be included
      15             :  * in all copies or substantial portions of the Software.
      16             :  *
      17             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      18             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      19             :  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
      20             :  * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
      21             :  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
      22             :  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
      23             :  * USE OR OTHER DEALINGS IN THE SOFTWARE.
      24             :  * ==========================================================================
      25             :  */
      26             : #ifdef HAVE_CONFIG_H
      27             : #include "orconfig.h"
      28             : #endif
      29             : #include <limits.h>    /* CHAR_BIT */
      30             : 
      31             : #include <stddef.h>    /* NULL */
      32             : #include <stdlib.h>    /* malloc(3) free(3) */
      33             : #include <stdio.h>     /* FILE fprintf(3) */
      34             : 
      35             : #include <inttypes.h>  /* UINT64_C uint64_t */
      36             : 
      37             : #include <string.h>    /* memset(3) */
      38             : 
      39             : #include <errno.h>     /* errno */
      40             : 
      41             : #include "ext/tor_queue.h" /* TAILQ(3) */
      42             : 
      43             : #include "ext/timeouts/timeout.h"
      44             : 
      45             : #ifndef TIMEOUT_DEBUG
      46             : #define TIMEOUT_DEBUG 0
      47             : #endif
      48             : 
      49             : #if TIMEOUT_DEBUG - 0
      50             : #include "ext/timeouts/timeout-debug.h"
      51             : #endif
      52             : 
      53             : #ifdef TIMEOUT_DISABLE_RELATIVE_ACCESS
      54             : #define TO_SET_TIMEOUTS(to, T) ((void)0)
      55             : #else
      56             : #define TO_SET_TIMEOUTS(to, T) ((to)->timeouts = (T))
      57             : #endif
      58             : 
      59             : /*
      60             :  * A N C I L L A R Y  R O U T I N E S
      61             :  *
      62             :  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
      63             : 
      64             : #define abstime_t timeout_t /* for documentation purposes */
      65             : #define reltime_t timeout_t /* "" */
      66             : 
      67             : #if !defined countof
      68             : #define countof(a) (sizeof (a) / sizeof *(a))
      69             : #endif
      70             : 
      71             : #if !defined endof
      72             : #define endof(a) (&(a)[countof(a)])
      73             : #endif
      74             : 
      75             : #if !defined MIN
      76             : #define MIN(a, b) (((a) < (b))? (a) : (b))
      77             : #endif
      78             : 
      79             : #if !defined MAX
      80             : #define MAX(a, b) (((a) > (b))? (a) : (b))
      81             : #endif
      82             : 
      83             : #if !defined TOR_TAILQ_CONCAT
      84             : #define TOR_TAILQ_CONCAT(head1, head2, field) do {                      \
      85             :         if (!TOR_TAILQ_EMPTY(head2)) {                                  \
      86             :                 *(head1)->tqh_last = (head2)->tqh_first;                \
      87             :                 (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
      88             :                 (head1)->tqh_last = (head2)->tqh_last;                  \
      89             :                 TOR_TAILQ_INIT((head2));                                \
      90             :         }                                                               \
      91             : } while (0)
      92             : #endif
      93             : 
      94             : #if !defined TOR_TAILQ_FOREACH_SAFE
      95             : #define TOR_TAILQ_FOREACH_SAFE(var, head, field, tvar)                      \
      96             :         for ((var) = TOR_TAILQ_FIRST(head);                                 \
      97             :             (var) && ((tvar) = TOR_TAILQ_NEXT(var, field), 1);              \
      98             :             (var) = (tvar))
      99             : #endif
     100             : 
     101             : 
     102             : /*
     103             :  * B I T  M A N I P U L A T I O N  R O U T I N E S
     104             :  *
     105             :  * The macros and routines below implement wheel parameterization. The
     106             :  * inputs are:
     107             :  *
     108             :  *   WHEEL_BIT - The number of value bits mapped in each wheel. The
     109             :  *               lowest-order WHEEL_BIT bits index the lowest-order (highest
     110             :  *               resolution) wheel, the next group of WHEEL_BIT bits the
     111             :  *               higher wheel, etc.
     112             :  *
     113             :  *   WHEEL_NUM - The number of wheels. WHEEL_BIT * WHEEL_NUM = the number of
     114             :  *               value bits used by all the wheels. For the default of 6 and
     115             :  *               4, only the low 24 bits are processed. Any timeout value
     116             :  *               larger than this will cycle through again.
     117             :  *
     118             :  * The implementation uses bit fields to remember which slot in each wheel
     119             :  * is populated, and to generate masks of expiring slots according to the
     120             :  * current update interval (i.e. the "tickless" aspect). The slots to
     121             :  * process in a wheel are (populated-set & interval-mask).
     122             :  *
     123             :  * WHEEL_BIT cannot be larger than 6 bits because 2^6 -> 64 is the largest
     124             :  * number of slots which can be tracked in a uint64_t integer bit field.
     125             :  * WHEEL_BIT cannot be smaller than 3 bits because of our rotr and rotl
     126             :  * routines, which only operate on all the value bits in an integer, and
     127             :  * there's no integer smaller than uint8_t.
     128             :  *
     129             :  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
     130             : 
     131             : #if !defined WHEEL_BIT
     132             : #define WHEEL_BIT 6
     133             : #endif
     134             : 
     135             : #if !defined WHEEL_NUM
     136             : #define WHEEL_NUM 4
     137             : #endif
     138             : 
     139             : #define WHEEL_LEN (1U << WHEEL_BIT)
     140             : #define WHEEL_MAX (WHEEL_LEN - 1)
     141             : #define WHEEL_MASK (WHEEL_LEN - 1)
     142             : #define TIMEOUT_MAX ((TIMEOUT_C(1) << (WHEEL_BIT * WHEEL_NUM)) - 1)
     143             : 
     144             : #include "ext/timeouts/timeout-bitops.c"
     145             : 
     146             : #if WHEEL_BIT == 6
     147             : #define ctz(n) ctz64(n)
     148             : #define clz(n) clz64(n)
     149             : #define fls(n) ((int)(64 - clz64(n)))
     150             : #else
     151             : #define ctz(n) ctz32(n)
     152             : #define clz(n) clz32(n)
     153             : #define fls(n) ((int)(32 - clz32((uint32_t)n)))
     154             : #endif
     155             : 
     156             : #if WHEEL_BIT == 6
     157             : #define WHEEL_C(n) UINT64_C(n)
     158             : #define WHEEL_PRIu PRIu64
     159             : #define WHEEL_PRIx PRIx64
     160             : 
     161             : typedef uint64_t wheel_t;
     162             : 
     163             : #elif WHEEL_BIT == 5
     164             : 
     165             : #define WHEEL_C(n) UINT32_C(n)
     166             : #define WHEEL_PRIu PRIu32
     167             : #define WHEEL_PRIx PRIx32
     168             : 
     169             : typedef uint32_t wheel_t;
     170             : 
     171             : #elif WHEEL_BIT == 4
     172             : 
     173             : #define WHEEL_C(n) UINT16_C(n)
     174             : #define WHEEL_PRIu PRIu16
     175             : #define WHEEL_PRIx PRIx16
     176             : 
     177             : typedef uint16_t wheel_t;
     178             : 
     179             : #elif WHEEL_BIT == 3
     180             : 
     181             : #define WHEEL_C(n) UINT8_C(n)
     182             : #define WHEEL_PRIu PRIu8
     183             : #define WHEEL_PRIx PRIx8
     184             : 
     185             : typedef uint8_t wheel_t;
     186             : 
     187             : #else
     188             : #error invalid WHEEL_BIT value
     189             : #endif
     190             : 
     191             : 
     192       12306 : static inline wheel_t rotl(const wheel_t v, int c) {
     193       12306 :         if (!(c &= (sizeof v * CHAR_BIT - 1)))
     194             :                 return v;
     195             : 
     196       10423 :         return (v << c) | (v >> (sizeof v * CHAR_BIT - c));
     197             : } /* rotl() */
     198             : 
     199             : 
     200       15375 : static inline wheel_t rotr(const wheel_t v, int c) {
     201       15375 :         if (!(c &= (sizeof v * CHAR_BIT - 1)))
     202             :                 return v;
     203             : 
     204        7568 :         return (v >> c) | (v << (sizeof v * CHAR_BIT - c));
     205             : } /* rotr() */
     206             : 
     207             : 
     208             : /*
     209             :  * T I M E R  R O U T I N E S
     210             :  *
     211             :  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
     212             : 
     213             : TOR_TAILQ_HEAD(timeout_list, timeout);
     214             : 
     215             : struct timeouts {
     216             :         struct timeout_list wheel[WHEEL_NUM][WHEEL_LEN], expired;
     217             : 
     218             :         wheel_t pending[WHEEL_NUM];
     219             : 
     220             :         timeout_t curtime;
     221             :         timeout_t hertz;
     222             : }; /* struct timeouts */
     223             : 
     224             : 
     225          15 : static struct timeouts *timeouts_init(struct timeouts *T, timeout_t hz) {
     226          15 :         unsigned i, j;
     227             : 
     228          90 :         for (i = 0; i < countof(T->wheel); i++) {
     229        4875 :                 for (j = 0; j < countof(T->wheel[i]); j++) {
     230        4800 :                         TOR_TAILQ_INIT(&T->wheel[i][j]);
     231             :                 }
     232             :         }
     233             : 
     234          15 :         TOR_TAILQ_INIT(&T->expired);
     235             : 
     236          90 :         for (i = 0; i < countof(T->pending); i++) {
     237          75 :                 T->pending[i] = 0;
     238             :         }
     239             : 
     240          15 :         T->curtime = 0;
     241          15 :         T->hertz = (hz)? hz : TIMEOUT_mHZ;
     242             : 
     243          15 :         return T;
     244             : } /* timeouts_init() */
     245             : 
     246             : 
     247          15 : TIMEOUT_PUBLIC struct timeouts *timeouts_open(timeout_t hz, int *error) {
     248          15 :         struct timeouts *T;
     249             : 
     250          15 :         if ((T = malloc(sizeof *T)))
     251          15 :                 return timeouts_init(T, hz);
     252             : 
     253           0 :         *error = errno;
     254             : 
     255           0 :         return NULL;
     256             : } /* timeouts_open() */
     257             : 
     258             : 
     259           9 : static void timeouts_reset(struct timeouts *T) {
     260           9 :         struct timeout_list reset;
     261           9 :         struct timeout *to;
     262           9 :         unsigned i, j;
     263             : 
     264           9 :         TOR_TAILQ_INIT(&reset);
     265             : 
     266          54 :         for (i = 0; i < countof(T->wheel); i++) {
     267        2925 :                 for (j = 0; j < countof(T->wheel[i]); j++) {
     268        2880 :                         TOR_TAILQ_CONCAT(&reset, &T->wheel[i][j], tqe);
     269             :                 }
     270             :         }
     271             : 
     272           9 :         TOR_TAILQ_CONCAT(&reset, &T->expired, tqe);
     273             : 
     274           9 :         TOR_TAILQ_FOREACH(to, &reset, tqe) {
     275           0 :                 to->pending = NULL;
     276           0 :                 TO_SET_TIMEOUTS(to, NULL);
     277             :         }
     278           9 : } /* timeouts_reset() */
     279             : 
     280             : 
     281           9 : TIMEOUT_PUBLIC void timeouts_close(struct timeouts *T) {
     282             :         /*
     283             :          * NOTE: Delete installed timeouts so timeout_pending() and
     284             :          * timeout_expired() worked as expected.
     285             :          */
     286           9 :         timeouts_reset(T);
     287             : 
     288           9 :         free(T);
     289           9 : } /* timeouts_close() */
     290             : 
     291             : 
     292             : TIMEOUT_PUBLIC timeout_t timeouts_hz(struct timeouts *T) {
     293             :         return T->hertz;
     294             : } /* timeouts_hz() */
     295             : 
     296             : 
     297        9324 : TIMEOUT_PUBLIC void timeouts_del(struct timeouts *T, struct timeout *to) {
     298        9324 :         if (to->pending) {
     299          29 :                 TOR_TAILQ_REMOVE(to->pending, to, tqe);
     300             : 
     301          29 :                 if (to->pending != &T->expired && TOR_TAILQ_EMPTY(to->pending)) {
     302          24 :                         ptrdiff_t index_ = to->pending - &T->wheel[0][0];
     303          24 :                         int wheel = (int) (index_ / WHEEL_LEN);
     304          24 :                         int slot = index_ % WHEEL_LEN;
     305             : 
     306          24 :                         T->pending[wheel] &= ~(WHEEL_C(1) << slot);
     307             :                 }
     308             : 
     309          29 :                 to->pending = NULL;
     310        9324 :                 TO_SET_TIMEOUTS(to, NULL);
     311             :         }
     312        9324 : } /* timeouts_del() */
     313             : 
     314             : 
     315        7003 : static inline reltime_t timeout_rem(struct timeouts *T, struct timeout *to) {
     316        7003 :         return to->expires - T->curtime;
     317             : } /* timeout_rem() */
     318             : 
     319             : 
     320        7003 : static inline int timeout_wheel(timeout_t timeout) {
     321             :         /* must be called with timeout != 0, so fls input is nonzero */
     322        7003 :         return (fls(MIN(timeout, TIMEOUT_MAX)) - 1) / WHEEL_BIT;
     323             : } /* timeout_wheel() */
     324             : 
     325             : 
     326        7003 : static inline int timeout_slot(int wheel, timeout_t expires) {
     327        7003 :         return WHEEL_MASK & ((expires >> (wheel * WHEEL_BIT)) - !!wheel);
     328             : } /* timeout_slot() */
     329             : 
     330             : 
     331        8208 : static void timeouts_sched(struct timeouts *T, struct timeout *to, timeout_t expires) {
     332        8208 :         timeout_t rem;
     333        8208 :         int wheel, slot;
     334             : 
     335        8208 :         timeouts_del(T, to);
     336             : 
     337        8208 :         to->expires = expires;
     338             : 
     339        8208 :         TO_SET_TIMEOUTS(to, T);
     340             : 
     341        8208 :         if (expires > T->curtime) {
     342        7003 :                 rem = timeout_rem(T, to);
     343             : 
     344             :                 /* rem is nonzero since:
     345             :                  *   rem == timeout_rem(T,to),
     346             :                  *       == to->expires - T->curtime
     347             :                  *   and above we have expires > T->curtime.
     348             :                  */
     349        7003 :                 wheel = timeout_wheel(rem);
     350        7003 :                 slot = timeout_slot(wheel, to->expires);
     351             : 
     352        7003 :                 to->pending = &T->wheel[wheel][slot];
     353        7003 :                 TOR_TAILQ_INSERT_TAIL(to->pending, to, tqe);
     354             : 
     355        7003 :                 T->pending[wheel] |= WHEEL_C(1) << slot;
     356             :         } else {
     357        1205 :                 to->pending = &T->expired;
     358        1205 :                 TOR_TAILQ_INSERT_TAIL(to->pending, to, tqe);
     359             :         }
     360        8208 : } /* timeouts_sched() */
     361             : 
     362             : 
     363             : #ifndef TIMEOUT_DISABLE_INTERVALS
     364             : static void timeouts_readd(struct timeouts *T, struct timeout *to) {
     365             :         to->expires += to->interval;
     366             : 
     367             :         if (to->expires <= T->curtime) {
     368             :                 /* If we've missed the next firing of this timeout, reschedule
     369             :                  * it to occur at the next multiple of its interval after
     370             :                  * the last time that it fired.
     371             :                  */
     372             :                 timeout_t n = T->curtime - to->expires;
     373             :                 timeout_t r = n % to->interval;
     374             :                 to->expires = T->curtime + (to->interval - r);
     375             :         }
     376             : 
     377             :         timeouts_sched(T, to, to->expires);
     378             : } /* timeouts_readd() */
     379             : #endif
     380             : 
     381             : 
     382        1234 : TIMEOUT_PUBLIC void timeouts_add(struct timeouts *T, struct timeout *to, timeout_t timeout) {
     383             : #ifndef TIMEOUT_DISABLE_INTERVALS
     384             :         if (to->flags & TIMEOUT_INT)
     385             :                 to->interval = MAX(1, timeout);
     386             : #endif
     387             : 
     388        1234 :         if (to->flags & TIMEOUT_ABS)
     389           0 :                 timeouts_sched(T, to, timeout);
     390             :         else
     391        1234 :                 timeouts_sched(T, to, T->curtime + timeout);
     392        1234 : } /* timeouts_add() */
     393             : 
     394             : 
     395        4997 : TIMEOUT_PUBLIC void timeouts_update(struct timeouts *T, abstime_t curtime) {
     396        4997 :         timeout_t elapsed = curtime - T->curtime;
     397        4997 :         struct timeout_list todo;
     398        4997 :         int wheel;
     399             : 
     400        4997 :         TOR_TAILQ_INIT(&todo);
     401             : 
     402             :         /*
     403             :          * There's no avoiding looping over every wheel. It's best to keep
     404             :          * WHEEL_NUM smallish.
     405             :          */
     406        7137 :         for (wheel = 0; wheel < WHEEL_NUM; wheel++) {
     407        6962 :                 wheel_t pending;
     408             : 
     409             :                 /*
     410             :                  * Calculate the slots expiring in this wheel
     411             :                  *
     412             :                  * If the elapsed time is greater than the maximum period of
     413             :                  * the wheel, mark every position as expiring.
     414             :                  *
     415             :                  * Otherwise, to determine the expired slots fill in all the
     416             :                  * bits between the last slot processed and the current
     417             :                  * slot, inclusive of the last slot. We'll bitwise-AND this
     418             :                  * with our pending set below.
     419             :                  *
     420             :                  * If a wheel rolls over, force a tick of the next higher
     421             :                  * wheel.
     422             :                  */
     423        6962 :                 if ((elapsed >> (wheel * WHEEL_BIT)) > WHEEL_MAX) {
     424        6962 :                         pending = (wheel_t)~WHEEL_C(0);
     425             :                 } else {
     426        6153 :                         wheel_t _elapsed = WHEEL_MASK & (elapsed >> (wheel * WHEEL_BIT));
     427        6153 :                         int oslot, nslot;
     428             : 
     429             :                         /*
     430             :                          * TODO: It's likely that at least one of the
     431             :                          * following three bit fill operations is redundant
     432             :                          * or can be replaced with a simpler operation.
     433             :                          */
     434        6153 :                         oslot = WHEEL_MASK & (T->curtime >> (wheel * WHEEL_BIT));
     435        6153 :                         pending = rotl(((WHEEL_C(1) << _elapsed) - 1), oslot);
     436             : 
     437        6153 :                         nslot = WHEEL_MASK & (curtime >> (wheel * WHEEL_BIT));
     438        6153 :                         pending |= rotr(rotl(((WHEEL_C(1) << _elapsed) - 1), nslot), (int)_elapsed);
     439        6153 :                         pending |= WHEEL_C(1) << nslot;
     440             :                 }
     441             : 
     442        9338 :                 while (pending & T->pending[wheel]) {
     443             :                         /* ctz input cannot be zero: loop condition. */
     444        2376 :                         int slot = ctz(pending & T->pending[wheel]);
     445        2376 :                         TOR_TAILQ_CONCAT(&todo, &T->wheel[wheel][slot], tqe);
     446        2376 :                         T->pending[wheel] &= ~(UINT64_C(1) << slot);
     447             :                 }
     448             : 
     449        6962 :                 if (!(0x1 & pending))
     450             :                         break; /* break if we didn't wrap around end of wheel */
     451             : 
     452             :                 /* if we're continuing, the next wheel must tick at least once */
     453        2140 :                 elapsed = MAX(elapsed, (WHEEL_LEN << (wheel * WHEEL_BIT)));
     454             :         }
     455             : 
     456        4997 :         T->curtime = curtime;
     457             : 
     458       11971 :         while (!TOR_TAILQ_EMPTY(&todo)) {
     459        6974 :                 struct timeout *to = TOR_TAILQ_FIRST(&todo);
     460             : 
     461        6974 :                 TOR_TAILQ_REMOVE(&todo, to, tqe);
     462        6974 :                 to->pending = NULL;
     463             : 
     464        6974 :                 timeouts_sched(T, to, to->expires);
     465             :         }
     466             : 
     467        4997 :         return;
     468             : } /* timeouts_update() */
     469             : 
     470             : TIMEOUT_PUBLIC timeout_t timeouts_get_curtime(struct timeouts *T) {
     471             :         return T->curtime;
     472             : } /* timeouts_get_curtime() */
     473             : 
     474             : TIMEOUT_PUBLIC void timeouts_step(struct timeouts *T, reltime_t elapsed) {
     475             :         timeouts_update(T, T->curtime + elapsed);
     476             : } /* timeouts_step() */
     477             : 
     478             : 
     479             : TIMEOUT_PUBLIC bool timeouts_pending(struct timeouts *T) {
     480             :         wheel_t pending = 0;
     481             :         int wheel;
     482             : 
     483             :         for (wheel = 0; wheel < WHEEL_NUM; wheel++) {
     484             :                 pending |= T->pending[wheel];
     485             :         }
     486             : 
     487             :         return !!pending;
     488             : } /* timeouts_pending() */
     489             : 
     490             : 
     491             : TIMEOUT_PUBLIC bool timeouts_expired(struct timeouts *T) {
     492             :         return !TOR_TAILQ_EMPTY(&T->expired);
     493             : } /* timeouts_expired() */
     494             : 
     495             : 
     496             : /*
     497             :  * Calculate the interval before needing to process any timeouts pending on
     498             :  * any wheel.
     499             :  *
     500             :  * (This is separated from the public API routine so we can evaluate our
     501             :  * wheel invariant assertions irrespective of the expired queue.)
     502             :  *
     503             :  * This might return a timeout value sooner than any installed timeout if
     504             :  * only higher-order wheels have timeouts pending. We can only know when to
     505             :  * process a wheel, not precisely when a timeout is scheduled. Our timeout
     506             :  * accuracy could be off by 2^(N*M)-1 units where N is the wheel number and
     507             :  * M is WHEEL_BIT. Only timeouts which have fallen through to wheel 0 can be
     508             :  * known exactly.
     509             :  *
     510             :  * We should never return a timeout larger than the lowest actual timeout.
     511             :  */
     512        3053 : static timeout_t timeouts_int(struct timeouts *T) {
     513        3053 :         timeout_t timeout = ~TIMEOUT_C(0), _timeout;
     514        3053 :         timeout_t relmask;
     515        3053 :         int wheel, slot;
     516             : 
     517        3053 :         relmask = 0;
     518             : 
     519       18318 :         for (wheel = 0; wheel < WHEEL_NUM; wheel++) {
     520       15265 :                 if (T->pending[wheel]) {
     521        9222 :                         slot = WHEEL_MASK & (T->curtime >> (wheel * WHEEL_BIT));
     522             : 
     523             :                         /* ctz input cannot be zero: T->pending[wheel] is
     524             :                          * nonzero, so rotr() is nonzero. */
     525        9222 :                         _timeout = (ctz(rotr(T->pending[wheel], slot)) + !!wheel) << (wheel * WHEEL_BIT);
     526             :                         /* +1 to higher order wheels as those timeouts are one rotation in the future (otherwise they'd be on a lower wheel or expired) */
     527             : 
     528        9222 :                         _timeout -= relmask & T->curtime;
     529             :                         /* reduce by how much lower wheels have progressed */
     530             : 
     531        9222 :                         timeout = MIN(_timeout, timeout);
     532             :                 }
     533             : 
     534       15265 :                 relmask <<= WHEEL_BIT;
     535       15265 :                 relmask |= WHEEL_MASK;
     536             :         }
     537             : 
     538        3053 :         return timeout;
     539             : } /* timeouts_int() */
     540             : 
     541             : 
     542             : /*
     543             :  * Calculate the interval our caller can wait before needing to process
     544             :  * events.
     545             :  */
     546        3053 : TIMEOUT_PUBLIC timeout_t timeouts_timeout(struct timeouts *T) {
     547        3053 :         if (!TOR_TAILQ_EMPTY(&T->expired))
     548             :                 return 0;
     549             : 
     550        3053 :         return timeouts_int(T);
     551             : } /* timeouts_timeout() */
     552             : 
     553             : 
     554        3149 : TIMEOUT_PUBLIC struct timeout *timeouts_get(struct timeouts *T) {
     555        3149 :         if (!TOR_TAILQ_EMPTY(&T->expired)) {
     556        1205 :                 struct timeout *to = TOR_TAILQ_FIRST(&T->expired);
     557             : 
     558        1205 :                 TOR_TAILQ_REMOVE(&T->expired, to, tqe);
     559        1205 :                 to->pending = NULL;
     560        1205 :                 TO_SET_TIMEOUTS(to, NULL);
     561             : 
     562             : #ifndef TIMEOUT_DISABLE_INTERVALS
     563             :                 if ((to->flags & TIMEOUT_INT) && to->interval > 0)
     564             :                         timeouts_readd(T, to);
     565             : #endif
     566             : 
     567        1205 :                 return to;
     568             :         } else {
     569             :                 return 0;
     570             :         }
     571             : } /* timeouts_get() */
     572             : 
     573             : 
     574             : /*
     575             :  * Use dumb looping to locate the earliest timeout pending on the wheel so
     576             :  * our invariant assertions can check the result of our optimized code.
     577             :  */
     578             : static struct timeout *timeouts_min(struct timeouts *T) {
     579             :         struct timeout *to, *min = NULL;
     580             :         unsigned i, j;
     581             : 
     582             :         for (i = 0; i < countof(T->wheel); i++) {
     583             :                 for (j = 0; j < countof(T->wheel[i]); j++) {
     584             :                         TOR_TAILQ_FOREACH(to, &T->wheel[i][j], tqe) {
     585             :                                 if (!min || to->expires < min->expires)
     586             :                                         min = to;
     587             :                         }
     588             :                 }
     589             :         }
     590             : 
     591             :         return min;
     592             : } /* timeouts_min() */
     593             : 
     594             : 
     595             : /*
     596             :  * Check some basic algorithm invariants. If these invariants fail then
     597             :  * something is definitely broken.
     598             :  */
     599             : #define report(...) do { \
     600             :         if ((fp)) \
     601             :                 fprintf(fp, __VA_ARGS__); \
     602             : } while (0)
     603             : 
     604             : #define check(expr, ...) do { \
     605             :         if (!(expr)) { \
     606             :                 report(__VA_ARGS__); \
     607             :                 return 0; \
     608             :         } \
     609             : } while (0)
     610             : 
     611             : TIMEOUT_PUBLIC bool timeouts_check(struct timeouts *T, FILE *fp) {
     612             :         timeout_t timeout;
     613             :         struct timeout *to;
     614             : 
     615             :         if ((to = timeouts_min(T))) {
     616             :                 check(to->expires > T->curtime, "missed timeout (expires:%" TIMEOUT_PRIu " <= curtime:%" TIMEOUT_PRIu ")\n", to->expires, T->curtime);
     617             : 
     618             :                 timeout = timeouts_int(T);
     619             :                 check(timeout <= to->expires - T->curtime, "wrong soft timeout (soft:%" TIMEOUT_PRIu " > hard:%" TIMEOUT_PRIu ") (expires:%" TIMEOUT_PRIu "; curtime:%" TIMEOUT_PRIu ")\n", timeout, (to->expires - T->curtime), to->expires, T->curtime);
     620             : 
     621             :                 timeout = timeouts_timeout(T);
     622             :                 check(timeout <= to->expires - T->curtime, "wrong soft timeout (soft:%" TIMEOUT_PRIu " > hard:%" TIMEOUT_PRIu ") (expires:%" TIMEOUT_PRIu "; curtime:%" TIMEOUT_PRIu ")\n", timeout, (to->expires - T->curtime), to->expires, T->curtime);
     623             :         } else {
     624             :                 timeout = timeouts_timeout(T);
     625             : 
     626             :                 if (!TOR_TAILQ_EMPTY(&T->expired))
     627             :                         check(timeout == 0, "wrong soft timeout (soft:%" TIMEOUT_PRIu " != hard:%" TIMEOUT_PRIu ")\n", timeout, TIMEOUT_C(0));
     628             :                 else
     629             :                         check(timeout == ~TIMEOUT_C(0), "wrong soft timeout (soft:%" TIMEOUT_PRIu " != hard:%" TIMEOUT_PRIu ")\n", timeout, ~TIMEOUT_C(0));
     630             :         }
     631             : 
     632             :         return 1;
     633             : } /* timeouts_check() */
     634             : 
     635             : 
     636             : #define ENTER                                                           \
     637             :         do {                                                            \
     638             :         static const int pc0 = __LINE__;                                \
     639             :         switch (pc0 + it->pc) {                                         \
     640             :         case __LINE__: (void)0
     641             : 
     642             : #define SAVE_AND_DO(do_statement)                                       \
     643             :         do {                                                            \
     644             :                 it->pc = __LINE__ - pc0;                                \
     645             :                 do_statement;                                           \
     646             :                 case __LINE__: (void)0;                                 \
     647             :         } while (0)
     648             : 
     649             : #define YIELD(rv)                                                       \
     650             :         SAVE_AND_DO(return (rv))
     651             : 
     652             : #define LEAVE                                                           \
     653             :         SAVE_AND_DO(break);                                             \
     654             :         }                                                               \
     655             :         } while (0)
     656             : 
     657             : TIMEOUT_PUBLIC struct timeout *timeouts_next(struct timeouts *T, struct timeouts_it *it) {
     658             :         struct timeout *to;
     659             : 
     660             :         ENTER;
     661             : 
     662             :         if (it->flags & TIMEOUTS_EXPIRED) {
     663             :                 if (it->flags & TIMEOUTS_CLEAR) {
     664             :                         while ((to = timeouts_get(T))) {
     665             :                                 YIELD(to);
     666             :                         }
     667             :                 } else {
     668             :                         TOR_TAILQ_FOREACH_SAFE(to, &T->expired, tqe, it->to) {
     669             :                                 YIELD(to);
     670             :                         }
     671             :                 }
     672             :         }
     673             : 
     674             :         if (it->flags & TIMEOUTS_PENDING) {
     675             :                 for (it->i = 0; it->i < countof(T->wheel); it->i++) {
     676             :                         for (it->j = 0; it->j < countof(T->wheel[it->i]); it->j++) {
     677             :                                 TOR_TAILQ_FOREACH_SAFE(to, &T->wheel[it->i][it->j], tqe, it->to) {
     678             :                                         YIELD(to);
     679             :                                 }
     680             :                         }
     681             :                 }
     682             :         }
     683             : 
     684             :         LEAVE;
     685             : 
     686             :         return NULL;
     687             : } /* timeouts_next */
     688             : 
     689             : #undef LEAVE
     690             : #undef YIELD
     691             : #undef SAVE_AND_DO
     692             : #undef ENTER
     693             : 
     694             : 
     695             : /*
     696             :  * T I M E O U T  R O U T I N E S
     697             :  *
     698             :  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
     699             : 
     700        1107 : TIMEOUT_PUBLIC struct timeout *timeout_init(struct timeout *to, int flags) {
     701        1107 :         memset(to, 0, sizeof *to);
     702             : 
     703        1107 :         to->flags = flags;
     704             : 
     705        1107 :         return to;
     706             : } /* timeout_init() */
     707             : 
     708             : 
     709             : #ifndef TIMEOUT_DISABLE_RELATIVE_ACCESS
     710             : TIMEOUT_PUBLIC bool timeout_pending(struct timeout *to) {
     711             :         return to->pending && to->pending != &to->timeouts->expired;
     712             : } /* timeout_pending() */
     713             : 
     714             : 
     715             : TIMEOUT_PUBLIC bool timeout_expired(struct timeout *to) {
     716             :         return to->pending && to->pending == &to->timeouts->expired;
     717             : } /* timeout_expired() */
     718             : 
     719             : 
     720             : TIMEOUT_PUBLIC void timeout_del(struct timeout *to) {
     721             :         timeouts_del(to->timeouts, to);
     722             : } /* timeout_del() */
     723             : #endif
     724             : 
     725             : 
     726             : /*
     727             :  * V E R S I O N  I N T E R F A C E S
     728             :  *
     729             :  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
     730             : 
     731             : TIMEOUT_PUBLIC int timeout_version(void) {
     732             :         return TIMEOUT_VERSION;
     733             : } /* timeout_version() */
     734             : 
     735             : 
     736             : TIMEOUT_PUBLIC const char *timeout_vendor(void) {
     737             :         return TIMEOUT_VENDOR;
     738             : } /* timeout_version() */
     739             : 
     740             : 
     741             : TIMEOUT_PUBLIC int timeout_v_rel(void) {
     742             :         return TIMEOUT_V_REL;
     743             : } /* timeout_version() */
     744             : 
     745             : 
     746             : TIMEOUT_PUBLIC int timeout_v_abi(void) {
     747             :         return TIMEOUT_V_ABI;
     748             : } /* timeout_version() */
     749             : 
     750             : 
     751             : TIMEOUT_PUBLIC int timeout_v_api(void) {
     752             :         return TIMEOUT_V_API;
     753             : } /* timeout_version() */

Generated by: LCOV version 1.14