Skip to content

fix: bound safe_terminate waits and gevent stdio exit#419

Merged
RonnyPfannschmidt merged 1 commit into
pytest-dev:masterfrom
RonnyPfannschmidt:fix/safe-terminate-timeout-and-gevent-fdopen
Jul 22, 2026
Merged

fix: bound safe_terminate waits and gevent stdio exit#419
RonnyPfannschmidt merged 1 commit into
pytest-dev:masterfrom
RonnyPfannschmidt:fix/safe-terminate-timeout-and-gevent-fdopen

Conversation

@RonnyPfannschmidt

Copy link
Copy Markdown
Member

Summary

  • Harden safe_terminate so a stuck killfunc cannot hang Group.terminate forever: wait with a timeout * 2 bound, continue past timed-out replies, and still run waitall. Adds a regression test for the hang seen via pytest-xdist teardown.
  • Fix gevent fdopen to use FileObject (Posix on Unix) instead of FileObjectThread, whose native threadpool prevented interpreter shutdown after init_popen_io and timed out test_popen_io[gevent].

Supersedes #221 (and the residual hang from #43 after waitall gained a timeout).

Test plan

  • pytest testing/test_multi.py::test_safe_terminate_does_not_hang_when_kill_blocks
  • pytest testing/test_basics.py::test_popen_io[gevent-sys.executable]
  • pytest -k gevent (related suite)
  • CI green

Made with Cursor

Harden safe_terminate so a stuck kill cannot hang Group.terminate
forever (supersedes pytest-dev#221 / residual of pytest-dev#43), and switch gevent fdopen
to FileObject so init_popen_io children can exit cleanly.

Co-authored-by: Cursor AI <ai@cursor.sh>
Co-authored-by: Cursor Grok 4.5 <grok@x.ai>
Copilot AI review requested due to automatic review settings July 22, 2026 12:30

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens execnet teardown behavior to avoid indefinite hangs during gateway group termination and fixes a gevent stdio wrapper choice that could keep the interpreter from exiting cleanly.

Changes:

  • Bound safe_terminate() waits (timeout * 2), continue past timed-out termination/kill pairs, and still attempt waitall.
  • Added a regression test covering the “kill blocks / terminate must not hang forever” scenario.
  • Switched gevent fdopen() from FileObjectThread to FileObject to avoid keeping a native threadpool alive on exit.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
testing/test_multi.py Adds a regression test ensuring safe_terminate() doesn’t hang when a kill function blocks.
src/execnet/multi.py Updates safe_terminate() typing and logic to bound waits and avoid indefinite blocking.
src/execnet/gateway_base.py Adjusts gevent fdopen() implementation to prevent shutdown hangs caused by FileObjectThread.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread testing/test_multi.py
Comment on lines +294 to +318
release_kill = execmodel.Event()
other_killed: list[int] = []

def term_slow() -> None:
execmodel.sleep(10)

def kill_hang() -> None:
kill_started.set()
release_kill.wait()

def kill_ok() -> None:
other_killed.append(1)

safe_terminate(
execmodel,
0.2,
[
(term_slow, kill_hang),
(term_slow, kill_ok),
],
)

assert kill_started.is_set()
assert other_killed == [1]
release_kill.set()
Comment thread src/execnet/multi.py
Comment on lines +363 to +367
try:
reply.waitfinish(timeout=wait_timeout)
except OSError:
# termkill still running (typically stuck in killfunc).
continue
@RonnyPfannschmidt
RonnyPfannschmidt merged commit 4022a67 into pytest-dev:master Jul 22, 2026
15 checks passed
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.

3 participants