periodic.c 5.04 KB
Newer Older
Nick Mathewson's avatar
Nick Mathewson committed
1
/* Copyright (c) 2015-2018, The Tor Project, Inc. */
2
3
/* See LICENSE for licensing information */

4
5
6
7
/**
 * \file periodic.c
 *
 * \brief Generic backend for handling periodic events.
8
9
10
11
 *
 * The events in this module are used by main.c to track items that need
 * to fire once every N seconds, possibly picking a new interval each time
 * that they fire.  See periodic_events[] in main.c for examples.
12
13
 */

14
#include "core/or/or.h"
15
#include "lib/evloop/compat_libevent.h"
16
#include "app/config/config.h"
17
#include "core/mainloop/mainloop.h"
18
#include "core/mainloop/periodic.h"
19
#include "lib/evloop/compat_libevent.h"
20

21
22
23
/** We disable any interval greater than this number of seconds, on the
 * grounds that it is probably an absolute time mistakenly passed in as a
 * relative time.
24
 */
25
26
static const int MAX_INTERVAL = 10 * 365 * 86400;

27
28
/** Set the event <b>event</b> to run in <b>next_interval</b> seconds from
 * now. */
29
30
31
32
static void
periodic_event_set_interval(periodic_event_item_t *event,
                            time_t next_interval)
{
33
34
35
36
  tor_assert(next_interval < MAX_INTERVAL);
  struct timeval tv;
  tv.tv_sec = next_interval;
  tv.tv_usec = 0;
37
  mainloop_event_schedule(event->ev, &tv);
38
39
40
41
42
}

/** Wraps dispatches for periodic events, <b>data</b> will be a pointer to the
 * event that needs to be called */
static void
43
periodic_event_dispatch(mainloop_event_t *ev, void *data)
44
45
{
  periodic_event_item_t *event = data;
46
  tor_assert(ev == event->ev);
47

48
49
50
51
  if (BUG(!periodic_event_is_enabled(event))) {
    return;
  }

52
  time_t now = time(NULL);
53
  update_current_time(now);
54
  const or_options_t *options = get_options();
55
//  log_debug(LD_GENERAL, "Dispatching %s", event->name);
56
57
58
  int r = event->fn(now, options);
  int next_interval = 0;

59
60
61
62
63
64
  if (!periodic_event_is_enabled(event)) {
    /* The event got disabled from inside its callback; no need to
     * reschedule. */
    return;
  }

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
  /* 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;
  }

84
85
//  log_debug(LD_GENERAL, "Scheduling %s for %d seconds", event->name,
//           next_interval);
86
  struct timeval tv = { next_interval , 0 };
87
  mainloop_event_schedule(ev, &tv);
88
89
}

90
/** Schedules <b>event</b> to run as soon as possible from now. */
91
92
93
void
periodic_event_reschedule(periodic_event_item_t *event)
{
94
95
96
97
  /* Don't reschedule a disabled event. */
  if (periodic_event_is_enabled(event)) {
    periodic_event_set_interval(event, 1);
  }
98
99
}

100
/** Initializes the libevent backend for a periodic event. */
101
void
102
periodic_event_setup(periodic_event_item_t *event)
103
{
104
  if (event->ev) { /* Already setup? This is a bug */
105
106
107
108
    log_err(LD_BUG, "Initial dispatch should only be done once.");
    tor_assert(0);
  }

109
110
  event->ev = mainloop_event_new(periodic_event_dispatch,
                                 event);
111
  tor_assert(event->ev);
112
113
114
115
116
117
118
119
120
121
122
}

/** 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->ev) { /* Not setup? This is a bug */
    log_err(LD_BUG, "periodic_event_launch without periodic_event_setup");
    tor_assert(0);
  }
123
124
125
126
127
  /* Event already enabled? This is a bug */
  if (periodic_event_is_enabled(event)) {
    log_err(LD_BUG, "periodic_event_launch on an already enabled event");
    tor_assert(0);
  }
128
129

  // Initial dispatch
130
  event->enabled = 1;
131
  periodic_event_dispatch(event->ev, event);
132
133
}

134
/** Release all storage associated with <b>event</b> */
135
136
137
138
139
void
periodic_event_destroy(periodic_event_item_t *event)
{
  if (!event)
    return;
140
  mainloop_event_free(event->ev);
141
  event->last_action_time = 0;
142
143
}

144
145
/** Enable the given event by setting its "enabled" flag and scheduling it to
 * run immediately in the event loop. This can be called for an event that is
146
147
148
149
150
151
152
153
154
155
 * already enabled. */
void
periodic_event_enable(periodic_event_item_t *event)
{
  tor_assert(event);
  /* Safely and silently ignore if this event is already enabled. */
  if (periodic_event_is_enabled(event)) {
    return;
  }

156
157
158
  tor_assert(event->ev);
  event->enabled = 1;
  mainloop_event_activate(event->ev);
159
160
161
162
163
164
165
166
167
168
169
170
171
172
}

/** Disable the given event which means the event is destroyed and then the
 * event's enabled flag is unset. This can be called for an event that is
 * already disabled. */
void
periodic_event_disable(periodic_event_item_t *event)
{
  tor_assert(event);
  /* Safely and silently ignore if this event is already disabled. */
  if (!periodic_event_is_enabled(event)) {
    return;
  }
  mainloop_event_cancel(event->ev);
173
  event->enabled = 0;
174
}