Orphaned compiler processes persist after Ctrl+C in parallel Spack builds
0 reputation · 03 Dec 2021, 11:58 UTC
0 reputation · 03 Dec 2021, 11:58 UTC
When a user interrupts a Spack build that uses the -j parallel job flag with SIGINT (Ctrl+C), the expectation is that Spack forwards the signal to all child build processes and removes the associated staging directory.
In practice, observations show that compiler or make processes may remain active after the interrupt, and the $SPACK_STAGE directory is sometimes left uncleaned, especially when a timeout is enforced.
What is the intended behavior for SIGINT propagation to child build jobs when -j is used?
Should Spack guarantee removal of the staging directory on both signal‑induced cancellation and timeout expiration?
How should the --keep-going mode consistently propagate cancellations up the dependency graph?
When a user presses Ctrl+C during a spack install -j N command, Spack installs a SIGINT handler that:
os.killpg with SIGINT for the process group of each tracked child job.$SPACK_STAGE directory.If every child respects SIGINT and terminates, the stage directory is cleaned up automatically.
The handler indeed uses os.killpg with SIGINT. However, if a child process:
setpgid or double‑fork),then os.killpg does not reach those processes, they can survive as orphans, and the cleanup trap never runs because Spack is still waiting for the tracked PIDs.
$SPACK_STAGECleanup only proceeds after all tracked child PIDs have exited. Lingering compiler or make processes block the trap, leaving the stage directory locked. A timeout‑induced SIGALRM may kill the Spack wrapper but not the compiler, producing the same effect.
spack install -j 4 <pkg>.ps -ef | grep -E '(cc|make|ld|as)' to see if any compiler/make remnants remain.$SPACK_STAGE for that build still exists: ls -d $SPACK_STAGE/*.If processes remain or the stage directory persists, note the PID, PPID, PGID, and full command line of the lingering process (e.g., via ps -o pid,ppid,pgid,command -p <pid>). That detail tells us whether the process changed its process group or is ignoring SIGINT, which determines the next mitigation (e.g., using a wrapper that forces SIGINT on the whole process tree or adjusting Spack’s job tracking).
The intended behavior is that a SIGINT (or timeout) should cancel all active jobs and mark any upstream dependents as failed/skipped, preventing new launches. In the current implementation:
Thus, propagation up the dependency graph is incomplete unless all child jobs honor the forwarded SIGINT.
If you observe orphaned processes after Ctrl+C, please provide the output of ps -o pid,ppid,pgid,command -p <orphan‑pid> for one of the lingering compiler/make processes. That information will confirm whether the process has changed its process group or is ignoring SIGINT and will guide the recommended fix.
Use comments to ask for clarification. Post a solution as an answer.
5,020 reputation · 03 Dec 2021, 19:11 UTC
One clarification worth adding: Spack intentionally preserves the staging directory after an interrupted or failed build so developers can inspect logs and artifacts, so automatic deletion is not guaranteed on cancellation. Reclaim space with spack clean --stage; confirm the exact cleanup flags in your installed version.
--keep-going changes failure tolerance for independent packages; it does not create a coordinated cancellation mechanism across the dependency graph. On SIGINT, the scheduler may stop launching new jobs and attempt to terminate running ones, but queued dependencies may not receive a clean cancellation signal.
To verify process-group behavior, run Spack in its own session (setsid spack install -j 2 <pkg>), interrupt it, then inspect with ps -o pid,ppid,pgid,sid,cmd and pstree -p to see whether compiler grandchildren escaped the original group. Check spack install --help and spack clean --help for version-specific timeout and stage flags.