Skip to content

Commit 64e3af6

Browse files
feat: Safer epoll timeout
1 parent 80c2d82 commit 64e3af6

File tree

5 files changed

+249
-238
lines changed

5 files changed

+249
-238
lines changed

src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,11 @@ feature! {
194194
#[allow(missing_docs)]
195195
pub mod unistd;
196196

197+
#[cfg(any(feature = "poll", feature = "event"))]
198+
mod timeout;
199+
#[cfg(any(feature = "poll", feature = "event"))]
200+
pub use timeout::*;
201+
197202
use std::ffi::{CStr, CString, OsStr};
198203
use std::mem::MaybeUninit;
199204
use std::os::unix::ffi::OsStrExt;

src/poll.rs

+8-229
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
//! Wait for events to trigger on specific file descriptors
22
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd};
3-
use std::time::Duration;
43

54
use crate::errno::Errno;
65
use crate::Result;
6+
use crate::Timeout;
7+
78
/// This is a wrapper around `libc::pollfd`.
89
///
910
/// It's meant to be used as an argument to the [`poll`](fn.poll.html) and
@@ -27,13 +28,14 @@ impl<'fd> PollFd<'fd> {
2728
/// ```no_run
2829
/// # use std::os::unix::io::{AsFd, AsRawFd, FromRawFd};
2930
/// # use nix::{
30-
/// # poll::{PollTimeout, PollFd, PollFlags, poll},
31+
/// # Timeout,
32+
/// # poll::{PollFd, PollFlags, poll},
3133
/// # unistd::{pipe, read}
3234
/// # };
3335
/// let (r, w) = pipe().unwrap();
3436
/// let pfd = PollFd::new(r.as_fd(), PollFlags::POLLIN);
3537
/// let mut fds = [pfd];
36-
/// poll(&mut fds, PollTimeout::NONE).unwrap();
38+
/// poll(&mut fds, Timeout::NONE).unwrap();
3739
/// let mut buf = [0u8; 80];
3840
/// read(r.as_raw_fd(), &mut buf[..]);
3941
/// ```
@@ -175,229 +177,6 @@ libc_bitflags! {
175177
}
176178
}
177179

