Skip to content

Commit 3ca8d07

Browse files
authored
Use "terminal driver" instead of "shell" in SIGINT docs (#13787)
Addressing the comment at #12108 (comment)
1 parent f5c3601 commit 3ca8d07

File tree

2 files changed

+29
-29
lines changed

2 files changed

+29
-29
lines changed

crates/uv/src/commands/run.rs

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,33 @@ use crate::commands::ExitStatus;
99
/// long as the command is the last thing that runs in this process; otherwise, we'd need to restore
1010
/// the default signal handlers after the command completes.
1111
pub(crate) async fn run_to_completion(mut handle: Child) -> anyhow::Result<ExitStatus> {
12-
// On Unix, shells will send SIGINT to the active process group when a user presses `Ctrl-C`. In
13-
// general, this means that uv should ignore SIGINT, allowing the child process to cleanly exit
14-
// instead. If uv forwarded the SIGINT immediately, the child process would receive _two_ SIGINT
15-
// signals which has semantic meaning for some programs, i.e., slow exit on the first signal and
16-
// fast exit on the second. The exception to this is if a child process changes its process
17-
// group, in which case the shell will _not_ send SIGINT to the child process and uv must take
18-
// ownership of forwarding the signal.
12+
// On Unix, the terminal driver will send SIGINT to the active process group when a user presses
13+
// `Ctrl-C`. In general, this means that uv should ignore SIGINT, allowing the child process to
14+
// cleanly exit instead. If uv forwarded the SIGINT immediately, the child process would receive
15+
// _two_ SIGINT signals which has semantic meaning for some programs, i.e., slow exit on the
16+
// first signal and fast exit on the second. The exception to this is if a child process changes
17+
// its process group, in which case the terminal driver will _not_ send SIGINT to the child
18+
// process and uv must take ownership of forwarding the signal.
1919
//
20-
// Note this assumes an interactive shell. If a signal is sent directly to the uv parent process
21-
// (e.g., `kill -2 <pid>`), the process group is not involved and a signal is not sent to the
22-
// child by default. In this context, uv must forward the signal to the child. We work around
23-
// this by forwarding SIGINT if it is received more than once. We could attempt to infer if the
24-
// parent is a shell using TTY detection(?), but there hasn't been sufficient motivation to
25-
// explore alternatives yet.
20+
// Note this assumes an interactive terminal. If a signal is sent directly to the uv parent
21+
// process (e.g., `kill -2 <pid>`), the process group is not involved and a signal is not sent
22+
// to the child by default. In this context, uv must forward the signal to the child. We work
23+
// around this by forwarding SIGINT if it is received more than once. We could attempt to infer
24+
// if the parent is a terminal using TTY detection(?), but there hasn't been sufficient
25+
// motivation to explore alternatives yet.
2626
//
27-
// Use of SIGTERM is also a bit complicated. If a shell receives a SIGTERM, it just waits for
27+
// Use of SIGTERM is also a bit complicated. If a terminal receives a SIGTERM, it just waits for
2828
// its children to exit — multiple SIGTERMs do not have any effect and the signals are not
2929
// forwarded to the children. Consequently, the description for SIGINT above does not apply to
30-
// SIGTERM in shells. It is _possible_ to have a parent process that sends a SIGTERM to the
31-
// process group; for example, `tini` supports this via a `-g` option. In this case, it's
32-
// possible that uv will improperly send a second SIGTERM to the child process. However,
33-
// this seems preferable to not forwarding it in the first place. In the Docker case, if `uv`
34-
// is invoked directly (instead of via an init system), it's PID 1 which has a special-cased
35-
// default signal handler for SIGTERM by default. Generally, if a process receives a SIGTERM and
36-
// does not have a SIGTERM handler, it is terminated. However, if PID 1 receives a SIGTERM, it
37-
// is not terminated. In this context, it is essential for uv to forward the SIGTERM to the
38-
// child process or the process will not be killable.
30+
// SIGTERM in the terminal driver. It is _possible_ to have a parent process that sends a
31+
// SIGTERM to the process group; for example, `tini` supports this via a `-g` option. In this
32+
// case, it's possible that uv will improperly send a second SIGTERM to the child process.
33+
// However, this seems preferable to not forwarding it in the first place. In the Docker case,
34+
// if `uv` is invoked directly (instead of via an init system), it's PID 1 which has a
35+
// special-cased default signal handler for SIGTERM by default. Generally, if a process receives
36+
// a SIGTERM and does not have a SIGTERM handler, it is terminated. However, if PID 1 receives a
37+
// SIGTERM, it is not terminated. In this context, it is essential for uv to forward the SIGTERM
38+
// to the child process or the process will not be killable.
3939
#[cfg(unix)]
4040
let status = {
4141
use std::ops::Deref;
@@ -162,8 +162,8 @@ pub(crate) async fn run_to_completion(mut handle: Child) -> anyhow::Result<ExitS
162162
continue;
163163
}
164164

165-
// The shell may still be forwarding these signals, give the child a moment to
166-
// handle that signal before hitting it with another one
165+
// The terminal may still be forwarding these signals, give the child a moment
166+
// to handle that signal before hitting it with another one
167167
debug!("Received SIGINT, forwarding to child at {child_pid} in 200ms");
168168
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
169169
let _ = signal::kill(child_pid, signal::Signal::SIGINT);
@@ -176,7 +176,7 @@ pub(crate) async fn run_to_completion(mut handle: Child) -> anyhow::Result<ExitS
176176
};
177177

178178
// We unconditionally forward SIGTERM to the child process; unlike SIGINT, this
179-
// isn't usually handled by the shell and in cases like
179+
// isn't usually handled by the terminal.
180180
debug!("Received SIGTERM, forwarding to child at {child_pid}");
181181
let _ = signal::kill(child_pid, signal::Signal::SIGTERM);
182182
}

docs/concepts/projects/run.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,9 @@ uv does not cede control of the process to the spawned command in order to provi
9191
messages on failure. Consequently, uv is responsible for forwarding some signals to the child
9292
process the requested command runs in.
9393

94-
On Unix systems, uv will forward SIGINT and SIGTERM to the child process. Since shells send SIGINT
95-
to the foreground process group on Ctrl-C, uv will only forward a SIGINT to the child process if it
96-
is seen more than once or the child process group differs from uv's.
94+
On Unix systems, uv will forward SIGINT and SIGTERM to the child process. Since terminals send
95+
SIGINT to the foreground process group on Ctrl-C, uv will only forward a SIGINT to the child process
96+
if it is sent more than once or the child process group differs from uv's.
9797

9898
On Windows, these concepts do not apply and uv ignores Ctrl-C events, deferring handling to the
9999
child process so it can exit cleanly.

0 commit comments

Comments
 (0)