Skip to main content

nu_engine/
exit.rs

1use std::sync::atomic::Ordering;
2
3use nu_protocol::engine::EngineState;
4
5/// Exit the process or clean jobs if appropriate.
6///
7/// Drops `tag` and exits the current process if there are no running jobs, or if `exit_warning_given` is true.
8/// When running in an interactive session, warns the user if there
9/// were jobs and sets `exit_warning_given` instead, returning `tag` itself in that case.
10///
11// Currently, this `tag` argument exists mostly so that a LineEditor can be dropped before exiting the process.
12pub fn cleanup_exit<T>(tag: T, engine_state: &EngineState, exit_code: i32) -> T {
13    if let Some(tag) = cleanup(tag, engine_state) {
14        return tag;
15    }
16
17    std::process::exit(exit_code);
18}
19
20/// clean jobs if appropriate.
21///
22/// Drops `tag` and exits the current process if there are no running jobs, or if `exit_warning_given` is true.
23/// When running in an interactive session, warns the user if there
24/// were jobs and sets `exit_warning_given` instead, returning `Some(tag)` itself in that case.
25/// Otherwise return None
26pub fn cleanup<T>(tag: T, engine_state: &EngineState) -> Option<T> {
27    let mut jobs = engine_state.jobs.lock().expect("failed to lock job table");
28
29    if engine_state.is_interactive
30        && jobs.iter().next().is_some()
31        && !engine_state.exit_warning_given.load(Ordering::SeqCst)
32    {
33        let job_count = jobs.iter().count();
34
35        println!("There are still background jobs running ({job_count}).");
36
37        println!("Running `exit` a second time will kill all of them.");
38
39        engine_state
40            .exit_warning_given
41            .store(true, Ordering::SeqCst);
42
43        return Some(tag);
44    }
45
46    let _ = jobs.kill_all();
47
48    drop(tag);
49    None
50}