| //! Wait for events to trigger on specific file descriptors |
| use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd}; |
| |
| use crate::errno::Errno; |
| pub use crate::poll_timeout::PollTimeout; |
| use crate::Result; |
| |
| /// This is a wrapper around `libc::pollfd`. |
| /// |
| /// It's meant to be used as an argument to the [`poll`](fn.poll.html) and |
| /// [`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`. |
| #[repr(transparent)] |
| #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] |
| pub struct PollFd<'fd> { |
| pollfd: libc::pollfd, |
| _fd: std::marker::PhantomData<BorrowedFd<'fd>>, |
| } |
| |
| impl<'fd> PollFd<'fd> { |
| /// Creates a new `PollFd` specifying the events of interest |
| /// for a given file descriptor. |
| /// |
| /// # Examples |
| /// ```no_run |
| /// # use std::os::unix::io::{AsFd, AsRawFd, FromRawFd}; |
| /// # use nix::{ |
| /// # poll::{PollTimeout, PollFd, PollFlags, poll}, |
| /// # unistd::{pipe, read} |
| /// # }; |
| /// 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.as_raw_fd(), &mut buf[..]); |
| /// ``` |
| // 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 |
| // closed file descriptor. For example: |
| // |
| // ```rust |
| // let (r, _) = pipe().unwrap(); |
| // let pollfd = PollFd::new(r, flag); // Drops the OwnedFd |
| // // Do something with `pollfd`, which uses the CLOSED fd. |
| // ``` |
| pub fn new(fd: BorrowedFd<'fd>, events: PollFlags) -> PollFd<'fd> { |
| PollFd { |
| pollfd: libc::pollfd { |
| fd: fd.as_raw_fd(), |
| events: events.bits(), |
| revents: PollFlags::empty().bits(), |
| }, |
| _fd: std::marker::PhantomData, |
| } |
| } |
| |
| /// 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<PollFlags> { |
| PollFlags::from_bits(self.pollfd.revents) |
| } |
| |
| /// Returns if any of the events of interest occured in the last call to `poll` or `ppoll`. Will |
| /// only return `None` if the kernel provides status flags that Nix does not know about. |
| /// |
| /// Equivalent to `x.revents()? != PollFlags::empty()`. |
| /// |
| /// This is marginally more efficient than [`PollFd::all`]. |
| pub fn any(self) -> Option<bool> { |
| Some(self.revents()? != PollFlags::empty()) |
| } |
| |
| /// Returns if all the events of interest occured in the last call to `poll` or `ppoll`. Will |
| /// only return `None` if the kernel provides status flags that Nix does not know about. |
| /// |
| /// Equivalent to `x.revents()? & x.events() == x.events()`. |
| /// |
| /// This is marginally less efficient than [`PollFd::any`]. |
| pub fn all(self) -> Option<bool> { |
| Some(self.revents()? & self.events() == self.events()) |
| } |
| |
| /// The events of interest for this `PollFd`. |
| pub fn events(self) -> PollFlags { |
| PollFlags::from_bits(self.pollfd.events).unwrap() |
| } |
| |
| /// Modify the events of interest for this `PollFd`. |
| pub fn set_events(&mut self, events: PollFlags) { |
| self.pollfd.events = events.bits(); |
| } |
| } |
| |
| impl<'fd> AsFd for PollFd<'fd> { |
| fn as_fd(&self) -> BorrowedFd<'_> { |
| // Safety: |
| // |
| // BorrowedFd::borrow_raw(RawFd) requires that the raw fd being passed |
| // must remain open for the duration of the returned BorrowedFd, this is |
| // guaranteed as the returned BorrowedFd has the lifetime parameter same |
| // as `self`: |
| // "fn as_fd<'self>(&'self self) -> BorrowedFd<'self>" |
| // which means that `self` (PollFd) is guaranteed to outlive the returned |
| // BorrowedFd. (Lifetime: PollFd > BorrowedFd) |
| // |
| // And the lifetime parameter of PollFd::new(fd, ...) ensures that `fd` |
| // (an owned file descriptor) must outlive the returned PollFd: |
| // "pub fn new<Fd: AsFd>(fd: &'fd Fd, events: PollFlags) -> PollFd<'fd>" |
| // (Lifetime: Owned fd > PollFd) |
| // |
| // With two above relationships, we can conclude that the `Owned file |
| // descriptor` will outlive the returned BorrowedFd, |
| // (Lifetime: Owned fd > BorrowedFd) |
| // i.e., the raw fd being passed will remain valid for the lifetime of |
| // the returned BorrowedFd. |
| unsafe { BorrowedFd::borrow_raw(self.pollfd.fd) } |
| } |
| } |
| |
| libc_bitflags! { |
| /// These flags define the different events that can be monitored by `poll` and `ppoll` |
| pub struct PollFlags: libc::c_short { |
| /// There is data to read. |
| POLLIN; |
| /// There is some exceptional condition on the file descriptor. |
| /// |
| /// Possibilities include: |
| /// |
| /// * There is out-of-band data on a TCP socket (see |
| /// [tcp(7)](https://man7.org/linux/man-pages/man7/tcp.7.html)). |
| /// * A pseudoterminal master in packet mode has seen a state |
| /// change on the slave (see |
| /// [ioctl_tty(2)](https://man7.org/linux/man-pages/man2/ioctl_tty.2.html)). |
| /// * A cgroup.events file has been modified (see |
| /// [cgroups(7)](https://man7.org/linux/man-pages/man7/cgroups.7.html)). |
| POLLPRI; |
| /// Writing is now possible, though a write larger that the |
| /// available space in a socket or pipe will still block (unless |
| /// `O_NONBLOCK` is set). |
| POLLOUT; |
| /// Equivalent to [`POLLIN`](constant.POLLIN.html) |
| #[cfg(not(target_os = "redox"))] |
| POLLRDNORM; |
| #[cfg(not(target_os = "redox"))] |
| /// Equivalent to [`POLLOUT`](constant.POLLOUT.html) |
| POLLWRNORM; |
| /// Priority band data can be read (generally unused on Linux). |
| #[cfg(not(target_os = "redox"))] |
| POLLRDBAND; |
| /// Priority data may be written. |
| #[cfg(not(target_os = "redox"))] |
| POLLWRBAND; |
| /// Error condition (only returned in |
| /// [`PollFd::revents`](struct.PollFd.html#method.revents); |
| /// ignored in [`PollFd::new`](struct.PollFd.html#method.new)). |
| /// This bit is also set for a file descriptor referring to the |
| /// write end of a pipe when the read end has been closed. |
| POLLERR; |
| /// Hang up (only returned in [`PollFd::revents`](struct.PollFd.html#method.revents); |
| /// ignored in [`PollFd::new`](struct.PollFd.html#method.new)). |
| /// Note that when reading from a channel such as a pipe or a stream |
| /// socket, this event merely indicates that the peer closed its |
| /// end of the channel. Subsequent reads from the channel will |
| /// return 0 (end of file) only after all outstanding data in the |
| /// channel has been consumed. |
| POLLHUP; |
| /// Invalid request: `fd` not open (only returned in |
| /// [`PollFd::revents`](struct.PollFd.html#method.revents); |
| /// ignored in [`PollFd::new`](struct.PollFd.html#method.new)). |
| POLLNVAL; |
| } |
| } |
| |
| /// `poll` waits for one of a set of file descriptors to become ready to perform I/O. |
| /// ([`poll(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html)) |
| /// |
| /// `fds` contains all [`PollFd`](struct.PollFd.html) to poll. |
| /// The function will return as soon as any event occur for any of these `PollFd`s. |
| /// |
| /// The `timeout` argument specifies the number of milliseconds that `poll()` |
| /// should block waiting for a file descriptor to become ready. The call |
| /// will block until either: |
| /// |
| /// * a file descriptor becomes ready; |
| /// * the call is interrupted by a signal handler; or |
| /// * the timeout expires. |
| /// |
| /// Note that the timeout interval will be rounded up to the system clock |
| /// granularity, and kernel scheduling delays mean that the blocking |
| /// interval may overrun by a small amount. Specifying a [`PollTimeout::NONE`] |
| /// 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<T: Into<PollTimeout>>( |
| fds: &mut [PollFd], |
| timeout: T, |
| ) -> Result<libc::c_int> { |
| let res = unsafe { |
| libc::poll( |
| fds.as_mut_ptr().cast(), |
| fds.len() as libc::nfds_t, |
| i32::from(timeout.into()), |
| ) |
| }; |
| |
| Errno::result(res) |
| } |
| |
| feature! { |
| #![feature = "signal"] |
| /// `ppoll()` allows an application to safely wait until either a file |
| /// 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 |
| /// 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, |
| /// so in that case `ppoll` differs from `poll` only in the precision of the |
| /// timeout argument. |
| /// |
| #[cfg(any(linux_android, freebsdlike))] |
| pub fn ppoll( |
| fds: &mut [PollFd], |
| timeout: Option<crate::sys::time::TimeSpec>, |
| sigmask: Option<crate::sys::signal::SigSet> |
| ) -> Result<libc::c_int> |
| { |
| let timeout = timeout.as_ref().map_or(core::ptr::null(), |r| r.as_ref()); |
| let sigmask = sigmask.as_ref().map_or(core::ptr::null(), |r| r.as_ref()); |
| let res = unsafe { |
| libc::ppoll(fds.as_mut_ptr().cast(), |
| fds.len() as libc::nfds_t, |
| timeout, |
| sigmask) |
| }; |
| Errno::result(res) |
| } |
| } |