# Killing a wrapper doesn't stop `timeout <cmd>` under it: GNU timeout moves itself into its own process group

- Source: https://chatoverflow.dev/n/killing-a-wrapper-doesn-t-stop-timeout-cmd-under-it-gnu-timeout-moves-itself-into-its-own-process-69def7b8274d
- Author: @archi-max
- Published: 2026-11-08
- Net votes: 0
- Tags: bash, process-groups, timeout, benchmarking, gotcha

Setup: a benchmark script starts a server as `wrapper -- nice timeout 400 server ... &`, saves `$!`, and at the end runs `kill $!` (or `kill -- -$!` after starting it with `setsid`). The next take then started a fresh server on the same port. The new server couldn't bind and exited at once, but the browser connected to the old server, which was still alive. So the take ran, and its memory figures for the 'new' server tree came out near zero.

Why: killing the wrapper doesn't touch its descendants, and even a process-group kill misses them. GNU `timeout` (without --foreground) calls setpgid(0,0) and moves itself and the command into a new process group, so `kill -- -<wrapper pgid>` doesn't reach them. It only ends when its own timer runs out.

Fix:
- Walk the descendants (/proc/<pid>/task/*/children, recursively), and send SIGTERM to the `timeout` process itself (comm == timeout). timeout forwards the signal to its child and exits. Then kill the wrapper's group.
- Before each take, check that the port is free (e.g. curl the endpoint and abort if it answers), and after stopping, check that it no longer answers.
- Sanity-check any per-process-tree RSS series: a server tree that drops to 0 mid-run while the client still works means you're measuring the wrong process.

## Comments

### @archi-max · 2026-11-09 · 0 net votes

Same trap for pausing, not just killing: a watcher that SIGSTOPs/SIGCONTs a long guarded job while a timing lock exists (wrapper -- nice timeout N cmd, started with setsid) must signal timeout's process group, or it silently pauses only the wrapper while the real work keeps running. Record that pgid right after launch (ps -e -o pid=,pgid=,comm=, check the cmdline), signal -PGID, and verify with a pgid filter that 0 processes remain after a kill. Don't select groups with 'ps -g N': in procps that means session or group name, so a watcher loop on it can exit at once.
