arceos_posix_api/imp/io_mpx/
epoll.rs

1//! `epoll` implementation.
2//!
3//! TODO: do not support `EPOLLET` flag
4
5use 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    // TODO: parse flags
26    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; // rw-------
119        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
140/// Creates a new epoll instance.
141///
142/// It returns a file descriptor referring to the new epoll instance.
143pub 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
154/// Control interface for an epoll file descriptor
155pub 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
170/// Waits for events on the epoll instance referred to by the file descriptor epfd.
171pub 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}