Skip to content

Commit f11b599

Browse files
Safer poll timeout
1 parent df5877c commit f11b599

File tree

3 files changed

+186
-10
lines changed

3 files changed

+186
-10
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
2121
([#1921](https://github.com/nix-rust/nix/pull/1921))
2222
- The `fd` argument to `sys::signalfd::signalfd` is now of type `Option<impl AsFd>`.
2323
([#1874](https://github.com/nix-rust/nix/pull/1874))
24+
- The `timeout` argument of `poll::poll` is now of type `poll::PollTimeout`.
25+
([#1876](https://github.com/nix-rust/nix/pull/1876))
2426

2527
### Fixed
2628
### Removed

src/poll.rs

Lines changed: 181 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
//! Wait for events to trigger on specific file descriptors
2+
use std::convert::TryFrom;
23
use std::os::unix::io::{AsRawFd, RawFd};
4+
use std::time::Duration;
35

46
use crate::errno::Errno;
57
use crate::Result;
6-
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
@@ -132,6 +133,179 @@ libc_bitflags! {
132133
}
133134
}
134135

136+
/// Timeout argument for [`poll`].
137+
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
138+
pub struct PollTimeout(i32);
139+
140+
impl PollTimeout {
141+
/// Blocks indefinitely.
142+
///
143+
/// > Specifying a negative value in timeout means an infinite timeout.
144+
pub const NONE: Self = Self(-1);
145+
/// Returns immediately.
146+
///
147+
/// > Specifying a timeout of zero causes poll() to return immediately, even if no file
148+
/// > descriptors are ready.
149+
pub const ZERO: Self = Self(0);
150+
/// Blocks for at most [`std::i32::MAX`] milliseconds.
151+
pub const MAX: Self = Self(i32::MAX);
152+
/// Returns if `self` equals [`PollTimeout::NONE`].
153+
pub fn is_none(&self) -> bool {
154+
// > Specifying a negative value in timeout means an infinite timeout.
155+
*self <= Self::NONE
156+
}
157+
/// Returns if `self` does not equal [`PollTimeout::NONE`].
158+
pub fn is_some(&self) -> bool {
159+
!self.is_none()
160+
}
161+
/// Returns the timeout in milliseconds if there is some, otherwise returns `None`.
162+
pub fn timeout(&self) -> Option<i32> {
163+
self.is_some().then_some(self.0)
164+
}
165+
}
166+
167+
impl TryFrom<Duration> for PollTimeout {
168+
type Error = <i32 as TryFrom<u128>>::Error;
169+
fn try_from(x: Duration) -> std::result::Result<Self, Self::Error> {
170+
Ok(Self(i32::try_from(x.as_millis())?))
171+
}
172+
}
173+
impl TryFrom<u128> for PollTimeout {
174+
type Error = <i32 as TryFrom<u128>>::Error;
175+
fn try_from(x: u128) -> std::result::Result<Self, Self::Error> {
176+
Ok(Self(i32::try_from(x)?))
177+
}
178+
}
179+
impl TryFrom<u64> for PollTimeout {
180+
type Error = <i32 as TryFrom<u64>>::Error;
181+
fn try_from(x: u64) -> std::result::Result<Self, Self::Error> {
182+
Ok(Self(i32::try_from(x)?))
183+
}
184+
}
185+
impl TryFrom<u32> for PollTimeout {
186+
type Error = <i32 as TryFrom<u32>>::Error;
187+
fn try_from(x: u32) -> std::result::Result<Self, Self::Error> {
188+
Ok(Self(i32::try_from(x)?))
189+
}
190+
}
191+
impl From<u16> for PollTimeout {
192+
fn from(x: u16) -> Self {
193+
Self(i32::from(x))
194+
}
195+
}
196+
impl From<u8> for PollTimeout {
197+
fn from(x: u8) -> Self {
198+
Self(i32::from(x))
199+
}
200+
}
201+
impl TryFrom<i128> for PollTimeout {
202+
type Error = <i32 as TryFrom<i128>>::Error;
203+
fn try_from(x: i128) -> std::result::Result<Self, Self::Error> {
204+
match x {
205+
// > Specifying a negative value in timeout means an infinite timeout.
206+
i128::MIN..=-1 => Ok(Self::NONE),
207+
millis @ 0.. => Ok(Self(
208+
i32::try_from(millis)?,
209+
)),
210+
}
211+
}
212+
}
213+
impl TryFrom<i64> for PollTimeout {
214+
type Error = <i32 as TryFrom<i64>>::Error;
215+
fn try_from(x: i64) -> std::result::Result<Self, Self::Error> {
216+
match x {
217+
i64::MIN..=-1 => Ok(Self::NONE),
218+
millis @ 0.. => Ok(Self(
219+
i32::try_from(millis)?,
220+
)),
221+
}
222+
}
223+
}
224+
impl From<i32> for PollTimeout {
225+
fn from(x: i32) -> Self {
226+
Self(x)
227+
}
228+
}
229+
impl From<i16> for PollTimeout {
230+
fn from(x: i16) -> Self {
231+
Self(i32::from(x))
232+
}
233+
}
234+
impl From<i8> for PollTimeout {
235+
fn from(x: i8) -> Self {
236+
Self(i32::from(x))
237+
}
238+
}
239+
impl TryFrom<PollTimeout> for Duration {
240+
type Error = ();
241+
fn try_from(x: PollTimeout) -> std::result::Result<Self, ()> {
242+
match x.timeout() {
243+
// SAFETY: When `x.timeout()` returns `Some(a)`, `a` is always non-negative.
244+
Some(millis) => Ok(Duration::from_millis(unsafe {
245+
u64::try_from(millis).unwrap_unchecked()
246+
})),
247+
None => Err(()),
248+
}
249+
}
250+
}
251+
impl TryFrom<PollTimeout> for u128 {
252+
type Error = <Self as TryFrom<i32>>::Error;
253+
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
254+
Self::try_from(x.0)
255+
}
256+
}
257+
impl TryFrom<PollTimeout> for u64 {
258+
type Error = <Self as TryFrom<i32>>::Error;
259+
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
260+
Self::try_from(x.0)
261+
}
262+
}
263+
impl TryFrom<PollTimeout> for u32 {
264+
type Error = <Self as TryFrom<i32>>::Error;
265+
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
266+
Self::try_from(x.0)
267+
}
268+
}
269+
impl TryFrom<PollTimeout> for u16 {
270+
type Error = <Self as TryFrom<i32>>::Error;
271+
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
272+
Self::try_from(x.0)
273+
}
274+
}
275+
impl TryFrom<PollTimeout> for u8 {
276+
type Error = <Self as TryFrom<i32>>::Error;
277+
fn try_from(x: PollTimeout) -> std::result::Result<Self, Self::Error> {
278+
Self::try_from(x.0)
279+
}
280+
}
281+
impl From<PollTimeout> for i128 {
282+
fn from(x: PollTimeout) -> Self {
283+
Self::from(x.0)
284+
}
285+
}
286+
impl From<PollTimeout> for i64 {
287+
fn from(x: PollTimeout) -> Self {
288+
Self::from(x.0)
289+
}
290+
}
291+
impl From<PollTimeout> for i32 {
292+
fn from(x: PollTimeout) -> Self {
293+
x.0
294+
}
295+
}
296+
impl TryFrom<PollTimeout> for i16 {
297+
type Error = <Self as TryFrom<i32>>::Error;
298+
fn try_from(x: PollTimeout) -> std::result::Result<Self,Self::Error> {
299+
Self::try_from(x.0)
300+
}
301+
}
302+
impl TryFrom<PollTimeout> for i8 {
303+
type Error = <Self as TryFrom<i32>>::Error;
304+
fn try_from(x: PollTimeout) -> std::result::Result<Self,Self::Error> {
305+
Self::try_from(x.0)
306+
}
307+
}
308+
135309
/// `poll` waits for one of a set of file descriptors to become ready to perform I/O.
136310
/// ([`poll(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html))
137311
///
@@ -148,16 +322,16 @@ libc_bitflags! {
148322
///
149323
/// Note that the timeout interval will be rounded up to the system clock
150324
/// granularity, and kernel scheduling delays mean that the blocking
151-
/// interval may overrun by a small amount. Specifying a negative value
152-
/// in timeout means an infinite timeout. Specifying a timeout of zero
153-
/// causes `poll()` to return immediately, even if no file descriptors are
154-
/// ready.
155-
pub fn poll(fds: &mut [PollFd], timeout: libc::c_int) -> Result<libc::c_int> {
325+
/// interval may overrun by a small amount. Specifying a [`PollTimeout::NONE`]
326+
/// in timeout means an infinite timeout. Specifying a timeout of
327+
/// [`PollTimeout::ZERO`] causes `poll()` to return immediately, even if no file
328+
/// descriptors are ready.
329+
pub fn poll<T: Into<PollTimeout>>(fds: &mut [PollFd], timeout: T) -> Result<libc::c_int> {
156330
let res = unsafe {
157331
libc::poll(
158332
fds.as_mut_ptr() as *mut libc::pollfd,
159333
fds.len() as libc::nfds_t,
160-
timeout,
334+
i32::from(timeout.into()),
161335
)
162336
};
163337

test/test_poll.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use nix::{
22
errno::Errno,
3-
poll::{poll, PollFd, PollFlags},
3+
poll::{poll, PollFd, PollFlags, PollTimeout},
44
unistd::{pipe, write},
55
};
66

@@ -22,14 +22,14 @@ fn test_poll() {
2222
let mut fds = [PollFd::new(r, PollFlags::POLLIN)];
2323

2424
// Poll an idle pipe. Should timeout
25-
let nfds = loop_while_eintr!(poll(&mut fds, 100));
25+
let nfds = loop_while_eintr!(poll(&mut fds, PollTimeout::from(100u8)));
2626
assert_eq!(nfds, 0);
2727
assert!(!fds[0].revents().unwrap().contains(PollFlags::POLLIN));
2828

2929
write(w, b".").unwrap();
3030

3131
// Poll a readable pipe. Should return an event.
32-
let nfds = poll(&mut fds, 100).unwrap();
32+
let nfds = poll(&mut fds, PollTimeout::from(100u8)).unwrap();
3333
assert_eq!(nfds, 1);
3434
assert!(fds[0].revents().unwrap().contains(PollFlags::POLLIN));
3535
}

0 commit comments

Comments
 (0)