From cbc0dcd8271186b85e49fdd0a44b43b7e3739216 Mon Sep 17 00:00:00 2001 From: Jonathon Reinhart Date: Mon, 14 Apr 2025 23:02:02 +0000 Subject: [PATCH] Remove Copy from PollFd PollFd implementing Copy makes it easy to accidentally refer to the wrong object after putting one into an array. Remove Copy to force move semantics. This also updates some related docs to improve overall clarity. This fixes #2630 --- changelog/2631.removed.md | 1 + src/poll.rs | 49 +++++++++++++++++++++++++++++---------- 2 files changed, 38 insertions(+), 12 deletions(-) create mode 100644 changelog/2631.removed.md diff --git a/changelog/2631.removed.md b/changelog/2631.removed.md new file mode 100644 index 0000000000..3f75506895 --- /dev/null +++ b/changelog/2631.removed.md @@ -0,0 +1 @@ +Removed the `Copy` trait from `PollFd` diff --git a/src/poll.rs b/src/poll.rs index 9bbae5b3ba..416a6945ec 100644 --- a/src/poll.rs +++ b/src/poll.rs @@ -11,10 +11,10 @@ use crate::Result; /// [`ppoll`](fn.ppoll.html) functions to specify the events of interest /// for a specific file descriptor. /// -/// After a call to `poll` or `ppoll`, the events that occurred can be -/// retrieved by calling [`revents()`](#method.revents) on the `PollFd`. +/// After a call to `poll` or `ppoll`, the events that occurred can be retrieved by calling +/// [`revents()`](#method.revents) on the `PollFd` object from the array passed to `poll`. #[repr(transparent)] -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct PollFd<'fd> { pollfd: libc::pollfd, _fd: std::marker::PhantomData>, @@ -33,11 +33,8 @@ impl<'fd> PollFd<'fd> { /// # }; /// let (r, w) = pipe().unwrap(); /// let pfd = PollFd::new(r.as_fd(), PollFlags::POLLIN); - /// let mut fds = [pfd]; - /// poll(&mut fds, PollTimeout::NONE).unwrap(); - /// let mut buf = [0u8; 80]; - /// read(&r, &mut buf[..]); /// ``` + /// These are placed in an array and passed to [`poll`] or [`ppoll`](fn.ppoll.html). // Unlike I/O functions, constructors like this must take `BorrowedFd` // instead of AsFd or &AsFd. Otherwise, an `OwnedFd` argument would be // dropped at the end of the method, leaving the structure referencing a @@ -61,7 +58,7 @@ impl<'fd> PollFd<'fd> { /// Returns the events that occurred in the last call to `poll` or `ppoll`. Will only return /// `None` if the kernel provides status flags that Nix does not know about. - pub fn revents(self) -> Option { + pub fn revents(&self) -> Option { PollFlags::from_bits(self.pollfd.revents) } @@ -71,7 +68,7 @@ impl<'fd> PollFd<'fd> { /// Equivalent to `x.revents()? != PollFlags::empty()`. /// /// This is marginally more efficient than [`PollFd::all`]. - pub fn any(self) -> Option { + pub fn any(&self) -> Option { Some(self.revents()? != PollFlags::empty()) } @@ -81,12 +78,12 @@ impl<'fd> PollFd<'fd> { /// Equivalent to `x.revents()? & x.events() == x.events()`. /// /// This is marginally less efficient than [`PollFd::any`]. - pub fn all(self) -> Option { + pub fn all(&self) -> Option { Some(self.revents()? & self.events() == self.events()) } /// The events of interest for this `PollFd`. - pub fn events(self) -> PollFlags { + pub fn events(&self) -> PollFlags { PollFlags::from_bits(self.pollfd.events).unwrap() } @@ -196,6 +193,34 @@ libc_bitflags! { /// in timeout means an infinite timeout. Specifying a timeout of /// [`PollTimeout::ZERO`] causes `poll()` to return immediately, even if no file /// descriptors are ready. +/// +/// The return value contains the number of `fds` which have selected events ([`PollFd::revents`]). +/// +/// # Examples +/// ```no_run +/// # use std::os::unix::io::{AsFd, AsRawFd, FromRawFd}; +/// # use nix::{ +/// # poll::{PollTimeout, PollFd, PollFlags, poll}, +/// # unistd::{pipe, read} +/// # }; +/// let (r0, w0) = pipe().unwrap(); +/// let (r1, w1) = pipe().unwrap(); +/// +/// let mut pollfds = [ +/// PollFd::new(r0.as_fd(), PollFlags::POLLIN), +/// PollFd::new(r1.as_fd(), PollFlags::POLLIN), +/// ]; +/// +/// let nready = poll(&mut pollfds, PollTimeout::NONE).unwrap(); +/// assert!(nready >= 1); // Since there is no timeout +/// +/// let mut buf = [0u8; 80]; +/// if pollfds[0].any().unwrap_or_default() { +/// read(&r0, &mut buf[..]); +/// } else if pollfds[1].any().unwrap_or_default() { +/// read(&r1, &mut buf[..]); +/// } +/// ``` pub fn poll>( fds: &mut [PollFd], timeout: T, @@ -217,7 +242,7 @@ feature! { /// descriptor becomes ready or until a signal is caught. /// ([`poll(2)`](https://man7.org/linux/man-pages/man2/poll.2.html)) /// -/// `ppoll` behaves like `poll`, but let you specify what signals may interrupt it +/// `ppoll` behaves like [`poll`], but let you specify what signals may interrupt it /// with the `sigmask` argument. If you want `ppoll` to block indefinitely, /// specify `None` as `timeout` (it is like `timeout = -1` for `poll`). /// If `sigmask` is `None`, then no signal mask manipulation is performed,