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}