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