diff --git a/core/nbio/impl_posix.odin b/core/nbio/impl_posix.odin index f76cf2849..8469c9ade 100644 --- a/core/nbio/impl_posix.odin +++ b/core/nbio/impl_posix.odin @@ -336,6 +336,12 @@ __tick :: proc(l: ^Event_Loop, timeout: time.Duration) -> General_Error { if .Error in event.flags { curr._impl.flags += {.Error} } if .EOF in event.flags { curr._impl.flags += {.EOF} } curr._impl.result = event.data + + // Remove refs to the list in case the operation would still block and `add_pending` + // is executed on it again. + curr._impl.prev = nil + curr._impl.next = nil + handle_completed(curr) } } @@ -1154,6 +1160,8 @@ stat_exec :: proc(op: ^Operation) { add_pending :: proc(op: ^Operation, filter: kq.Filter, ident: uintptr) { debug("adding pending", op.type) + assert(op._impl.next == nil) + assert(op._impl.prev == nil) op._impl.flags += {.For_Kernel} _, val, just_inserted, err := map_entry(&op.l.submitted, Queue_Identifier{ ident = ident, filter = filter }) diff --git a/tests/core/nbio/nbio.odin b/tests/core/nbio/nbio.odin index 6c3fd0e8c..6121d1ac7 100644 --- a/tests/core/nbio/nbio.odin +++ b/tests/core/nbio/nbio.odin @@ -256,3 +256,50 @@ wake_up :: proc(t: ^testing.T) { } } } + +// Tests that if multiple accepts are queued, and a dial comes in which completes one of them, +// the rest are queued again properly. +@(test) +still_pending :: proc(t: ^testing.T) { + if event_loop_guard(t) { + testing.set_fail_timeout(t, time.Minute) + + sock, ep := open_next_available_local_port(t) + defer nbio.close(sock) + + N :: 3 + + State :: struct { + accepted: int, + } + state: State + + on_accept :: proc(op: ^nbio.Operation, t: ^testing.T, state: ^State) { + ev(t, op.accept.err, nil) + state.accepted += 1 + nbio.close(op.accept.client) + } + + on_dial :: proc(op: ^nbio.Operation, t: ^testing.T) { + ev(t, op.dial.err, nil) + nbio.close(op.dial.socket) + } + + for _ in 0..