Skip to content

multi: add timeout to reply.get - #221

Closed
JCourt1 wants to merge 1 commit into
pytest-dev:masterfrom
JCourt1:master
Closed

multi: add timeout to reply.get#221
JCourt1 wants to merge 1 commit into
pytest-dev:masterfrom
JCourt1:master

Conversation

@JCourt1

@JCourt1 JCourt1 commented Sep 18, 2023

Copy link
Copy Markdown

It is possible for this call to hang, like the other calls in this function. Passing through the timeout removes that possibility.

This was first noted on #43

It is possible for this call to hang, like the other calls in this function. Passing through the timeout removes that possibility

@RonnyPfannschmidt RonnyPfannschmidt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This looks a bit like each action is getting 2 spawns (one outer,one inner)

The invocation nesting Looks incorrect

@RonnyPfannschmidt

Copy link
Copy Markdown
Member

@JCourt1 do you have a example of the hang? the code in question should pass in all cases for threading, is there a different execmodel involved

(the killfunc has a own timeout that should apply)

@JCourt1

JCourt1 commented Sep 19, 2023

Copy link
Copy Markdown
Author

should pass in all cases for threading

Yes I agree with you in principle, I was confused by this. I can say though that empirically I am hitting the issue... Unfortunately I'm not able to reproduce this in a minimal example outside of the codebase I am encountering it in. It's through pytest-xdist, and I think that does just use "thread" as the execmodel. I just hit it again actually:

  File ".../venv/lib/python3.11/site-packages/xdist/dsession.py", line 87, in pytest_sessionfinish
    nm.teardown_nodes()
  File ".../venv/lib/python3.11/site-packages/xdist/workermanage.py", line 81, in teardown_nodes
    self.group.terminate(self.EXIT_TIMEOUT)
  File ".../venv/lib/python3.11/site-packages/execnet/multi.py", line 214, in terminate
    safe_terminate(
  File ".../venv/lib/python3.11/site-packages/execnet/multi.py", line 310, in safe_terminate
    reply.get()
  File ".../venv/lib/python3.11/site-packages/execnet/gateway_base.py", line 282, in get
    self.waitfinish(timeout)
  File ".../venv/lib/python3.11/site-packages/execnet/gateway_base.py", line 289, in waitfinish
    if not self._result_ready.wait(timeout):
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../.pyenv/versions/3.11.5/lib/python3.11/threading.py", line 622, in wait
    signaled = self._cond.wait(timeout)
               ^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../.pyenv/versions/3.11.5/lib/python3.11/threading.py", line 320, in wait
    waiter.acquire()
KeyboardInterrupt

re. this:

This looks a bit like each action is getting 2 spawns (one outer,one inner)

The invocation nesting Looks incorrect

I get the impression that it is that way just to allow the termkills to be run in parallel. But do we even need the for loop over replyList? Could this be deleted:

    for reply in replylist:
        reply.get()

@RonnyPfannschmidt

Copy link
Copy Markdown
Member

I believe the intent was to complete all Tasks from the pool

Its unclear what's is needed for the other backends

@RonnyPfannschmidt

Copy link
Copy Markdown
Member

Superseded by #419 — hardens the timeout handling (so a stuck kill cannot raise/hang mid-loop) and includes a regression test, plus an unrelated gevent fdopen exit hang fix that showed up while working on this.

RonnyPfannschmidt added a commit that referenced this pull request Jul 22, 2026
Harden safe_terminate so a stuck kill cannot hang Group.terminate
forever (supersedes #221 / residual of #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>
@RonnyPfannschmidt

Copy link
Copy Markdown
Member

thanks for providing this initial version it helped me do the complete fix in #419

RonnyPfannschmidt added a commit to RonnyPfannschmidt/execnet that referenced this pull request Aug 1, 2026
safe_terminate had no caller in src/ since termination moved into
AsyncGroup._terminate_one -- only the deprecated-name map and three tests,
so the pytest-dev#43/pytest-dev#221 bound was being tested on a function nothing used.  Removed,
and the bound is now asserted on Group.terminate() against a worker that
ignores SIGINT and never returns from its exec.

AsyncGroup allocated ids as "gw%d" % len(self._gateways), over a list that
terminate() empties -- so gw0 could name two different workers in one
session, in its traces and in whatever the caller keyed on it.

Also records the two findings that are not fixes: execnet.aio can drop an
item when a receive is cancelled after the host already took it (the
docstring promised otherwise and now says what it can), and the channel has
no flow control at all -- 500 MiB lands in a non-consuming peer's memory in
0.33s with nothing pushing back.  The second wants HTTP/2-shaped windows
plus the reporting that makes a full window distinguishable from a hang,
and a credit field is cheaper to reserve before the protocol ships.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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