tiny_std/linux/
epoll.rs

1use rusl::platform::EpollOp;
2pub use rusl::platform::{EpollEvent, EpollEventMask};
3
4use crate::error::{Error, Result};
5use crate::unix::fd::{OwnedFd, RawFd};
6
7pub struct EpollDriver {
8    epoll_fd: OwnedFd,
9}
10
11#[derive(Debug, Copy, Clone)]
12pub enum EpollTimeout {
13    WaitForever,
14    WaitMillis(u32),
15    NoWait,
16}
17
18impl EpollDriver {
19    /// Creates a new epoll driver
20    /// # Errors
21    /// OS errors during setup syscall
22    #[inline]
23    pub fn create(cloexec: bool) -> Result<Self> {
24        let fd = rusl::select::epoll_create(cloexec)?;
25        Ok(Self {
26            epoll_fd: OwnedFd(fd),
27        })
28    }
29
30    /// Registers a new entry into the `EpollDriver`'s underlying kernel state.
31    /// The identifier is any u64 which the user can use to tell ready fds apart or indeed
32    /// store any data they'd like.
33    /// # Errors
34    /// OS errors registering the fd, such as a nonsensical mask, or a bad provided `fd`
35    #[inline]
36    pub fn register(&self, fd: RawFd, identifier: u64, mask: EpollEventMask) -> Result<()> {
37        rusl::select::epoll_ctl(
38            self.epoll_fd.0,
39            EpollOp::Add,
40            fd,
41            &EpollEvent::new(identifier, mask),
42        )?;
43        Ok(())
44    }
45
46    /// Unregisters an `fd` from the `EpollDriver`'s underlying kernel state.
47    /// # Errors
48    /// OS errors unregistering the fd, such as a bad provided `fd`
49    #[inline]
50    pub fn unregister(&self, fd: RawFd) -> Result<()> {
51        rusl::select::epoll_del(self.epoll_fd.0, fd)?;
52        Ok(())
53    }
54
55    /// Replaces the previous registration for the provided fd with a new one.
56    /// # Errors
57    /// OS errors modifying the fd, such as a bad provided `fd` or a nonsensical `mask`.
58    #[inline]
59    pub fn modify(&self, fd: RawFd, identifier: u64, mask: EpollEventMask) -> Result<()> {
60        rusl::select::epoll_ctl(
61            self.epoll_fd.0,
62            EpollOp::Mod,
63            fd,
64            &EpollEvent::new(identifier, mask),
65        )?;
66        Ok(())
67    }
68
69    /// Waits for any registered `fd` to become ready.
70    /// The `timeout` can at most be `i32::MAX` milliseconds
71    /// # Errors
72    /// Os errors occurring during wait, or a timeout that is too long
73    #[inline]
74    #[expect(clippy::cast_possible_wrap)]
75    pub fn wait(&self, event_buf: &mut [EpollEvent], timeout: EpollTimeout) -> Result<usize> {
76        let num_ready = match timeout {
77            EpollTimeout::WaitForever => rusl::select::epoll_wait(self.epoll_fd.0, event_buf, -1)?,
78            EpollTimeout::WaitMillis(time) => {
79                if time > i32::MAX as u32 {
80                    return Err(Error::Uncategorized(
81                        "Epoll wait with a timeout bigger than i32::MAX",
82                    ));
83                }
84                rusl::select::epoll_wait(self.epoll_fd.0, event_buf, time as i32)?
85            }
86            EpollTimeout::NoWait => rusl::select::epoll_wait(self.epoll_fd.0, event_buf, 0)?,
87        };
88        Ok(num_ready)
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95    use rusl::platform::STDOUT;
96
97    #[test]
98    fn test_epoll_driver() {
99        let drive = EpollDriver::create(true).unwrap();
100        drive.register(STDOUT, 1, EpollEventMask::EPOLLOUT).unwrap();
101        println!("Dummy out");
102        let mut buf = [EpollEvent::new(0, EpollEventMask::empty())];
103        drive
104            .wait(&mut buf, EpollTimeout::WaitMillis(1_000))
105            .unwrap();
106        assert_eq!(1, buf[0].get_data());
107        assert!(
108            buf[0].get_events().contains(EpollEventMask::EPOLLOUT),
109            "Expected EPOLLOUT, got {:?}",
110            buf[0].get_events()
111        );
112    }
113}