Skip to content

Commit d3bb7e5

Browse files
dgrove-ossdas
authored andcommitted
Linux: avoid zombie process when dispatch_main is called
If the main thread calls pthread_exit on Linux, the process becomes a zombie. Modify dispatch_main to avoid this by adding a pthread_key destructor that calls dispatch_sig_thread (effectively stalling pthread exit until the program really exits). This patch relies on the TSD destructors being called in order of creation, which is currently the case in glibc. Signed-off-by: Daniel A. Steffen <dsteffen@apple.com>
1 parent 2ab938a commit d3bb7e5

File tree

1 file changed

+15
-0
lines changed

1 file changed

+15
-0
lines changed

src/queue.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
#define pthread_workqueue_t void*
4949
#endif
5050

51+
static void _dispatch_sig_thread(void *ctxt);
5152
static void _dispatch_cache_cleanup(void *value);
5253
static void _dispatch_sync_f(dispatch_queue_t dq, void *ctxt,
5354
dispatch_function_t func, pthread_priority_t pp);
@@ -5690,6 +5691,17 @@ dispatch_main(void)
56905691
_dispatch_object_debug(&_dispatch_main_q, "%s", __func__);
56915692
_dispatch_program_is_probably_callback_driven = true;
56925693
_dispatch_ktrace0(ARIADNE_ENTER_DISPATCH_MAIN_CODE);
5694+
#ifdef __linux__
5695+
// On Linux, if the main thread calls pthread_exit, the process becomes a zombie.
5696+
// To avoid that, just before calling pthread_exit we register a TSD destructor
5697+
// that will call _dispatch_sig_thread -- thus capturing the main thread in sigsuspend.
5698+
// This relies on an implementation detail (currently true in glibc) that TSD destructors
5699+
// will be called in the order of creation to cause all the TSD cleanup functions to
5700+
// run before the thread becomes trapped in sigsuspend.
5701+
pthread_key_t dispatch_main_key;
5702+
pthread_key_create(&dispatch_main_key, _dispatch_sig_thread);
5703+
pthread_setspecific(dispatch_main_key, &dispatch_main_key);
5704+
#endif
56935705
pthread_exit(NULL);
56945706
DISPATCH_INTERNAL_CRASH(errno, "pthread_exit() returned");
56955707
#if HAVE_PTHREAD_MAIN_NP
@@ -5773,11 +5785,14 @@ _dispatch_queue_cleanup2(void)
57735785
// overload the "probably" variable to mean that dispatch_main() or
57745786
// similar non-POSIX API was called
57755787
// this has to run before the DISPATCH_COCOA_COMPAT below
5788+
// See dispatch_main for call to _dispatch_sig_thread on linux.
5789+
#ifndef __linux__
57765790
if (_dispatch_program_is_probably_callback_driven) {
57775791
_dispatch_barrier_async_detached_f(_dispatch_get_root_queue(
57785792
_DISPATCH_QOS_CLASS_DEFAULT, true), NULL, _dispatch_sig_thread);
57795793
sleep(1); // workaround 6778970
57805794
}
5795+
#endif
57815796

57825797
#if DISPATCH_COCOA_COMPAT
57835798
dispatch_once_f(&_dispatch_main_q_port_pred, dq,

0 commit comments

Comments
 (0)