From f0c3b6238168e351835fdc64b5c93b84d6528870 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 20 Oct 2017 10:16:00 -0400 Subject: [PATCH] Expose a new function to make the event loop exit once and for all. Instead of calling tor_cleanup(), exit(x), we can now call tor_shutdown_event_loop_and_exit. --- src/common/compat_libevent.h | 1 + src/or/main.c | 47 +++++++++++++++++++++++++++++++++++- src/or/main.h | 2 ++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h index 834354c405..3d8672fc63 100644 --- a/src/common/compat_libevent.h +++ b/src/common/compat_libevent.h @@ -30,6 +30,7 @@ periodic_timer_t *periodic_timer_new(struct event_base *base, void periodic_timer_free(periodic_timer_t *); #define tor_event_base_loopexit event_base_loopexit +#define tor_event_base_loopbreak event_base_loopbreak /** Defines a configuration for using libevent with Tor: passed as an argument * to tor_libevent_initialize() to describe how we want to set up. */ diff --git a/src/or/main.c b/src/or/main.c index e3a73ef03e..6af61d5132 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -192,6 +192,14 @@ static smartlist_t *active_linked_connection_lst = NULL; * loop_once. If so, there's no need to trigger a loopexit in order * to handle linked connections. */ static int called_loop_once = 0; +/** Flag: if true, it's time to shut down, so the main loop should exit as + * soon as possible. + */ +static int main_loop_should_exit = 0; +/** The return value that the main loop should yield when it exits, if + * main_loop_should_exit is true. + */ +static int main_loop_exit_value = 0; /** We set this to 1 when we've opened a circuit, so we can print a log * entry to inform the user that Tor is working. We set it to 0 when @@ -648,6 +656,30 @@ tell_event_loop_to_run_external_code(void) } } +/** + * After finishing the current callback (if any), shut down the main loop, + * clean up the process, and exit with exitcode. + */ +void +tor_shutdown_event_loop_and_exit(int exitcode) +{ + if (main_loop_should_exit) + return; /* Ignore multiple calls to this function. */ + + main_loop_should_exit = 1; + main_loop_exit_value = exitcode; + + /* Unlike loopexit, loopbreak prevents other callbacks from running. */ + tor_event_base_loopbreak(tor_libevent_get_base()); +} + +/** Return true iff tor_shutdown_event_loop_and_exit() has been called. */ +int +tor_event_loop_shutdown_is_pending(void) +{ + return main_loop_should_exit; +} + /** Helper: Tell the main loop to begin reading bytes into conn from * its linked connection, if it is not doing so already. Called by * connection_start_reading and connection_start_writing as appropriate. */ @@ -2595,6 +2627,9 @@ do_main_loop(void) } #endif /* defined(HAVE_SYSTEMD) */ + main_loop_should_exit = 0; + main_loop_exit_value = 0; + return run_main_loop_until_done(); } @@ -2610,6 +2645,9 @@ run_main_loop_once(void) if (nt_service_is_stopping()) return 0; + if (main_loop_should_exit) + return 0; + #ifndef _WIN32 /* Make it easier to tell whether libevent failure is our fault or not. */ errno = 0; @@ -2656,6 +2694,9 @@ run_main_loop_once(void) } } + if (main_loop_should_exit) + return 0; + /* And here is where we put callbacks that happen "every time the event loop * runs." They must be very fast, or else the whole Tor process will get * slowed down. @@ -2684,7 +2725,11 @@ run_main_loop_until_done(void) do { loop_result = run_main_loop_once(); } while (loop_result == 1); - return loop_result; + + if (main_loop_should_exit) + return main_loop_exit_value; + else + return loop_result; } /** Libevent callback: invoked when we get a signal. diff --git a/src/or/main.h b/src/or/main.h index 808aa14143..5ab77c03ed 100644 --- a/src/or/main.h +++ b/src/or/main.h @@ -46,6 +46,8 @@ MOCK_DECL(void,connection_stop_writing,(connection_t *conn)); MOCK_DECL(void,connection_start_writing,(connection_t *conn)); void tell_event_loop_to_run_external_code(void); +void tor_shutdown_event_loop_and_exit(int exitcode); +int tor_event_loop_shutdown_is_pending(void); void connection_stop_reading_from_linked_conn(connection_t *conn);