@@ -43,46 +43,28 @@ Nbio_Mailbox_Error :: enum {
4343// _NBio_State holds the nbio event loop and keepalive timer for one nbio_mbox instance.
4444@(private)
4545_NBio_State :: struct {
46- loop: ^nbio.Event_Loop,
47- keepalive: ^nbio.Operation,
48- allocator: mem.Allocator,
49- ref_count: int , // atomic
50- wake_pending: bool , // atomic — guards the cross-thread timeout queue (capacity 128)
46+ loop: ^nbio.Event_Loop,
47+ keepalive: ^nbio.Operation,
48+ allocator: mem.Allocator,
5149}
5250
5351// _noop is the required no-op callback for nbio operations (used by keepalive timer).
5452@(private)
5553_noop :: proc (_: ^nbio.Operation) {}
5654
57- // _noop_clear clears the wake_pending flag and releases a reference.
58- // Runs in the event-loop thread after timeout fires.
59- @(private)
60- _noop_clear :: proc (_: ^nbio.Operation, state: ^_NBio_State) {
61- intrinsics.atomic_store (&state.wake_pending, false )
62- if intrinsics.atomic_add (&state.ref_count, -1 ) == 1 {
63- free (state, state.allocator)
64- }
65- }
66-
67- // _nbio_wake fires a zero-duration timeout to wake the nbio event loop.
68- // Uses an atomic CAS flag so at most one timeout is queued at a time, preventing
69- // the 128-slot cross-thread queue from overflowing under high-frequency sends.
55+ // _nbio_wake wakes the nbio event loop via nbio.wake_up.
56+ // Uses QueueUserAPC on Windows — no cross-thread operation allocation, no 128-slot queue.
57+ // Safe to call from any thread.
7058@(private)
7159_nbio_wake :: proc (ctx: rawptr ) {
7260 if ctx == nil {
7361 return
7462 }
7563 state := (^_NBio_State)(ctx)
76- // CAS false→true. Returns old value; if old != false, wake already pending — skip.
77- if intrinsics.atomic_compare_exchange_strong (&state.wake_pending, false , true ) != false {
78- return
79- }
80- // Take a reference for the pending timeout task.
81- intrinsics.atomic_add (&state.ref_count, 1 )
82- nbio.timeout_poly (0 , state, _noop_clear, state.loop)
64+ nbio.wake_up (state.loop)
8365}
8466
85- // _nbio_close removes the keepalive timer and releases the primary reference .
67+ // _nbio_close removes the keepalive timer and frees state .
8668// Must be called from the event-loop thread — nbio.remove panics cross-thread.
8769@(private)
8870_nbio_close :: proc (ctx: rawptr ) {
@@ -94,11 +76,7 @@ _nbio_close :: proc(ctx: rawptr) {
9476 nbio.remove (state.keepalive)
9577 state.keepalive = nil
9678 }
97- if intrinsics.atomic_add (&state.ref_count, -1 ) == 1 {
98- free (state, state.allocator) // no pending callback — free now
99- } else {
100- nbio.tick (0 ) // drain pending _noop_clear → it will free state
101- }
79+ free (state, state.allocator)
10280}
10381
10482@(private)
@@ -115,7 +93,6 @@ _init_timeout_wakeup :: proc(
11593 }
11694 state.loop = loop
11795 state.allocator = allocator
118- state.ref_count = 1
11996 state.keepalive = nbio.timeout (time.Hour * 24 , _noop, loop)
12097 if state.keepalive == nil {
12198 free (state, allocator)
0 commit comments