From 72fe3543feb23ae555e08628b70a3fae4da5706c Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 22 Apr 2020 12:24:34 +0200 Subject: [PATCH] unix,win: add uv_library_shutdown() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make it possible to explicitly tell libuv to release any resources it's still holding onto (memory, threads, file descriptors, etc.) Before this commit, cleanup was performed in various destructors. This commit centralizes the cleanup logic, enabling the addition of `uv_library_shutdown()`, but maintains the current observable behavior of cleaning up when libuv is unloaded by means of `dlclose(3)`. Fixes: https://github.com/libuv/libuv/issues/2763 PR-URL: https://github.com/libuv/libuv/pull/2764 Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Richard Lau Reviewed-By: Saúl Ibarra Corretgé --- docs/src/misc.rst | 16 ++++++++++++++++ include/uv.h | 2 ++ src/threadpool.c | 4 ++-- src/unix/aix.c | 2 +- src/unix/bsd-proctitle.c | 7 +++++++ src/unix/internal.h | 2 -- src/unix/no-proctitle.c | 3 +++ src/unix/proctitle.c | 2 +- src/unix/signal.c | 4 ++-- src/uv-common.c | 16 ++++++++++++++++ src/uv-common.h | 4 ++++ src/win/signal.c | 5 +++++ src/win/util.c | 4 ++++ test/task.h | 1 + 14 files changed, 64 insertions(+), 8 deletions(-) diff --git a/docs/src/misc.rst b/docs/src/misc.rst index 7640b8b0c..7bf71b124 100644 --- a/docs/src/misc.rst +++ b/docs/src/misc.rst @@ -233,6 +233,22 @@ API sure the allocator is changed while no memory was allocated with the previous allocator, or that they are compatible. +.. c:function:: void uv_library_shutdown(void); + + .. versionadded:: 1.38.0 + + Release any global state that libuv is holding onto. Libuv will normally + do so automatically when it is unloaded but it can be instructed to perform + cleanup manually. + + .. warning:: Only call :c:func:`uv_library_shutdown()` once. + + .. warning:: Don't call :c:func:`uv_library_shutdown()` when there are + still event loops or I/O requests active. + + .. warning:: Don't call libuv functions after calling + :c:func:`uv_library_shutdown()`. + .. c:function:: uv_buf_t uv_buf_init(char* base, unsigned int len) Constructor for :c:type:`uv_buf_t`. diff --git a/include/uv.h b/include/uv.h index 3a2a504db..fb51dfd6f 100644 --- a/include/uv.h +++ b/include/uv.h @@ -265,6 +265,8 @@ typedef void* (*uv_realloc_func)(void* ptr, size_t size); typedef void* (*uv_calloc_func)(size_t count, size_t size); typedef void (*uv_free_func)(void* ptr); +UV_EXTERN void uv_library_shutdown(void); + UV_EXTERN int uv_replace_allocator(uv_malloc_func malloc_func, uv_realloc_func realloc_func, uv_calloc_func calloc_func, diff --git a/src/threadpool.c b/src/threadpool.c index a8f433f05..0998938f3 100644 --- a/src/threadpool.c +++ b/src/threadpool.c @@ -160,8 +160,8 @@ static void post(QUEUE* q, enum uv__work_kind kind) { } +void uv__threadpool_cleanup(void) { #ifndef _WIN32 -UV_DESTRUCTOR(static void cleanup(void)) { unsigned int i; if (nthreads == 0) @@ -181,8 +181,8 @@ UV_DESTRUCTOR(static void cleanup(void)) { threads = NULL; nthreads = 0; -} #endif +} static void init_threads(void) { diff --git a/src/unix/aix.c b/src/unix/aix.c index 417ee5512..6b4594b43 100644 --- a/src/unix/aix.c +++ b/src/unix/aix.c @@ -926,7 +926,7 @@ int uv_get_process_title(char* buffer, size_t size) { } -UV_DESTRUCTOR(static void free_args_mem(void)) { +void uv__process_title_cleanup(void) { uv__free(args_mem); /* Keep valgrind happy. */ args_mem = NULL; } diff --git a/src/unix/bsd-proctitle.c b/src/unix/bsd-proctitle.c index 0ce47c8f6..723b81c01 100644 --- a/src/unix/bsd-proctitle.c +++ b/src/unix/bsd-proctitle.c @@ -37,6 +37,13 @@ static void init_process_title_mutex_once(void) { } +void uv__process_title_cleanup(void) { + /* TODO(bnoordhuis) uv_mutex_destroy(&process_title_mutex) + * and reset process_title_mutex_once? + */ +} + + char** uv_setup_args(int argc, char** argv) { process_title = argc > 0 ? uv__strdup(argv[0]) : NULL; return argv; diff --git a/src/unix/internal.h b/src/unix/internal.h index 598554b60..402ee877d 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -106,10 +106,8 @@ int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset); #if defined(__clang__) || \ defined(__GNUC__) || \ defined(__INTEL_COMPILER) -# define UV_DESTRUCTOR(declaration) __attribute__((destructor)) declaration # define UV_UNUSED(declaration) __attribute__((unused)) declaration #else -# define UV_DESTRUCTOR(declaration) declaration # define UV_UNUSED(declaration) declaration #endif diff --git a/src/unix/no-proctitle.c b/src/unix/no-proctitle.c index 165740ca3..32aa0af1f 100644 --- a/src/unix/no-proctitle.c +++ b/src/unix/no-proctitle.c @@ -29,6 +29,9 @@ char** uv_setup_args(int argc, char** argv) { return argv; } +void uv__process_title_cleanup(void) { +} + int uv_set_process_title(const char* title) { return 0; } diff --git a/src/unix/proctitle.c b/src/unix/proctitle.c index d124d3c7f..4ee991fcc 100644 --- a/src/unix/proctitle.c +++ b/src/unix/proctitle.c @@ -145,7 +145,7 @@ int uv_get_process_title(char* buffer, size_t size) { } -UV_DESTRUCTOR(static void free_args_mem(void)) { +void uv__process_title_cleanup(void) { uv__free(args_mem); /* Keep valgrind happy. */ args_mem = NULL; } diff --git a/src/unix/signal.c b/src/unix/signal.c index 1e7e8ac57..1c83e095b 100644 --- a/src/unix/signal.c +++ b/src/unix/signal.c @@ -77,7 +77,7 @@ static void uv__signal_global_init(void) { } -UV_DESTRUCTOR(static void uv__signal_global_fini(void)) { +void uv__signal_cleanup(void) { /* We can only use signal-safe functions here. * That includes read/write and close, fortunately. * We do all of this directly here instead of resetting @@ -98,7 +98,7 @@ UV_DESTRUCTOR(static void uv__signal_global_fini(void)) { static void uv__signal_global_reinit(void) { - uv__signal_global_fini(); + uv__signal_cleanup(); if (uv__make_pipe(uv__signal_lock_pipefd, 0)) abort(); diff --git a/src/uv-common.c b/src/uv-common.c index 5cb1a8c89..f69a2c1d4 100644 --- a/src/uv-common.c +++ b/src/uv-common.c @@ -821,3 +821,19 @@ void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { uv__free(cpu_infos); } + + +#ifdef __GNUC__ /* Also covers __clang__ and __INTEL_COMPILER. */ +__attribute__((destructor)) +#endif +void uv_library_shutdown(void) { + static int was_shutdown; + + if (was_shutdown) + return; + + uv__process_title_cleanup(); + uv__signal_cleanup(); + uv__threadpool_cleanup(); + was_shutdown = 1; +} diff --git a/src/uv-common.h b/src/uv-common.h index f08fb8ae0..28cc852ce 100644 --- a/src/uv-common.h +++ b/src/uv-common.h @@ -201,6 +201,10 @@ int uv__next_timeout(const uv_loop_t* loop); void uv__run_timers(uv_loop_t* loop); void uv__timer_close(uv_timer_t* handle); +void uv__process_title_cleanup(void); +void uv__signal_cleanup(void); +void uv__threadpool_cleanup(void); + #define uv__has_active_reqs(loop) \ ((loop)->active_reqs.count > 0) diff --git a/src/win/signal.c b/src/win/signal.c index 276dc6097..3d9f92cfb 100644 --- a/src/win/signal.c +++ b/src/win/signal.c @@ -46,6 +46,11 @@ void uv_signals_init(void) { } +void uv__signal_cleanup(void) { + /* TODO(bnoordhuis) Undo effects of uv_signal_init()? */ +} + + static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2) { /* Compare signums first so all watchers with the same signnum end up * adjacent. */ diff --git a/src/win/util.c b/src/win/util.c index aaa7ba032..ff8a28b43 100644 --- a/src/win/util.c +++ b/src/win/util.c @@ -361,6 +361,10 @@ char** uv_setup_args(int argc, char** argv) { } +void uv__process_title_cleanup(void) { +} + + int uv_set_process_title(const char* title) { int err; int length; diff --git a/test/task.h b/test/task.h index 27b731032..e95e3bde5 100644 --- a/test/task.h +++ b/test/task.h @@ -230,6 +230,7 @@ typedef enum { do { \ close_loop(uv_default_loop()); \ ASSERT(0 == uv_loop_close(uv_default_loop())); \ + uv_library_shutdown(); \ } while (0) /* Just sugar for wrapping the main() for a task or helper. */