Skip to content

馃И test(fork): fork once the event loop has closed - #714

Open
gaborbernat wants to merge 1 commit into
tox-dev:mainfrom
gaborbernat:fix/netbsd-cancelled-acquire-fork
Open

馃И test(fork): fork once the event loop has closed#714
gaborbernat wants to merge 1 commit into
tox-dev:mainfrom
gaborbernat:fix/netbsd-cancelled-acquire-fork

Conversation

@gaborbernat

Copy link
Copy Markdown
Member

Closes #707.

test_cancelled_async_acquire_unregisters_reused_descriptor forks from inside the coroutine, one statement after await loop.shutdown_default_executor(). That await joins the executor worker the canceled acquire left behind, but Thread.join() returns as soon as the interpreter releases the thread state, while the OS thread is still unwinding. NetBSD deadlocks a child forked from a process that still carries one, so the fork wedges, os.waitpid never returns, and the 10 second subprocess timeout kills the interpreter with SIGKILL. That is the returncode: -9 in the report.

The descriptor swap and the fork move to module scope, after asyncio.run() has closed the loop and the runner has shut the executor down. The script then leaves the worker time to go and checks threading.active_count(), exiting 6 rather than forking on a thread that is still counted. The subprocess timeout goes to 30 seconds so a slow builder is not the failure mode either.

I have no NetBSD machine, so this is reasoned from the report rather than reproduced: the run reaches the fork and hangs there, and the window between the join and the fork is the only NetBSD-specific thing in the script. @0-wiz-0, could you run the suite with this branch? If it still fails, exit code 6 says a thread outlived the loop, and any other outcome puts the deadlock elsewhere, where a backtrace of the wedged process would tell us where.

The canceled-acquire test forked from inside the coroutine, right after
awaiting shutdown_default_executor(). Thread.join() returns once the
interpreter releases the thread state, while the OS thread is still
unwinding, and NetBSD deadlocks a child forked from a process that still
carries one, so the fork wedged and the 10s subprocess timeout killed it
(tox-dev#707).

Move the descriptor swap and the fork to module scope, after asyncio.run
has closed the loop, leave the worker time to go, and report a thread
that is still counted rather than forking on it. Raise the subprocess
timeout to 30s so a slow builder is not the failure mode either.
@gaborbernat gaborbernat added the skip news Internal change; exempt from the news fragment check label Aug 22, 2026
@gaborbernat gaborbernat reopened this Aug 22, 2026
@0-wiz-0

0-wiz-0 commented Aug 23, 2026

Copy link
Copy Markdown

I've tested 3.32.3 with this patch applied on top, and get 1248 passed, 58 skipped - the test failure is fixed. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip news Internal change; exempt from the news fragment check

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3.23.3: test failure

2 participants