diff --git a/2279.added.md b/2279.added.md new file mode 100644 index 0000000000..5bbbbb40f7 --- /dev/null +++ b/2279.added.md @@ -0,0 +1 @@ +Added a new API sigsuspend. diff --git a/Cargo.toml b/Cargo.toml index f064e20f1c..202c57b52a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ targets = [ ] [dependencies] -libc = { git = "https://github.com/rust-lang/libc", rev = "cb18b837963c37a8d21732f3ca2c2096f04e6830", features = ["extra_traits"] } +libc = { git = "https://github.com/rust-lang/libc", rev = "2f93bfb7678e18a9fc5373dec49384bd23f601c3", features = ["extra_traits"] } bitflags = "2.3.1" cfg-if = "1.0" pin-utils = { version = "0.1.0", optional = true } diff --git a/src/sys/signal.rs b/src/sys/signal.rs index efa11a134b..c9b593d0db 100644 --- a/src/sys/signal.rs +++ b/src/sys/signal.rs @@ -582,6 +582,35 @@ impl SigSet { }) } + /// Wait for a signal + /// + /// # Return value + /// If `sigsuspend(2)` is interrupted (EINTR), this function returns `Ok`. + /// If `sigsuspend(2)` set other error, this function returns `Err`. + /// + /// For more information see the + /// [`sigsuspend(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigsuspend.html). + #[cfg(any( + bsd, + linux_android, + solarish, + target_os = "haiku", + target_os = "hurd", + target_os = "aix", + target_os = "fushsia" + ))] + #[doc(alias("sigsuspend"))] + pub fn suspend(&self) -> Result<()> { + let res = unsafe { + libc::sigsuspend(&self.sigset as *const libc::sigset_t) + }; + match Errno::result(res).map(drop) { + Err(Errno::EINTR) => Ok(()), + Err(e) => Err(e), + Ok(_) => unreachable!("because this syscall always returns -1 if returns"), + } + } + /// Converts a `libc::sigset_t` object to a [`SigSet`] without checking whether the /// `libc::sigset_t` is already initialized. /// diff --git a/test/sys/test_signal.rs b/test/sys/test_signal.rs index eb6347d5b0..12872f1228 100644 --- a/test/sys/test_signal.rs +++ b/test/sys/test_signal.rs @@ -343,6 +343,57 @@ fn test_sigwait() { .unwrap(); } +#[cfg(any( + bsd, + linux_android, + solarish, + target_os = "haiku", + target_os = "hurd", + target_os = "aix", + target_os = "fushsia" +))] +#[test] +fn test_sigsuspend() { + // This test change signal handler + let _m = crate::SIGNAL_MTX.lock(); + static SIGNAL_RECIEVED: AtomicBool = AtomicBool::new(false); + extern "C" fn test_sigsuspend_handler(_: libc::c_int) { + assert!(!SIGNAL_RECIEVED.swap(true, Ordering::SeqCst)); + } + thread::spawn(|| { + const SIGNAL: Signal = Signal::SIGUSR1; + + // Add signal mask to this thread + let mut signal_set = SigSet::empty(); + signal_set.add(SIGNAL); + signal_set.thread_block().unwrap(); + + // Set signal handler and save old one. + let act = SigAction::new( + SigHandler::Handler(test_sigsuspend_handler), + SaFlags::empty(), + SigSet::empty(), + ); + let old_act = unsafe { sigaction(SIGNAL, &act) } + .expect("expect to be able to set new action and get old action"); + + raise(SIGNAL).expect("expect be able to send signal"); + // Now `SIGNAL` was sended but it is blocked. + let mut not_wait_set = SigSet::all(); + not_wait_set.remove(SIGNAL); + // signal handler must run in SigSet::suspend() + assert!(!SIGNAL_RECIEVED.load(Ordering::SeqCst)); + not_wait_set.suspend().unwrap(); + assert!(SIGNAL_RECIEVED.load(Ordering::SeqCst)); + + // Restore the signal handler. + unsafe { sigaction(SIGNAL, &old_act) } + .expect("expect to be able to restore old action "); + }) + .join() + .unwrap(); +} + #[test] fn test_from_sigset_t_unchecked() { let src_set = SigSet::empty();