-
Notifications
You must be signed in to change notification settings - Fork 666
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Safer poll timeout #1876
Safer poll timeout #1876
Conversation
74f8396
to
ed82f41
Compare
CI is failing because you're trying to use rust 1.58.0, but the 1.58 container installs 1.58.1. Change the VERSION to 1.58.1 |
ed39c68
to
297c74c
Compare
Thank you for letting me know. Why is this the case? |
That's just the convention for the rust-lang containers. They always include the newest patch release. |
src/poll.rs
Outdated
// specialization. | ||
impl PollTimeout { | ||
/// Blocks indefinitely. | ||
pub const NONE: Self = Self(1 << 31); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this supposed to be C's INFTIM
, and if so shouldn't it be -1
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I was originally using a unsigned integer type here for some reason e.g. 1 << 31 == -1
. But it makes more sense to use -1
now, will change this.
src/poll.rs
Outdated
@@ -132,12 +384,12 @@ libc_bitflags! { | |||
/// in timeout means an infinite timeout. Specifying a timeout of zero |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should update the docs for the timeout
variable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated docs
CHANGELOG.md
Outdated
- The MSRV is now 1.56.1 | ||
([#1792](https://github.com/nix-rust/nix/pull/1792)) | ||
- The MSRV is now 1.58.1 | ||
([#1876](https://github.com/nix-rust/nix/pull/1876)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can delete this part, and all of the other MSRV stuff, since we'll merge #1862 first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dropped this commit
a921a5d
to
749d693
Compare
src/poll.rs
Outdated
|
||
/// Error type for [`PollTimeout::try_from::<i68>()`]. | ||
#[derive(Debug, Clone, Copy)] | ||
pub enum TryFromI64Error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think TryFromI128Error and TryFromI64Error are exactly the same. Could you use a single error type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made some changes to the conversions to remove this error type.
src/poll.rs
Outdated
type Error = (); | ||
fn try_from(x: PollTimeout) -> std::result::Result<Self, ()> { | ||
match x.timeout() { | ||
// SAFETY: `x.0` is always positive. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// SAFETY: `x.0` is always positive. | |
// SAFETY: `x.0` is always nonnegative. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
src/poll.rs
Outdated
pub const MAX: Self = Self(i32::MAX); | ||
/// Returns if `self` equals [`PollTimeout::NONE`]. | ||
pub fn is_none(&self) -> bool { | ||
*self == Self::NONE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to program defensively, I recommend:
*self == Self::NONE | |
*self <= Self::NONE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You've right, fixed it.
src/poll.rs
Outdated
/// in timeout means an infinite timeout. Specifying a timeout of | ||
/// [`PollTimeout::ZERO`] causes `poll()` to return immediately, even if no file | ||
/// descriptors are ready. | ||
pub fn poll(fds: &mut [PollFd], timeout: PollTimeout) -> Result<libc::c_int> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be more usable, and more backwards compatible, to write the signature like this:
pub fn poll(fds: &mut [PollFd], timeout: PollTimeout) -> Result<libc::c_int> { | |
pub fn poll<T: Into<PollTimeout>>(fds: &mut [PollFd], timeout: T) -> Result<libc::c_int> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, made the change.
7c63be2
to
f11b599
Compare
3786bff
to
81794fe
Compare
297be36
to
0d3316d
Compare
899f471
to
8852303
Compare
Fixed |
src/poll.rs
Outdated
!self.is_none() | ||
} | ||
/// Returns the timeout in milliseconds if there is some, otherwise returns `None`. | ||
pub fn timeout(&self) -> Option<i32> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think naming this method as_millis
would be more idiomatic. For example, it would mirror Duration::as_millis
. And since it will never return a negative value, should it return u32
instead of i32
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've split this into 2 functions:
/// Returns the timeout in milliseconds if there is some, otherwise returns `None`.
pub fn as_millis(&self) -> Option<i32> {
self.is_some().then_some(self.0)
}
/// Returns the timeout as a `Duration` if there is some, otherwise returns `None`.
pub fn timeout(&self) -> Option<Duration> {
self.as_millis().map(|x|Duration::from_millis(u64::try_from(x).unwrap()))
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think using i32
or u32
each have their own drawbacks here, ideally we could use u31
with something like https://crates.io/crates/ux but this crate is not actively maintained. I tried my hand at making a better version as an experiment with https://crates.io/crates/ux2 but I am busy so am not able to maintain this alone.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that u31
suits best here, but before switching to the ux
or ux2
crate, u32
is the better choice compared to i32
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about this one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am okay to use u32
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to u32.
src/poll.rs
Outdated
impl TryFrom<i128> for PollTimeout { | ||
type Error = <i32 as TryFrom<i128>>::Error; | ||
fn try_from(x: i128) -> std::result::Result<Self, Self::Error> { | ||
match x { | ||
// > Specifying a negative value in timeout means an infinite timeout. | ||
i128::MIN..=-1 => Ok(Self::NONE), | ||
millis @ 0.. => Ok(Self(i32::try_from(millis)?)), | ||
} | ||
} | ||
} | ||
impl TryFrom<i64> for PollTimeout { | ||
type Error = <i32 as TryFrom<i64>>::Error; | ||
fn try_from(x: i64) -> std::result::Result<Self, Self::Error> { | ||
match x { | ||
i64::MIN..=-1 => Ok(Self::NONE), | ||
millis @ 0.. => Ok(Self(i32::try_from(millis)?)), | ||
} | ||
} | ||
} | ||
impl From<i32> for PollTimeout { | ||
fn from(x: i32) -> Self { | ||
Self(x) | ||
} | ||
} | ||
impl From<i16> for PollTimeout { | ||
fn from(x: i16) -> Self { | ||
Self(i32::from(x)) | ||
} | ||
} | ||
impl From<i8> for PollTimeout { | ||
fn from(x: i8) -> Self { | ||
Self(i32::from(x)) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, accepting negative values for timeout
seems to be a Linuxism. POSIX only requires that -1
be interpreted as "forever", and FreeBSD will return EINVAL
for other negative values. Please make it impossible to contruct a PollTimeout
with a negative value other than -1
.
5ab2dcd
to
e6b1a07
Compare
BTW, we have changed our changelog mode, please see CONTRIBUTING.md on how to add changelog entries |
058899a
to
776aaf1
Compare
422f03c
to
a8bcce3
Compare
Left some comments, I think this PR is ready for merge after addressing these comments |
42bdd0e
to
167d4be
Compare
167d4be
to
80c2d82
Compare
The CI failure is not related to this PR |
I can't reproduce the failure. Could github somehow be using a cached Cargo.lock file? |
Try this? I can reproduce it even with an empty hello-world crate |
Ahh, so it's a toolchain bug. I thought it was a libc bug. I'll open a bug upstream. In the meantime, we'll either have to disable this target, or pin our rustc version. |
I will pin the nightly toolchain to the version just before that |
Implements a wrapper around
i32
for thetimeout
argument inpoll::poll
.This prevents the error case:
Most of the code additions are simple conversions to/from integer primitives.
An alternative approach to implement the same safety with less code would require something like https://internals.rust-lang.org/t/non-negative-integer-types/17796 and would offer less usability like converting to/from
std::time::Duration
s.