diff --git a/changes/ticket25932 b/changes/ticket25932 new file mode 100644 index 000000000..67f56f7cc --- /dev/null +++ b/changes/ticket25932 @@ -0,0 +1,9 @@ + o Minor features (mainloop): + - Move responsibility for + closing connections, circuits, and channels + from a once-per-second callback to a callback that is only scheduled as + needed. Once enough items are removed from our once-per-second + callback, we can eliminate it entirely to conserve CPU when idle. + Closes ticket + 25932. + diff --git a/src/or/channel.c b/src/or/channel.c index 68245db49..c30e50801 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -69,6 +69,7 @@ #include "circuitmux.h" #include "entrynodes.h" #include "geoip.h" +#include "main.h" #include "nodelist.h" #include "relay.h" #include "rephist.h" @@ -404,6 +405,7 @@ channel_register(channel_t *chan) /* Put it in the finished list, creating it if necessary */ if (!finished_channels) finished_channels = smartlist_new(); smartlist_add(finished_channels, chan); + mainloop_schedule_postloop_cleanup(); } else { /* Put it in the active list, creating it if necessary */ if (!active_channels) active_channels = smartlist_new(); @@ -1548,6 +1550,7 @@ channel_change_state_(channel_t *chan, channel_state_t to_state) if (active_channels) smartlist_remove(active_channels, chan); if (!finished_channels) finished_channels = smartlist_new(); smartlist_add(finished_channels, chan); + mainloop_schedule_postloop_cleanup(); } /* Need to put on active list? */ else if (!was_active && is_active) { @@ -1666,6 +1669,7 @@ channel_listener_change_state(channel_listener_t *chan_l, if (active_listeners) smartlist_remove(active_listeners, chan_l); if (!finished_listeners) finished_listeners = smartlist_new(); smartlist_add(finished_listeners, chan_l); + mainloop_schedule_postloop_cleanup(); } /* Need to put on active list? */ else if (!was_active && is_active) { diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index 9a82713cb..ea2f2c15c 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -2067,6 +2067,7 @@ circuit_mark_for_close_, (circuit_t *circ, int reason, int line, circuits_pending_close = smartlist_new(); smartlist_add(circuits_pending_close, circ); + mainloop_schedule_postloop_cleanup(); log_info(LD_GENERAL, "Circuit %u (id: %" PRIu32 ") marked for close at " "%s:%d (orig reason: %d, new reason: %d)", diff --git a/src/or/main.c b/src/or/main.c index ffe407329..18b9b780d 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -446,6 +446,7 @@ add_connection_to_closeable_list(connection_t *conn) tor_assert(conn->marked_for_close); assert_connection_ok(conn, time(NULL)); smartlist_add(closeable_connection_lst, conn); + mainloop_schedule_postloop_cleanup(); } /** Return 1 if conn is on the closeable list, else return 0. */ @@ -1599,6 +1600,30 @@ reschedule_directory_downloads(void) periodic_event_reschedule(launch_descriptor_fetches_event); } +/** Mainloop callback: clean up circuits, channels, and connections + * that are pending close. */ +static void +postloop_cleanup_cb(mainloop_event_t *ev, void *arg) +{ + (void)ev; + (void)arg; + circuit_close_all_marked(); + close_closeable_connections(); + channel_run_cleanup(); + channel_listener_run_cleanup(); +} + +/** Event to run postloop_cleanup_cb */ +static mainloop_event_t *postloop_cleanup_ev=NULL; + +/** Schedule a post-loop event to clean up marked channels, connections, and + * circuits. */ +void +mainloop_schedule_postloop_cleanup(void) +{ + mainloop_event_activate(postloop_cleanup_ev); +} + #define LONGEST_TIMER_PERIOD (30 * 86400) /** Helper: Return the number of seconds between now and next, * clipped to the range [1 second, LONGEST_TIMER_PERIOD]. */ @@ -1710,23 +1735,10 @@ run_scheduled_events(time_t now) run_connection_housekeeping(i, now); } - /* 6. And remove any marked circuits... */ - circuit_close_all_marked(); - - /* 8. and blow away any connections that need to die. have to do this now, - * because if we marked a conn for close and left its socket -1, then - * we'll pass it to poll/select and bad things will happen. - */ - close_closeable_connections(); - /* 8b. And if anything in our state is ready to get flushed to disk, we * flush it. */ or_state_save(now); - /* 8c. Do channel cleanup just like for connections */ - channel_run_cleanup(); - channel_listener_run_cleanup(); - /* 11b. check pending unconfigured managed proxies */ if (!net_is_disabled() && pt_proxies_configuration_pending()) pt_configure_remaining_proxies(); @@ -2628,6 +2640,10 @@ do_main_loop(void) schedule_active_linked_connections_event = mainloop_event_postloop_new(schedule_active_linked_connections_cb, NULL); } + if (!postloop_cleanup_ev) { + postloop_cleanup_ev = + mainloop_event_postloop_new(postloop_cleanup_cb, NULL); + } /* initialize dns resolve map, spawn workers if needed */ if (dns_init() < 0) { @@ -3512,6 +3528,7 @@ tor_free_all(int postfork) tor_event_free(initialize_periodic_events_event); mainloop_event_free(directory_all_unreachable_cb_event); mainloop_event_free(schedule_active_linked_connections_event); + mainloop_event_free(postloop_cleanup_ev); #ifdef HAVE_SYSTEMD_209 periodic_timer_free(systemd_watchdog_timer); diff --git a/src/or/main.h b/src/or/main.h index 0e3de7d4b..4f3b84e30 100644 --- a/src/or/main.h +++ b/src/or/main.h @@ -61,6 +61,7 @@ void dns_servers_relaunch_checks(void); void reset_all_main_loop_timers(void); void reschedule_descriptor_update_check(void); void reschedule_directory_downloads(void); +void mainloop_schedule_postloop_cleanup(void); MOCK_DECL(long,get_uptime,(void)); MOCK_DECL(void,reset_uptime,(void));