From d8f47cf5fbb5fc157872a50189404897d0b6b3d1 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 9 Jan 2026 11:46:54 -0700 Subject: [PATCH 1/4] Handle signal mask in epoll_pwait --- redox-rt/src/signal.rs | 33 +++++++++++++++++++++++++++++++++ src/platform/redox/epoll.rs | 26 +++++++++++++++++++------- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs index 026ba119b8..495fa472e2 100644 --- a/redox-rt/src/signal.rs +++ b/redox-rt/src/signal.rs @@ -783,6 +783,38 @@ pub fn await_signal_async(inner_allowset: u64) -> Result { res?; unreachable!() } + +/// Run a callback with the specified signal mask, atomically +pub fn callback_or_signal_async Result>(inner_allowset: u64, callback: F) -> Result { + let _guard = tmp_disable_signals(); + let control = &unsafe { Tcb::current().unwrap() }.os_specific.control; + + let old_allowset = get_allowset_raw(&control.word); + let pending = set_allowset_raw(&control.word, old_allowset, inner_allowset); + let res = if pending == 0 { + // Run callback if no pending signals + callback() + } else { + // If pending signals, pretend callback returned EINTR + Err(Error::new(EINTR)) + }; + + if let Err(err) = &res { + if err.errno == EINTR { + // Run trampoline if EINTR returned + unsafe { + manually_enter_trampoline(); + } + } + } + + // POSIX says it shall restore the mask to what it was prior to the call, which is interpreted + // as allowing any changes to sigprocmask inside the signal handler, to be discarded. + set_allowset_raw(&control.word, inner_allowset, old_allowset); + + res +} + /*#[unsafe(no_mangle)] pub extern "C" fn __redox_rt_debug_sigctl() { let tcb = &RtTcb::current().control; @@ -830,6 +862,7 @@ pub fn await_signal_sync(inner_allowset: u64, timeout: Option<&TimeSpec>) -> Res // Normally ETIMEDOUT but not for sigtimedwait. .ok_or(Error::new(EAGAIN)) } + fn try_claim_multiple( mut proc_pending: u64, mut thread_pending: u64, diff --git a/src/platform/redox/epoll.rs b/src/platform/redox/epoll.rs index 14e8a6853f..e89577bcab 100644 --- a/src/platform/redox/epoll.rs +++ b/src/platform/redox/epoll.rs @@ -96,7 +96,7 @@ impl PalEpoll for Sys { events: *mut epoll_event, maxevents: c_int, timeout: c_int, - _sigset: *const sigset_t, + sigset: *const sigset_t, ) -> Result { // TODO: sigset assert_eq!(mem::size_of::(), mem::size_of::()); @@ -131,12 +131,24 @@ impl PalEpoll for Sys { None }; - let bytes_read = Sys::read(epfd, unsafe { - slice::from_raw_parts_mut( - events as *mut u8, - maxevents as usize * mem::size_of::(), - ) - })?; + let callback = || { + let res = redox_rt::sys::posix_read(epfd as usize, unsafe { + slice::from_raw_parts_mut( + events as *mut u8, + maxevents as usize * mem::size_of::(), + ) + }); + res + }; + + let bytes_read = if sigset.is_null() { + callback() + } else { + // Allowset is inverse of sigset mask + let allowset = !*sigset; + redox_rt::signal::callback_or_signal_async(allowset, callback) + }?; + let read = bytes_read as usize / mem::size_of::(); let mut count = 0; From 0a9095bb17b241dd3ac4b224729b01ad7903b5e0 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 9 Jan 2026 12:21:38 -0700 Subject: [PATCH 2/4] Fix epoll_pwait callback internally handling EINTR --- redox-rt/src/signal.rs | 11 ++--------- src/platform/redox/epoll.rs | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs index 495fa472e2..e3fb026661 100644 --- a/redox-rt/src/signal.rs +++ b/redox-rt/src/signal.rs @@ -790,15 +790,8 @@ pub fn callback_or_signal_async Result>(inner_allowset: u64 let control = &unsafe { Tcb::current().unwrap() }.os_specific.control; let old_allowset = get_allowset_raw(&control.word); - let pending = set_allowset_raw(&control.word, old_allowset, inner_allowset); - let res = if pending == 0 { - // Run callback if no pending signals - callback() - } else { - // If pending signals, pretend callback returned EINTR - Err(Error::new(EINTR)) - }; - + set_allowset_raw(&control.word, old_allowset, inner_allowset); + let res = callback(); if let Err(err) = &res { if err.errno == EINTR { // Run trampoline if EINTR returned diff --git a/src/platform/redox/epoll.rs b/src/platform/redox/epoll.rs index e89577bcab..059dc10e63 100644 --- a/src/platform/redox/epoll.rs +++ b/src/platform/redox/epoll.rs @@ -132,7 +132,7 @@ impl PalEpoll for Sys { }; let callback = || { - let res = redox_rt::sys::posix_read(epfd as usize, unsafe { + let res = syscall::read(epfd as usize, unsafe { slice::from_raw_parts_mut( events as *mut u8, maxevents as usize * mem::size_of::(), From a04c27a71ec6af0f218194e269a2876f3b0e6d46 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 9 Jan 2026 12:59:00 -0700 Subject: [PATCH 3/4] Early exit from poll if there are fds, and all are closed --- redox-rt/src/signal.rs | 5 ++++- src/header/poll/mod.rs | 26 ++++++++++++++++++++------ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs index e3fb026661..177dcca416 100644 --- a/redox-rt/src/signal.rs +++ b/redox-rt/src/signal.rs @@ -785,7 +785,10 @@ pub fn await_signal_async(inner_allowset: u64) -> Result { } /// Run a callback with the specified signal mask, atomically -pub fn callback_or_signal_async Result>(inner_allowset: u64, callback: F) -> Result { +pub fn callback_or_signal_async Result>( + inner_allowset: u64, + callback: F, +) -> Result { let _guard = tmp_disable_signals(); let control = &unsafe { Tcb::current().unwrap() }.os_specific.control; diff --git a/src/header/poll/mod.rs b/src/header/poll/mod.rs index 8c8cbd22b0..5f4326c293 100644 --- a/src/header/poll/mod.rs +++ b/src/header/poll/mod.rs @@ -11,6 +11,7 @@ use crate::{ error::ResultExt, fs::File, header::{ + errno::EBADF, signal::sigset_t, sys_epoll::{ EPOLL_CLOEXEC, EPOLL_CTL_ADD, EPOLLERR, EPOLLHUP, EPOLLIN, EPOLLNVAL, EPOLLOUT, @@ -19,7 +20,10 @@ use crate::{ }, time::timespec, }, - platform::types::{c_int, c_short, c_ulong}, + platform::{ + self, + types::{c_int, c_short, c_ulong}, + }, }; pub const POLLIN: c_short = 0x001; @@ -65,12 +69,14 @@ pub fn poll_epoll(fds: &mut [pollfd], timeout: c_int, sigmask: *const sigset_t) File::new(epfd) }; + let mut closed = 0; for i in 0..fds.len() { let mut pfd = &mut fds[i]; - // Ignore the entry with negative fd, set the revents to 0 + pfd.revents = 0; + + // Ignore the entry with negative fd if pfd.fd < 0 { - pfd.revents = 0; continue; } @@ -86,13 +92,21 @@ pub fn poll_epoll(fds: &mut [pollfd], timeout: c_int, sigmask: *const sigset_t) } } - pfd.revents = 0; - if unsafe { epoll_ctl(*ep, EPOLL_CTL_ADD, pfd.fd, &mut event) } < 0 { - return -1; + if platform::ERRNO.get() == EBADF { + pfd.revents |= POLLNVAL; + closed += 1; + } else { + return -1; + } } } + // Early exit if there are fds, and all are closed (revents = POLLNVAL) + if closed > 0 && closed == fds.len() { + return closed as i32; + } + let mut events: [epoll_event; 32] = unsafe { mem::zeroed() }; let res = unsafe { epoll_pwait( From b9fc058a47a248f3c14454506a4d075ceeda929e Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 9 Jan 2026 13:34:15 -0700 Subject: [PATCH 4/4] Remove finished todo --- src/platform/redox/epoll.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/platform/redox/epoll.rs b/src/platform/redox/epoll.rs index 059dc10e63..fe3166222b 100644 --- a/src/platform/redox/epoll.rs +++ b/src/platform/redox/epoll.rs @@ -98,7 +98,6 @@ impl PalEpoll for Sys { timeout: c_int, sigset: *const sigset_t, ) -> Result { - // TODO: sigset assert_eq!(mem::size_of::(), mem::size_of::()); if maxevents <= 0 {