Skip to content

refactor(networking): event-driven wakeups, drop running-flag polling#743

Merged
tcoratger merged 1 commit into
leanEthereum:mainfrom
tcoratger:refactor/asyncio-event-wakeups
May 21, 2026
Merged

refactor(networking): event-driven wakeups, drop running-flag polling#743
tcoratger merged 1 commit into
leanEthereum:mainfrom
tcoratger:refactor/asyncio-event-wakeups

Conversation

@tcoratger
Copy link
Copy Markdown
Collaborator

Summary

Two related cleanups to async wakeup patterns in the networking subspec.

client/event_source/live.py — real fix (was polling)

__anext__ polled the running flag every 500ms via asyncio.wait_for(..., timeout=0.5) + TimeoutError + continue. Replaced with a single asyncio.wait(..., FIRST_COMPLETED) race between the event queue and a new _stop_event: asyncio.Event field. Stop is now observed with no latency instead of up to 500ms.

  • Added _stop_event field.
  • dial() and listen() call _stop_event.clear() next to each _running = True so restart still works.
  • stop() calls _stop_event.set() next to _running = False so any waiter wakes immediately.

gossipsub/behavior.py — cleanup, not bug fix

get_next_event already raced the queue against a stop event, but the body manually cancelled and awaited each pending task and duplicated the cancellation handling in an outer except block. Collapsed to a single try/finally cancel pattern (~46 lines → ~24 lines).

Behavioural note: the original swallowed asyncio.CancelledError and returned None. The new version lets cancellation propagate (the asyncio convention). The sole caller (_forward_gossipsub_events in live.py:387) wraps the call in except asyncio.CancelledError: pass, so it already handles propagation correctly.

Test plan

  • ruff check — clean on both files.
  • uv run pytest tests/lean_spec/subspecs/networking/client/test_event_source.py tests/lean_spec/subspecs/networking/gossipsub/ — 215 tests pass.

🤖 Generated with Claude Code

LiveNetworkEventSource.__anext__ polled the running flag every 500ms
via asyncio.wait_for + TimeoutError + continue. Add a stop event,
set it on stop() and clear it on dial()/listen(), then race the
queue against the stop event. Stop is now observed with no latency
instead of up to half a second.

GossipsubBehavior.get_next_event already raced an event, but the
implementation manually cancelled and awaited each pending task and
swallowed cancellation in a duplicate handler. Collapse to a single
try/finally cancel pattern. Cancellation now propagates naturally,
which the sole caller (_forward_gossipsub_events) already handles
with its own except asyncio.CancelledError block.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tcoratger tcoratger merged commit ba9f9d1 into leanEthereum:main May 21, 2026
13 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.

1 participant