diff --git a/Cargo.toml b/Cargo.toml index fae4c72..5b1ac4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,4 +15,4 @@ humantime = "2.1" libc = "0.2" signal-hook = "0.3" signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"] } -tokio = { version = "1", features = ["macros", "process", "rt-multi-thread"] } +tokio = { version = "1", features = ["macros", "process", "rt-multi-thread", "time"] } diff --git a/src/main.rs b/src/main.rs index 1718cbb..2652be6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,9 +2,8 @@ use clap::Parser; use futures::stream::StreamExt; use signal_hook::consts::signal; use signal_hook_tokio::Signals; -use std::thread::sleep; use std::time::Duration; -use tokio::process::Command; +use tokio::{process::Command, time::sleep}; /// A utility for retrying failed console commands #[derive(Parser)] @@ -111,7 +110,17 @@ async fn run(args: Args) -> i32 { num_attempts, humantime::Duration::from(delay) )); - sleep(delay); + + let backoff_sleep = sleep(delay); + tokio::pin!(backoff_sleep); + + tokio::select! { + Some(_signal) = signals.next() => { + return last_exit_code; + } + _ = &mut backoff_sleep => {} + } + delay *= args.delay_multiplier; } } diff --git a/test.sh b/test.sh index 55dd36c..0793416 100755 --- a/test.sh +++ b/test.sh @@ -148,6 +148,38 @@ assertEqual \ "child received TERM signal" \ "$(tail -n 1 ./output)" +# +# Given: The child exit code is non-zero, +# and we are sleeping before the next attempt +# When: A stop signal is received +# Then: The sleep is interrupted +# +givenScript "exit 1" +${binPath} --attempts 2 --delay 5s -- ./script >output 2>&1 & +processId=$! + +# Wait until we enter sleep by detecting the 'retrying in' output +for i in $(seq 1 100); do + if grep -q 'retrying in' ./output; then + break + fi + sleep 0.05 +done + +beforeTs=$(date +%s) +kill -s TERM ${processId} +wait ${processId} 2>/dev/null +afterTs=$(date +%s) +elapsed=$((afterTs-beforeTs)) + +interrupted=false +[ ${elapsed} -lt 2 ] && interrupted=true + +assertEqual \ + "Sleep between attempts is interrupted when a stop signal is received" \ + "true" \ + "${interrupted}" + rm ./script ./output >/dev/null 2>&1 [ ${failCount} = 0 ] && exit 0 || exit 1