diff --git a/src/posix/poll.rs b/src/posix/poll.rs index e45226ea..1872c65f 100644 --- a/src/posix/poll.rs +++ b/src/posix/poll.rs @@ -5,11 +5,12 @@ use std::os::unix::io::RawFd; use std::slice; use std::time::Duration; +use nix::libc::c_int; use nix::poll::{PollFd, PollFlags}; #[cfg(target_os = "linux")] use nix::sys::signal::SigSet; #[cfg(target_os = "linux")] -use nix::sys::time::{TimeSpec, TimeValLike}; +use nix::sys::time::TimeSpec; pub fn wait_read_fd(fd: RawFd, timeout: Duration) -> io::Result<()> { wait_fd(fd, PollFlags::POLLIN, timeout) @@ -24,23 +25,12 @@ fn wait_fd(fd: RawFd, events: PollFlags, timeout: Duration) -> io::Result<()> { let mut fd = PollFd::new(fd, events); - let milliseconds = - timeout.as_secs() as i64 * 1000 + i64::from(timeout.subsec_nanos()) / 1_000_000; - #[cfg(target_os = "linux")] - let wait_res = { - let timespec = TimeSpec::milliseconds(milliseconds); - nix::poll::ppoll( - slice::from_mut(&mut fd), - Some(timespec), - Some(SigSet::empty()), - ) - }; - #[cfg(not(target_os = "linux"))] - let wait_res = nix::poll::poll(slice::from_mut(&mut fd), milliseconds as nix::libc::c_int); - - let wait = match wait_res { + let wait = match poll_clamped(&mut fd, timeout) { Ok(r) => r, - Err(e) => return Err(io::Error::from(crate::Error::from(e))), + Err(e) => { + dbg!(e); + return Err(io::Error::from(crate::Error::from(e))); + } }; // All errors generated by poll or ppoll are already caught by the nix wrapper around libc, so // here we only need to check if there's at least 1 event @@ -63,3 +53,39 @@ fn wait_fd(fd: RawFd, events: PollFlags, timeout: Duration) -> io::Result<()> { Err(io::Error::new(io::ErrorKind::Other, EIO.desc())) } + +/// Poll with a duration clamped to the maximum value representable by the `TimeSpec` used by +/// `ppoll`. +#[cfg(target_os = "linux")] +fn poll_clamped(fd: &mut PollFd, timeout: Duration) -> nix::Result { + use nix::libc::c_long; + use nix::sys::time::time_t; + + // We need to clamp manually as TimeSpec::from_duration translates durations with more than + // i64::MAX seconds to negative timespans. This happens due to casting to i64 and is still the + // case as of nix 0.29. + let secs_limit = time_t::MAX as u64; + let secs = timeout.as_secs(); + let spec = if secs <= secs_limit { + TimeSpec::new(secs as time_t, timeout.subsec_nanos() as c_long) + } else { + TimeSpec::new(time_t::MAX, 999_999_999) + }; + + nix::poll::ppoll(slice::from_mut(fd), Some(spec), Some(SigSet::empty())) +} + +// Poll with a duration clamped to the maximum millisecond value representable by the `c_int` used +// by `poll`. +#[cfg(not(target_os = "linux"))] +fn poll_clamped(fd: &mut PollFd, timeout: Duration) -> nix::Result { + let secs_limit = (c_int::MAX as u64) / 1000; + let secs = timeout.as_secs(); + let millis = if secs <= secs_limit { + secs as c_int * 1000 + timeout.subsec_millis() as c_int + } else { + c_int::MAX + }; + + nix::poll::poll(slice::from_mut(fd), millis) +}