Skip to content

Create the stopper's waiting coroutines only when the waiting starts - #1336

Merged
nolar merged 1 commit into
nolar:mainfrom
akashchamp:fix-unawaited-event-wait
Oct 13, 2026
Merged

nolar merged 1 commit into
nolar:mainfrom
akashchamp:fix-unawaited-event-wait

Conversation

@akashchamp

Copy link
Copy Markdown
Contributor

await stopped.wait(n) built the Event.wait() coroutine up front and handed it to asyncio.wait_for() in a new task. When the daemon gets cancelled before that task has run its first step, the task is cancelled unstarted and Event.wait() is collected without ever being awaited. That is the RuntimeWarning from the issue: a daemon that keeps calling stopped.wait(2) after the stopper is set will usually get cancelled at exactly that point once cancellation_timeout runs out.

The fix moves the wait_for(...) call into a small async method, so nothing is created until the task actually runs. A task cancelled before it starts now leaves nothing behind.

closes: #1315

Verification:

  • Standalone repro on main (start a task that does await stopped.wait(2), await asyncio.sleep(0), then cancel it) prints RuntimeWarning: coroutine 'Event.wait' was never awaited, with tracemalloc pointing at aioenums.py:172. With this change it prints nothing.
  • New tests/primitives/test_flag_waiters.py covers the same case. The suite turns warnings into errors, so it fails on main and passes with the fix.
  • pytest tests/handling/daemons tests/primitives tests/causation tests/reactor: 1332 passed. The full pytest tests run gives the same results as on main. The only failures are in admission/authentication tests that need oscrypto, which cannot detect the local libcrypto here.
  • pre-commit run on the changed files passes. mypy reports nothing new.

`await stopped.wait(n)` created the `Event.wait()` coroutine eagerly and
passed it to `asyncio.wait_for()` in a new task. If the daemon was cancelled
before that task made its first step, the task was cancelled unstarted, and
the `Event.wait()` coroutine was garbage-collected without being awaited,
which produced a RuntimeWarning (e.g. when the daemon spins on an already
set stopper until it is cancelled by `cancellation_timeout`).

Wrap the waiting into an async method, so that the coroutines are created
only once the task actually runs.

Fixes nolar#1315

Signed-off-by: Akash Kumar <116457960+akashchamp@users.noreply.github.com>
@akashchamp
akashchamp requested a review from nolar as a code owner October 8, 2026 05:04
@nolar
nolar merged commit ea7bc3c into nolar:main Oct 13, 2026
35 checks passed
@nolar

nolar commented Oct 14, 2026

Copy link
Copy Markdown
Owner

Thank you for the contribution. Released as 1.45.1

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.

RuntimeWarning: coroutine 'Event.wait' was never awaited after a daemon succeeds

2 participants