Merge branch 'ebadf-epoll' into 'master'

Workaround epoll edge cases

See merge request redox-os/relibc!1368
This commit is contained in:
Jeremy Soller
2026-05-21 06:43:39 -06:00
2 changed files with 78 additions and 46 deletions
+38 -25
View File
@@ -5,19 +5,19 @@
use core::{mem, ptr, slice};
use crate::{
error::Errno,
fs::File,
header::{
bits_sigset_t::sigset_t,
errno::EBADF,
errno::{EBADF, EINTR},
sys_epoll::{
EPOLL_CLOEXEC, EPOLL_CTL_ADD, EPOLLERR, EPOLLHUP, EPOLLIN, EPOLLNVAL, EPOLLOUT,
EPOLLPRI, EPOLLRDBAND, EPOLLRDNORM, EPOLLWRBAND, EPOLLWRNORM, epoll_create1, epoll_ctl,
epoll_data, epoll_event, epoll_pwait,
EPOLLPRI, EPOLLRDBAND, EPOLLRDNORM, EPOLLWRBAND, EPOLLWRNORM, epoll_data, epoll_event,
},
time::timespec,
},
platform::{
self,
ERRNO, PalEpoll, Sys,
types::{c_int, c_short, c_ulong},
},
};
@@ -77,10 +77,13 @@ pub unsafe fn poll_epoll(fds: &mut [pollfd], timeout: c_int, sigmask: *const sig
];
let ep = {
let epfd = epoll_create1(EPOLL_CLOEXEC);
if epfd < 0 {
return -1;
}
let epfd = match Sys::epoll_create1(EPOLL_CLOEXEC) {
Ok(epfd) => epfd,
Err(Errno(err)) => {
ERRNO.set(err);
return -1;
}
};
File::new(epfd)
};
@@ -108,11 +111,14 @@ pub unsafe fn poll_epoll(fds: &mut [pollfd], timeout: c_int, sigmask: *const sig
}
}
if unsafe { epoll_ctl(*ep, EPOLL_CTL_ADD, pfd.fd, &raw mut event) } < 0 {
if platform::ERRNO.get() == EBADF {
match unsafe { Sys::epoll_ctl(*ep, EPOLL_CTL_ADD, pfd.fd, &raw mut event) } {
Ok(_) => {}
Err(Errno(EBADF)) => {
pfd.revents |= POLLNVAL;
closed += 1;
} else {
}
Err(Errno(err)) => {
ERRNO.set(err);
return -1;
}
}
@@ -124,29 +130,36 @@ pub unsafe fn poll_epoll(fds: &mut [pollfd], timeout: c_int, sigmask: *const sig
}
let mut events: [epoll_event; 32] = unsafe { mem::zeroed() };
let res = unsafe {
epoll_pwait(
match unsafe {
Sys::epoll_pwait(
*ep,
events.as_mut_ptr(),
events.len() as c_int,
timeout,
sigmask,
)
};
if res < 0 {
return -1;
}
for event in events.iter().take(res as usize) {
let pi = unsafe { event.data.u64 as usize };
// TODO: Error status when fd does not match?
if let Some(pfd) = fds.get_mut(pi) {
for (p, ep) in event_map.iter() {
if event.events & ep > 0 {
pfd.revents |= p;
} {
Ok(res) => {
for event in events.iter().take(res) {
let pi = unsafe { event.data.u64 as usize };
// TODO: Error status when fd does not match?
if let Some(pfd) = fds.get_mut(pi) {
for (p, ep) in event_map.iter() {
if event.events & ep > 0 {
pfd.revents |= p;
}
}
}
}
}
Err(Errno(err)) => {
if err == EINTR && closed > 0 {
// some fds are closed by signal
} else {
ERRNO.set(err);
return -1;
}
}
}
let mut count = 0;
+40 -21
View File
@@ -10,6 +10,7 @@ use crate::{
io::prelude::*,
};
use core::{mem, slice};
use log::warn;
use syscall::{
data::{Event, TimeSpec},
flag::EVENT_READ,
@@ -103,27 +104,21 @@ impl PalEpoll for Sys {
}
let timer_opt = if timeout != -1 {
let mut timer = File::open(c"/scheme/time/4".into(), O_RDWR)?;
Sys::write(
epfd,
&Event {
id: timer.fd as usize,
flags: EVENT_READ,
data: 0,
},
)?;
let mut time = TimeSpec::default();
let _ = timer
.read(&mut time)
.map_err(|err| Errno(err.raw_os_error().unwrap_or(EIO)))?;
time.tv_sec += (timeout as i64) / 1000;
time.tv_nsec += (timeout % 1000) * 1000000;
let _ = timer
.write(&time)
.map_err(|err| Errno(err.raw_os_error().unwrap_or(EIO)))?;
Some(timer)
// TODO: Any of syscall inside this can fail, there's a race condition on kernel reusing fd numbers
let mut i = 10;
loop {
match register_timeout(epfd, timeout) {
Ok(timer) => break Some(timer),
Err(_) if i > 0 => {
i += 1;
continue;
}
Err(_) => {
warn!("Gave up writing timeout for epoll");
break None;
}
}
}
} else {
None
};
@@ -174,3 +169,27 @@ impl PalEpoll for Sys {
Ok(count)
}
}
fn register_timeout(epfd: i32, timeout: i32) -> Result<File, Errno> {
let mut timer = File::open(c"/scheme/time/4".into(), O_RDWR)?;
if let Err(e) = Sys::write(
epfd,
&Event {
id: timer.fd as usize,
flags: EVENT_READ,
data: 0,
},
) {
return Err(e);
}
let mut time = TimeSpec::default();
let _ = timer
.read(&mut time)
.map_err(|err| Errno(err.raw_os_error().unwrap_or(EIO)))?;
time.tv_sec += (timeout as i64) / 1000;
time.tv_nsec += (timeout % 1000) * 1000000;
let _ = timer
.write(&time)
.map_err(|err| Errno(err.raw_os_error().unwrap_or(EIO)))?;
Ok(timer)
}