Skip to content

Commit

Permalink
kqueue: Ignore EPIPE coming out of kevent (#584)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
carllerche authored Apr 7, 2017
1 parent c401da5 commit fe551aa
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 8 deletions.
36 changes: 30 additions & 6 deletions src/sys/unix/kqueue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
Expand Down
4 changes: 2 additions & 2 deletions test/test_broken_pipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ pub fn broken_pipe() {
let mut event_loop: EventLoop<BrokenPipeHandler> = 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);
Expand Down

0 comments on commit fe551aa

Please sign in to comment.