178-
/// Timeout argument for [`poll`].
179-
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
180-
pub struct PollTimeout(i32);
181-
182-
impl PollTimeout {
183-
/// Blocks indefinitely.
184-
///
185-
/// > Specifying a negative value in timeout means an infinite timeout.
186-
pub const NONE: Self = Self(-1);
187-
/// Returns immediately.
188-
///
189-
/// > Specifying a timeout of zero causes poll() to return immediately, even if no file
190-
/// > descriptors are ready.
191-
pub const ZERO: Self = Self(0);
192-
/// Blocks for at most [`std::i32::MAX`] milliseconds.
193-
pub const MAX: Self = Self(i32::MAX);
194-
/// Returns if `self` equals [`PollTimeout::NONE`].
195-
pub fn is_none(&self) -> bool {
196-
// > Specifying a negative value in timeout means an infinite timeout.
197-
*self <= Self::NONE
198-
}
199-
/// Returns if `self` does not equal [`PollTimeout::NONE`].
200-
pub fn is_some(&self) -> bool {
201-
!self.is_none()
202-
}
203-
/// Returns the timeout in milliseconds if there is some, otherwise returns `None`.
204-
pub fn as_millis(&self) -> Option<u32> {
205-
self.is_some().then_some(u32::try_from(self.0).unwrap())
206-
}
207-
/// Returns the timeout as a `Duration` if there is some, otherwise returns `None`.
208-
pub fn timeout(&self) -> Option<Duration> {
209-
self.as_millis()
210-
.map(|x| Duration::from_millis(u64::from(x)))
211-
}
212-
}
213-
214-
/// Error type for integer conversions into `PollTimeout`.
215-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
216-
pub enum PollTimeoutTryFromError {
217-
/// Passing a value less than -1 is invalid on some systems, see
218-
/// <https://man.freebsd.org/cgi/man.cgi?poll#end>.
219-
TooNegative,
220-
/// Passing a value greater than `i32::MAX` is invalid.
221-
TooPositive,
222-
}
223-
224-
impl std::fmt::Display for PollTimeoutTryFromError {
225-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226-
match self {
227-
Self::TooNegative => write!(f, "Passed a negative timeout less than -1."),
228-
Self::TooPositive => write!(f, "Passed a positive timeout greater than `i32::MAX` milliseconds.")
229-
}
230-
}
231-
}
232-
233-
impl std::error::Error for PollTimeoutTryFromError {}
234-
235-
impl<T: Into<PollTimeout>> From<Option<T>> for PollTimeout {
236-
fn from(x: Option<T>) -> Self {
237-
x.map_or(Self::NONE, |x| x.into())
238-
}
239-
}
240-
impl TryFrom<Duration> for PollTimeout {
241-
type Error = PollTimeoutTryFromError;
242-
fn try_from(x: Duration) -> std::result::Result<Self, Self::Error> {
243-
Ok(Self(
244-
i32::try_from(x.as_millis())
245-
.map_err(|_| PollTimeoutTryFromError::TooPositive)?,
246-
))
247-
}
248-
}
249-
impl TryFrom<u128> for PollTimeout {
250-
type Error = PollTimeoutTryFromError;
251-
fn try_from(x: u128) -> std::result::Result<Self, Self::Error> {
252-
Ok(Self(
253-
i32::try_from(x)
254-
.map_err(|_| PollTimeoutTryFromError::TooPositive)?,
255-
))
256-
}
257-
}
258-
impl TryFrom<u64> for PollTimeout {
259-
type Error = PollTimeoutTryFromError;
260-
fn try_from(x: u64) -> std::result::Result<Self, Self::Error> {
261-
Ok(Self(
262-
i32::try_from(x)
263-
.map_err(|_| PollTimeoutTryFromError::TooPositive)?,
264-
))
265-
}
266-
}
267-
impl TryFrom<u32> for PollTimeout {
268-
type Error = PollTimeoutTryFromError;
269-
fn try_from(x: u32) -> std::result::Result<Self, Self::Error> {
270-
Ok(Self(
271-
i32::try_from(x)
272-
.map_err(|_| PollTimeoutTryFromError::TooPositive)?,
273-
))
274-
}
275-
}
276-
impl From<u16> for PollTimeout {
277-
fn from(x: u16) -> Self {
278-
Self(i32::from(x))
279-
}
280-
}
281-
impl From<u8> for PollTimeout {
282-
fn from(x: u8) -> Self {
283-
Self(i32::from(x))
284-
}
285-
}
286-
impl TryFrom<i128> for PollTimeout {
287-
type Error = PollTimeoutTryFromError;
288-
fn try_from(x: i128) -> std::result::Result<Self, Self::Error> {
289-
match x {
290-
..=-2 => Err(PollTimeoutTryFromError::TooNegative),
291-
-1.. => Ok(Self(
292-
i32::try_from(x)
293-
.map_err(|_| PollTimeoutTryFromError::TooPositive)?,
294-
)),
295-
}
296-
}
297-
}
298-
impl TryFrom<i64> for PollTimeout {
299-
type Error = PollTimeoutTryFromError;
300-
fn try_from(x: i64) -> std::result::Result<Self, Self::Error> {
301-
match x {
302-
..=-2 => Err(PollTimeoutTryFromError::TooNegative),
303-
-1.. => Ok(Self(
304-
i32::try_from(x)
305-
.map_err(|_| PollTimeoutTryFromError::TooPositive)?,
306-
)),
307-
}
308-
}
309-
}
310-
impl TryFrom<i32> for PollTimeout {
311-
type Error = PollTimeoutTryFromError;
312-
fn try_from(x: i32) -> std::result::Result<Self, Self::Error> {
313-
match x {
314-
..=-2 => Err(PollTimeoutTryFromError::TooNegative),
315-
-1.. => Ok(Self(x)),
316-
}
317-
}
318-
}
319-
impl TryFrom<i16> for PollTimeout {
320-
type Error = PollTimeoutTryFromError;
321-
fn try_from(x: i16) -> std::result::Result<Self, Self::Error> {
322-
match x {
323-
..=-2 => Err(PollTimeoutTryFromError::TooNegative),
324-
-1.. => Ok(Self(i32::from(x))),
325-
}
326-
}
327-
}
328-
impl TryFrom<i8> for PollTimeout {
329-
type Error = PollTimeoutTryFromError;
330-
fn try_from(x: i8) -> std::result::Result<Self, Self::Error> {
331-
match x {
332-
..=-2 => Err(PollTimeoutTryFromError::TooNegative),
333-
-1.. => Ok(Self(i32::from(x))),
334-
}
335-
}
336-
}
337-
impl TryFrom<PollTimeout> for Duration {
338-
type Error = ();
339-
fn try_from(x: PollTimeout) -> std::result::Result<Self, ()> {
340-
x.timeout().ok_or(())
341-
}
342-
}
343-
impl TryFrom<PollTimeout> for u128 {
344-
type Error = <Self as TryFrom<i32>>::Error;
345-
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
346-
Self::try_from(x.0)
347-
}
348-
}
349-
impl TryFrom<PollTimeout> for u64 {
350-
type Error = <Self as TryFrom<i32>>::Error;
351-
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
352-
Self::try_from(x.0)
353-
}
354-
}
355-
impl TryFrom<PollTimeout> for u32 {
356-
type Error = <Self as TryFrom<i32>>::Error;
357-
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
358-
Self::try_from(x.0)
359-
}
360-
}
361-
impl TryFrom<PollTimeout> for u16 {
362-
type Error = <Self as TryFrom<i32>>::Error;
363-
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
364-
Self::try_from(x.0)
365-
}
366-
}
367-
impl TryFrom<PollTimeout> for u8 {
368-
type Error = <Self as TryFrom<i32>>::Error;
369-
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
370-
Self::try_from(x.0)
371-
}
372-
}
373-
impl From<PollTimeout> for i128 {
374-
fn from(x: PollTimeout) -> Self {
375-
Self::from(x.0)
376-
}
377-
}
378-
impl From<PollTimeout> for i64 {
379-
fn from(x: PollTimeout) -> Self {
380-
Self::from(x.0)
381-
}
382-
}
383-
impl From<PollTimeout> for i32 {
384-
fn from(x: PollTimeout) -> Self {
385-
x.0
386-
}
387-
}
388-
impl TryFrom<PollTimeout> for i16 {
389-
type Error = <Self as TryFrom<i32>>::Error;
390-
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
391-
Self::try_from(x.0)
392-
}
393-
}
394-
impl TryFrom<PollTimeout> for i8 {
395-
type Error = <Self as TryFrom<i32>>::Error;
396-
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
397-
Self::try_from(x.0)
398-
}
399-
}
400-
401180
/// `poll` waits for one of a set of file descriptors to become ready to perform I/O.
402181
/// ([`poll(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html))
403182
///
@@ -414,11 +193,11 @@ impl TryFrom<PollTimeout> for i8 {
414193
///
415194
/// Note that the timeout interval will be rounded up to the system clock
416195
/// granularity, and kernel scheduling delays mean that the blocking
417-
/// interval may overrun by a small amount. Specifying a [`PollTimeout::NONE`]
196+
/// interval may overrun by a small amount. Specifying a [`Timeout::NONE`]
418197
/// in timeout means an infinite timeout. Specifying a timeout of
419-
/// [`PollTimeout::ZERO`] causes `poll()` to return immediately, even if no file
198+
/// [`Timeout::ZERO`] causes `poll()` to return immediately, even if no file
420199
/// descriptors are ready.
421-
pub fn poll<T: Into<PollTimeout>>(
200+
pub fn poll<T: Into<Timeout>>(
422201
fds: &mut [PollFd],
423202
timeout: T,
424203
) -> Result<libc::c_int> {

src/sys/epoll.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::errno::Errno;
22
use crate::Result;
3+
use crate::Timeout;
34
use libc::{self, c_int};
45
use std::mem;
56
use std::os::unix::io::{AsFd, AsRawFd, FromRawFd, OwnedFd, RawFd};
@@ -73,11 +74,12 @@ impl EpollEvent {
7374
/// ```
7475
/// # use nix::sys::{epoll::{Epoll, EpollEvent, EpollFlags, EpollCreateFlags}, eventfd::{eventfd, EfdFlags}};
7576
/// # use nix::unistd::write;
77+
/// # use nix::Timeout;
7678
/// # use std::os::unix::io::{OwnedFd, FromRawFd, AsFd};
7779
/// # use std::time::{Instant, Duration};
7880
/// # fn main() -> nix::Result<()> {
7981
/// const DATA: u64 = 17;
80-
/// const MILLIS: u64 = 100;
82+
/// const MILLIS: u8 = 100;
8183
///
8284
/// // Create epoll
8385
/// let epoll = Epoll::new(EpollCreateFlags::empty())?;
@@ -92,11 +94,11 @@ impl EpollEvent {
9294
///
9395
/// // Wait on event
9496
/// let mut events = [EpollEvent::empty()];
95-
/// epoll.wait(&mut events, MILLIS as isize)?;
97+
/// epoll.wait(&mut events, MILLIS)?;
9698
///
9799
/// // Assert data correct & timeout didn't occur
98100
/// assert_eq!(events[0].data(), DATA);
99-
/// assert!(now.elapsed() < Duration::from_millis(MILLIS));
101+
/// assert!(now.elapsed().as_millis() < MILLIS.into());
100102
/// # Ok(())
101103
/// # }
102104
/// ```
@@ -140,17 +142,17 @@ impl Epoll {
140142
/// (This can be thought of as fetching items from the ready list of the epoll instance.)
141143
///
142144
/// [`epoll_wait`](https://man7.org/linux/man-pages/man2/epoll_wait.2.html)
143-
pub fn wait(
145+
pub fn wait<T: Into<Timeout>>(
144146
&self,
145147
events: &mut [EpollEvent],
146-
timeout: isize,
148+
timeout: T,
147149
) -> Result<usize> {
148150
let res = unsafe {
149151
libc::epoll_wait(
150152
self.0.as_raw_fd(),
151153
events.as_mut_ptr().cast(),
152154
events.len() as c_int,
153-
timeout as c_int,
155+
timeout.into().into(),
154156
)
155157
};
156158

0 commit comments

Comments
 (0)