]>
Commit | Line | Data |
---|---|---|
064997fb FG |
1 | //! epoll support. |
2 | //! | |
3 | //! This is an experiment, and it isn't yet clear whether epoll is the right | |
4 | //! level of abstraction at which to introduce safety. But it works fairly well | |
5 | //! in simple examples 🙂. | |
6 | //! | |
7 | //! # Examples | |
8 | //! | |
353b0b11 | 9 | //! ```no_run |
064997fb FG |
10 | //! # #![cfg_attr(io_lifetimes_use_std, feature(io_safety))] |
11 | //! # #[cfg(feature = "net")] | |
12 | //! # fn main() -> std::io::Result<()> { | |
13 | //! use io_lifetimes::AsFd; | |
353b0b11 | 14 | //! use rustix::io::{epoll, ioctl_fionbio, read, write}; |
064997fb FG |
15 | //! use rustix::net::{ |
16 | //! accept, bind_v4, listen, socket, AddressFamily, Ipv4Addr, Protocol, SocketAddrV4, | |
17 | //! SocketType, | |
18 | //! }; | |
353b0b11 | 19 | //! use std::collections::HashMap; |
064997fb FG |
20 | //! use std::os::unix::io::AsRawFd; |
21 | //! | |
22 | //! // Create a socket and listen on it. | |
23 | //! let listen_sock = socket(AddressFamily::INET, SocketType::STREAM, Protocol::default())?; | |
24 | //! bind_v4(&listen_sock, &SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0))?; | |
25 | //! listen(&listen_sock, 1)?; | |
26 | //! | |
27 | //! // Create an epoll object. Using `Owning` here means the epoll object will | |
28 | //! // take ownership of the file descriptors registered with it. | |
353b0b11 | 29 | //! let epoll = epoll::epoll_create(epoll::CreateFlags::CLOEXEC)?; |
064997fb FG |
30 | //! |
31 | //! // Register the socket with the epoll object. | |
353b0b11 FG |
32 | //! epoll::epoll_add(&epoll, &listen_sock, 1, epoll::EventFlags::IN)?; |
33 | //! | |
34 | //! // Keep track of the sockets we've opened. | |
35 | //! let mut next_id = 2; | |
36 | //! let mut sockets = HashMap::new(); | |
064997fb FG |
37 | //! |
38 | //! // Process events. | |
39 | //! let mut event_list = epoll::EventVec::with_capacity(4); | |
40 | //! loop { | |
353b0b11 | 41 | //! epoll::epoll_wait(&epoll, &mut event_list, -1)?; |
064997fb | 42 | //! for (_event_flags, target) in &event_list { |
353b0b11 | 43 | //! if target == 1 { |
064997fb FG |
44 | //! // Accept a new connection, set it to non-blocking, and |
45 | //! // register to be notified when it's ready to write to. | |
353b0b11 | 46 | //! let conn_sock = accept(&listen_sock)?; |
064997fb | 47 | //! ioctl_fionbio(&conn_sock, true)?; |
353b0b11 FG |
48 | //! epoll::epoll_add( |
49 | //! &epoll, | |
50 | //! &conn_sock, | |
51 | //! next_id, | |
52 | //! epoll::EventFlags::OUT | epoll::EventFlags::ET, | |
53 | //! )?; | |
54 | //! | |
55 | //! // Keep track of the socket. | |
56 | //! sockets.insert(next_id, conn_sock); | |
57 | //! next_id += 1; | |
064997fb FG |
58 | //! } else { |
59 | //! // Write a message to the stream and then unregister it. | |
353b0b11 FG |
60 | //! let target = sockets.remove(&target).unwrap(); |
61 | //! write(&target, b"hello\n")?; | |
62 | //! let _ = epoll::epoll_del(&epoll, &target)?; | |
064997fb FG |
63 | //! } |
64 | //! } | |
65 | //! } | |
66 | //! # } | |
67 | //! # #[cfg(not(feature = "net"))] | |
68 | //! # fn main() {} | |
69 | //! ``` | |
70 | ||
71 | #![allow(unsafe_code)] | |
72 | ||
73 | use super::super::c; | |
353b0b11 FG |
74 | use crate::backend::io::syscalls; |
75 | use crate::fd::{AsFd, AsRawFd, OwnedFd}; | |
487cf647 | 76 | use crate::io; |
064997fb FG |
77 | use alloc::vec::Vec; |
78 | use bitflags::bitflags; | |
fe692bf9 | 79 | use core::slice; |
064997fb FG |
80 | |
81 | bitflags! { | |
82 | /// `EPOLL_*` for use with [`Epoll::new`]. | |
83 | pub struct CreateFlags: c::c_uint { | |
84 | /// `EPOLL_CLOEXEC` | |
85 | const CLOEXEC = linux_raw_sys::general::EPOLL_CLOEXEC; | |
86 | } | |
87 | } | |
88 | ||
89 | bitflags! { | |
90 | /// `EPOLL*` for use with [`Epoll::add`]. | |
91 | #[derive(Default)] | |
92 | pub struct EventFlags: u32 { | |
93 | /// `EPOLLIN` | |
94 | const IN = linux_raw_sys::general::EPOLLIN as u32; | |
95 | ||
96 | /// `EPOLLOUT` | |
97 | const OUT = linux_raw_sys::general::EPOLLOUT as u32; | |
98 | ||
99 | /// `EPOLLPRI` | |
100 | const PRI = linux_raw_sys::general::EPOLLPRI as u32; | |
101 | ||
102 | /// `EPOLLERR` | |
103 | const ERR = linux_raw_sys::general::EPOLLERR as u32; | |
104 | ||
105 | /// `EPOLLHUP` | |
106 | const HUP = linux_raw_sys::general::EPOLLHUP as u32; | |
107 | ||
353b0b11 FG |
108 | /// `EPOLLRDNORM` |
109 | const RDNORM = linux_raw_sys::general::EPOLLRDNORM as u32; | |
110 | ||
111 | /// `EPOLLRDBAND` | |
112 | const RDBAND = linux_raw_sys::general::EPOLLRDBAND as u32; | |
113 | ||
114 | /// `EPOLLWRNORM` | |
115 | const WRNORM = linux_raw_sys::general::EPOLLWRNORM as u32; | |
116 | ||
117 | /// `EPOLLWRBAND` | |
118 | const WRBAND = linux_raw_sys::general::EPOLLWRBAND as u32; | |
119 | ||
120 | /// `EPOLLMSG` | |
121 | const MSG = linux_raw_sys::general::EPOLLMSG as u32; | |
122 | ||
123 | /// `EPOLLRDHUP` | |
124 | const RDHUP = linux_raw_sys::general::EPOLLRDHUP as u32; | |
125 | ||
064997fb FG |
126 | /// `EPOLLET` |
127 | const ET = linux_raw_sys::general::EPOLLET as u32; | |
128 | ||
129 | /// `EPOLLONESHOT` | |
130 | const ONESHOT = linux_raw_sys::general::EPOLLONESHOT as u32; | |
131 | ||
132 | /// `EPOLLWAKEUP` | |
133 | const WAKEUP = linux_raw_sys::general::EPOLLWAKEUP as u32; | |
134 | ||
135 | /// `EPOLLEXCLUSIVE` | |
136 | const EXCLUSIVE = linux_raw_sys::general::EPOLLEXCLUSIVE as u32; | |
137 | } | |
138 | } | |
139 | ||
353b0b11 | 140 | /// `epoll_create1(flags)`—Creates a new `Epoll`. |
064997fb | 141 | /// |
353b0b11 FG |
142 | /// Use the [`CreateFlags::CLOEXEC`] flag to prevent the resulting file |
143 | /// descriptor from being implicitly passed across `exec` boundaries. | |
144 | #[inline] | |
145 | #[doc(alias = "epoll_create1")] | |
146 | pub fn epoll_create(flags: CreateFlags) -> io::Result<OwnedFd> { | |
147 | syscalls::epoll_create(flags) | |
064997fb FG |
148 | } |
149 | ||
353b0b11 FG |
150 | /// `epoll_ctl(self, EPOLL_CTL_ADD, data, event)`—Adds an element to an |
151 | /// `Epoll`. | |
152 | /// | |
153 | /// This registers interest in any of the events set in `events` occurring | |
154 | /// on the file descriptor associated with `data`. | |
155 | /// | |
fe692bf9 FG |
156 | /// If `epoll_del` is not called on the I/O source passed into this function |
157 | /// before the I/O source is `close`d, then the `epoll` will act as if the I/O | |
158 | /// source is still registered with it. This can lead to spurious events being | |
159 | /// returned from `epoll_wait`. If a file descriptor is an | |
160 | /// `Arc<dyn SystemResource>`, then `epoll` can be thought to maintain a | |
161 | /// `Weak<dyn SystemResource>` to the file descriptor. | |
353b0b11 | 162 | #[doc(alias = "epoll_ctl")] |
fe692bf9 | 163 | #[inline] |
353b0b11 FG |
164 | pub fn epoll_add( |
165 | epoll: impl AsFd, | |
166 | source: impl AsFd, | |
167 | data: u64, | |
168 | event_flags: EventFlags, | |
169 | ) -> io::Result<()> { | |
170 | // SAFETY: We're calling `epoll_ctl` via FFI and we know how it | |
171 | // behaves. | |
172 | unsafe { | |
173 | syscalls::epoll_add( | |
174 | epoll.as_fd(), | |
175 | source.as_fd().as_raw_fd(), | |
176 | &linux_raw_sys::general::epoll_event { | |
177 | events: event_flags.bits(), | |
178 | data, | |
179 | }, | |
180 | ) | |
064997fb FG |
181 | } |
182 | } | |
183 | ||
353b0b11 FG |
184 | /// `epoll_ctl(self, EPOLL_CTL_MOD, target, event)`—Modifies an element in |
185 | /// this `Epoll`. | |
186 | /// | |
187 | /// This sets the events of interest with `target` to `events`. | |
188 | #[doc(alias = "epoll_ctl")] | |
fe692bf9 | 189 | #[inline] |
353b0b11 FG |
190 | pub fn epoll_mod( |
191 | epoll: impl AsFd, | |
192 | source: impl AsFd, | |
193 | data: u64, | |
194 | event_flags: EventFlags, | |
195 | ) -> io::Result<()> { | |
196 | // SAFETY: We're calling `epoll_ctl` via FFI and we know how it | |
197 | // behaves. | |
198 | unsafe { | |
199 | let raw_fd = source.as_fd().as_raw_fd(); | |
200 | syscalls::epoll_mod( | |
201 | epoll.as_fd(), | |
202 | raw_fd, | |
203 | &linux_raw_sys::general::epoll_event { | |
204 | events: event_flags.bits(), | |
205 | data, | |
206 | }, | |
207 | ) | |
064997fb FG |
208 | } |
209 | } | |
210 | ||
353b0b11 FG |
211 | /// `epoll_ctl(self, EPOLL_CTL_DEL, target, NULL)`—Removes an element in |
212 | /// this `Epoll`. | |
213 | /// | |
214 | /// This also returns the owning `Data`. | |
215 | #[doc(alias = "epoll_ctl")] | |
fe692bf9 | 216 | #[inline] |
353b0b11 FG |
217 | pub fn epoll_del(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { |
218 | // SAFETY: We're calling `epoll_ctl` via FFI and we know how it | |
219 | // behaves. | |
220 | unsafe { | |
221 | let raw_fd = source.as_fd().as_raw_fd(); | |
222 | syscalls::epoll_del(epoll.as_fd(), raw_fd) | |
064997fb FG |
223 | } |
224 | } | |
225 | ||
353b0b11 FG |
226 | /// `epoll_wait(self, events, timeout)`—Waits for registered events of |
227 | /// interest. | |
228 | /// | |
229 | /// For each event of interest, an element is written to `events`. On | |
230 | /// success, this returns the number of written elements. | |
fe692bf9 | 231 | #[inline] |
353b0b11 FG |
232 | pub fn epoll_wait( |
233 | epoll: impl AsFd, | |
234 | event_list: &mut EventVec, | |
235 | timeout: c::c_int, | |
236 | ) -> io::Result<()> { | |
237 | // SAFETY: We're calling `epoll_wait` via FFI and we know how it | |
238 | // behaves. | |
239 | unsafe { | |
240 | event_list.events.set_len(0); | |
241 | let nfds = syscalls::epoll_wait( | |
242 | epoll.as_fd(), | |
243 | event_list.events[..].as_mut_ptr().cast(), | |
244 | event_list.events.capacity(), | |
245 | timeout, | |
246 | )?; | |
247 | event_list.events.set_len(nfds); | |
248 | } | |
249 | ||
250 | Ok(()) | |
064997fb FG |
251 | } |
252 | ||
253 | /// An iterator over the `Event`s in an `EventVec`. | |
353b0b11 | 254 | pub struct Iter<'a> { |
fe692bf9 | 255 | iter: slice::Iter<'a, Event>, |
064997fb FG |
256 | } |
257 | ||
353b0b11 FG |
258 | impl<'a> Iterator for Iter<'a> { |
259 | type Item = (EventFlags, u64); | |
064997fb | 260 | |
fe692bf9 | 261 | #[inline] |
064997fb | 262 | fn next(&mut self) -> Option<Self::Item> { |
353b0b11 FG |
263 | self.iter |
264 | .next() | |
265 | .map(|event| (event.event_flags, event.data)) | |
064997fb FG |
266 | } |
267 | } | |
268 | ||
269 | /// A record of an event that occurred. | |
270 | #[repr(C)] | |
271 | #[cfg_attr(target_arch = "x86_64", repr(packed))] | |
272 | struct Event { | |
273 | // Match the layout of `linux_raw_sys::general::epoll_event`. We just use a | |
353b0b11 | 274 | // `u64` instead of the full union. |
064997fb | 275 | event_flags: EventFlags, |
353b0b11 | 276 | data: u64, |
064997fb FG |
277 | } |
278 | ||
279 | /// A vector of `Event`s, plus context for interpreting them. | |
353b0b11 | 280 | pub struct EventVec { |
064997fb | 281 | events: Vec<Event>, |
064997fb FG |
282 | } |
283 | ||
353b0b11 | 284 | impl EventVec { |
064997fb FG |
285 | /// Constructs an `EventVec` with memory for `capacity` `Event`s. |
286 | #[inline] | |
287 | pub fn with_capacity(capacity: usize) -> Self { | |
288 | Self { | |
289 | events: Vec::with_capacity(capacity), | |
064997fb FG |
290 | } |
291 | } | |
292 | ||
293 | /// Returns the current `Event` capacity of this `EventVec`. | |
294 | #[inline] | |
295 | pub fn capacity(&self) -> usize { | |
296 | self.events.capacity() | |
297 | } | |
298 | ||
299 | /// Reserves enough memory for at least `additional` more `Event`s. | |
300 | #[inline] | |
301 | pub fn reserve(&mut self, additional: usize) { | |
302 | self.events.reserve(additional); | |
303 | } | |
304 | ||
305 | /// Reserves enough memory for exactly `additional` more `Event`s. | |
306 | #[inline] | |
307 | pub fn reserve_exact(&mut self, additional: usize) { | |
308 | self.events.reserve_exact(additional); | |
309 | } | |
310 | ||
311 | /// Clears all the `Events` out of this `EventVec`. | |
312 | #[inline] | |
313 | pub fn clear(&mut self) { | |
314 | self.events.clear(); | |
315 | } | |
316 | ||
317 | /// Shrinks the capacity of this `EventVec` as much as possible. | |
318 | #[inline] | |
319 | pub fn shrink_to_fit(&mut self) { | |
320 | self.events.shrink_to_fit(); | |
321 | } | |
322 | ||
323 | /// Returns an iterator over the `Event`s in this `EventVec`. | |
324 | #[inline] | |
353b0b11 | 325 | pub fn iter(&self) -> Iter<'_> { |
064997fb FG |
326 | Iter { |
327 | iter: self.events.iter(), | |
064997fb FG |
328 | } |
329 | } | |
330 | ||
331 | /// Returns the number of `Event`s logically contained in this `EventVec`. | |
332 | #[inline] | |
333 | pub fn len(&mut self) -> usize { | |
334 | self.events.len() | |
335 | } | |
336 | ||
337 | /// Tests whether this `EventVec` is logically empty. | |
338 | #[inline] | |
339 | pub fn is_empty(&mut self) -> bool { | |
340 | self.events.is_empty() | |
341 | } | |
342 | } | |
343 | ||
353b0b11 FG |
344 | impl<'a> IntoIterator for &'a EventVec { |
345 | type IntoIter = Iter<'a>; | |
346 | type Item = (EventFlags, u64); | |
064997fb FG |
347 | |
348 | #[inline] | |
349 | fn into_iter(self) -> Self::IntoIter { | |
350 | self.iter() | |
351 | } | |
352 | } |