From fe551aa8f80e796321177e542b74ff46b7f9552f Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Fri, 7 Apr 2017 14:17:27 -0700 Subject: [PATCH] kqueue: Ignore EPIPE coming out of `kevent` (#584) This commit updates mio to ignore `EPIPE` coming out of `kevent`. It turns out that older versions of OSX (10.10 and 10.11 have been confirmed) will return an `EPIPE` when registering one half of a pipe where the other half has been closed. On other platforms this is an ok operation which just returns that the pipe is readable/writable (to return an error/eof), so this brings the OSX behavior in line by ignoring the error and moving to the next event. Closes #582 --- src/sys/unix/kqueue.rs | 36 ++++++++++++++++++++++++++++++------ test/test_broken_pipe.rs | 4 ++-- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/sys/unix/kqueue.rs b/src/sys/unix/kqueue.rs index 6b8c92b93..b47be1afc 100644 --- a/src/sys/unix/kqueue.rs +++ b/src/sys/unix/kqueue.rs @@ -95,13 +95,37 @@ impl Selector { ::std::ptr::null()))); for change in changes.iter() { debug_assert_eq!(change.flags & libc::EV_ERROR, libc::EV_ERROR); - if change.data != 0 { - // there’s some error, but we want to ignore ENOENT error for EV_DELETE - let orig_flags = if change.filter == libc::EVFILT_READ { r } else { w }; - if !(change.data as i32 == libc::ENOENT && orig_flags & libc::EV_DELETE != 0) { - return Err(::std::io::Error::from_raw_os_error(change.data as i32)); - } + + // Test to see if an error happened + if change.data == 0 { + continue + } + + // Older versions of OSX (10.11 and 10.10 have been witnessed) + // can return EPIPE when registering a pipe file descriptor + // where the other end has already disappeared. For example code + // that creates a pipe, closes a file descriptor, and then + // registers the other end will see an EPIPE returned from + // `register`. + // + // It also turns out that kevent will still report events on the + // file descriptor, telling us that it's readable/hup at least + // after we've done this registration. As a result we just + // ignore `EPIPE` here instead of propagating it. + // + // More info can be found at carllerche/mio#582 + if change.data as i32 == libc::EPIPE && + change.filter == libc::EVFILT_WRITE { + continue } + + // ignore ENOENT error for EV_DELETE + let orig_flags = if change.filter == libc::EVFILT_READ { r } else { w }; + if change.data as i32 == libc::ENOENT && orig_flags & libc::EV_DELETE != 0 { + continue + } + + return Err(::std::io::Error::from_raw_os_error(change.data as i32)); } Ok(()) } diff --git a/test/test_broken_pipe.rs b/test/test_broken_pipe.rs index 1e3b73cd3..1cd0ca746 100644 --- a/test/test_broken_pipe.rs +++ b/test/test_broken_pipe.rs @@ -19,8 +19,8 @@ pub fn broken_pipe() { let mut event_loop: EventLoop = EventLoop::new().unwrap(); let (reader, _) = unix::pipe().unwrap(); - // On Darwin this returns a "broken pipe" error. - let _ = event_loop.register(&reader, Token(1), Ready::all(), PollOpt::edge()); + event_loop.register(&reader, Token(1), Ready::all(), PollOpt::edge()) + .unwrap(); let mut handler = BrokenPipeHandler; drop(reader);