Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Graceful quit on Ctrl-C #721

Merged
merged 1 commit into from Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
43 changes: 36 additions & 7 deletions crates/cli/src/config.rs
Expand Up @@ -7,7 +7,11 @@ use std::{
io::{IsTerminal, Write},
path::Path,
process::Stdio,
sync::Arc,
sync::{
atomic::{AtomicU8, Ordering},
Arc,
},
time::Duration,
};

use clearscreen::ClearScreen;
Expand All @@ -17,6 +21,7 @@ use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
use tokio::{process::Command as TokioCommand, time::sleep};
use tracing::{debug, debug_span, error, instrument, trace, trace_span, Instrument};
use watchexec::{
action::ActionHandler,
command::{Command, Program, Shell, SpawnOptions},
error::RuntimeError,
job::{CommandState, Job},
Expand Down Expand Up @@ -97,6 +102,7 @@ pub fn make_config(args: &Args, state: &State) -> Result<Config> {
.signals()
.any(|sig| sig == Signal::Terminate || sig == Signal::Interrupt)
{
// no need to be graceful as there's no commands
action.quit();
return action;
}
Expand Down Expand Up @@ -193,10 +199,13 @@ pub fn make_config(args: &Args, state: &State) -> Result<Config> {
.collect(),
);

let quit_again = Arc::new(AtomicU8::new(0));

config.on_action_async(move |mut action| {
let add_envs = add_envs.clone();
let command = command.clone();
let emit_file = emit_file.clone();
let quit_again = quit_again.clone();
let signal_map = signal_map.clone();
let workdir = workdir.clone();
Box::new(
Expand All @@ -206,6 +215,7 @@ pub fn make_config(args: &Args, state: &State) -> Result<Config> {
let add_envs = add_envs.clone();
let command = command.clone();
let emit_file = emit_file.clone();
let quit_again = quit_again.clone();
let signal_map = signal_map.clone();
let workdir = workdir.clone();

Expand Down Expand Up @@ -234,6 +244,28 @@ pub fn make_config(args: &Args, state: &State) -> Result<Config> {
}
};

let quit = |mut action: ActionHandler| {
match quit_again.fetch_add(1, Ordering::Relaxed) {
0 => {
eprintln!("[Waiting {stop_timeout:?} for processes to exit before stopping...]");
// eprintln!("[Waiting {stop_timeout:?} for processes to exit before stopping... Ctrl-C again to exit faster]");
// see TODO in action/worker.rs
action.quit_gracefully(
stop_signal.unwrap_or(Signal::Terminate),
stop_timeout,
);
}
1 => {
action.quit_gracefully(Signal::ForceStop, Duration::ZERO);
}
_ => {
action.quit();
}
}

action
};

if once {
debug!("debug mode: run once and quit");
show_events();
Expand All @@ -249,8 +281,7 @@ pub fn make_config(args: &Args, state: &State) -> Result<Config> {
// this blocks the event loop, but also this is a debug feature so i don't care
job.start().await;
job.to_wait().await;
action.quit();
return action;
return quit(action);
}

let is_keyboard_eof = action
Expand All @@ -260,8 +291,7 @@ pub fn make_config(args: &Args, state: &State) -> Result<Config> {
if stdin_quit && is_keyboard_eof {
debug!("keyboard EOF, quit");
show_events();
action.quit();
return action;
return quit(action);
}

let signals: Vec<Signal> = action.signals().collect();
Expand All @@ -275,8 +305,7 @@ pub fn make_config(args: &Args, state: &State) -> Result<Config> {
{
debug!("unmapped terminate or interrupt signal, quit");
show_events();
action.quit();
return action;
return quit(action);
}

// pass all other signals on
Expand Down
2 changes: 2 additions & 0 deletions crates/lib/src/action/worker.rs
Expand Up @@ -74,6 +74,8 @@ pub async fn worker(
job.delete().await;
});
}
// TODO: spawn to process actions, and allow events to come in while
// waiting for graceful shutdown, e.g. a second Ctrl-C to hasten
debug!("waiting for graceful shutdown tasks");
tasks.join_all().await;
debug!("waiting for job tasks to end");
Expand Down