Skip to content

Commit 303c91f

Browse files
committed
Refactor a litte
1 parent 1061f32 commit 303c91f

File tree

1 file changed

+47
-35
lines changed

1 file changed

+47
-35
lines changed

crates/uv/src/commands/run.rs

+47-35
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,41 @@ pub(crate) async fn run_to_completion(mut handle: Child) -> anyhow::Result<ExitS
3232
// this seems preferable to not forwarding it in the first place.
3333
#[cfg(unix)]
3434
let status = {
35+
use std::ops::Deref;
36+
3537
use nix::sys::signal;
3638
use nix::unistd::{getpgid, Pid};
3739
use tokio::select;
3840
use tokio::signal::unix::{signal as handle_signal, SignalKind};
3941

40-
// Get the parent and child process group ids
41-
let child_pid = handle
42-
.id()
43-
.and_then(|id| id.try_into().ok())
44-
.map(Pid::from_raw);
42+
/// Simple new type for `Pid` allowing construction from [`Child`].
43+
///
44+
/// `None` if the child process has exited or the PID is invalid.
45+
struct ChildPid(Option<Pid>);
46+
47+
impl Deref for ChildPid {
48+
type Target = Option<Pid>;
49+
fn deref(&self) -> &Self::Target {
50+
&self.0
51+
}
52+
}
53+
54+
impl From<&Child> for ChildPid {
55+
fn from(child: &Child) -> Self {
56+
Self(
57+
child
58+
.id()
59+
.and_then(|id| id.try_into().ok())
60+
.map(Pid::from_raw),
61+
)
62+
}
63+
}
64+
65+
// Get the parent PGID
4566
let parent_pgid = getpgid(None)?;
67+
if let Some(child_pid) = *ChildPid::from(&handle) {
68+
debug!("Spawned child {child_pid} in process group {parent_pgid}");
69+
}
4670

4771
let mut sigterm_handle = handle_signal(SignalKind::terminate())?;
4872
let mut sigint_handle = handle_signal(SignalKind::interrupt())?;
@@ -56,8 +80,14 @@ pub(crate) async fn run_to_completion(mut handle: Child) -> anyhow::Result<ExitS
5680
_ = sigint_handle.recv() => {
5781
// See above for commentary on handling of SIGINT.
5882

83+
// If the child has already exited, we can't send it signals
84+
let Some(child_pid) = *ChildPid::from(&handle) else {
85+
debug!("Received SIGINT, but the child has already exited");
86+
continue;
87+
};
88+
5989
// Check if the child pgid has changed
60-
let child_pgid = getpgid(child_pid)?;
90+
let child_pgid = getpgid(Some(child_pid))?;
6191

6292
// Increment the number of interrupts seen
6393
sigint_count += 1;
@@ -66,15 +96,24 @@ pub(crate) async fn run_to_completion(mut handle: Child) -> anyhow::Result<ExitS
6696
// and we should forward it. If we've received multiple SIGINTs, forward it
6797
// regardless.
6898
if child_pgid == parent_pgid && sigint_count < 2 {
99+
debug!("Received SIGINT, assuming the child received it as part of the process group");
69100
continue;
70101
}
71102

72-
let _ = send_signal(&handle, child_pid, signal::Signal::SIGINT);
103+
debug!("Received SIGINT, forwarding to child at {child_pid}");
104+
let _ = signal::kill(child_pid, signal::Signal::SIGINT);
73105
},
74106
_ = sigterm_handle.recv() => {
107+
// If the child has already exited, we can't send it signals
108+
let Some(child_pid) = *ChildPid::from(&handle) else {
109+
debug!("Received SIGINT, but the child has already exited");
110+
continue;
111+
};
112+
75113
// We unconditionally forward SIGTERM to the child process; unlike SIGINT, this
76114
// isn't usually handled by the shell and in cases like
77-
let _ = send_signal(&handle, child_pid, signal::Signal::SIGTERM);
115+
debug!("Received SIGTERM, forwarding to child at {child_pid}");
116+
let _ = signal::kill(child_pid, signal::Signal::SIGTERM);
78117
}
79118
};
80119
}
@@ -116,30 +155,3 @@ pub(crate) async fn run_to_completion(mut handle: Child) -> anyhow::Result<ExitS
116155
Ok(ExitStatus::Failure)
117156
}
118157
}
119-
120-
/// Send a signal to a child process on Unix.
121-
///
122-
/// Includes a safety check that the process has not exited.
123-
#[cfg(unix)]
124-
fn send_signal(
125-
child: &Child,
126-
child_pid: Option<nix::unistd::Pid>,
127-
signal: nix::sys::signal::Signal,
128-
) -> anyhow::Result<()> {
129-
use nix::sys::signal;
130-
131-
// If the child has already exited, we can't send it signals
132-
let Some(child_pid) = child_pid else {
133-
anyhow::bail!("Child process has already exited");
134-
};
135-
136-
// The child can exit and a different process can take its PID; this may be
137-
// overly defensive but seems better than sending a signal to the wrong process.
138-
if child.id().is_none() {
139-
anyhow::bail!("Child process has already exited");
140-
}
141-
142-
signal::kill(child_pid, signal)?;
143-
144-
Ok(())
145-
}

0 commit comments

Comments
 (0)