Merge pull request #279 from apple/mad/greedy-signalfd

Fix improper double-fire of signal sources on Linux
diff --git a/src/event/event_epoll.c b/src/event/event_epoll.c
index c003bac..c86421b 100644
--- a/src/event/event_epoll.c
+++ b/src/event/event_epoll.c
@@ -474,12 +474,24 @@
 {
 	dispatch_unote_linkage_t dul, dul_next;
 	struct signalfd_siginfo si;
+	ssize_t rc;
 
-	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);
+	// 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);
 	}
 }