#[cfg(unix)]
use std::{
io,
mem::MaybeUninit,
panic::{AssertUnwindSafe, catch_unwind},
ptr,
};
#[cfg(windows)]
use std::{panic, thread};
use std::{
sync::{
Arc, Mutex, MutexGuard, PoisonError,
atomic::{AtomicBool, Ordering},
},
task::{Context, Poll, Wake, Waker},
};
#[cfg(unix)]
use errno::{Errno, errno, set_errno};
#[cfg(unix)]
use libc::c_int;
use ntest::timeout;
#[cfg(windows)]
use windows_sys::{
Win32::{
Foundation::{FALSE, TRUE},
System::Console::{CTRL_BREAK_EVENT, CTRL_C_EVENT, CTRL_CLOSE_EVENT},
},
core::BOOL,
};
#[cfg(unix)]
use crate::signal::fill_pipe;
use crate::{
LocalRuntime,
signal::{Signal, Signals, handle},
};
#[cfg(unix)]
#[test]
#[timeout(15000)]
fn the_handler_leaves_errno_as_it_found_it() {
let _turn = take_turn();
let runtime = LocalRuntime::new().unwrap();
let made = Signals::new(&runtime, []).unwrap();
fill_pipe().unwrap();
set_errno(Errno(libc::E2BIG));
handle(libc::SIGUSR1);
assert_eq!(errno(), Errno(libc::E2BIG));
drop(made);
drop(Signals::new(&runtime, []).unwrap());
}
#[cfg(unix)]
#[test]
#[timeout(15000)]
fn a_listener_whose_creation_panics_gives_back_what_it_took_over() {
let _turn = take_turn();
let runtime = LocalRuntime::new().unwrap();
let before = handler(libc::SIGWINCH);
let mut first = Signals::new(&runtime, [Signal::User2]).unwrap();
let panics = Waker::from(Arc::new(PanicOnWake));
let polled = first.poll_recv(&mut Context::from_waker(&panics));
assert!(polled.is_pending());
handle(libc::SIGUSR2);
let created = catch_unwind(AssertUnwindSafe(|| {
Signals::new(&runtime, [Signal::WindowChange])
}));
assert!(created.is_err());
assert_eq!(handler(libc::SIGWINCH), before);
}
#[cfg(unix)]
#[test]
#[timeout(15000)]
fn a_waker_that_panics_keeps_no_other_listener_from_being_woken() {
let _turn = take_turn();
let runtime = LocalRuntime::new().unwrap();
let mut first = Signals::new(&runtime, [Signal::User2]).unwrap();
let mut second = Signals::new(&runtime, [Signal::User2]).unwrap();
let mut third = Signals::new(&runtime, []).unwrap();
let panics = Waker::from(Arc::new(PanicOnWake));
let polled = first.poll_recv(&mut Context::from_waker(&panics));
assert!(polled.is_pending());
let (flag, waker) = Flag::new();
let polled = second.poll_recv(&mut Context::from_waker(&waker));
assert!(polled.is_pending());
handle(libc::SIGUSR2);
let mut cx = Context::from_waker(Waker::noop());
let panic = catch_unwind(AssertUnwindSafe(|| third.poll_recv(&mut cx)))
.expect_err("the waker's panic reaches the poll");
assert_eq!(panic.downcast_ref::<&str>(), Some(&"a waker that panics"));
assert!(flag.0.load(Ordering::SeqCst));
for listener in [&mut first, &mut second] {
let polled = listener.poll_recv(&mut cx).map(Result::unwrap);
assert_eq!(polled, Poll::Ready(Signal::User2));
}
}
#[cfg(windows)]
#[test]
#[timeout(15000)]
fn the_console_routine_takes_only_the_events_listened_for() {
let _turn = take_turn();
let runtime = LocalRuntime::new().unwrap();
let mut signals = Signals::new(&runtime, [Signal::Break]).unwrap();
assert_eq!(console(CTRL_BREAK_EVENT), TRUE);
assert_eq!(runtime.block_on(signals.recv()).unwrap(), Signal::Break);
assert_eq!(console(CTRL_C_EVENT), FALSE);
assert_eq!(console(CTRL_CLOSE_EVENT), FALSE);
drop(signals);
assert_eq!(console(CTRL_BREAK_EVENT), FALSE);
}
#[cfg(windows)]
#[test]
#[timeout(15000)]
fn a_waker_that_panics_leaves_the_console_routine_to_return() {
let _turn = take_turn();
let runtime = LocalRuntime::new().unwrap();
let mut first = Signals::new(&runtime, [Signal::Break]).unwrap();
let mut second = Signals::new(&runtime, [Signal::Break]).unwrap();
let panics = Waker::from(Arc::new(PanicWithPayloadOnWake));
let polled = first.poll_recv(&mut Context::from_waker(&panics));
assert!(polled.is_pending());
let (flag, waker) = Flag::new();
let polled = second.poll_recv(&mut Context::from_waker(&waker));
assert!(polled.is_pending());
assert_eq!(console(CTRL_BREAK_EVENT), TRUE);
assert!(flag.0.load(Ordering::SeqCst));
let mut cx = Context::from_waker(Waker::noop());
for listener in [&mut first, &mut second] {
let polled = listener.poll_recv(&mut cx).map(Result::unwrap);
assert_eq!(polled, Poll::Ready(Signal::Break));
}
}
fn take_turn() -> MutexGuard<'static, ()> {
static TURN: Mutex<()> = Mutex::new(());
TURN.lock().unwrap_or_else(PoisonError::into_inner)
}
#[cfg(unix)]
fn handler(raw: c_int) -> libc::sighandler_t {
let mut action = MaybeUninit::<libc::sigaction>::uninit();
let read = unsafe { libc::sigaction(raw, ptr::null(), action.as_mut_ptr()) };
assert_eq!(read, 0, "{}", io::Error::last_os_error());
unsafe { action.assume_init() }.sa_sigaction
}
#[cfg(windows)]
fn console(event: u32) -> BOOL {
thread::spawn(move || handle(event)).join().unwrap()
}
#[cfg(unix)]
struct PanicOnWake;
#[cfg(unix)]
impl Wake for PanicOnWake {
fn wake(self: Arc<Self>) {
panic!("a waker that panics");
}
}
#[cfg(windows)]
struct PanicWithPayloadOnWake;
#[cfg(windows)]
impl Wake for PanicWithPayloadOnWake {
fn wake(self: Arc<Self>) {
panic::panic_any(PayloadPanickingOnDrop);
}
}
#[cfg(windows)]
struct PayloadPanickingOnDrop;
#[cfg(windows)]
impl Drop for PayloadPanickingOnDrop {
fn drop(&mut self) {
panic!("a panic payload that panics as it is dropped");
}
}
struct Flag(AtomicBool);
impl Flag {
fn new() -> (Arc<Self>, Waker) {
let flag = Arc::new(Self(AtomicBool::new(false)));
let waker = Waker::from(flag.clone());
(flag, waker)
}
}
impl Wake for Flag {
fn wake(self: Arc<Self>) {
self.0.store(true, Ordering::SeqCst);
}
}