nix/poll.rs
1//! Wait for events to trigger on specific file descriptors
2use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd};
3
4use crate::errno::Errno;
5pub use crate::poll_timeout::{PollTimeout, PollTimeoutTryFromError};
6use crate::Result;
7
8/// This is a wrapper around `libc::pollfd`.
9///
10/// It's meant to be used as an argument to the [`poll`](fn.poll.html) and
11/// [`ppoll`](fn.ppoll.html) functions to specify the events of interest
12/// for a specific file descriptor.
13///
14/// After a call to `poll` or `ppoll`, the events that occurred can be retrieved by calling
15/// [`revents()`](#method.revents) on the `PollFd` object from the array passed to `poll`.
16#[repr(transparent)]
17#[derive(Clone, Debug, Eq, Hash, PartialEq)]
18pub struct PollFd<'fd> {
19 pollfd: libc::pollfd,
20 _fd: std::marker::PhantomData<BorrowedFd<'fd>>,
21}
22
23impl<'fd> PollFd<'fd> {
24 /// Creates a new `PollFd` specifying the events of interest
25 /// for a given file descriptor.
26 ///
27 /// # Examples
28 /// ```no_run
29 /// # use std::os::unix::io::{AsFd, AsRawFd, FromRawFd};
30 /// # use nix::{
31 /// # poll::{PollTimeout, PollFd, PollFlags, poll},
32 /// # unistd::{pipe, read}
33 /// # };
34 /// let (r, w) = pipe().unwrap();
35 /// let pfd = PollFd::new(r.as_fd(), PollFlags::POLLIN);
36 /// ```
37 /// These are placed in an array and passed to [`poll`] or [`ppoll`](fn.ppoll.html).
38 // Unlike I/O functions, constructors like this must take `BorrowedFd`
39 // instead of AsFd or &AsFd. Otherwise, an `OwnedFd` argument would be
40 // dropped at the end of the method, leaving the structure referencing a
41 // closed file descriptor. For example:
42 //
43 // ```rust
44 // let (r, _) = pipe().unwrap();
45 // let pollfd = PollFd::new(r, flag); // Drops the OwnedFd
46 // // Do something with `pollfd`, which uses the CLOSED fd.
47 // ```
48 pub fn new(fd: BorrowedFd<'fd>, events: PollFlags) -> PollFd<'fd> {
49 PollFd {
50 pollfd: libc::pollfd {
51 fd: fd.as_raw_fd(),
52 events: events.bits(),
53 revents: PollFlags::empty().bits(),
54 },
55 _fd: std::marker::PhantomData,
56 }
57 }
58
59 /// Returns the events that occurred in the last call to `poll` or `ppoll`. Will only return
60 /// `None` if the kernel provides status flags that Nix does not know about.
61 pub fn revents(&self) -> Option<PollFlags> {
62 PollFlags::from_bits(self.pollfd.revents)
63 }
64
65 /// Returns if any of the events of interest occured in the last call to `poll` or `ppoll`. Will
66 /// only return `None` if the kernel provides status flags that Nix does not know about.
67 ///
68 /// Equivalent to `x.revents()? != PollFlags::empty()`.
69 ///
70 /// This is marginally more efficient than [`PollFd::all`].
71 pub fn any(&self) -> Option<bool> {
72 Some(self.revents()? != PollFlags::empty())
73 }
74
75 /// Returns if all the events of interest occured in the last call to `poll` or `ppoll`. Will
76 /// only return `None` if the kernel provides status flags that Nix does not know about.
77 ///
78 /// Equivalent to `x.revents()? & x.events() == x.events()`.
79 ///
80 /// This is marginally less efficient than [`PollFd::any`].
81 pub fn all(&self) -> Option<bool> {
82 Some(self.revents()? & self.events() == self.events())
83 }
84
85 /// The events of interest for this `PollFd`.
86 pub fn events(&self) -> PollFlags {
87 PollFlags::from_bits(self.pollfd.events).unwrap()
88 }
89
90 /// Modify the events of interest for this `PollFd`.
91 pub fn set_events(&mut self, events: PollFlags) {
92 self.pollfd.events = events.bits();
93 }
94}
95
96impl AsFd for PollFd<'_> {
97 fn as_fd(&self) -> BorrowedFd<'_> {
98 // Safety:
99 //
100 // BorrowedFd::borrow_raw(RawFd) requires that the raw fd being passed
101 // must remain open for the duration of the returned BorrowedFd, this is
102 // guaranteed as the returned BorrowedFd has the lifetime parameter same
103 // as `self`:
104 // "fn as_fd<'self>(&'self self) -> BorrowedFd<'self>"
105 // which means that `self` (PollFd) is guaranteed to outlive the returned
106 // BorrowedFd. (Lifetime: PollFd > BorrowedFd)
107 //
108 // And the lifetime parameter of PollFd::new(fd, ...) ensures that `fd`
109 // (an owned file descriptor) must outlive the returned PollFd:
110 // "pub fn new<Fd: AsFd>(fd: &'fd Fd, events: PollFlags) -> PollFd<'fd>"
111 // (Lifetime: Owned fd > PollFd)
112 //
113 // With two above relationships, we can conclude that the `Owned file
114 // descriptor` will outlive the returned BorrowedFd,
115 // (Lifetime: Owned fd > BorrowedFd)
116 // i.e., the raw fd being passed will remain valid for the lifetime of
117 // the returned BorrowedFd.
118 unsafe { BorrowedFd::borrow_raw(self.pollfd.fd) }
119 }
120}
121
122libc_bitflags! {
123 /// These flags define the different events that can be monitored by `poll` and `ppoll`
124 pub struct PollFlags: libc::c_short {
125 /// There is data to read.
126 POLLIN;
127 /// There is some exceptional condition on the file descriptor.
128 ///
129 /// Possibilities include:
130 ///
131 /// * There is out-of-band data on a TCP socket (see
132 /// [tcp(7)](https://man7.org/linux/man-pages/man7/tcp.7.html)).
133 /// * A pseudoterminal master in packet mode has seen a state
134 /// change on the slave (see
135 /// [ioctl_tty(2)](https://man7.org/linux/man-pages/man2/ioctl_tty.2.html)).
136 /// * A cgroup.events file has been modified (see
137 /// [cgroups(7)](https://man7.org/linux/man-pages/man7/cgroups.7.html)).
138 POLLPRI;
139 /// Writing is now possible, though a write larger that the
140 /// available space in a socket or pipe will still block (unless
141 /// `O_NONBLOCK` is set).
142 POLLOUT;
143 /// Equivalent to [`POLLIN`](constant.POLLIN.html)
144 #[cfg(not(target_os = "redox"))]
145 POLLRDNORM;
146 #[cfg(not(target_os = "redox"))]
147 /// Equivalent to [`POLLOUT`](constant.POLLOUT.html)
148 POLLWRNORM;
149 /// Priority band data can be read (generally unused on Linux).
150 #[cfg(not(target_os = "redox"))]
151 POLLRDBAND;
152 /// Priority data may be written.
153 #[cfg(not(target_os = "redox"))]
154 POLLWRBAND;
155 /// Error condition (only returned in
156 /// [`PollFd::revents`](struct.PollFd.html#method.revents);
157 /// ignored in [`PollFd::new`](struct.PollFd.html#method.new)).
158 /// This bit is also set for a file descriptor referring to the
159 /// write end of a pipe when the read end has been closed.
160 POLLERR;
161 /// Hang up (only returned in [`PollFd::revents`](struct.PollFd.html#method.revents);
162 /// ignored in [`PollFd::new`](struct.PollFd.html#method.new)).
163 /// Note that when reading from a channel such as a pipe or a stream
164 /// socket, this event merely indicates that the peer closed its
165 /// end of the channel. Subsequent reads from the channel will
166 /// return 0 (end of file) only after all outstanding data in the
167 /// channel has been consumed.
168 POLLHUP;
169 /// Invalid request: `fd` not open (only returned in
170 /// [`PollFd::revents`](struct.PollFd.html#method.revents);
171 /// ignored in [`PollFd::new`](struct.PollFd.html#method.new)).
172 POLLNVAL;
173 }
174}
175
176/// `poll` waits for one of a set of file descriptors to become ready to perform I/O.
177/// ([`poll(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html))
178///
179/// `fds` contains all [`PollFd`](struct.PollFd.html) to poll.
180/// The function will return as soon as any event occur for any of these `PollFd`s.
181///
182/// The `timeout` argument specifies the number of milliseconds that `poll()`
183/// should block waiting for a file descriptor to become ready. The call
184/// will block until either:
185///
186/// * a file descriptor becomes ready;
187/// * the call is interrupted by a signal handler; or
188/// * the timeout expires.
189///
190/// Note that the timeout interval will be rounded up to the system clock
191/// granularity, and kernel scheduling delays mean that the blocking
192/// interval may overrun by a small amount. Specifying a [`PollTimeout::NONE`]
193/// in timeout means an infinite timeout. Specifying a timeout of
194/// [`PollTimeout::ZERO`] causes `poll()` to return immediately, even if no file
195/// descriptors are ready.
196///
197/// The return value contains the number of `fds` which have selected events ([`PollFd::revents`]).
198///
199/// # Examples
200/// ```no_run
201/// # use std::os::unix::io::{AsFd, AsRawFd, FromRawFd};
202/// # use nix::{
203/// # poll::{PollTimeout, PollFd, PollFlags, poll},
204/// # unistd::{pipe, read}
205/// # };
206/// let (r0, w0) = pipe().unwrap();
207/// let (r1, w1) = pipe().unwrap();
208///
209/// let mut pollfds = [
210/// PollFd::new(r0.as_fd(), PollFlags::POLLIN),
211/// PollFd::new(r1.as_fd(), PollFlags::POLLIN),
212/// ];
213///
214/// let nready = poll(&mut pollfds, PollTimeout::NONE).unwrap();
215/// assert!(nready >= 1); // Since there is no timeout
216///
217/// let mut buf = [0u8; 80];
218/// if pollfds[0].any().unwrap_or_default() {
219/// read(&r0, &mut buf[..]);
220/// } else if pollfds[1].any().unwrap_or_default() {
221/// read(&r1, &mut buf[..]);
222/// }
223/// ```
224pub fn poll<T: Into<PollTimeout>>(
225 fds: &mut [PollFd],
226 timeout: T,
227) -> Result<libc::c_int> {
228 let res = unsafe {
229 libc::poll(
230 fds.as_mut_ptr().cast(),
231 fds.len() as libc::nfds_t,
232 i32::from(timeout.into()),
233 )
234 };
235
236 Errno::result(res)
237}
238
239feature! {
240#![feature = "signal"]
241/// `ppoll()` allows an application to safely wait until either a file
242/// descriptor becomes ready or until a signal is caught.
243/// ([`poll(2)`](https://man7.org/linux/man-pages/man2/poll.2.html))
244///
245/// `ppoll` behaves like [`poll`], but let you specify what signals may interrupt it
246/// with the `sigmask` argument. If you want `ppoll` to block indefinitely,
247/// specify `None` as `timeout` (it is like `timeout = -1` for `poll`).
248/// If `sigmask` is `None`, then no signal mask manipulation is performed,
249/// so in that case `ppoll` differs from `poll` only in the precision of the
250/// timeout argument.
251///
252#[cfg(any(linux_android, freebsdlike))]
253pub fn ppoll(
254 fds: &mut [PollFd],
255 timeout: Option<crate::sys::time::TimeSpec>,
256 sigmask: Option<crate::sys::signal::SigSet>
257 ) -> Result<libc::c_int>
258{
259 let timeout = timeout.as_ref().map_or(core::ptr::null(), |r| r.as_ref());
260 let sigmask = sigmask.as_ref().map_or(core::ptr::null(), |r| r.as_ref());
261 let res = unsafe {
262 libc::ppoll(fds.as_mut_ptr().cast(),
263 fds.len() as libc::nfds_t,
264 timeout,
265 sigmask)
266 };
267 Errno::result(res)
268}
269}