LCOV - code coverage report
Current view: top level - lib/evloop - timers.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 83 89 93.3 %
Date: 2021-11-24 03:28:48 Functions: 13 14 92.9 %

          Line data    Source code
       1             : /* Copyright (c) 2016-2021, The Tor Project, Inc. */
       2             : /* See LICENSE for licensing information */
       3             : 
       4             : /**
       5             :  * \file timers.c
       6             :  * \brief Wrapper around William Ahern's fast hierarchical timer wheel
       7             :  *   implementation, to tie it in with a libevent backend.
       8             :  *
       9             :  * Only use these functions from the main thread.
      10             :  *
      11             :  * The main advantage of tor_timer_t over using libevent's timers is that
      12             :  * they're way more efficient if we need to have thousands or millions of
      13             :  * them.  For more information, see
      14             :  *   https://www.25thandclement.com/~william/projects/timeout.c.html
      15             :  *
      16             :  * Periodic timers are available in the backend, but I've turned them off.
      17             :  * We can turn them back on if needed.
      18             :  */
      19             : 
      20             : /* Notes:
      21             :  *
      22             :  * Having a way to free all timers on shutdown would free people from the
      23             :  * need to track them.  Not sure if that's clever though.
      24             :  *
      25             :  * In an ideal world, Libevent would just switch to use this backend, and we
      26             :  * could throw this file away.  But even if Libevent does switch, we'll be
      27             :  * stuck with legacy libevents for some time.
      28             :  */
      29             : 
      30             : #include "orconfig.h"
      31             : 
      32             : #define TOR_TIMERS_PRIVATE
      33             : 
      34             : #include "lib/evloop/compat_libevent.h"
      35             : #include "lib/evloop/timers.h"
      36             : #include "lib/intmath/muldiv.h"
      37             : #include "lib/log/log.h"
      38             : #include "lib/log/util_bug.h"
      39             : #include "lib/malloc/malloc.h"
      40             : #include "lib/time/compat_time.h"
      41             : 
      42             : #ifdef HAVE_SYS_TIME_H
      43             : #include <sys/time.h>
      44             : #endif
      45             : 
      46             : #ifdef _WIN32
      47             : // For struct timeval.
      48             : #include <winsock2.h>
      49             : #endif
      50             : 
      51             : struct timeout_cb_t {
      52             :   timer_cb_fn_t cb;
      53             :   void *arg;
      54             : };
      55             : 
      56             : /*
      57             :  * These definitions are for timeouts.c  and timeouts.h.
      58             :  */
      59             : #ifdef COCCI
      60             : #define TIMEOUT_PUBLIC
      61             : #elif defined(__GNUC__)
      62             : /* We're not exposing any of the functions outside this file. */
      63             : #define TIMEOUT_PUBLIC __attribute__((__unused__)) static
      64             : #else
      65             : /* We're not exposing any of the functions outside this file. */
      66             : #define TIMEOUT_PUBLIC static
      67             : #endif /* defined(COCCI) || ... */
      68             : /* We're not using periodic events. */
      69             : #define TIMEOUT_DISABLE_INTERVALS
      70             : /* We always know the global_timeouts object, so we don't need each timeout
      71             :  * to keep a pointer to it. */
      72             : #define TIMEOUT_DISABLE_RELATIVE_ACCESS
      73             : /* We're providing our own struct timeout_cb_t. */
      74             : #define TIMEOUT_CB_OVERRIDE
      75             : /* We're going to support timers that are pretty far out in advance. Making
      76             :  * this big can be inefficient, but having a significant number of timers
      77             :  * above TIMEOUT_MAX can also be super-inefficient. Choosing 5 here sets
      78             :  * timeout_max to 2^30 ticks, or 29 hours with our value for USEC_PER_TICK */
      79             : #define WHEEL_NUM 5
      80             : #if SIZEOF_VOID_P == 4
      81             : /* On 32-bit platforms, we want to override wheel_bit, so that timeout.c will
      82             :  * use 32-bit math. */
      83             : #define WHEEL_BIT 5
      84             : #endif
      85             : 
      86             : #include "ext/timeouts/timeout.c"
      87             : 
      88             : static struct timeouts *global_timeouts = NULL;
      89             : static struct mainloop_event_t *global_timer_event = NULL;
      90             : 
      91             : static monotime_t start_of_time;
      92             : 
      93             : /** We need to choose this value carefully.  Because we're using timer wheels,
      94             :  * it actually costs us to have extra resolution we don't use.  So for now,
      95             :  * I'm going to define our resolution as .1 msec, and hope that's good enough.
      96             :  *
      97             :  * Note that two of the most popular libevent backends (epoll without timerfd,
      98             :  * and windows select), simply can't support sub-millisecond resolution,
      99             :  * do this is optimistic for a lot of users.
     100             :  */
     101             : #define USEC_PER_TICK 100
     102             : 
     103             : /** One million microseconds in a second */
     104             : #define USEC_PER_SEC 1000000
     105             : 
     106             : /** Check at least once every N seconds. */
     107             : #define MIN_CHECK_SECONDS 3600
     108             : 
     109             : /** Check at least once every N ticks. */
     110             : #define MIN_CHECK_TICKS \
     111             :   (((timeout_t)MIN_CHECK_SECONDS) * (1000000 / USEC_PER_TICK))
     112             : 
     113             : /**
     114             :  * Convert the timeval in <b>tv</b> to a timeout_t, and return it.
     115             :  *
     116             :  * The output resolution is set by USEC_PER_TICK. Only use this to convert
     117             :  * delays to number of ticks; the time represented by 0 is undefined.
     118             :  */
     119             : static timeout_t
     120        1234 : tv_to_timeout(const struct timeval *tv)
     121             : {
     122        1234 :   uint64_t usec = tv->tv_usec;
     123        1234 :   usec += ((uint64_t)USEC_PER_SEC) * tv->tv_sec;
     124        1234 :   return usec / USEC_PER_TICK;
     125             : }
     126             : 
     127             : /**
     128             :  * Convert the timeout in <b>t</b> to a timeval in <b>tv_out</b>. Only
     129             :  * use this for delays, not absolute times.
     130             :  */
     131             : static void
     132        1819 : timeout_to_tv(timeout_t t, struct timeval *tv_out)
     133             : {
     134        1819 :   t *= USEC_PER_TICK;
     135        1819 :   tv_out->tv_usec = (int)(t % USEC_PER_SEC);
     136        1819 :   tv_out->tv_sec = (time_t)(t / USEC_PER_SEC);
     137        1819 : }
     138             : 
     139             : /**
     140             :  * Update the timer <b>tv</b> to the current time in <b>tv</b>.
     141             :  */
     142             : static void
     143        4997 : timer_advance_to_cur_time(const monotime_t *now)
     144             : {
     145        4997 :   timeout_t cur_tick = CEIL_DIV(monotime_diff_usec(&start_of_time, now),
     146             :                                 USEC_PER_TICK);
     147        4997 :   timeouts_update(global_timeouts, cur_tick);
     148        4997 : }
     149             : 
     150             : /**
     151             :  * Adjust the time at which the libevent timer should fire based on
     152             :  * the next-expiring time in <b>global_timeouts</b>
     153             :  */
     154             : static void
     155        1819 : libevent_timer_reschedule(void)
     156             : {
     157        1819 :   monotime_t now;
     158        1819 :   monotime_get(&now);
     159        1819 :   timer_advance_to_cur_time(&now);
     160             : 
     161        1819 :   timeout_t delay = timeouts_timeout(global_timeouts);
     162             : 
     163        1819 :   struct timeval d;
     164        1819 :   if (delay > MIN_CHECK_TICKS)
     165             :     delay = MIN_CHECK_TICKS;
     166        1819 :   timeout_to_tv(delay, &d);
     167        1819 :   mainloop_event_schedule(global_timer_event, &d);
     168        1819 : }
     169             : 
     170             : /** Run the callback of every timer that has expired, based on the current
     171             :  * output of monotime_get(). */
     172             : STATIC void
     173        1944 : timers_run_pending(void)
     174             : {
     175        1944 :   monotime_t now;
     176        1944 :   monotime_get(&now);
     177        1944 :   timer_advance_to_cur_time(&now);
     178             : 
     179        1944 :   tor_timer_t *t;
     180        3149 :   while ((t = timeouts_get(global_timeouts))) {
     181        1205 :     t->callback.cb(t, t->callback.arg, &now);
     182             :   }
     183        1944 : }
     184             : 
     185             : /**
     186             :  * Invoked when the libevent timer has expired: see which tor_timer_t events
     187             :  * have fired, activate their callbacks, and reschedule the libevent timer.
     188             :  */
     189             : static void
     190        1764 : libevent_timer_callback(mainloop_event_t *ev, void *arg)
     191             : {
     192        1764 :   (void)ev;
     193        1764 :   (void)arg;
     194             : 
     195        1764 :   timers_run_pending();
     196             : 
     197        1764 :   libevent_timer_reschedule();
     198        1764 : }
     199             : 
     200             : /**
     201             :  * Initialize the timers subsystem.  Requires that libevent has already been
     202             :  * initialized.
     203             :  */
     204             : void
     205          15 : timers_initialize(void)
     206             : {
     207          15 :   if (BUG(global_timeouts))
     208             :     return; // LCOV_EXCL_LINE
     209             : 
     210          15 :   timeout_error_t err = 0;
     211          15 :   global_timeouts = timeouts_open(0, &err);
     212          15 :   if (!global_timeouts) {
     213             :     // LCOV_EXCL_START -- this can only fail on malloc failure.
     214             :     log_err(LD_BUG, "Unable to open timer backend: %s", strerror(err));
     215             :     tor_assert(0);
     216             :     // LCOV_EXCL_STOP
     217             :   }
     218             : 
     219          15 :   monotime_init();
     220          15 :   monotime_get(&start_of_time);
     221             : 
     222          15 :   mainloop_event_t *timer_event;
     223          15 :   timer_event = mainloop_event_new(libevent_timer_callback, NULL);
     224          15 :   tor_assert(timer_event);
     225          15 :   global_timer_event = timer_event;
     226             : 
     227          15 :   libevent_timer_reschedule();
     228             : }
     229             : 
     230             : /**
     231             :  * Release all storage held in the timers subsystem.  Does not fire timers.
     232             :  */
     233             : void
     234         203 : timers_shutdown(void)
     235             : {
     236         203 :   if (global_timer_event) {
     237           9 :     mainloop_event_free(global_timer_event);
     238           9 :     global_timer_event = NULL;
     239             :   }
     240         203 :   if (global_timeouts) {
     241           9 :     timeouts_close(global_timeouts);
     242           9 :     global_timeouts = NULL;
     243             :   }
     244         203 : }
     245             : 
     246             : /**
     247             :  * Allocate and return a new timer, with given callback and argument.
     248             :  */
     249             : tor_timer_t *
     250        1107 : timer_new(timer_cb_fn_t cb, void *arg)
     251             : {
     252        1107 :   tor_timer_t *t = tor_malloc(sizeof(tor_timer_t));
     253        1107 :   timeout_init(t, 0);
     254        1107 :   timer_set_cb(t, cb, arg);
     255        1107 :   return t;
     256             : }
     257             : 
     258             : /**
     259             :  * Release all storage held by <b>t</b>, and unschedule it if was already
     260             :  * scheduled.
     261             :  */
     262             : void
     263        1171 : timer_free_(tor_timer_t *t)
     264             : {
     265        1171 :   if (! t)
     266             :     return;
     267             : 
     268        1107 :   timeouts_del(global_timeouts, t);
     269        1107 :   tor_free(t);
     270             : }
     271             : 
     272             : /**
     273             :  * Change the callback and argument associated with a timer <b>t</b>.
     274             :  */
     275             : void
     276        1234 : timer_set_cb(tor_timer_t *t, timer_cb_fn_t cb, void *arg)
     277             : {
     278        1234 :   t->callback.cb = cb;
     279        1234 :   t->callback.arg = arg;
     280        1234 : }
     281             : 
     282             : /**
     283             :  * Set *<b>cb_out</b> (if provided) to this timer's callback function,
     284             :  * and *<b>arg_out</b> (if provided) to this timer's callback argument.
     285             :  */
     286             : void
     287           0 : timer_get_cb(const tor_timer_t *t,
     288             :              timer_cb_fn_t *cb_out, void **arg_out)
     289             : {
     290           0 :   if (cb_out)
     291           0 :     *cb_out = t->callback.cb;
     292           0 :   if (arg_out)
     293           0 :     *arg_out = t->callback.arg;
     294           0 : }
     295             : 
     296             : /**
     297             :  * Schedule the timer t to fire at the current time plus a delay of
     298             :  * <b>delay</b> microseconds.  All times are relative to monotime_get().
     299             :  */
     300             : void
     301        1234 : timer_schedule(tor_timer_t *t, const struct timeval *tv)
     302             : {
     303        1234 :   const timeout_t delay = tv_to_timeout(tv);
     304             : 
     305        1234 :   monotime_t now;
     306        1234 :   monotime_get(&now);
     307        1234 :   timer_advance_to_cur_time(&now);
     308             : 
     309             :   /* Take the old timeout value. */
     310        1234 :   timeout_t to = timeouts_timeout(global_timeouts);
     311             : 
     312        1234 :   timeouts_add(global_timeouts, t, delay);
     313             : 
     314             :   /* Should we update the libevent timer? */
     315        1234 :   if (to <= delay) {
     316        1194 :     return; /* we're already going to fire before this timer would trigger. */
     317             :   }
     318          40 :   libevent_timer_reschedule();
     319             : }
     320             : 
     321             : /**
     322             :  * Cancel the timer <b>t</b> if it is currently scheduled.  (It's okay to call
     323             :  * this on an unscheduled timer.
     324             :  */
     325             : void
     326           9 : timer_disable(tor_timer_t *t)
     327             : {
     328           9 :   timeouts_del(global_timeouts, t);
     329             :   /* We don't reschedule the libevent timer here, since it's okay if it fires
     330             :    * early. */
     331           9 : }

Generated by: LCOV version 1.14