handle EPOLLHUP on Linux
If epoll_wait() reports EPOLLHUP, we must respond immediately and
unregister the event or else Dispatch will go into a spinloop.
This makes dispatch_io_pipe_close pass on Linux with identical
output to Darwin.
@@ -549,6 +549,20 @@ _dispatch_get_buffer_size(dispatch_muxnote_t dmn, bool writer)
return (uintptr_t)n;
}
+static void
+_dispatch_event_merge_hangup(dispatch_unote_t du)
+{
+ // consumed by dux_merge_evt()
+ _dispatch_retain_unote_owner(du);
+ dispatch_unote_state_t du_state = _dispatch_unote_state(du);
+ du_state |= DU_STATE_NEEDS_DELETE;
+ du_state &= ~DU_STATE_ARMED;
+ _dispatch_unote_state_set(du, du_state);
+ uintptr_t data = 0; // EOF
+ os_atomic_store2o(du._dr, ds_pending_data, ~data, relaxed);
+ dux_merge_evt(du._du, EV_DELETE|EV_DISPATCH, data, 0);
+}
+
static void
_dispatch_event_merge_fd(dispatch_muxnote_t dmn, uint32_t events)
{
@@ -583,6 +597,20 @@ _dispatch_event_merge_fd(dispatch_muxnote_t dmn, uint32_t events)
}
}
+ // SR-9033: EPOLLHUP is an unmaskable event which we must respond to
+ if (events & EPOLLHUP) {
+ LIST_FOREACH_SAFE(dul, &dmn->dmn_readers_head, du_link, dul_next) {
+ dispatch_unote_t du = _dispatch_unote_linkage_get_unote(dul);
+ _dispatch_event_merge_hangup(du);
+ }
+ LIST_FOREACH_SAFE(dul, &dmn->dmn_writers_head, du_link, dul_next) {
+ dispatch_unote_t du = _dispatch_unote_linkage_get_unote(dul);
+ _dispatch_event_merge_hangup(du);
+ }
+ epoll_ctl(_dispatch_epfd, EPOLL_CTL_DEL, dmn->dmn_fd, NULL);
+ return;
+ }
+
events = _dispatch_muxnote_armed_events(dmn);
if (events) _dispatch_epoll_update(dmn, events, EPOLL_CTL_MOD);
}
