• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Wait for events to trigger on specific file descriptors
2 use std::os::unix::io::{AsRawFd, RawFd};
3 
4 use crate::errno::Errno;
5 use 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)]
17 pub struct PollFd {
18     pollfd: libc::pollfd,
19 }
20 
21 impl PollFd {
22     /// Creates a new `PollFd` specifying the events of interest
23     /// for a given file descriptor.
new(fd: RawFd, events: PollFlags) -> PollFd24     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.
revents(self) -> Option<PollFlags>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`].
any(self) -> Option<bool>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`].
all(self) -> Option<bool>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`.
events(self) -> PollFlags61     pub fn events(self) -> PollFlags {
62         PollFlags::from_bits(self.pollfd.events).unwrap()
63     }
64 
65     /// Modify the events of interest for this `PollFd`.
set_events(&mut self, events: PollFlags)66     pub fn set_events(&mut self, events: PollFlags) {
67         self.pollfd.events = events.bits();
68     }
69 }
70 
71 impl AsRawFd for PollFd {
as_raw_fd(&self) -> RawFd72     fn as_raw_fd(&self) -> RawFd {
73         self.pollfd.fd
74     }
75 }
76 
77 libc_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.
poll(fds: &mut [PollFd], timeout: libc::c_int) -> Result<libc::c_int>155 pub 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 
167 feature! {
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"))]
181 pub 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 }
198