Skip to content

Commit 1dea4f9

Browse files
committed
Fix improper double-fire of signal sources on Linux
1 parent 602604c commit 1dea4f9

File tree

1 file changed

+18
-6
lines changed

1 file changed

+18
-6
lines changed

src/event/event_epoll.c

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -467,12 +467,24 @@ _dispatch_event_merge_signal(dispatch_muxnote_t dmn)
467467
{
468468
dispatch_unote_linkage_t dul, dul_next;
469469
struct signalfd_siginfo si;
470-
471-
dispatch_assume(read(dmn->dmn_fd, &si, sizeof(si)) == sizeof(si));
472-
473-
TAILQ_FOREACH_SAFE(dul, &dmn->dmn_readers_head, du_link, dul_next) {
474-
dispatch_unote_t du = _dispatch_unote_linkage_get_unote(dul);
475-
dux_merge_evt(du._du, EV_ADD|EV_ENABLE|EV_CLEAR, 1, 0, 0);
470+
ssize_t rc;
471+
472+
// Linux has the weirdest semantics around signals: if it finds a thread
473+
// that has not masked a process wide-signal, it may deliver it to this
474+
// thread, meaning that the signalfd may have been made readable, but the
475+
// signal consumed through the legacy delivery mechanism.
476+
//
477+
// Because of this we can get a misfire of the signalfd yielding EAGAIN the
478+
// first time around. The _dispatch_muxnote_signal_block_and_raise() hack
479+
// will kick in, the thread with the wrong mask will be fixed up, and the
480+
// signal delivered to us again properly.
481+
if ((rc = read(dmn->dmn_fd, &si, sizeof(si))) == sizeof(si)) {
482+
TAILQ_FOREACH_SAFE(dul, &dmn->dmn_readers_head, du_link, dul_next) {
483+
dispatch_unote_t du = _dispatch_unote_linkage_get_unote(dul);
484+
dux_merge_evt(du._du, EV_ADD|EV_ENABLE|EV_CLEAR, 1, 0, 0);
485+
}
486+
} else {
487+
dispatch_assume(rc == -1 && errno == EAGAIN);
476488
}
477489
}
478490

0 commit comments

Comments
 (0)