nix/
poll.rs

1//! Wait for events to trigger on specific file descriptors
2use std::os::unix::io::{AsRawFd, RawFd};
3
4use crate::errno::Errno;
5use crate::Result;
6
7/// This is a wrapper around `libc::pollfd`.
8///
9/// It's meant to be used as an argument to the [`poll`](fn.poll.html) and
10/// [`ppoll`](fn.ppoll.html) functions to specify the events of interest
11/// for a specific file descriptor.
12///
13/// After a call to `poll` or `ppoll`, the events that occurred can be
14/// retrieved by calling [`revents()`](#method.revents) on the `PollFd`.
15#[repr(transparent)]
16#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
17pub struct PollFd {
18    pollfd: libc::pollfd,
19}
20
21impl PollFd {
22    /// Creates a new `PollFd` specifying the events of interest
23    /// for a given file descriptor.
24    pub const fn new(fd: RawFd, events: PollFlags) -> PollFd {
25        PollFd {
26            pollfd: libc::pollfd {
27                fd,
28                events: events.bits(),
29                revents: PollFlags::empty().bits(),
30            },
31        }
32    }
33
34    /// Returns the events that occurred in the last call to `poll` or `ppoll`.  Will only return
35    /// `None` if the kernel provides status flags that Nix does not know about.
36    pub fn revents(self) -> Option<PollFlags> {
37        PollFlags::from_bits(self.pollfd.revents)
38    }
39
40    /// Returns if any of the events of interest occured in the last call to `poll` or `ppoll`. Will
41    /// only return `None` if the kernel provides status flags that Nix does not know about.
42    ///
43    /// Equivalent to `x.revents()? != PollFlags::empty()`.
44    ///
45    /// This is marginally more efficient than [`PollFd::all`].
46    pub fn any(self) -> Option<bool> {
47        Some(self.revents()? != PollFlags::empty())
48    }
49
50    /// Returns if all the events of interest occured in the last call to `poll` or `ppoll`. Will
51    /// only return `None` if the kernel provides status flags that Nix does not know about.
52    ///
53    /// Equivalent to `x.revents()? & x.events() == x.events()`.
54    ///
55    /// This is marginally less efficient than [`PollFd::any`].
56    pub fn all(self) -> Option<bool> {
57        Some(self.revents()? & self.events() == self.events())
58    }
59
60    /// The events of interest for this `PollFd`.
61    pub fn events(self) -> PollFlags {
62        PollFlags::from_bits(self.pollfd.events).unwrap()
63    }
64
65    /// Modify the events of interest for this `PollFd`.
66    pub fn set_events(&mut self, events: PollFlags) {
67        self.pollfd.events = events.bits();
68    }
69}
70
71impl AsRawFd for PollFd {
72    fn as_raw_fd(&self) -> RawFd {
73        self.pollfd.fd
74    }
75}
76
77libc_bitflags! {
78    /// These flags define the different events that can be monitored by `poll` and `ppoll`
79    pub struct PollFlags: libc::c_short {
80        /// There is data to read.
81        POLLIN;
82        /// There is some exceptional condition on the file descriptor.
83        ///
84        /// Possibilities include:
85        ///
86        /// *  There is out-of-band data on a TCP socket (see
87        ///    [tcp(7)](https://man7.org/linux/man-pages/man7/tcp.7.html)).
88        /// *  A pseudoterminal master in packet mode has seen a state
89        ///    change on the slave (see
90        ///    [ioctl_tty(2)](https://man7.org/linux/man-pages/man2/ioctl_tty.2.html)).
91        /// *  A cgroup.events file has been modified (see
92        ///    [cgroups(7)](https://man7.org/linux/man-pages/man7/cgroups.7.html)).
93        POLLPRI;
94        /// Writing is now possible, though a write larger that the
95        /// available space in a socket or pipe will still block (unless
96        /// `O_NONBLOCK` is set).
97        POLLOUT;
98        /// Equivalent to [`POLLIN`](constant.POLLIN.html)
99        #[cfg(not(target_os = "redox"))]
100        #[cfg_attr(docsrs, doc(cfg(all())))]
101        POLLRDNORM;
102        #[cfg(not(target_os = "redox"))]
103        #[cfg_attr(docsrs, doc(cfg(all())))]
104        /// Equivalent to [`POLLOUT`](constant.POLLOUT.html)
105        POLLWRNORM;
106        /// Priority band data can be read (generally unused on Linux).
107        #[cfg(not(target_os = "redox"))]
108        #[cfg_attr(docsrs, doc(cfg(all())))]
109        POLLRDBAND;
110        /// Priority data may be written.
111        #[cfg(not(target_os = "redox"))]
112        #[cfg_attr(docsrs, doc(cfg(all())))]
113        POLLWRBAND;
114        /// Error condition (only returned in
115        /// [`PollFd::revents`](struct.PollFd.html#method.revents);
116        /// ignored in [`PollFd::new`](struct.PollFd.html#method.new)).
117        /// This bit is also set for a file descriptor referring to the
118        /// write end of a pipe when the read end has been closed.
119        POLLERR;
120        /// Hang up (only returned in [`PollFd::revents`](struct.PollFd.html#method.revents);
121        /// ignored in [`PollFd::new`](struct.PollFd.html#method.new)).
122        /// Note that when reading from a channel such as a pipe or a stream
123        /// socket, this event merely indicates that the peer closed its
124        /// end of the channel.  Subsequent reads from the channel will
125        /// return 0 (end of file) only after all outstanding data in the
126        /// channel has been consumed.
127        POLLHUP;
128        /// Invalid request: `fd` not open (only returned in
129        /// [`PollFd::revents`](struct.PollFd.html#method.revents);
130        /// ignored in [`PollFd::new`](struct.PollFd.html#method.new)).
131        POLLNVAL;
132    }
133}
134
135/// `poll` waits for one of a set of file descriptors to become ready to perform I/O.
136/// ([`poll(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html))
137///
138/// `fds` contains all [`PollFd`](struct.PollFd.html) to poll.
139/// The function will return as soon as any event occur for any of these `PollFd`s.
140///
141/// The `timeout` argument specifies the number of milliseconds that `poll()`
142/// should block waiting for a file descriptor to become ready.  The call
143/// will block until either:
144///
145/// *  a file descriptor becomes ready;
146/// *  the call is interrupted by a signal handler; or
147/// *  the timeout expires.
148///
149/// Note that the timeout interval will be rounded up to the system clock
150/// 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.
155pub fn poll(fds: &mut [PollFd], timeout: libc::c_int) -> Result<libc::c_int> {
156    let res = unsafe {
157        libc::poll(
158            fds.as_mut_ptr() as *mut libc::pollfd,
159            fds.len() as libc::nfds_t,
160            timeout,
161        )
162    };
163
164    Errno::result(res)
165}
166
167feature! {
168#![feature = "signal"]
169/// `ppoll()` allows an application to safely wait until either a file
170/// descriptor becomes ready or until a signal is caught.
171/// ([`poll(2)`](https://man7.org/linux/man-pages/man2/poll.2.html))
172///
173/// `ppoll` behaves like `poll`, but let you specify what signals may interrupt it
174/// with the `sigmask` argument. If you want `ppoll` to block indefinitely,
175/// specify `None` as `timeout` (it is like `timeout = -1` for `poll`).
176/// If `sigmask` is `None`, then no signal mask manipulation is performed,
177/// so in that case `ppoll` differs from `poll` only in the precision of the
178/// timeout argument.
179///
180#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))]
181pub fn ppoll(
182    fds: &mut [PollFd],
183    timeout: Option<crate::sys::time::TimeSpec>,
184    sigmask: Option<crate::sys::signal::SigSet>
185    ) -> Result<libc::c_int>
186{
187    let timeout = timeout.as_ref().map_or(core::ptr::null(), |r| r.as_ref());
188    let sigmask = sigmask.as_ref().map_or(core::ptr::null(), |r| r.as_ref());
189    let res = unsafe {
190        libc::ppoll(fds.as_mut_ptr() as *mut libc::pollfd,
191                    fds.len() as libc::nfds_t,
192                    timeout,
193                    sigmask)
194    };
195    Errno::result(res)
196}
197}