Merge pull request #295 from apple/mad/SR-5759

Track armed events in muxnotes closely
diff --git a/src/event/event_epoll.c b/src/event/event_epoll.c
index add4dde..a3dc17c 100644
--- a/src/event/event_epoll.c
+++ b/src/event/event_epoll.c
@@ -47,12 +47,13 @@
 	TAILQ_ENTRY(dispatch_muxnote_s) dmn_list;
 	TAILQ_HEAD(, dispatch_unote_linkage_s) dmn_readers_head;
 	TAILQ_HEAD(, dispatch_unote_linkage_s) dmn_writers_head;
-	int     dmn_fd;
-	uint32_t dmn_ident;
-	uint32_t dmn_events;
-	int16_t dmn_filter;
-	bool    dmn_skip_outq_ioctl;
-	bool    dmn_skip_inq_ioctl;
+	int       dmn_fd;
+	uint32_t  dmn_ident;
+	uint32_t  dmn_events;
+	uint16_t  dmn_disarmed_events;
+	int8_t    dmn_filter;
+	bool      dmn_skip_outq_ioctl : 1;
+	bool      dmn_skip_inq_ioctl : 1;
 } *dispatch_muxnote_t;
 
 typedef struct dispatch_epoll_timeout_s {
@@ -84,6 +85,13 @@
 #pragma mark dispatch_muxnote_t
 
 DISPATCH_ALWAYS_INLINE
+static inline uint32_t
+_dispatch_muxnote_armed_events(dispatch_muxnote_t dmn)
+{
+	return dmn->dmn_events & ~dmn->dmn_disarmed_events;
+}
+
+DISPATCH_ALWAYS_INLINE
 static inline struct dispatch_muxnote_bucket_s *
 _dispatch_muxnote_bucket(uint32_t ident)
 {
@@ -95,7 +103,7 @@
 DISPATCH_ALWAYS_INLINE
 static inline dispatch_muxnote_t
 _dispatch_muxnote_find(struct dispatch_muxnote_bucket_s *dmb,
-		uint32_t ident, int16_t filter)
+		uint32_t ident, int8_t filter)
 {
 	dispatch_muxnote_t dmn;
 	if (filter == EVFILT_WRITE) filter = EVFILT_READ;
@@ -143,7 +151,7 @@
 	dispatch_muxnote_t dmn;
 	struct stat sb;
 	int fd = (int)du._du->du_ident;
-	int16_t filter = du._du->du_filter;
+	int8_t filter = du._du->du_filter;
 	bool skip_outq_ioctl = false, skip_inq_ioctl = false;
 	sigset_t sigmask;
 
@@ -207,33 +215,27 @@
 #pragma mark dispatch_unote_t
 
 static int
-_dispatch_epoll_update(dispatch_muxnote_t dmn, int op)
+_dispatch_epoll_update(dispatch_muxnote_t dmn, uint32_t events, int op)
 {
 	dispatch_once_f(&epoll_init_pred, NULL, _dispatch_epoll_init);
 	struct epoll_event ev = {
-		.events = dmn->dmn_events,
+		.events = events,
 		.data = { .ptr = dmn },
 	};
 	return epoll_ctl(_dispatch_epfd, op, dmn->dmn_fd, &ev);
 }
 
-bool
-_dispatch_unote_register(dispatch_unote_t du,
-		DISPATCH_UNUSED dispatch_wlh_t wlh, dispatch_priority_t pri)
+DISPATCH_ALWAYS_INLINE
+static inline uint32_t
+_dispatch_unote_required_events(dispatch_unote_t du)
 {
-	struct dispatch_muxnote_bucket_s *dmb;
-	dispatch_muxnote_t dmn;
 	uint32_t events = EPOLLFREE;
 
-	dispatch_assert(!_dispatch_unote_registered(du));
-	du._du->du_priority = pri;
-
 	switch (du._du->du_filter) {
 	case DISPATCH_EVFILT_CUSTOM_ADD:
 	case DISPATCH_EVFILT_CUSTOM_OR:
 	case DISPATCH_EVFILT_CUSTOM_REPLACE:
-		du._du->du_wlh = DISPATCH_WLH_ANON;
-		return true;
+		return 0;
 	case EVFILT_WRITE:
 		events |= EPOLLOUT;
 		break;
@@ -246,20 +248,35 @@
 		events |= EPOLLONESHOT;
 	}
 
+	return events;
+}
+
+bool
+_dispatch_unote_register(dispatch_unote_t du,
+		DISPATCH_UNUSED dispatch_wlh_t wlh, dispatch_priority_t pri)
+{
+	struct dispatch_muxnote_bucket_s *dmb;
+	dispatch_muxnote_t dmn;
+	uint32_t events = _dispatch_unote_required_events(du);
+
+	dispatch_assert(!_dispatch_unote_registered(du));
+	du._du->du_priority = pri;
+
 	dmb = _dispatch_unote_muxnote_bucket(du);
 	dmn = _dispatch_unote_muxnote_find(dmb, du);
 	if (dmn) {
-		events &= ~dmn->dmn_events;
-		if (events) {
-			dmn->dmn_events |= events;
-			if (_dispatch_epoll_update(dmn, EPOLL_CTL_MOD) < 0) {
-				dmn->dmn_events &= ~events;
+		if (events & ~_dispatch_muxnote_armed_events(dmn)) {
+			events |= _dispatch_muxnote_armed_events(dmn);
+			if (_dispatch_epoll_update(dmn, events, EPOLL_CTL_MOD) < 0) {
 				dmn = NULL;
+			} else {
+				dmn->dmn_events = events;
+				dmn->dmn_disarmed_events &= ~events;
 			}
 		}
 	} else {
 		dmn = _dispatch_muxnote_create(du, events);
-		if (_dispatch_epoll_update(dmn, EPOLL_CTL_ADD) < 0) {
+		if (_dispatch_epoll_update(dmn, events, EPOLL_CTL_ADD) < 0) {
 			_dispatch_muxnote_dispose(dmn);
 			dmn = NULL;
 		} else {
@@ -286,8 +303,13 @@
 {
 	dispatch_muxnote_t dmn = _dispatch_unote_get_linkage(du)->du_muxnote;
 	dispatch_assert(_dispatch_unote_registered(du));
+	uint32_t events = _dispatch_unote_required_events(du);
 
-	_dispatch_epoll_update(dmn, EPOLL_CTL_MOD);
+	if (events & dmn->dmn_disarmed_events) {
+		dmn->dmn_disarmed_events &= ~events;
+		events = _dispatch_muxnote_armed_events(dmn);
+		_dispatch_epoll_update(dmn, events, EPOLL_CTL_MOD);
+	}
 }
 
 bool
@@ -314,17 +336,26 @@
 		dul->du_muxnote = NULL;
 
 		if (TAILQ_EMPTY(&dmn->dmn_readers_head)) {
-			events &= (uint32_t)(~EPOLLIN);
+			events &= (uint32_t)~EPOLLIN;
+			if (dmn->dmn_disarmed_events & EPOLLIN) {
+				dmn->dmn_disarmed_events &= (uint16_t)~EPOLLIN;
+				dmn->dmn_events &= (uint32_t)~EPOLLIN;
+			}
 		}
 		if (TAILQ_EMPTY(&dmn->dmn_writers_head)) {
-			events &= (uint32_t)(~EPOLLOUT);
+			events &= (uint32_t)~EPOLLOUT;
+			if (dmn->dmn_disarmed_events & EPOLLOUT) {
+				dmn->dmn_disarmed_events &= (uint16_t)~EPOLLOUT;
+				dmn->dmn_events &= (uint32_t)~EPOLLOUT;
+			}
 		}
 
-		if (events == dmn->dmn_events) {
-			// nothing to do
-		} else if (events & (EPOLLIN | EPOLLOUT)) {
-			dmn->dmn_events = events;
-			_dispatch_epoll_update(dmn, EPOLL_CTL_MOD);
+		if (events & (EPOLLIN | EPOLLOUT)) {
+			if (events != _dispatch_muxnote_armed_events(dmn)) {
+				dmn->dmn_events = events;
+				events = _dispatch_muxnote_armed_events(dmn);
+				_dispatch_epoll_update(dmn, events, EPOLL_CTL_MOD);
+			}
 		} else {
 			epoll_ctl(_dispatch_epfd, EPOLL_CTL_DEL, dmn->dmn_fd, NULL);
 			TAILQ_REMOVE(_dispatch_unote_muxnote_bucket(du), dmn, dmn_list);
@@ -533,6 +564,8 @@
 	dispatch_unote_linkage_t dul, dul_next;
 	uintptr_t data;
 
+	dmn->dmn_disarmed_events |= (events & (EPOLLIN | EPOLLOUT));
+
 	if (events & EPOLLIN) {
 		data = _dispatch_get_buffer_size(dmn, false);
 		TAILQ_FOREACH_SAFE(dul, &dmn->dmn_readers_head, du_link, dul_next) {
@@ -548,6 +581,9 @@
 			dux_merge_evt(du._du, EV_ADD|EV_ENABLE|EV_DISPATCH, ~data, 0, 0);
 		}
 	}
+
+	events = _dispatch_muxnote_armed_events(dmn);
+	if (events) _dispatch_epoll_update(dmn, events, EPOLL_CTL_MOD);
 }
 
 DISPATCH_NOINLINE