From e0236584b489193fa00b47f06f0ff4f2cbc0aaf4 Mon Sep 17 00:00:00 2001 From: Neels Hofmeyr Date: Fri, 4 Jun 2021 16:30:04 +0200 Subject: osmo_select_shutdown_request(): allow finishing pending writes on SIGTERM Allow telling osmo_select_main* to only service pending writes (shutdown mode). Introduce API fuctions to indicate a shutdown request, and find out whether shutdown is complete. Some osmo programs have a curious sleep of few seconds upon receiving SIGTERM. The idea presumably was to finish off pending writes before halting the program. But a sleep() on program exit is annoying, especially when there usually are no pending writes, and when osmo-bsc is launched numerous times for tests. Change-Id: Ib94d4316924103459577087c2214188679db2227 --- include/osmocom/core/select.h | 3 ++ src/select.c | 70 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/include/osmocom/core/select.h b/include/osmocom/core/select.h index b4101998..e9f19a56 100644 --- a/include/osmocom/core/select.h +++ b/include/osmocom/core/select.h @@ -105,5 +105,8 @@ struct osmo_signalfd { struct osmo_signalfd * osmo_signalfd_setup(void *ctx, sigset_t set, osmo_signalfd_cb *cb, void *data); +void osmo_select_shutdown_request(); +int osmo_select_shutdown_requested(); +bool osmo_select_shutdown_done(); /*! @} */ diff --git a/src/select.c b/src/select.c index 71ee7f60..f7eb5ea3 100644 --- a/src/select.c +++ b/src/select.c @@ -69,6 +69,11 @@ struct poll_state { static __thread struct poll_state g_poll; #endif /* FORCE_IO_SELECT */ +/*! See osmo_select_shutdown_request() */ +static int _osmo_select_shutdown_requested = 0; +/*! See osmo_select_shutdown_request() */ +static bool _osmo_select_shutdown_done = false; + /*! Set up an osmo-fd. Will not register it. * \param[inout] ofd Osmo FD to be set-up * \param[in] fd OS-level file descriptor number @@ -316,6 +321,7 @@ static int poll_disp_fds(int n_fd) struct osmo_fd *ufd; unsigned int i; int work = 0; + int shutdown_pending_writes = 0; for (i = 0; i < n_fd; i++) { struct pollfd *p = &g_poll.poll[i]; @@ -340,6 +346,11 @@ static int poll_disp_fds(int n_fd) /* make sure we never report more than the user requested */ flags &= ufd->when; + if (_osmo_select_shutdown_requested > 0) { + if (ufd->when & OSMO_FD_WRITE) + shutdown_pending_writes++; + } + if (flags) { work = 1; /* make sure to clear any log context before processing the next incoming message @@ -351,6 +362,9 @@ static int poll_disp_fds(int n_fd) } } + if (_osmo_select_shutdown_requested > 0 && !shutdown_pending_writes) + _osmo_select_shutdown_done = true; + return work; } @@ -370,7 +384,8 @@ static int _osmo_select_main(int polling) return 0; /* fire timers */ - osmo_timers_update(); + if (!_osmo_select_shutdown_requested) + osmo_timers_update(); OSMO_ASSERT(osmo_ctx->select); @@ -596,6 +611,59 @@ osmo_signalfd_setup(void *ctx, sigset_t set, osmo_signalfd_cb *cb, void *data) #endif /* HAVE_SYS_SIGNALFD_H */ +/*! Request osmo_select_* to only service pending OSMO_FD_WRITE requests. Once all writes are done, + * osmo_select_shutdown_done() returns true. This allows for example to send all outbound packets before terminating the + * process. + * + * Usage example: + * + * static void signal_handler(int signum) + * { + * fprintf(stdout, "signal %u received\n", signum); + * + * switch (signum) { + * case SIGINT: + * case SIGTERM: + * // If the user hits Ctrl-C the third time, just terminate immediately. + * if (osmo_select_shutdown_requested() >= 2) + * exit(-1); + * // Request write-only mode in osmo_select_main_ctx() + * osmo_select_shutdown_request(); + * break; + * [...] + * } + * + * main() + * { + * signal(SIGINT, &signal_handler); + * signal(SIGTERM, &signal_handler); + * + * [...] + * + * // After the signal_handler issued osmo_select_shutdown_request(), osmo_select_shutdown_done() returns true + * // as soon as all write queues are empty. + * while (!osmo_select_shutdown_done()) { + * osmo_select_main_ctx(0); + * } + * } + */ +void osmo_select_shutdown_request() +{ + _osmo_select_shutdown_requested++; +}; + +/*! Return the number of times osmo_select_shutdown_request() was called before. */ +int osmo_select_shutdown_requested() +{ + return _osmo_select_shutdown_requested; +}; + +/*! Return true after osmo_select_shutdown_requested() was called, and after an osmo_select poll loop found no more + * pending OSMO_FD_WRITE on any registered socket. */ +bool osmo_select_shutdown_done() { + return _osmo_select_shutdown_done; +}; + /*! @} */ #endif /* _HAVE_SYS_SELECT_H */ -- cgit v1.2.3