diff --git a/src/or/main.c b/src/or/main.c index eb8835198a..1c5ac198b0 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -1484,8 +1484,9 @@ initialize_periodic_events_cb(evutil_socket_t fd, short events, void *data) for (int i = 0; periodic_events[i].name; ++i) { periodic_event_item_t *item = &periodic_events[i]; - if (item->roles & roles && !periodic_event_is_enabled(item)) { - periodic_event_launch(item); + if (item->roles & roles) { + /* This is safe to be called on an already enabled event. */ + periodic_event_enable(item); log_debug(LD_GENERAL, "Launching periodic event %s", item->name); } } @@ -1541,18 +1542,13 @@ rescan_periodic_events(const or_options_t *options) for (int i = 0; periodic_events[i].name; ++i) { periodic_event_item_t *item = &periodic_events[i]; - int is_enabled = periodic_event_is_enabled(item); - int need_item = (item->roles & roles); - /* We need this event but it is *not* enabled. */ - if (need_item && !is_enabled) { - periodic_event_launch(item); - continue; - } - /* We do *not* need this event but it is enabled. */ - if (!need_item && is_enabled) { - periodic_event_destroy(item); - continue; + /* Enable the event if needed. It is safe to enable an event that was + * already enabled. Same goes for disabling it. */ + if (item->roles & roles) { + periodic_event_enable(item); + } else { + periodic_event_disable(item); } } } diff --git a/src/or/periodic.c b/src/or/periodic.c index 42bea3ae65..76aa418b35 100644 --- a/src/or/periodic.c +++ b/src/or/periodic.c @@ -43,12 +43,22 @@ periodic_event_dispatch(mainloop_event_t *ev, void *data) periodic_event_item_t *event = data; tor_assert(ev == event->ev); + if (BUG(!periodic_event_is_enabled(event))) { + return; + } + time_t now = time(NULL); const or_options_t *options = get_options(); // log_debug(LD_GENERAL, "Dispatching %s", event->name); int r = event->fn(now, options); int next_interval = 0; + if (!periodic_event_is_enabled(event)) { + /* The event got disabled from inside its callback; no need to + * reschedule. */ + return; + } + /* update the last run time if action was taken */ if (r==0) { log_err(LD_BUG, "Invalid return value for periodic event from %s.", @@ -114,8 +124,8 @@ periodic_event_launch(periodic_event_item_t *event) } // Initial dispatch - periodic_event_dispatch(event->ev, event); event->enabled = 1; + periodic_event_dispatch(event->ev, event); } /** Release all storage associated with event */ @@ -126,6 +136,35 @@ periodic_event_destroy(periodic_event_item_t *event) return; mainloop_event_free(event->ev); event->last_action_time = 0; +} + +/** Enable the given event which means the event is launched and then the + * event's enabled flag is set. This can be called for an event that is + * 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; + } + + periodic_event_launch(event); +} + +/** 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); event->enabled = 0; } diff --git a/src/or/periodic.h b/src/or/periodic.h index 1b346a1cc8..dde27db5af 100644 --- a/src/or/periodic.h +++ b/src/or/periodic.h @@ -69,6 +69,8 @@ void periodic_event_launch(periodic_event_item_t *event); void periodic_event_setup(periodic_event_item_t *event); void periodic_event_destroy(periodic_event_item_t *event); void periodic_event_reschedule(periodic_event_item_t *event); +void periodic_event_enable(periodic_event_item_t *event); +void periodic_event_disable(periodic_event_item_t *event); #endif /* !defined(TOR_PERIODIC_H) */