Skip to main content

wl_data_control_protocol_evt/
epoll.rs

1use rustix::{
2    buffer::spare_capacity,
3    event::epoll::{self, EventFlags},
4    fs::Timespec,
5    io::Errno,
6};
7use std::{
8    collections::HashMap,
9    os::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd},
10};
11
12pub struct Epoll {
13    epollfd: OwnedFd,
14}
15
16/// An error returned from `epoll`
17#[derive(Debug)]
18pub struct EpollError(pub Errno);
19
20impl From<Errno> for EpollError {
21    fn from(errno: Errno) -> Self {
22        Self(errno)
23    }
24}
25
26impl core::fmt::Display for EpollError {
27    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
28        write!(f, "EpollError({})", self.0)
29    }
30}
31
32impl core::error::Error for EpollError {}
33
34impl Epoll {
35    pub(crate) fn new(wl_fd: BorrowedFd<'_>) -> Result<Self, EpollError> {
36        let epollfd = epoll::create(epoll::CreateFlags::CLOEXEC)?;
37        let this = Self { epollfd };
38        this.register(wl_fd, EventFlags::IN)?;
39        Ok(this)
40    }
41
42    #[expect(clippy::cast_sign_loss)]
43    fn register(&self, fd: BorrowedFd<'_>, flags: EventFlags) -> Result<(), EpollError> {
44        epoll::add(
45            &self.epollfd,
46            fd,
47            epoll::EventData::new_u64(fd.as_fd().as_raw_fd() as u64),
48            flags,
49        )?;
50        Ok(())
51    }
52
53    pub(crate) fn register_readable(&self, fd: BorrowedFd<'_>) -> Result<(), EpollError> {
54        self.register(fd, EventFlags::IN)
55    }
56
57    pub(crate) fn register_writable(&self, fd: BorrowedFd<'_>) -> Result<(), EpollError> {
58        self.register(fd, EventFlags::OUT)
59    }
60
61    pub(crate) fn delete(&self, fd: BorrowedFd<'_>) -> Result<(), EpollError> {
62        epoll::delete(&self.epollfd, fd)?;
63        Ok(())
64    }
65
66    pub(crate) fn wait<R, W>(
67        &self,
68        epoll_events: &mut Vec<epoll::Event>,
69        timeout: Option<&Timespec>,
70        wl_fd: BorrowedFd<'_>,
71        readers: &HashMap<i32, R>,
72        writers: &HashMap<i32, W>,
73    ) -> Result<Option<EpollResult>, EpollError> {
74        epoll::wait(&self.epollfd, spare_capacity(epoll_events), timeout)?;
75        EpollResult::new(epoll_events, wl_fd, readers, writers)
76    }
77}
78
79impl AsFd for Epoll {
80    fn as_fd(&self) -> BorrowedFd<'_> {
81        self.epollfd.as_fd()
82    }
83}
84
85impl AsRawFd for Epoll {
86    fn as_raw_fd(&self) -> std::os::unix::prelude::RawFd {
87        self.epollfd.as_raw_fd()
88    }
89}
90
91#[derive(Default)]
92pub struct FdSet {
93    pub(crate) ready: Vec<i32>,
94    pub(crate) dead: Vec<i32>,
95}
96
97#[derive(Default)]
98pub struct EpollResult {
99    pub(crate) wl_is_readable: bool,
100    pub(crate) readers: FdSet,
101    pub(crate) writers: FdSet,
102}
103
104impl EpollResult {
105    #[expect(clippy::cast_possible_truncation)]
106    fn new<R, W>(
107        events: &[epoll::Event],
108        wl_fd: BorrowedFd<'_>,
109        readers: &HashMap<i32, R>,
110        writers: &HashMap<i32, W>,
111    ) -> Result<Option<Self>, EpollError> {
112        let mut wl_is_readable = false;
113        let mut readers_fd_set = FdSet::default();
114        let mut writers_fd_set = FdSet::default();
115
116        for event in events {
117            let fd = event.data.u64() as i32;
118            let revents: EventFlags = event.flags;
119
120            if fd == wl_fd.as_raw_fd() {
121                if revents.intersects(EventFlags::HUP | EventFlags::ERR) {
122                    return Err(EpollError(Errno::CONNRESET));
123                } else if revents.contains(EventFlags::IN) {
124                    wl_is_readable = true;
125                }
126            } else if readers.contains_key(&fd) {
127                if revents.intersects(EventFlags::ERR) {
128                    log::error!("reader with FD {fd} returned revents {revents:?}, removing it");
129                    readers_fd_set.dead.push(fd);
130                } else if revents.intersects(EventFlags::IN | EventFlags::HUP) {
131                    readers_fd_set.ready.push(fd);
132                }
133            } else if writers.contains_key(&fd) {
134                if revents.intersects(EventFlags::ERR | EventFlags::HUP) {
135                    log::error!("writer with FD {fd} returned revents {revents:?}, removing it");
136                    writers_fd_set.dead.push(fd);
137                } else if revents.contains(EventFlags::OUT) {
138                    writers_fd_set.ready.push(fd);
139                }
140            }
141        }
142
143        if !wl_is_readable
144            && readers_fd_set.ready.is_empty()
145            && readers_fd_set.dead.is_empty()
146            && writers_fd_set.ready.is_empty()
147            && writers_fd_set.dead.is_empty()
148        {
149            Ok(None)
150        } else {
151            Ok(Some(Self {
152                wl_is_readable,
153                readers: readers_fd_set,
154                writers: writers_fd_set,
155            }))
156        }
157    }
158}