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//!
9//! ```no_run
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;
14//! use rustix::io::{epoll, ioctl_fionbio, read, write};
15//! use rustix::net::{
16//! accept, bind_v4, listen, socket, AddressFamily, Ipv4Addr, Protocol, SocketAddrV4,
17//! SocketType,
18//! };
19//! use std::collections::HashMap;
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.
29//! let epoll = epoll::epoll_create(epoll::CreateFlags::CLOEXEC)?;
30//!
31//! // Register the socket with the epoll object.
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();
37//!
38//! // Process events.
39//! let mut event_list = epoll::EventVec::with_capacity(4);
40//! loop {
41//! epoll::epoll_wait(&epoll, &mut event_list, -1)?;
42//! for (_event_flags, target) in &event_list {
43//! if target == 1 {
44//! // Accept a new connection, set it to non-blocking, and
45//! // register to be notified when it's ready to write to.
46//! let conn_sock = accept(&listen_sock)?;
47//! ioctl_fionbio(&conn_sock, true)?;
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;
58//! } else {
59//! // Write a message to the stream and then unregister it.
60//! let target = sockets.remove(&target).unwrap();
61//! write(&target, b"hello\n")?;
62//! let _ = epoll::epoll_del(&epoll, &target)?;
63//! }
64//! }
65//! }
66//! # }
67//! # #[cfg(not(feature = "net"))]
68//! # fn main() {}
69//! ```
70
71#![allow(unsafe_code)]
72
73use super::super::c;
74use crate::backend::io::syscalls;
75use crate::fd::{AsFd, AsRawFd, OwnedFd};
76use crate::io;
77use alloc::vec::Vec;
78use bitflags::bitflags;
79use core::slice;
80
81bitflags! {
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
89bitflags! {
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
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
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
140/// `epoll_create1(flags)`—Creates a new `Epoll`.
141///
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")]
146pub fn epoll_create(flags: CreateFlags) -> io::Result<OwnedFd> {
147 syscalls::epoll_create(flags)
148}
149
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///
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.
162#[doc(alias = "epoll_ctl")]
163#[inline]
164pub 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 epfd: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 )
181 }
182}
183
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#[inline]
190pub 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: i32 = source.as_fd().as_raw_fd();
200 syscalls::epoll_mod(
201 epfd:epoll.as_fd(),
202 raw_fd,
203 &linux_raw_sys::general::epoll_event {
204 events: event_flags.bits(),
205 data,
206 },
207 )
208 }
209}
210
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")]
216#[inline]
217pub 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: i32 = source.as_fd().as_raw_fd();
222 syscalls::epoll_del(epfd:epoll.as_fd(), raw_fd)
223 }
224}
225
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.
231#[inline]
232pub 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(new_len:0);
241 let nfds: usize = syscalls::epoll_wait(
242 epfd:epoll.as_fd(),
243 events:event_list.events[..].as_mut_ptr().cast(),
244 num_events:event_list.events.capacity(),
245 timeout,
246 )?;
247 event_list.events.set_len(new_len:nfds);
248 }
249
250 Ok(())
251}
252
253/// An iterator over the `Event`s in an `EventVec`.
254pub struct Iter<'a> {
255 iter: slice::Iter<'a, Event>,
256}
257
258impl<'a> Iterator for Iter<'a> {
259 type Item = (EventFlags, u64);
260
261 #[inline]
262 fn next(&mut self) -> Option<Self::Item> {
263 self.iter
264 .next()
265 .map(|event: &Event| (event.event_flags, event.data))
266 }
267}
268
269/// A record of an event that occurred.
270#[repr(C)]
271#[cfg_attr(target_arch = "x86_64", repr(packed))]
272struct Event {
273 // Match the layout of `linux_raw_sys::general::epoll_event`. We just use a
274 // `u64` instead of the full union.
275 event_flags: EventFlags,
276 data: u64,
277}
278
279/// A vector of `Event`s, plus context for interpreting them.
280pub struct EventVec {
281 events: Vec<Event>,
282}
283
284impl EventVec {
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),
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]
325 pub fn iter(&self) -> Iter<'_> {
326 Iter {
327 iter: self.events.iter(),
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
344impl<'a> IntoIterator for &'a EventVec {
345 type IntoIter = Iter<'a>;
346 type Item = (EventFlags, u64);
347
348 #[inline]
349 fn into_iter(self) -> Self::IntoIter {
350 self.iter()
351 }
352}
353