fwd/vendor/signal-hook/tests/iterator.rs
John Doty 9c435dc440 Vendor dependencies
Let's see how I like this workflow.
2022-12-19 08:38:22 -08:00

260 lines
6.5 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#![cfg(not(windows))]
extern crate signal_hook;
use std::collections::HashSet;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{self, RecvTimeoutError};
use std::sync::Arc;
use std::thread::{self, JoinHandle};
use std::time::Duration;
use signal_hook::consts::{SIGUSR1, SIGUSR2};
use signal_hook::iterator::{Handle, Signals};
use signal_hook::low_level::raise;
use serial_test::serial;
fn send_sigusr1() {
raise(SIGUSR1).unwrap();
}
fn send_sigusr2() {
raise(SIGUSR2).unwrap();
}
fn setup_without_any_signals() -> (Signals, Handle) {
let signals = Signals::new(&[]).unwrap();
let controller = signals.handle();
(signals, controller)
}
fn setup_for_sigusr2() -> (Signals, Handle) {
let signals = Signals::new(&[SIGUSR2]).unwrap();
let controller = signals.handle();
(signals, controller)
}
macro_rules! assert_signals {
($actual:expr, $($expected:expr),+ $(,)?) => {
let actual = $actual.collect::<HashSet<libc::c_int>>();
let expected = vec!($($expected),+).into_iter().collect::<HashSet<libc::c_int>>();
assert_eq!(actual, expected);
};
}
macro_rules! assert_no_signals {
($signals:expr) => {
assert_eq!($signals.next(), None);
};
}
#[test]
#[serial]
fn forever_terminates_when_closed() {
let (mut signals, controller) = setup_for_sigusr2();
// Detect early terminations.
let stopped = Arc::new(AtomicBool::new(false));
let stopped_bg = Arc::clone(&stopped);
let thread = thread::spawn(move || {
// Eat all the signals there are (might come from a concurrent test, in theory).
// Would wait forever, but it should be terminated by the close below.
for _sig in &mut signals {}
stopped_bg.store(true, Ordering::SeqCst);
});
// Wait a bit to see if the thread terminates by itself.
thread::sleep(Duration::from_millis(100));
assert!(!stopped.load(Ordering::SeqCst));
controller.close();
thread.join().unwrap();
}
// A reproducer for #16: if we had the mio-support enabled (which is enabled also by the
// tokio-support feature), blocking no longer works. The .wait() would return immediately (an empty
// iterator, possibly), .forever() would do a busy loop.
// flag)
#[test]
#[serial]
fn signals_block_wait() {
let mut signals = Signals::new(&[SIGUSR2]).unwrap();
let (s, r) = mpsc::channel();
let finish = Arc::new(AtomicBool::new(false));
let thread_id = thread::spawn({
let finish = Arc::clone(&finish);
move || {
// Technically, it may spuriously return early. But it shouldn't be doing it too much,
// so we just try to wait multiple times if they *all* return right away, it is
// broken.
for _ in 0..10 {
for _ in signals.wait() {
if finish.load(Ordering::SeqCst) {
// Asked to terminate at the end of the thread. Do so (but without
// signalling the receipt).
return;
} else {
panic!("Someone really did send us SIGUSR2, which breaks the test");
}
}
}
let _ = s.send(());
}
});
// A RAII guard to make sure we shut down the thread even if the test fails.
struct ThreadGuard {
thread: Option<JoinHandle<()>>,
finish: Arc<AtomicBool>,
}
impl ThreadGuard {
fn shutdown(&mut self) {
// Tell it to shut down
self.finish.store(true, Ordering::SeqCst);
// Wake it up
send_sigusr2();
// Wait for it to actually terminate.
if let Some(thread) = self.thread.take() {
thread.join().unwrap(); // Propagate panics
}
}
}
impl Drop for ThreadGuard {
fn drop(&mut self) {
self.shutdown(); // OK if done twice, won't have the thread any more.
}
}
let mut bg_thread = ThreadGuard {
thread: Some(thread_id),
finish,
};
let err = r
.recv_timeout(Duration::from_millis(100))
.expect_err("Wait didn't wait properly");
assert_eq!(err, RecvTimeoutError::Timeout);
bg_thread.shutdown();
}
#[test]
#[serial]
fn pending_doesnt_block() {
let (mut signals, _) = setup_for_sigusr2();
let mut recieved_signals = signals.pending();
assert_no_signals!(recieved_signals);
}
#[test]
#[serial]
fn wait_returns_recieved_signals() {
let (mut signals, _) = setup_for_sigusr2();
send_sigusr2();
let recieved_signals = signals.wait();
assert_signals!(recieved_signals, SIGUSR2);
}
#[test]
#[serial]
fn forever_returns_recieved_signals() {
let (mut signals, _) = setup_for_sigusr2();
send_sigusr2();
let signal = signals.forever().take(1);
assert_signals!(signal, SIGUSR2);
}
#[test]
#[serial]
fn wait_doesnt_block_when_closed() {
let (mut signals, controller) = setup_for_sigusr2();
controller.close();
let mut recieved_signals = signals.wait();
assert_no_signals!(recieved_signals);
}
#[test]
#[serial]
fn wait_unblocks_when_closed() {
let (mut signals, controller) = setup_without_any_signals();
let thread = thread::spawn(move || {
signals.wait();
});
controller.close();
thread.join().unwrap();
}
#[test]
#[serial]
fn forever_doesnt_block_when_closed() {
let (mut signals, controller) = setup_for_sigusr2();
controller.close();
let mut signal = signals.forever();
assert_no_signals!(signal);
}
#[test]
#[serial]
fn add_signal_after_creation() {
let (mut signals, _) = setup_without_any_signals();
signals.add_signal(SIGUSR1).unwrap();
send_sigusr1();
assert_signals!(signals.pending(), SIGUSR1);
}
#[test]
#[serial]
fn delayed_signal_consumed() {
let (mut signals, _) = setup_for_sigusr2();
signals.add_signal(SIGUSR1).unwrap();
send_sigusr1();
let mut recieved_signals = signals.wait();
send_sigusr2();
assert_signals!(recieved_signals, SIGUSR1, SIGUSR2);
// The pipe still contains the byte from the second
// signal and so wait won't block but won't return
// a signal.
recieved_signals = signals.wait();
assert_no_signals!(recieved_signals);
}
#[test]
#[serial]
fn is_closed_initially_returns_false() {
let (_, controller) = setup_for_sigusr2();
assert!(!controller.is_closed());
}
#[test]
#[serial]
fn is_closed_returns_true_when_closed() {
let (_, controller) = setup_for_sigusr2();
controller.close();
assert!(controller.is_closed());
}