From fbeff307f7780b26c9645ec7dbd685807add6581 Mon Sep 17 00:00:00 2001 From: Kevin Butler Date: Mon, 2 Nov 2015 09:48:18 -0500 Subject: [PATCH] Infrastructure for replacing global periodic events in main.c (This is from Kevin's bug3199 patch series; nick extracted it into a new file and changed the interface a little, then did some API tweaks on it.) --- src/or/include.am | 2 + src/or/periodic.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++ src/or/periodic.h | 54 +++++++++++++++++++++ 3 files changed, 174 insertions(+) create mode 100644 src/or/periodic.c create mode 100644 src/or/periodic.h diff --git a/src/or/include.am b/src/or/include.am index d0e955f495..264c4ae802 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -63,6 +63,7 @@ LIBTOR_A_SOURCES = \ src/or/onion_fast.c \ src/or/onion_tap.c \ src/or/transports.c \ + src/or/periodic.c \ src/or/policies.c \ src/or/reasons.c \ src/or/relay.c \ @@ -173,6 +174,7 @@ ORHEADERS = \ src/or/onion_tap.h \ src/or/or.h \ src/or/transports.h \ + src/or/periodic.h \ src/or/policies.h \ src/or/reasons.h \ src/or/relay.h \ diff --git a/src/or/periodic.c b/src/or/periodic.c new file mode 100644 index 0000000000..6fa1d8a2b5 --- /dev/null +++ b/src/or/periodic.c @@ -0,0 +1,118 @@ +/* Copyright (c) 2015, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "or.h" +#include "config.h" +#include "periodic.h" + +static const int MAX_INTERVAL = 10 * 365 * 86400; + +/** DOCDOC */ +static void +periodic_event_set_interval(periodic_event_item_t *event, + time_t next_interval) +{ + /** update the interval time if it's changed */ + if (next_interval != event->interval) { + tor_assert(next_interval < MAX_INTERVAL); + struct timeval tv; + tv.tv_sec = next_interval; + tv.tv_usec = 0; + event->interval = (int)next_interval; + periodic_timer_update_interval(event->timer, &tv); + } +} + +/** Wraps dispatches for periodic events, data will be a pointer to the + * event that needs to be called */ +static void +periodic_event_dispatch(periodic_timer_t *timer, void *data) +{ + periodic_event_item_t *event = data; + tor_assert(timer == event->timer); + + time_t now = time(NULL); + const or_options_t *options = get_options(); + int r = event->fn(now, options); + int next_interval = 0; + + /* update the last run time if action was taken */ + if (r==0) { + log_err(LD_BUG, "Invalid return value for periodic event from %s.", + event->name); + tor_assert(r != 0); + } else if (r > 0) { + event->last_action_time = now; + /* If the event is meant to happen after ten years, that's likely + * a bug, and somebody gave an absolute time rather than an interval. + */ + tor_assert(r < MAX_INTERVAL); + next_interval = r; + } else { + /* no action was taken, it is likely a precondition failed, + * we should reschedule for next second incase the precondition + * passes then */ + next_interval = 1; + } + + periodic_event_set_interval(event, next_interval); + + log_info(LD_GENERAL, "Dispatching %s", event->name); +} + +/** DOCDOC */ +void +periodic_event_reschedule(periodic_event_item_t *event) +{ + periodic_event_set_interval(event, 1); +} + +/** Handles initial dispatch for periodic events. It should happen 1 second + * after the events are created to mimic behaviour before #3199's refactor */ +void +periodic_event_launch(periodic_event_item_t *event) +{ + if (event->timer) { /** Already setup? This is a bug */ + log_err(LD_BUG, "Initial dispatch should only be done once."); + tor_assert(0); + } + + struct timeval interval; + interval.tv_sec = event->interval; + interval.tv_usec = 0; + + periodic_timer_t *timer = periodic_timer_new(tor_libevent_get_base(), + &interval, + periodic_event_dispatch, + event); + tor_assert(timer); + event->timer = timer; + + // Initial dispatch + periodic_event_dispatch(timer, event); +} + +/** DOCDOC */ +void +periodic_event_destroy(periodic_event_item_t *event) +{ + if (!event) + return; + periodic_timer_free(event->timer); + event->timer = 0; + event->interval = 0; + event->last_action_time = 0; +} + +/** DOCDOC */ +void +periodic_event_assert_in_range(periodic_event_item_t *ev, + time_t start, time_t end) +{ + if (ev->last_action_time < start-1 || ev->last_action_time > end) { + log_err(LD_BUG, "[Refactor Bug] Missed an interval in a range," + "Got %lu, wanted %lu <= x <= %lu.", ev->last_action_time, + start, end); + tor_assert(0); + } +} diff --git a/src/or/periodic.h b/src/or/periodic.h new file mode 100644 index 0000000000..7d5b1b47bf --- /dev/null +++ b/src/or/periodic.h @@ -0,0 +1,54 @@ +/* Copyright (c) 2015, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_PERIODIC_H +#define TOR_PERIODIC_H + +/** Callback function for a periodic event to take action. +* The return value influences the next time the function will get called. +* Return -1 to not update last_action_time and be polled again in +* the next second. If a positive value is returned it will update the +* interval time. If the returned value is larger than now then it +* is assumed to be a future time to poll again. */ +typedef int (*periodic_event_helper_t)(time_t now, + const or_options_t *options); + + +/** A single item for the periodic-events-function table. */ +typedef struct periodic_event_item_t { + periodic_event_helper_t fn; /**< The function to run the event */ + int interval; /**< The interval for running the function (In seconds). */ + time_t last_action_time; /**< The last time the function did something */ + periodic_timer_t *timer; /**< Timer object for this event */ + const char *name; /**< Name of the function -- for debug */ +} periodic_event_item_t; + +/** events will get their interval from first execution */ +#define PERIODIC_EVENT(fn) { fn##_callback, 0, 0, NULL, #fn } + +#if 0 +/** Refactor test, check the last_action_time was now or (now - delta - 1) +* It returns an incremented now value and accounts for the current +* implementation's off by one error in it's comparisons. */ +#define INCREMENT_DELTA_AND_TEST(id, now, delta) \ + (now+delta); \ + STMT_BEGIN \ + periodic_event_item_t *ev = &periodic_events[id]; \ + if (ev->last_action_time != now - delta - 1 && \ + ev->last_action_time != now) { \ + log_err(LD_BUG, "[Refactor Bug] Missed an interval " \ + "for %s, Got %lu, wanted %lu or %lu.", ev->name, \ + ev->last_action_time, now, now-delta); \ + tor_assert(0); \ + } \ + STMT_END +#endif + +void periodic_event_assert_in_range(periodic_event_item_t *event, + time_t start, time_t end); +void periodic_event_launch(periodic_event_item_t *event); +void periodic_event_destroy(periodic_event_item_t *event); +void periodic_event_reschedule(periodic_event_item_t *event); + +#endif +