#include "orconfig.h"
#define TOR_TIMERS_PRIVATE
#include "lib/evloop/compat_libevent.h"
#include "lib/evloop/timers.h"
#include "lib/intmath/muldiv.h"
#include "lib/log/log.h"
#include "lib/log/util_bug.h"
#include "lib/malloc/malloc.h"
#include "lib/time/compat_time.h"
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef _WIN32
#include <winsock2.h>
#endif
struct timeout_cb_t {
timer_cb_fn_t cb;
void *arg;
};
#ifdef COCCI
#define TIMEOUT_PUBLIC
#elif defined(__GNUC__)
#define TIMEOUT_PUBLIC __attribute__((__unused__)) static
#else
#define TIMEOUT_PUBLIC static
#endif
#define TIMEOUT_DISABLE_INTERVALS
#define TIMEOUT_DISABLE_RELATIVE_ACCESS
#define TIMEOUT_CB_OVERRIDE
#define WHEEL_NUM 5
#if SIZEOF_VOID_P == 4
#define WHEEL_BIT 5
#endif
#include "ext/timeouts/timeout.c"
static struct timeouts *global_timeouts = NULL;
static struct mainloop_event_t *global_timer_event = NULL;
static monotime_t start_of_time;
#define USEC_PER_TICK 100
#define USEC_PER_SEC 1000000
#define MIN_CHECK_SECONDS 3600
#define MIN_CHECK_TICKS \
(((timeout_t)MIN_CHECK_SECONDS) * (1000000 / USEC_PER_TICK))
static timeout_t
tv_to_timeout(const struct timeval *tv)
{
uint64_t usec = tv->tv_usec;
usec += ((uint64_t)USEC_PER_SEC) * tv->tv_sec;
return usec / USEC_PER_TICK;
}
static void
timeout_to_tv(timeout_t t, struct timeval *tv_out)
{
t *= USEC_PER_TICK;
tv_out->tv_usec = (int)(t % USEC_PER_SEC);
tv_out->tv_sec = (time_t)(t / USEC_PER_SEC);
}
static void
timer_advance_to_cur_time(const monotime_t *now)
{
timeout_t cur_tick = CEIL_DIV(monotime_diff_usec(&start_of_time, now),
USEC_PER_TICK);
timeouts_update(global_timeouts, cur_tick);
}
static void
libevent_timer_reschedule(void)
{
monotime_t now;
monotime_get(&now);
timer_advance_to_cur_time(&now);
timeout_t delay = timeouts_timeout(global_timeouts);
struct timeval d;
if (delay > MIN_CHECK_TICKS)
delay = MIN_CHECK_TICKS;
timeout_to_tv(delay, &d);
mainloop_event_schedule(global_timer_event, &d);
}
STATIC void
timers_run_pending(void)
{
monotime_t now;
monotime_get(&now);
timer_advance_to_cur_time(&now);
tor_timer_t *t;
while ((t = timeouts_get(global_timeouts))) {
t->callback.cb(t, t->callback.arg, &now);
}
}
static void
libevent_timer_callback(mainloop_event_t *ev, void *arg)
{
(void)ev;
(void)arg;
timers_run_pending();
libevent_timer_reschedule();
}
void
timers_initialize(void)
{
if (BUG(global_timeouts))
return;
timeout_error_t err = 0;
global_timeouts = timeouts_open(0, &err);
if (!global_timeouts) {
log_err(LD_BUG, "Unable to open timer backend: %s", strerror(err));
tor_assert(0);
}
monotime_init();
monotime_get(&start_of_time);
mainloop_event_t *timer_event;
timer_event = mainloop_event_new(libevent_timer_callback, NULL);
tor_assert(timer_event);
global_timer_event = timer_event;
libevent_timer_reschedule();
}
void
timers_shutdown(void)
{
if (global_timer_event) {
mainloop_event_free(global_timer_event);
global_timer_event = NULL;
}
if (global_timeouts) {
timeouts_close(global_timeouts);
global_timeouts = NULL;
}
}
tor_timer_t *
timer_new(timer_cb_fn_t cb, void *arg)
{
tor_timer_t *t = tor_malloc(sizeof(tor_timer_t));
timeout_init(t, 0);
timer_set_cb(t, cb, arg);
return t;
}
void
timer_free_(tor_timer_t *t)
{
if (! t)
return;
timeouts_del(global_timeouts, t);
tor_free(t);
}
void
timer_set_cb(tor_timer_t *t, timer_cb_fn_t cb, void *arg)
{
t->callback.cb = cb;
t->callback.arg = arg;
}
void
timer_get_cb(const tor_timer_t *t,
timer_cb_fn_t *cb_out, void **arg_out)
{
if (cb_out)
*cb_out = t->callback.cb;
if (arg_out)
*arg_out = t->callback.arg;
}
void
timer_schedule(tor_timer_t *t, const struct timeval *tv)
{
const timeout_t delay = tv_to_timeout(tv);
monotime_t now;
monotime_get(&now);
timer_advance_to_cur_time(&now);
timeout_t to = timeouts_timeout(global_timeouts);
timeouts_add(global_timeouts, t, delay);
if (to <= delay) {
return;
}
libevent_timer_reschedule();
}
void
timer_disable(tor_timer_t *t)
{
timeouts_del(global_timeouts, t);
}