Skip to content

Commit

Permalink
fix: make sure that cargo test can be killed with ctrl+c (#6803)
Browse files Browse the repository at this point in the history
I am not sure why we were catching ctrl+c in the first place. This was
introduced in #4229 I think. But messing up with signal handlers in
tests doesn't feel right and, indeed, prevents tests from being killed.

Test Plan
---------

Manual testing: running `cargo t -p integration-tests` and then killing
it in the middle with ctrl+c leaves the test running in the background
before this PR, and properly kills the process after.
  • Loading branch information
matklad authored May 13, 2022
1 parent 6195773 commit c03a37e
Showing 1 changed file with 4 additions and 48 deletions.
52 changes: 4 additions & 48 deletions test-utils/actix-test-utils/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
use actix_rt::signal;
use futures::{future, select, task::Poll, FutureExt};
use once_cell::sync::Lazy;
use std::sync::{
atomic::{AtomicBool, Ordering},
Mutex,
};
use std::sync::Mutex;

pub struct ShutdownableThread {
pub join: Option<std::thread::JoinHandle<()>>,
Expand Down Expand Up @@ -40,32 +35,11 @@ impl Drop for ShutdownableThread {
}
}

static CAUGHT_SIGINT: AtomicBool = AtomicBool::new(false);

macro_rules! handle_interrupt {
($future:expr) => {
async move {
assert!(!CAUGHT_SIGINT.load(Ordering::SeqCst), "SIGINT received");
select! {
_ = {
future::poll_fn(|_| {
if CAUGHT_SIGINT.load(Ordering::SeqCst) {
return Poll::Ready(());
}
Poll::Pending
})
}.fuse() => panic!("SIGINT received"),
output = $future.fuse() => output,
}
}
};
}

#[inline]
pub fn spawn_interruptible<F: std::future::Future + 'static>(
f: F,
) -> actix_rt::task::JoinHandle<F::Output> {
actix_rt::spawn(handle_interrupt!(f))
actix_rt::spawn(f)
}

// Number of actix instances that are currently running.
Expand All @@ -80,38 +54,20 @@ pub fn setup_actix() -> actix_rt::SystemRunner {
let default_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
if actix_rt::System::is_registered() {
let exit_code = if CAUGHT_SIGINT.load(Ordering::SeqCst) { 130 } else { 1 };
actix_rt::System::current().stop_with_code(exit_code);
actix_rt::System::current().stop_with_code(1);
}
default_hook(info);
}));
});

static TRAP_SIGINT_HOOK: std::sync::Once = std::sync::Once::new();

// This is a workaround to ensure all threads get the exit memo.
// Plainly polling ctrl_c() on busy threads like ours can be problematic.
TRAP_SIGINT_HOOK.call_once(|| {
std::thread::Builder::new()
.name("SIGINT trap".into())
.spawn(|| {
let sys = actix_rt::System::new();
sys.block_on(async {
signal::ctrl_c().await.expect("failed to listen for SIGINT");
CAUGHT_SIGINT.store(true, Ordering::SeqCst);
});
sys.run().unwrap();
})
.expect("failed to spawn SIGINT handler thread");
});
actix_rt::System::new()
}

pub fn block_on_interruptible<F: std::future::Future>(
sys: &actix_rt::SystemRunner,
f: F,
) -> F::Output {
sys.block_on(handle_interrupt!(f))
sys.block_on(f)
}

pub fn run_actix<F: std::future::Future>(f: F) {
Expand Down

0 comments on commit c03a37e

Please sign in to comment.