1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//! Process-group teardown: the crate's only `unsafe`.
//!
//! It lives in its own module so the answer to "where does this crate use
//! `unsafe`, and why" is one small file rather than a block buried in the
//! runner.
//!
//! # Why any unsafe at all
//!
//! Killing an agent means killing what the agent *started*. A code review runs
//! `git`, a test runner, a language server; those are children of the CLI, not
//! of us, and `Child::kill` reaches only the CLI itself. The portable answer is
//! to put each run in its own process group ([`std::process::Command::process_group`],
//! which is safe) and signal the group.
//!
//! Signalling a group is where safety runs out: `std` has no API for it, so the
//! options are `libc::kill`, which is `unsafe` because it is a raw FFI call, or
//! a dependency such as `nix` for a safe wrapper. A whole crate for one call is
//! the larger cost, so the crate takes the `unsafe` and confines it here.
//!
//! `Cargo.toml` sets `unsafe_code = "deny"` rather than `forbid` precisely so
//! this one audited use can be excepted; nothing else in the crate may add one
//! without also changing that lint.
//!
//! # Windows
//!
//! Not implemented. Containing a process tree on Windows needs a Job Object
//! (`CreateJobObject` + `AssignProcessToJobObject` with
//! `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE`), which this crate does not set up, so
//! only the direct child is killed and grandchildren survive cancellation. This
//! is a real gap for a Windows host, tracked in the README's cancellation
//! section, and the tests that prove the Unix behaviour are `#![cfg(unix)]`.
/// Signal an entire process group so commands the agent spawned die with it.
///
/// Best effort by nature: the group may already have exited, which is not a
/// failure. Call this **before** reaping the child, because reaping clears the
/// pid this needs to address the group.
pub
/// Signal a group by its leader pid, for a caller holding the pid rather than
/// the [`tokio::process::Child`].
///
/// [`crate::Run`]'s `Drop` needs this. `Drop` cannot await, so its only other
/// option is to abort the driver task and rely on the runtime polling that task
/// so its guard runs. That makes teardown depend on scheduling, and it does not
/// reliably happen: a dropped `Run` left grandchildren alive and *sleeping* on
/// Linux, while the identical teardown worked from `cancel` and from a timeout,
/// both of which call this directly. Killing here is synchronous and depends on
/// nothing being polled.
pub
/// No-op: see the module docs. Only the direct child is killed on Windows.
pub
/// No-op counterpart for non-unix.
pub