diff --git a/src/event/event_epoll.c b/src/event/event_epoll.c index 2788b1008..647552f65 100644 --- a/src/event/event_epoll.c +++ b/src/event/event_epoll.c @@ -117,9 +117,28 @@ _dispatch_muxnote_dispose(dispatch_muxnote_t dmn) free(dmn); } +static pthread_t manager_thread; + +static void +_dispatch_muxnote_signal_block_and_raise(int signo) +{ + // On linux, for signals to be delivered to the signalfd, signals + // must be blocked, else any thread that hasn't them blocked may + // receive them. Fix that by lazily noticing, blocking said signal, + // and raising the signal again when it happens + _dispatch_sigmask(); + pthread_kill(manager_thread, signo); +} + static dispatch_muxnote_t _dispatch_muxnote_create(dispatch_unote_t du, uint32_t events) { + static sigset_t signals_with_unotes; + static struct sigaction sa = { + .sa_handler = _dispatch_muxnote_signal_block_and_raise, + .sa_flags = SA_RESTART, + }; + dispatch_muxnote_t dmn; struct stat sb; int fd = du._du->du_ident; @@ -129,13 +148,17 @@ _dispatch_muxnote_create(dispatch_unote_t du, uint32_t events) switch (filter) { case EVFILT_SIGNAL: + if (!sigismember(&signals_with_unotes, du._du->du_ident)) { + manager_thread = pthread_self(); + sigaddset(&signals_with_unotes, du._du->du_ident); + sigaction(du._du->du_ident, &sa, NULL); + } sigemptyset(&sigmask); sigaddset(&sigmask, du._du->du_ident); fd = signalfd(-1, &sigmask, SFD_NONBLOCK | SFD_CLOEXEC); if (fd < 0) { return NULL; } - sigprocmask(SIG_BLOCK, &sigmask, NULL); break; case EVFILT_WRITE: diff --git a/src/init.c b/src/init.c index a04daeb96..4ccb1f2d9 100644 --- a/src/init.c +++ b/src/init.c @@ -72,6 +72,29 @@ dispatch_atfork_child(void) _dispatch_unsafe_fork = 0; } +int +_dispatch_sigmask(void) +{ + sigset_t mask; + int r = 0; + + /* Workaround: 6269619 Not all signals can be delivered on any thread */ + r |= sigfillset(&mask); + r |= sigdelset(&mask, SIGILL); + r |= sigdelset(&mask, SIGTRAP); +#if HAVE_DECL_SIGEMT + r |= sigdelset(&mask, SIGEMT); +#endif + r |= sigdelset(&mask, SIGFPE); + r |= sigdelset(&mask, SIGBUS); + r |= sigdelset(&mask, SIGSEGV); + r |= sigdelset(&mask, SIGSYS); + r |= sigdelset(&mask, SIGPIPE); + r |= sigdelset(&mask, SIGPROF); + r |= pthread_sigmask(SIG_BLOCK, &mask, NULL); + (void)dispatch_assume_zero(r); +} + #pragma mark - #pragma mark dispatch_globals diff --git a/src/internal.h b/src/internal.h index 743d0b2c6..489da74a8 100644 --- a/src/internal.h +++ b/src/internal.h @@ -990,6 +990,8 @@ extern int _dispatch_evfilt_machport_direct_enabled; #endif // DISPATCH_USE_EVFILT_MACHPORT_DIRECT +int _dispatch_sigmask(void); + /* #includes dependent on internal.h */ #include "object_internal.h" #include "semaphore_internal.h" diff --git a/src/queue.c b/src/queue.c index 088c5cfd2..be831ab4c 100644 --- a/src/queue.c +++ b/src/queue.c @@ -66,7 +66,6 @@ static void _dispatch_worker_thread2(int priority, int options, void *context); #endif #if DISPATCH_USE_PTHREAD_POOL static void *_dispatch_worker_thread(void *context); -static int _dispatch_pthread_sigmask(int how, sigset_t *set, sigset_t *oset); #endif #if DISPATCH_COCOA_COMPAT @@ -5466,13 +5465,8 @@ _dispatch_worker_thread(void *context) pqc->dpq_thread_configure(); } - sigset_t mask; - int r; // workaround tweaks the kernel workqueue does for us - r = sigfillset(&mask); - (void)dispatch_assume_zero(r); - r = _dispatch_pthread_sigmask(SIG_BLOCK, &mask, NULL); - (void)dispatch_assume_zero(r); + _dispatch_sigmask(); _dispatch_introspection_thread_add(); const int64_t timeout = 5ull * NSEC_PER_SEC; @@ -5489,37 +5483,6 @@ _dispatch_worker_thread(void *context) return NULL; } - -int -_dispatch_pthread_sigmask(int how, sigset_t *set, sigset_t *oset) -{ - int r; - - /* Workaround: 6269619 Not all signals can be delivered on any thread */ - - r = sigdelset(set, SIGILL); - (void)dispatch_assume_zero(r); - r = sigdelset(set, SIGTRAP); - (void)dispatch_assume_zero(r); -#if HAVE_DECL_SIGEMT - r = sigdelset(set, SIGEMT); - (void)dispatch_assume_zero(r); -#endif - r = sigdelset(set, SIGFPE); - (void)dispatch_assume_zero(r); - r = sigdelset(set, SIGBUS); - (void)dispatch_assume_zero(r); - r = sigdelset(set, SIGSEGV); - (void)dispatch_assume_zero(r); - r = sigdelset(set, SIGSYS); - (void)dispatch_assume_zero(r); - r = sigdelset(set, SIGPIPE); - (void)dispatch_assume_zero(r); - r = sigdelset(set, SIGPROF); - (void)dispatch_assume_zero(r); - - return pthread_sigmask(how, set, oset); -} #endif // DISPATCH_USE_PTHREAD_POOL #pragma mark - @@ -5749,6 +5712,7 @@ dispatch_main(void) pthread_key_t dispatch_main_key; pthread_key_create(&dispatch_main_key, _dispatch_sig_thread); pthread_setspecific(dispatch_main_key, &dispatch_main_key); + _dispatch_sigmask(); #endif pthread_exit(NULL); DISPATCH_INTERNAL_CRASH(errno, "pthread_exit() returned");