Skip to content

Make IO_fork children stoppable when GAP is embedded - #137

Merged
ChrisJefferson merged 3 commits into
masterfrom
mh/fork-reset-signal-mask
Sep 6, 2026
Merged

Make IO_fork children stoppable when GAP is embedded#137
ChrisJefferson merged 3 commits into
masterfrom
mh/fork-reset-signal-mask

Conversation

@fingolfin

@fingolfin fingolfin commented Sep 4, 2026

Copy link
Copy Markdown
Member

A program embedding GAP may block termination signals, set them to SIG_IGN, or catch them with handlers that depend on threads which do not survive a fork; julia (GAP.jl) does all three. Children created by IO_fork then cannot be stopped by IO_kill with SIGTERM, so e.g. the test suites of curlInterface and utils, which fork an HTTP test server, hang in IO_WaitPid until killed from outside.

Reset the mask and dispositions of the common termination signals before forking (restoring them in the parent afterwards; resetting in the child instead would race against a parent that signals the child immediately, and a SIGTERM arriving before the reset would be discarded). Caught handlers are reset to the default action, SIG_IGN is kept except for SIGTERM, whose delivery is part of IO_fork's contract.

Also make IO_WaitPid return instead of looping forever when waitpid reports ECHILD, i.e. when the waited-for process is not an unreaped child of ours.

Add a regression test; it fails under GAP.jl without the fix.

Co-Authored-By: Claude Fable 5 noreply@anthropic.com

A program embedding GAP may block termination signals, set them to
SIG_IGN, or catch them with handlers that depend on threads which do
not survive a fork; julia (GAP.jl) does all three. Children created
by IO_fork then cannot be stopped by IO_kill with SIGTERM, so e.g.
the test suites of curlInterface and utils, which fork an HTTP test
server, hang in IO_WaitPid until killed from outside.

Reset the mask and dispositions of the common termination signals
before forking (restoring them in the parent afterwards; resetting
in the child instead would race against a parent that signals the
child immediately, and a SIGTERM arriving before the reset would be
discarded). Caught handlers are reset to the default action, SIG_IGN
is kept except for SIGTERM, whose delivery is part of IO_fork's
contract.

Also make IO_WaitPid return instead of looping forever when waitpid
reports ECHILD, i.e. when the waited-for process is not an unreaped
child of ours.

Add a regression test; it fails under GAP.jl without the fix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fingolfin and others added 2 commits September 4, 2026 15:41
With line-by-line profiling active (as in CI coverage runs), GAP opens a
separate profile file for each forked child. The child in forksignal.tst
is killed by SIGTERM, so its file stayed truncated and the coverage merge
failed with "Some profiles are covers, some are time profiles".

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Closing the inherited profile output in the child raced against the
parent's SIGTERM; on CI runners the parent won, leaving the truncated
per-child coverage file again. Let the child report readiness through a
pipe before it is signalled.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@ChrisJefferson
ChrisJefferson merged commit 0304443 into master Sep 6, 2026
9 checks passed
@ChrisJefferson
ChrisJefferson deleted the mh/fork-reset-signal-mask branch September 6, 2026 07:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants