Skip to content

Commit

Permalink
Handle signals and clean up subprocesses
Browse files Browse the repository at this point in the history
  • Loading branch information
stepchowfun committed Apr 5, 2024
1 parent 25d1364 commit de962f1
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 49 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.24.0] - 2024-04-05

### Fixed
- Docuum now cleans up child processes when exiting due to a signal (`SIGHUP`, `SIGINT`, or `SIGTERM`).

## [0.23.1] - 2023-10-02

### Added
Expand Down
119 changes: 103 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "docuum"
version = "0.23.1"
version = "0.24.0"
authors = ["Stephan Boyer <stephan@stephanboyer.com>"]
edition = "2021"
description = "LRU eviction of Docker images."
Expand All @@ -24,11 +24,10 @@ colored = "2"
dirs = "3"
env_logger = { version = "0.8", default-features = false, features = ["termcolor", "atty"] }
log = "0.4"
scopeguard = "1"
regex = { version = "1.5.5", default-features = false, features = ["std", "unicode-perl"] }
serde_json = "1.0"
serde_yaml = "0.8"
tempfile = "3"
regex = { version = "1.5.5", default-features = false, features = ["std", "unicode-perl"] }

[target.'cfg(target_os = "linux")'.dependencies]
sysinfo = "0.23.5"
Expand All @@ -37,6 +36,10 @@ sysinfo = "0.23.5"
version = "2"
features = ["wrap_help"]

[dependencies.ctrlc]
version = "3"
features = ["termination"] # [tag:ctrlc_term]

[dependencies.serde]
version = "1"
features = ["derive"]
41 changes: 37 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use {
io::{self, Write},
process::exit,
str::FromStr,
sync::{Arc, Mutex},
thread::sleep,
time::Duration,
},
Expand Down Expand Up @@ -231,8 +232,34 @@ fn settings() -> io::Result<Settings> {
})
}

// This function consumes and runs all the registered destructors. We use this mechanism instead of
// RAII for things that need to be cleaned up even when the process is killed due to a signal.
#[allow(clippy::type_complexity)]
fn run_destructors(destructors: &Arc<Mutex<Vec<Box<dyn FnOnce() + Send>>>>) {
let mut mutex_guard = destructors.lock().unwrap();
let destructor_fns = std::mem::take(&mut *mutex_guard);
for destructor in destructor_fns {
destructor();
}
}

// Let the fun begin!
fn main() {
// If Docuum is in the foreground process group for some TTY, the process will receive a SIGINT
// when the user types CTRL+C at the terminal. The default behavior is to crash when this signal
// is received. However, we would rather clean up resources before terminating, so we trap the
// signal here. This code also traps SIGHUP and SIGTERM, since we compile the `ctrlc` crate with
// the `termination` feature [ref:ctrlc_term].
let destructors = Arc::new(Mutex::new(Vec::<Box<dyn FnOnce() + Send>>::new()));
let destructors_clone = destructors.clone();
if let Err(error) = ctrlc::set_handler(move || {
run_destructors(&destructors_clone);
exit(1);
}) {
// Log the error and proceed anyway.
error!("{}", error);
}

// Determine whether to print colored output.
colored::control::set_override(atty::is(Stream::Stderr));

Expand Down Expand Up @@ -265,10 +292,16 @@ fn main() {

// Stream Docker events and vacuum when necessary. Restart if an error occurs.
loop {
if let Err(e) = run(&settings, &mut state, &mut first_run) {
error!("{}", e);
info!("Retrying in 5 seconds\u{2026}");
sleep(Duration::from_secs(5));
// This will run until an error occurs (it never returns `Ok`).
if let Err(error) = run(&settings, &mut state, &mut first_run, &destructors) {
error!("{}", error);
}

// Clean up any resources left over from that run.
run_destructors(&destructors);

// Wait a moment and then retry.
info!("Retrying in 5 seconds\u{2026}");
sleep(Duration::from_secs(5));
}
}
Loading

0 comments on commit de962f1

Please sign in to comment.