tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

timeout.c (20597B)


      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 static inline wheel_t rotl(const wheel_t v, int c) {
    193 if (!(c &= (sizeof v * CHAR_BIT - 1)))
    194 	return v;
    195 
    196 return (v << c) | (v >> (sizeof v * CHAR_BIT - c));
    197 } /* rotl() */
    198 
    199 
    200 static inline wheel_t rotr(const wheel_t v, int c) {
    201 if (!(c &= (sizeof v * CHAR_BIT - 1)))
    202 	return v;
    203 
    204 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 static struct timeouts *timeouts_init(struct timeouts *T, timeout_t hz) {
    226 unsigned i, j;
    227 
    228 for (i = 0; i < countof(T->wheel); i++) {
    229 	for (j = 0; j < countof(T->wheel[i]); j++) {
    230 		TOR_TAILQ_INIT(&T->wheel[i][j]);
    231 	}
    232 }
    233 
    234 TOR_TAILQ_INIT(&T->expired);
    235 
    236 for (i = 0; i < countof(T->pending); i++) {
    237 	T->pending[i] = 0;
    238 }
    239 
    240 T->curtime = 0;
    241 T->hertz = (hz)? hz : TIMEOUT_mHZ;
    242 
    243 return T;
    244 } /* timeouts_init() */
    245 
    246 
    247 TIMEOUT_PUBLIC struct timeouts *timeouts_open(timeout_t hz, int *error) {
    248 struct timeouts *T;
    249 
    250 if ((T = malloc(sizeof *T)))
    251 	return timeouts_init(T, hz);
    252 
    253 *error = errno;
    254 
    255 return NULL;
    256 } /* timeouts_open() */
    257 
    258 
    259 static void timeouts_reset(struct timeouts *T) {
    260 struct timeout_list reset;
    261 struct timeout *to;
    262 unsigned i, j;
    263 
    264 TOR_TAILQ_INIT(&reset);
    265 
    266 for (i = 0; i < countof(T->wheel); i++) {
    267 	for (j = 0; j < countof(T->wheel[i]); j++) {
    268 		TOR_TAILQ_CONCAT(&reset, &T->wheel[i][j], tqe);
    269 	}
    270 }
    271 
    272 TOR_TAILQ_CONCAT(&reset, &T->expired, tqe);
    273 
    274 TOR_TAILQ_FOREACH(to, &reset, tqe) {
    275 	to->pending = NULL;
    276 	TO_SET_TIMEOUTS(to, NULL);
    277 }
    278 } /* timeouts_reset() */
    279 
    280 
    281 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 timeouts_reset(T);
    287 
    288 free(T);
    289 } /* timeouts_close() */
    290 
    291 
    292 TIMEOUT_PUBLIC timeout_t timeouts_hz(struct timeouts *T) {
    293 return T->hertz;
    294 } /* timeouts_hz() */
    295 
    296 
    297 TIMEOUT_PUBLIC void timeouts_del(struct timeouts *T, struct timeout *to) {
    298 if (to->pending) {
    299 	TOR_TAILQ_REMOVE(to->pending, to, tqe);
    300 
    301 	if (to->pending != &T->expired && TOR_TAILQ_EMPTY(to->pending)) {
    302 		ptrdiff_t index_ = to->pending - &T->wheel[0][0];
    303 		int wheel = (int) (index_ / WHEEL_LEN);
    304 		int slot = index_ % WHEEL_LEN;
    305 
    306 		T->pending[wheel] &= ~(WHEEL_C(1) << slot);
    307 	}
    308 
    309 	to->pending = NULL;
    310 	TO_SET_TIMEOUTS(to, NULL);
    311 }
    312 } /* timeouts_del() */
    313 
    314 
    315 static inline reltime_t timeout_rem(struct timeouts *T, struct timeout *to) {
    316 return to->expires - T->curtime;
    317 } /* timeout_rem() */
    318 
    319 
    320 static inline int timeout_wheel(timeout_t timeout) {
    321 /* must be called with timeout != 0, so fls input is nonzero */
    322 return (fls(MIN(timeout, TIMEOUT_MAX)) - 1) / WHEEL_BIT;
    323 } /* timeout_wheel() */
    324 
    325 
    326 static inline int timeout_slot(int wheel, timeout_t expires) {
    327 return WHEEL_MASK & ((expires >> (wheel * WHEEL_BIT)) - !!wheel);
    328 } /* timeout_slot() */
    329 
    330 
    331 static void timeouts_sched(struct timeouts *T, struct timeout *to, timeout_t expires) {
    332 timeout_t rem;
    333 int wheel, slot;
    334 
    335 timeouts_del(T, to);
    336 
    337 to->expires = expires;
    338 
    339 TO_SET_TIMEOUTS(to, T);
    340 
    341 if (expires > T->curtime) {
    342 	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 	wheel = timeout_wheel(rem);
    350 	slot = timeout_slot(wheel, to->expires);
    351 
    352 	to->pending = &T->wheel[wheel][slot];
    353 	TOR_TAILQ_INSERT_TAIL(to->pending, to, tqe);
    354 
    355 	T->pending[wheel] |= WHEEL_C(1) << slot;
    356 } else {
    357 	to->pending = &T->expired;
    358 	TOR_TAILQ_INSERT_TAIL(to->pending, to, tqe);
    359 }
    360 } /* 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 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 if (to->flags & TIMEOUT_ABS)
    389 	timeouts_sched(T, to, timeout);
    390 else
    391 	timeouts_sched(T, to, T->curtime + timeout);
    392 } /* timeouts_add() */
    393 
    394 
    395 TIMEOUT_PUBLIC void timeouts_update(struct timeouts *T, abstime_t curtime) {
    396 timeout_t elapsed = curtime - T->curtime;
    397 struct timeout_list todo;
    398 int wheel;
    399 
    400 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 for (wheel = 0; wheel < WHEEL_NUM; wheel++) {
    407 	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 	if ((elapsed >> (wheel * WHEEL_BIT)) > WHEEL_MAX) {
    424 		pending = (wheel_t)~WHEEL_C(0);
    425 	} else {
    426 		wheel_t _elapsed = WHEEL_MASK & (elapsed >> (wheel * WHEEL_BIT));
    427 		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 		oslot = WHEEL_MASK & (T->curtime >> (wheel * WHEEL_BIT));
    435 		pending = rotl(((WHEEL_C(1) << _elapsed) - 1), oslot);
    436 
    437 		nslot = WHEEL_MASK & (curtime >> (wheel * WHEEL_BIT));
    438 		pending |= rotr(rotl(((WHEEL_C(1) << _elapsed) - 1), nslot), (int)_elapsed);
    439 		pending |= WHEEL_C(1) << nslot;
    440 	}
    441 
    442 	while (pending & T->pending[wheel]) {
    443 		/* ctz input cannot be zero: loop condition. */
    444 		int slot = ctz(pending & T->pending[wheel]);
    445 		TOR_TAILQ_CONCAT(&todo, &T->wheel[wheel][slot], tqe);
    446 		T->pending[wheel] &= ~(UINT64_C(1) << slot);
    447 	}
    448 
    449 	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 	elapsed = MAX(elapsed, (WHEEL_LEN << (wheel * WHEEL_BIT)));
    454 }
    455 
    456 T->curtime = curtime;
    457 
    458 while (!TOR_TAILQ_EMPTY(&todo)) {
    459 	struct timeout *to = TOR_TAILQ_FIRST(&todo);
    460 
    461 	TOR_TAILQ_REMOVE(&todo, to, tqe);
    462 	to->pending = NULL;
    463 
    464 	timeouts_sched(T, to, to->expires);
    465 }
    466 
    467 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 static timeout_t timeouts_int(struct timeouts *T) {
    513 timeout_t timeout = ~TIMEOUT_C(0), _timeout;
    514 timeout_t relmask;
    515 int wheel, slot;
    516 
    517 relmask = 0;
    518 
    519 for (wheel = 0; wheel < WHEEL_NUM; wheel++) {
    520 	if (T->pending[wheel]) {
    521 		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 		_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 		_timeout -= relmask & T->curtime;
    529 		/* reduce by how much lower wheels have progressed */
    530 
    531 		timeout = MIN(_timeout, timeout);
    532 	}
    533 
    534 	relmask <<= WHEEL_BIT;
    535 	relmask |= WHEEL_MASK;
    536 }
    537 
    538 return timeout;
    539 } /* timeouts_int() */
    540 
    541 
    542 /*
    543 * Calculate the interval our caller can wait before needing to process
    544 * events.
    545 */
    546 TIMEOUT_PUBLIC timeout_t timeouts_timeout(struct timeouts *T) {
    547 if (!TOR_TAILQ_EMPTY(&T->expired))
    548 	return 0;
    549 
    550 return timeouts_int(T);
    551 } /* timeouts_timeout() */
    552 
    553 
    554 TIMEOUT_PUBLIC struct timeout *timeouts_get(struct timeouts *T) {
    555 if (!TOR_TAILQ_EMPTY(&T->expired)) {
    556 	struct timeout *to = TOR_TAILQ_FIRST(&T->expired);
    557 
    558 	TOR_TAILQ_REMOVE(&T->expired, to, tqe);
    559 	to->pending = NULL;
    560 	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 	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 TIMEOUT_PUBLIC struct timeout *timeout_init(struct timeout *to, int flags) {
    701 memset(to, 0, sizeof *to);
    702 
    703 to->flags = flags;
    704 
    705 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() */