]>
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 | use super::super::c; | |
72 | use super::super::conv::{ret, ret_owned_fd, ret_u32}; | |
353b0b11 | 73 | use crate::fd::{AsFd, AsRawFd, OwnedFd}; |
487cf647 | 74 | use crate::io; |
064997fb FG |
75 | use alloc::vec::Vec; |
76 | use bitflags::bitflags; | |
77 | use core::convert::TryInto; | |
353b0b11 | 78 | use core::ptr::null_mut; |
064997fb FG |
79 | |
80 | bitflags! { | |
81 | /// `EPOLL_*` for use with [`Epoll::new`]. | |
82 | pub struct CreateFlags: c::c_int { | |
83 | /// `EPOLL_CLOEXEC` | |
84 | const CLOEXEC = c::EPOLL_CLOEXEC; | |
85 | } | |
86 | } | |
87 | ||
88 | bitflags! { | |
89 | /// `EPOLL*` for use with [`Epoll::add`]. | |
90 | #[derive(Default)] | |
91 | pub struct EventFlags: u32 { | |
92 | /// `EPOLLIN` | |
93 | const IN = c::EPOLLIN as u32; | |
94 | ||
95 | /// `EPOLLOUT` | |
96 | const OUT = c::EPOLLOUT as u32; | |
97 | ||
98 | /// `EPOLLPRI` | |
99 | const PRI = c::EPOLLPRI as u32; | |
100 | ||
101 | /// `EPOLLERR` | |
102 | const ERR = c::EPOLLERR as u32; | |
103 | ||
104 | /// `EPOLLHUP` | |
105 | const HUP = c::EPOLLHUP as u32; | |
106 | ||
353b0b11 FG |
107 | /// `EPOLLRDNORM` |
108 | const RDNORM = c::EPOLLRDNORM as u32; | |
109 | ||
110 | /// `EPOLLRDBAND` | |
111 | const RDBAND = c::EPOLLRDBAND as u32; | |
112 | ||
113 | /// `EPOLLWRNORM` | |
114 | const WRNORM = c::EPOLLWRNORM as u32; | |
115 | ||
116 | /// `EPOLLWRBAND` | |
117 | const WRBAND = c::EPOLLWRBAND as u32; | |
118 | ||
119 | /// `EPOLLMSG` | |
120 | const MSG = c::EPOLLMSG as u32; | |
121 | ||
122 | /// `EPOLLRDHUP` | |
123 | const RDHUP = c::EPOLLRDHUP as u32; | |
124 | ||
064997fb FG |
125 | /// `EPOLLET` |
126 | const ET = c::EPOLLET as u32; | |
127 | ||
128 | /// `EPOLLONESHOT` | |
129 | const ONESHOT = c::EPOLLONESHOT as u32; | |
130 | ||
131 | /// `EPOLLWAKEUP` | |
132 | const WAKEUP = c::EPOLLWAKEUP as u32; | |
133 | ||
134 | /// `EPOLLEXCLUSIVE` | |
135 | #[cfg(not(target_os = "android"))] | |
136 | const EXCLUSIVE = c::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 | // SAFETY: We're calling `epoll_create1` via FFI and we know how it | |
148 | // behaves. | |
149 | unsafe { ret_owned_fd(c::epoll_create1(flags.bits())) } | |
064997fb FG |
150 | } |
151 | ||
353b0b11 FG |
152 | /// `epoll_ctl(self, EPOLL_CTL_ADD, data, event)`—Adds an element to an |
153 | /// `Epoll`. | |
154 | /// | |
155 | /// Note that if `epoll_del` is not called on the I/O source passed into | |
156 | /// this function before the I/O source is `close`d, then the `epoll` will | |
157 | /// act as if the I/O source is still registered with it. This can lead to | |
158 | /// spurious events being returned from `epoll_wait`. If a file descriptor | |
159 | /// is an `Arc<dyn SystemResource>`, then `epoll` can be thought to maintain | |
160 | /// a `Weak<dyn SystemResource>` to the file descriptor. | |
161 | #[doc(alias = "epoll_ctl")] | |
162 | pub fn epoll_add( | |
163 | epoll: impl AsFd, | |
164 | source: impl AsFd, | |
165 | data: u64, | |
166 | event_flags: EventFlags, | |
167 | ) -> io::Result<()> { | |
168 | // SAFETY: We're calling `epoll_ctl` via FFI and we know how it | |
169 | // behaves. | |
170 | unsafe { | |
171 | let raw_fd = source.as_fd().as_raw_fd(); | |
172 | ret(c::epoll_ctl( | |
173 | epoll.as_fd().as_raw_fd(), | |
174 | c::EPOLL_CTL_ADD, | |
175 | raw_fd, | |
176 | &mut c::epoll_event { | |
177 | events: event_flags.bits(), | |
178 | r#u64: 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")] | |
189 | pub fn epoll_mod( | |
190 | epoll: impl AsFd, | |
191 | source: impl AsFd, | |
192 | data: u64, | |
193 | event_flags: EventFlags, | |
194 | ) -> io::Result<()> { | |
195 | let raw_fd = source.as_fd().as_raw_fd(); | |
196 | ||
197 | // SAFETY: We're calling `epoll_ctl` via FFI and we know how it | |
198 | // behaves. | |
199 | unsafe { | |
200 | ret(c::epoll_ctl( | |
201 | epoll.as_fd().as_raw_fd(), | |
202 | c::EPOLL_CTL_MOD, | |
203 | raw_fd, | |
204 | &mut c::epoll_event { | |
205 | events: event_flags.bits(), | |
206 | r#u64: data, | |
207 | }, | |
208 | )) | |
064997fb FG |
209 | } |
210 | } | |
211 | ||
353b0b11 FG |
212 | /// `epoll_ctl(self, EPOLL_CTL_DEL, target, NULL)`—Removes an element in |
213 | /// this `Epoll`. | |
214 | #[doc(alias = "epoll_ctl")] | |
215 | pub fn epoll_del(epoll: impl AsFd, source: impl AsFd) -> io::Result<()> { | |
216 | // SAFETY: We're calling `epoll_ctl` via FFI and we know how it | |
217 | // behaves. | |
218 | unsafe { | |
219 | let raw_fd = source.as_fd().as_raw_fd(); | |
220 | ret(c::epoll_ctl( | |
221 | epoll.as_fd().as_raw_fd(), | |
222 | c::EPOLL_CTL_DEL, | |
223 | raw_fd, | |
224 | null_mut(), | |
225 | )) | |
064997fb FG |
226 | } |
227 | } | |
228 | ||
353b0b11 FG |
229 | /// `epoll_wait(self, events, timeout)`—Waits for registered events of |
230 | /// interest. | |
231 | /// | |
232 | /// For each event of interest, an element is written to `events`. On | |
233 | /// success, this returns the number of written elements. | |
234 | pub fn epoll_wait( | |
235 | epoll: impl AsFd, | |
236 | event_list: &mut EventVec, | |
237 | timeout: c::c_int, | |
238 | ) -> io::Result<()> { | |
239 | // SAFETY: We're calling `epoll_wait` via FFI and we know how it | |
240 | // behaves. | |
241 | unsafe { | |
242 | event_list.events.set_len(0); | |
243 | let nfds = ret_u32(c::epoll_wait( | |
244 | epoll.as_fd().as_raw_fd(), | |
245 | event_list.events.as_mut_ptr().cast::<c::epoll_event>(), | |
246 | event_list.events.capacity().try_into().unwrap_or(i32::MAX), | |
247 | timeout, | |
248 | ))?; | |
249 | event_list.events.set_len(nfds as usize); | |
250 | } | |
251 | ||
252 | Ok(()) | |
064997fb FG |
253 | } |
254 | ||
255 | /// An iterator over the `Event`s in an `EventVec`. | |
353b0b11 FG |
256 | pub struct Iter<'a> { |
257 | iter: core::slice::Iter<'a, Event>, | |
064997fb FG |
258 | } |
259 | ||
353b0b11 FG |
260 | impl<'a> Iterator for Iter<'a> { |
261 | type Item = (EventFlags, u64); | |
064997fb FG |
262 | |
263 | fn next(&mut self) -> Option<Self::Item> { | |
353b0b11 | 264 | // SAFETY: `self.context` is guaranteed to be valid because we hold |
064997fb FG |
265 | // `'context` for it. And we know this event is associated with this |
266 | // context because `wait` sets both. | |
353b0b11 FG |
267 | self.iter |
268 | .next() | |
269 | .map(|event| (event.event_flags, event.data)) | |
064997fb FG |
270 | } |
271 | } | |
272 | ||
273 | /// A record of an event that occurred. | |
274 | #[repr(C)] | |
275 | #[cfg_attr( | |
276 | any( | |
277 | all( | |
278 | target_arch = "x86", | |
279 | not(target_env = "musl"), | |
280 | not(target_os = "android"), | |
281 | ), | |
282 | target_arch = "x86_64", | |
283 | ), | |
284 | repr(packed) | |
285 | )] | |
286 | struct Event { | |
287 | // Match the layout of `c::epoll_event`. We just use a `u64` instead of | |
353b0b11 | 288 | // the full union. |
064997fb | 289 | event_flags: EventFlags, |
353b0b11 | 290 | data: u64, |
064997fb FG |
291 | } |
292 | ||
293 | /// A vector of `Event`s, plus context for interpreting them. | |
353b0b11 | 294 | pub struct EventVec { |
064997fb | 295 | events: Vec<Event>, |
064997fb FG |
296 | } |
297 | ||
353b0b11 | 298 | impl EventVec { |
064997fb FG |
299 | /// Constructs an `EventVec` with memory for `capacity` `Event`s. |
300 | #[inline] | |
301 | pub fn with_capacity(capacity: usize) -> Self { | |
302 | Self { | |
303 | events: Vec::with_capacity(capacity), | |
064997fb FG |
304 | } |
305 | } | |
306 | ||
307 | /// Returns the current `Event` capacity of this `EventVec`. | |
308 | #[inline] | |
309 | pub fn capacity(&self) -> usize { | |
310 | self.events.capacity() | |
311 | } | |
312 | ||
313 | /// Reserves enough memory for at least `additional` more `Event`s. | |
314 | #[inline] | |
315 | pub fn reserve(&mut self, additional: usize) { | |
316 | self.events.reserve(additional); | |
317 | } | |
318 | ||
319 | /// Reserves enough memory for exactly `additional` more `Event`s. | |
320 | #[inline] | |
321 | pub fn reserve_exact(&mut self, additional: usize) { | |
322 | self.events.reserve_exact(additional); | |
323 | } | |
324 | ||
325 | /// Clears all the `Events` out of this `EventVec`. | |
326 | #[inline] | |
327 | pub fn clear(&mut self) { | |
328 | self.events.clear(); | |
329 | } | |
330 | ||
331 | /// Shrinks the capacity of this `EventVec` as much as possible. | |
332 | #[inline] | |
333 | pub fn shrink_to_fit(&mut self) { | |
334 | self.events.shrink_to_fit(); | |
335 | } | |
336 | ||
337 | /// Returns an iterator over the `Event`s in this `EventVec`. | |
338 | #[inline] | |
353b0b11 | 339 | pub fn iter(&self) -> Iter<'_> { |
064997fb FG |
340 | Iter { |
341 | iter: self.events.iter(), | |
064997fb FG |
342 | } |
343 | } | |
344 | ||
345 | /// Returns the number of `Event`s logically contained in this `EventVec`. | |
346 | #[inline] | |
347 | pub fn len(&mut self) -> usize { | |
348 | self.events.len() | |
349 | } | |
350 | ||
351 | /// Tests whether this `EventVec` is logically empty. | |
352 | #[inline] | |
353 | pub fn is_empty(&mut self) -> bool { | |
354 | self.events.is_empty() | |
355 | } | |
356 | } | |
357 | ||
353b0b11 FG |
358 | impl<'a> IntoIterator for &'a EventVec { |
359 | type IntoIter = Iter<'a>; | |
360 | type Item = (EventFlags, u64); | |
064997fb FG |
361 | |
362 | #[inline] | |
363 | fn into_iter(self) -> Self::IntoIter { | |
364 | self.iter() | |
365 | } | |
366 | } |