Skip to main content

Module handle_wake

Module handle_wake 

Source
Expand description

GIL-free completion wake for Handle.as_asyncio (HDL-17 to HDL-21 in crate::handle).

Each asyncio loop that can watch a file descriptor (loop.add_reader, which every selector-based loop, including all of Monarch’s, provides) gets one [LoopWake]: a queue of u64 tokens and a non-blocking pipe whose read end the loop watches. A loop without add_reader keeps the call_soon_threadsafe path (HDL-21).

waker and read_fd are the two ends of the pipe; tx and rx are the two ends of the token queue. When a Handle completes, its Tokio observer pushes its token and writes one byte to the pipe ([Notifier::notify]), without the GIL. The byte only wakes the loop; the queue says which awaits completed. The loop’s reader drains the pipe, then the queue, and settles the future of each completed await.

The loop’s selector is the channel’s only strong owner, through its on_readable callback. The registry holds the loop as a weak key and the channel through a weak reference. Each entry holds its pending await’s future strongly, because that reference is the waiting task’s only owner outside the task’s own cycle; asyncio holds tasks weakly. Closing the loop therefore releases the channel, its read end, its entries and their futures; the write end lasts until every notifier is done (HDL-20). __traverse__ lets the garbage collector free an unclosed loop (HDL-19, which also records the case that is not freed):

registry:         weak(loop) ──▶ weak(LoopWake)
loop's selector:  read_fd    ──▶ LoopWake.on_readable

LoopWake
  ├─ tx / rx    queue of u64 tokens
  ├─ waker      pipe write end, shared with every Notifier
  ├─ read_fd    pipe read end, registered with `add_reader`
  └─ entries    token → (asyncio future, Handle)