Skip to content

Commit e194202

Browse files
committed
Fix the signal story on linux
- export _dispatch_sigmask() as the way to block signals in a process using dispatch - call it when dispatch_main() is called - each time dispatch registers a signal handler, register a sigaction that will catch signals for threads with wrong masks and will fix the thread configuration then raise the signal again so that it is delivered to the signalfd as expected. Patch by Pierre Habouzit <phabouzit@apple.com> from PR-231 with minor compilation fixes by Dave Grove.
1 parent 257757d commit e194202

File tree

4 files changed

+48
-39
lines changed

4 files changed

+48
-39
lines changed

src/event/event_epoll.c

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,26 @@ _dispatch_muxnote_dispose(dispatch_muxnote_t dmn)
117117
free(dmn);
118118
}
119119

120+
static void
121+
_dispatch_muxnote_signal_block_and_raise(int signo)
122+
{
123+
// On linux, for signals to be delivered to the signalfd, signals
124+
// must be blocked, else any thread that hasn't them blocked may
125+
// receive them. Fix that by lazily noticing, blocking said signal,
126+
// and raising the signal again when it happens
127+
_dispatch_sigmask();
128+
raise(signo);
129+
}
130+
120131
static dispatch_muxnote_t
121132
_dispatch_muxnote_create(dispatch_unote_t du, uint32_t events)
122133
{
134+
static sigset_t signals_with_unotes;
135+
static struct sigaction sa = {
136+
.sa_handler = _dispatch_muxnote_signal_block_and_raise,
137+
.sa_flags = SA_RESTART,
138+
};
139+
123140
dispatch_muxnote_t dmn;
124141
struct stat sb;
125142
int fd = du._du->du_ident;
@@ -129,13 +146,16 @@ _dispatch_muxnote_create(dispatch_unote_t du, uint32_t events)
129146

130147
switch (filter) {
131148
case EVFILT_SIGNAL:
149+
if (!sigismember(&signals_with_unotes, du._du->du_ident)) {
150+
sigaddset(&signals_with_unotes, du._du->du_ident);
151+
sigaction(du._du->du_ident, &sa, NULL);
152+
}
132153
sigemptyset(&sigmask);
133154
sigaddset(&sigmask, du._du->du_ident);
134155
fd = signalfd(-1, &sigmask, SFD_NONBLOCK | SFD_CLOEXEC);
135156
if (fd < 0) {
136157
return NULL;
137158
}
138-
sigprocmask(SIG_BLOCK, &sigmask, NULL);
139159
break;
140160

141161
case EVFILT_WRITE:

src/init.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,29 @@ dispatch_atfork_child(void)
7272
_dispatch_unsafe_fork = 0;
7373
}
7474

75+
int
76+
_dispatch_sigmask(void)
77+
{
78+
sigset_t mask;
79+
int r = 0;
80+
81+
/* Workaround: 6269619 Not all signals can be delivered on any thread */
82+
r |= sigfillset(&mask);
83+
r |= sigdelset(&mask, SIGILL);
84+
r |= sigdelset(&mask, SIGTRAP);
85+
#if HAVE_DECL_SIGEMT
86+
r |= sigdelset(&mask, SIGEMT);
87+
#endif
88+
r |= sigdelset(&mask, SIGFPE);
89+
r |= sigdelset(&mask, SIGBUS);
90+
r |= sigdelset(&mask, SIGSEGV);
91+
r |= sigdelset(&mask, SIGSYS);
92+
r |= sigdelset(&mask, SIGPIPE);
93+
r |= sigdelset(&mask, SIGPROF);
94+
r |= pthread_sigmask(SIG_BLOCK, &mask, NULL);
95+
(void)dispatch_assume_zero(r);
96+
}
97+
7598
#pragma mark -
7699
#pragma mark dispatch_globals
77100

src/internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -990,6 +990,8 @@ extern int _dispatch_evfilt_machport_direct_enabled;
990990
#endif // DISPATCH_USE_EVFILT_MACHPORT_DIRECT
991991

992992

993+
int _dispatch_sigmask(void);
994+
993995
/* #includes dependent on internal.h */
994996
#include "object_internal.h"
995997
#include "semaphore_internal.h"

src/queue.c

Lines changed: 2 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ static void _dispatch_worker_thread2(int priority, int options, void *context);
6666
#endif
6767
#if DISPATCH_USE_PTHREAD_POOL
6868
static void *_dispatch_worker_thread(void *context);
69-
static int _dispatch_pthread_sigmask(int how, sigset_t *set, sigset_t *oset);
7069
#endif
7170

7271
#if DISPATCH_COCOA_COMPAT
@@ -5466,13 +5465,8 @@ _dispatch_worker_thread(void *context)
54665465
pqc->dpq_thread_configure();
54675466
}
54685467

5469-
sigset_t mask;
5470-
int r;
54715468
// workaround tweaks the kernel workqueue does for us
5472-
r = sigfillset(&mask);
5473-
(void)dispatch_assume_zero(r);
5474-
r = _dispatch_pthread_sigmask(SIG_BLOCK, &mask, NULL);
5475-
(void)dispatch_assume_zero(r);
5469+
_dispatch_sigmask();
54765470
_dispatch_introspection_thread_add();
54775471

54785472
const int64_t timeout = 5ull * NSEC_PER_SEC;
@@ -5489,37 +5483,6 @@ _dispatch_worker_thread(void *context)
54895483

54905484
return NULL;
54915485
}
5492-
5493-
int
5494-
_dispatch_pthread_sigmask(int how, sigset_t *set, sigset_t *oset)
5495-
{
5496-
int r;
5497-
5498-
/* Workaround: 6269619 Not all signals can be delivered on any thread */
5499-
5500-
r = sigdelset(set, SIGILL);
5501-
(void)dispatch_assume_zero(r);
5502-
r = sigdelset(set, SIGTRAP);
5503-
(void)dispatch_assume_zero(r);
5504-
#if HAVE_DECL_SIGEMT
5505-
r = sigdelset(set, SIGEMT);
5506-
(void)dispatch_assume_zero(r);
5507-
#endif
5508-
r = sigdelset(set, SIGFPE);
5509-
(void)dispatch_assume_zero(r);
5510-
r = sigdelset(set, SIGBUS);
5511-
(void)dispatch_assume_zero(r);
5512-
r = sigdelset(set, SIGSEGV);
5513-
(void)dispatch_assume_zero(r);
5514-
r = sigdelset(set, SIGSYS);
5515-
(void)dispatch_assume_zero(r);
5516-
r = sigdelset(set, SIGPIPE);
5517-
(void)dispatch_assume_zero(r);
5518-
r = sigdelset(set, SIGPROF);
5519-
(void)dispatch_assume_zero(r);
5520-
5521-
return pthread_sigmask(how, set, oset);
5522-
}
55235486
#endif // DISPATCH_USE_PTHREAD_POOL
55245487

55255488
#pragma mark -
@@ -5749,6 +5712,7 @@ dispatch_main(void)
57495712
pthread_key_t dispatch_main_key;
57505713
pthread_key_create(&dispatch_main_key, _dispatch_sig_thread);
57515714
pthread_setspecific(dispatch_main_key, &dispatch_main_key);
5715+
_dispatch_sigmask();
57525716
#endif
57535717
pthread_exit(NULL);
57545718
DISPATCH_INTERNAL_CRASH(errno, "pthread_exit() returned");

0 commit comments

Comments
 (0)