From 1dea4f9c7af323db68f7dd22f54836ab82c60a62 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Fri, 14 Jul 2017 11:57:58 -0700 Subject: [PATCH] Fix improper double-fire of signal sources on Linux --- src/event/event_epoll.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/event/event_epoll.c b/src/event/event_epoll.c index 68140d50c..dfdd00cdb 100644 --- a/src/event/event_epoll.c +++ b/src/event/event_epoll.c @@ -467,12 +467,24 @@ _dispatch_event_merge_signal(dispatch_muxnote_t dmn) { dispatch_unote_linkage_t dul, dul_next; struct signalfd_siginfo si; - - dispatch_assume(read(dmn->dmn_fd, &si, sizeof(si)) == sizeof(si)); - - TAILQ_FOREACH_SAFE(dul, &dmn->dmn_readers_head, du_link, dul_next) { - dispatch_unote_t du = _dispatch_unote_linkage_get_unote(dul); - dux_merge_evt(du._du, EV_ADD|EV_ENABLE|EV_CLEAR, 1, 0, 0); + ssize_t rc; + + // Linux has the weirdest semantics around signals: if it finds a thread + // that has not masked a process wide-signal, it may deliver it to this + // thread, meaning that the signalfd may have been made readable, but the + // signal consumed through the legacy delivery mechanism. + // + // Because of this we can get a misfire of the signalfd yielding EAGAIN the + // first time around. The _dispatch_muxnote_signal_block_and_raise() hack + // will kick in, the thread with the wrong mask will be fixed up, and the + // signal delivered to us again properly. + if ((rc = read(dmn->dmn_fd, &si, sizeof(si))) == sizeof(si)) { + TAILQ_FOREACH_SAFE(dul, &dmn->dmn_readers_head, du_link, dul_next) { + dispatch_unote_t du = _dispatch_unote_linkage_get_unote(dul); + dux_merge_evt(du._du, EV_ADD|EV_ENABLE|EV_CLEAR, 1, 0, 0); + } + } else { + dispatch_assume(rc == -1 && errno == EAGAIN); } }