arceos_posix_api/imp/io_mpx/
epoll.rs1use alloc::collections::BTreeMap;
6use alloc::collections::btree_map::Entry;
7use alloc::sync::Arc;
8use core::{ffi::c_int, time::Duration};
9
10use axerrno::{LinuxError, LinuxResult};
11use axhal::time::wall_time;
12use axsync::Mutex;
13
14use crate::ctypes;
15use crate::imp::fd_ops::{FileLike, add_file_like, get_file_like};
16
17pub struct EpollInstance {
18 events: Mutex<BTreeMap<usize, ctypes::epoll_event>>,
19}
20
21unsafe impl Send for ctypes::epoll_event {}
22unsafe impl Sync for ctypes::epoll_event {}
23
24impl EpollInstance {
25 pub fn new(_flags: usize) -> Self {
27 Self {
28 events: Mutex::new(BTreeMap::new()),
29 }
30 }
31
32 fn from_fd(fd: c_int) -> LinuxResult<Arc<Self>> {
33 get_file_like(fd)?
34 .into_any()
35 .downcast::<EpollInstance>()
36 .map_err(|_| LinuxError::EINVAL)
37 }
38
39 fn control(&self, op: usize, fd: usize, event: &ctypes::epoll_event) -> LinuxResult<usize> {
40 match get_file_like(fd as c_int) {
41 Ok(_) => {}
42 Err(e) => return Err(e),
43 }
44
45 match op as u32 {
46 ctypes::EPOLL_CTL_ADD => {
47 if let Entry::Vacant(e) = self.events.lock().entry(fd) {
48 e.insert(*event);
49 } else {
50 return Err(LinuxError::EEXIST);
51 }
52 }
53 ctypes::EPOLL_CTL_MOD => {
54 let mut events = self.events.lock();
55 if let Entry::Occupied(mut ocp) = events.entry(fd) {
56 ocp.insert(*event);
57 } else {
58 return Err(LinuxError::ENOENT);
59 }
60 }
61 ctypes::EPOLL_CTL_DEL => {
62 let mut events = self.events.lock();
63 if let Entry::Occupied(ocp) = events.entry(fd) {
64 ocp.remove_entry();
65 } else {
66 return Err(LinuxError::ENOENT);
67 }
68 }
69 _ => {
70 return Err(LinuxError::EINVAL);
71 }
72 }
73 Ok(0)
74 }
75
76 fn poll_all(&self, events: &mut [ctypes::epoll_event]) -> LinuxResult<usize> {
77 let ready_list = self.events.lock();
78 let mut events_num = 0;
79
80 for (infd, ev) in ready_list.iter() {
81 match get_file_like(*infd as c_int)?.poll() {
82 Err(_) => {
83 if (ev.events & ctypes::EPOLLERR) != 0 {
84 events[events_num].events = ctypes::EPOLLERR;
85 events[events_num].data = ev.data;
86 events_num += 1;
87 }
88 }
89 Ok(state) => {
90 if state.readable && (ev.events & ctypes::EPOLLIN != 0) {
91 events[events_num].events = ctypes::EPOLLIN;
92 events[events_num].data = ev.data;
93 events_num += 1;
94 }
95
96 if state.writable && (ev.events & ctypes::EPOLLOUT != 0) {
97 events[events_num].events = ctypes::EPOLLOUT;
98 events[events_num].data = ev.data;
99 events_num += 1;
100 }
101 }
102 }
103 }
104 Ok(events_num)
105 }
106}
107
108impl FileLike for EpollInstance {
109 fn read(&self, _buf: &mut [u8]) -> LinuxResult<usize> {
110 Err(LinuxError::ENOSYS)
111 }
112
113 fn write(&self, _buf: &[u8]) -> LinuxResult<usize> {
114 Err(LinuxError::ENOSYS)
115 }
116
117 fn stat(&self) -> LinuxResult<ctypes::stat> {
118 let st_mode = 0o600u32; Ok(ctypes::stat {
120 st_ino: 1,
121 st_nlink: 1,
122 st_mode,
123 ..Default::default()
124 })
125 }
126
127 fn into_any(self: Arc<Self>) -> alloc::sync::Arc<dyn core::any::Any + Send + Sync> {
128 self
129 }
130
131 fn poll(&self) -> LinuxResult<axio::PollState> {
132 Err(LinuxError::ENOSYS)
133 }
134
135 fn set_nonblocking(&self, _nonblocking: bool) -> LinuxResult {
136 Ok(())
137 }
138}
139
140pub fn sys_epoll_create(size: c_int) -> c_int {
144 debug!("sys_epoll_create <= {}", size);
145 syscall_body!(sys_epoll_create, {
146 if size < 0 {
147 return Err(LinuxError::EINVAL);
148 }
149 let epoll_instance = EpollInstance::new(0);
150 add_file_like(Arc::new(epoll_instance))
151 })
152}
153
154pub unsafe fn sys_epoll_ctl(
156 epfd: c_int,
157 op: c_int,
158 fd: c_int,
159 event: *mut ctypes::epoll_event,
160) -> c_int {
161 debug!("sys_epoll_ctl <= epfd: {} op: {} fd: {}", epfd, op, fd);
162 syscall_body!(sys_epoll_ctl, {
163 let ret = unsafe {
164 EpollInstance::from_fd(epfd)?.control(op as usize, fd as usize, &(*event))? as c_int
165 };
166 Ok(ret)
167 })
168}
169
170pub unsafe fn sys_epoll_wait(
172 epfd: c_int,
173 events: *mut ctypes::epoll_event,
174 maxevents: c_int,
175 timeout: c_int,
176) -> c_int {
177 debug!(
178 "sys_epoll_wait <= epfd: {}, maxevents: {}, timeout: {}",
179 epfd, maxevents, timeout
180 );
181
182 syscall_body!(sys_epoll_wait, {
183 if maxevents <= 0 {
184 return Err(LinuxError::EINVAL);
185 }
186 let events = unsafe { core::slice::from_raw_parts_mut(events, maxevents as usize) };
187 let deadline =
188 (!timeout.is_negative()).then(|| wall_time() + Duration::from_millis(timeout as u64));
189 let epoll_instance = EpollInstance::from_fd(epfd)?;
190 loop {
191 #[cfg(feature = "net")]
192 axnet::poll_interfaces();
193 let events_num = epoll_instance.poll_all(events)?;
194 if events_num > 0 {
195 return Ok(events_num as c_int);
196 }
197
198 if deadline.is_some_and(|ddl| wall_time() >= ddl) {
199 debug!(" timeout!");
200 return Ok(0);
201 }
202 crate::sys_sched_yield();
203 }
204 })
205}