kill: implementation for Windows - #13604
Conversation
Merging this PR will degrade performance by 2.15%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Simulation | hostname_ip_lookup[100000] |
250 µs | 275.6 µs | -9.32% |
| ❌ | Memory | hostname_basic |
56.3 KB | 59.8 KB | -5.9% |
| ❌ | Memory | seq_custom_separator |
58.4 KB | 61.9 KB | -5.7% |
| ❌ | Memory | nl_many_lines[100000] |
66.2 KB | 69.7 KB | -5.06% |
| ❌ | Memory | expand_custom_tabstops[50000] |
67 KB | 70.5 KB | -5% |
| ❌ | Memory | cut_fields_newline_delim |
69.3 KB | 72.8 KB | -4.85% |
| ❌ | Memory | fold_custom_width[50000] |
76.1 KB | 79.6 KB | -4.43% |
| ❌ | Simulation | ls_recursive_balanced_tree[(6, 4, 15)] |
112.5 ms | 117.5 ms | -4.29% |
| ❌ | Memory | split_numeric_suffix |
80.5 KB | 84 KB | -4.2% |
| ❌ | Memory | df_deep_directory |
80.7 KB | 84.3 KB | -4.19% |
| ❌ | Memory | join_custom_separator |
86 KB | 89.5 KB | -3.94% |
| ❌ | Memory | cksum_multiple_files |
91.2 KB | 94.7 KB | -3.73% |
| ❌ | Memory | rm_recursive_tree |
98 KB | 101.5 KB | -3.48% |
| ❌ | Simulation | single_date_now |
191.5 µs | 197.7 µs | -3.1% |
| ⚡ | Simulation | ls_recursive_deep_tree[(200, 2)] |
3.6 ms | 3.4 ms | +6.05% |
| ⚡ | Simulation | cksum_crc32b |
41.9 ms | 39.7 ms | +5.71% |
| ⚡ | Simulation | numfmt_from_si[10000] |
84.9 ms | 81.4 ms | +4.32% |
| ⚡ | Simulation | ls_recursive_mixed_tree |
2.9 ms | 2.8 ms | +3.88% |
| ⚡ | Simulation | cksum_default |
40.4 ms | 39.2 ms | +3.06% |
| ⚡ | Simulation | cksum_crc |
41 ms | 39.8 ms | +3.01% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing nikolalukovic:feature/kill-windows (c2377ca) with main (eae5c43)
Footnotes
-
122 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
|
GNU testsuite comparison: |
| _ => Err(unsupported(translate!( | ||
| "kill-error-process-groups-unsupported" | ||
| ))), |
There was a problem hiding this comment.
how do we get the handle?
There was a problem hiding this comment.
There's no direct public Win32 or NT API for that. So, we got to get creative, and creative is unfortunately a bit involved. If you're using AI to code this, I assume it won't trouble you too much. Otherwise, let's consider this a TODO. The idea would be to iterate through all processes using NT APIs and filter down to the current process and get its Job ID. Then get all handles with SystemExtendedHandleInformation and get the matching Job handle and kill it. Something like that - it's probably quite a bit of code. (More than I would've assumed intuitively. 🙁)
There was a problem hiding this comment.
lets give it a shot
There was a problem hiding this comment.
kill 0 — the four implementations
| Unix (POSIX) | This Windows port | Cygwin / MSYS2 | busybox-w32 | |
|---|---|---|---|---|
| Target set | Every process whose pgid == caller's pgid |
Every process in the caller's immediate Job object and its child jobs | Every Cygwin process with pgid == myself->pgid && ctty == myself->ctty |
Caller + its descendants (parent-pid tree walk) |
| Grouping primitive | Kernel process group (setpgid/setsid) |
Kernel Job object | Userspace _pinfo table in Cygwin's shared memory |
None — recomputed per call from a snapshot |
| Who created the group | The caller (or its shell), deliberately, to bound a blast radius | Someone else — terminal, IDE, Docker, CI agent, PCA — as a kill switch for you | Cygwin's own runtime | N/A |
| Enumeration | Kernel does it; no snapshot | QueryInformationJobObject(NULL, JobObjectBasicProcessIdList) — snapshot, so a process spawned mid-sweep escapes |
Cygwin's private NT object directory | CreateToolhelp32Snapshot — snapshot, same race |
| Delivery | Real signal | Per-PID OpenProcess + TerminateProcess, caller last |
Real emulated signal via the target's signal pipe; its signal thread runs the handler | TerminateProcess per pid |
| Catchable / blockable / ignorable | Yes (except KILL, STOP) | No — nothing is catchable | Yes, genuinely | No |
| Reaches non-participating processes | All, subject to permission | Yes — any native process the job holds | No — a native process unrelated to Cygwin has no _pinfo entry and is unreachable |
Yes, if it's a descendant |
| Caller signalled too | Yes | Yes, deliberately last so it doesn't die mid-sweep | Yes | Yes |
Concrete invocations
| Invocation | Unix | This Windows port | Cygwin | busybox-w32 |
|---|---|---|---|---|
kill -0 0 |
Probe; 0 if any member signallable | Probe every member (OpenProcess + 0-timeout wait); terminates nothing |
Probe | Probe |
kill 0 |
SIGTERM, handlers run | Force-terminate all, exit 143 each |
SIGTERM, handlers run | Force-terminate |
kill -9 0 |
SIGKILL | Force-terminate, exit 137 each |
SIGKILL | Force-terminate |
kill -INT 0 |
SIGINT, handlers run | Force-terminate, exit 130 — not a CTRL_BREAK (that path exists but only for a console group led by a known pid) |
SIGINT, handlers run | Force-terminate |
kill -STOP 0 |
Suspends the group | Rejected, unsupported signal on Windows |
Suspends (SuspendThread on all threads) |
Rejected |
kill -TSTP 0 |
Suspends (unless caught) | Silent no-op (Disposition::Ignore) — legitimate, TSTP is catchable |
Suspends | — |
kill -TERM -N |
Signals group N |
Rejected, negative-PID error | Works, any pgid |
— |
| Caller in no group/job | N/A — always in a group | Signals only itself | N/A | Self only, if childless |
| Some members denied | 0 if ≥1 succeeded |
Same: Ok if ≥1 delivered, first error if none |
Same | — |
| Exit status of a member | WIFSIGNALED, shell shows 128+n |
Literal exit code 128+n — WIFSIGNALED is not representable |
WIFSIGNALED (emulated properly) |
TerminateProcess code |
…heck in send_signal_to_pid
Implement kill util for Windows since #13363 added most of plumbing needed.