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.)
This commit is contained in:
Kevin Butler 2015-11-02 09:48:18 -05:00 committed by Nick Mathewson
parent faba114a34
commit fbeff307f7
3 changed files with 174 additions and 0 deletions

View File

@ -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 \

118
src/or/periodic.c Normal file
View File

@ -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, <b>data</b> 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);
}
}

54
src/or/periodic.h Normal file
View File

@ -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 <b>last_action_time</b> 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 <b>now</b> 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 <b>now</b> 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