wl_data_control_protocol_evt/
epoll.rs1use 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#[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}