nbio: put clearing of list nodes in proper place and simplify test

This commit is contained in:
Laytan Laats
2026-02-27 21:04:43 +01:00
parent 334a554de3
commit f61a216c9f
2 changed files with 33 additions and 55 deletions

View File

@@ -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,8 +1160,8 @@ stat_exec :: proc(op: ^Operation) {
add_pending :: proc(op: ^Operation, filter: kq.Filter, ident: uintptr) {
debug("adding pending", op.type)
op._impl.next = nil
op._impl.prev = nil
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 })

View File

@@ -257,77 +257,49 @@ 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)
reused_ops :: proc(t: ^testing.T) {
still_pending :: proc(t: ^testing.T) {
if event_loop_guard(t) {
testing.set_fail_timeout(t, time.Minute)
@static payload := [4]byte{'P', 'I', 'N', 'G'}
sock, ep := open_next_available_local_port(t)
defer nbio.close(sock)
N :: 3
State :: struct {
listener: nbio.TCP_Socket,
accepts: [4]^nbio.Operation,
client: nbio.TCP_Socket,
server_client: nbio.TCP_Socket,
recv_buf: [4]byte,
recv_done: bool,
accepted: int,
}
state: State
state := State{
listener = sock,
}
on_accept :: proc(op: ^nbio.Operation, t: ^testing.T, state: ^State, slot: int) {
if state.recv_done {
nbio.close(op.accept.client)
return
}
on_accept :: proc(op: ^nbio.Operation, t: ^testing.T, state: ^State) {
ev(t, op.accept.err, nil)
if state.server_client == 0 {
state.server_client = op.accept.client
nbio.recv_poly2(state.server_client, {state.recv_buf[:]}, t, state, on_recv, timeout=0)
}
state.accepts[slot] = nbio.accept_poly3(op.accept.socket, t, state, slot, on_accept, timeout=nbio.NO_TIMEOUT)
state.accepted += 1
nbio.close(op.accept.client)
}
on_recv :: proc(op: ^nbio.Operation, t: ^testing.T, state: ^State) {
ev(t, op.recv.err, nil)
ev(t, op.recv.received, 4)
ev(t, state.recv_buf, payload)
state.recv_done = true
for accept in state.accepts {
if accept != nil {
nbio.remove(accept)
}
}
nbio.close(state.server_client)
nbio.close(state.client)
nbio.close(state.listener)
}
on_dial :: proc(op: ^nbio.Operation, t: ^testing.T, state: ^State) {
on_dial :: proc(op: ^nbio.Operation, t: ^testing.T) {
ev(t, op.dial.err, nil)
state.client = op.dial.socket
nbio.send_poly2(state.client, {payload[:]}, t, state, on_send)
nbio.close(op.dial.socket)
}
on_send :: proc(op: ^nbio.Operation, t: ^testing.T, state: ^State) {
ev(t, op.send.err, nil)
ev(t, op.send.sent, (4))
for _ in 0..<N {
nbio.accept_poly2(sock, t, &state, on_accept)
}
for slot in 0 ..< 4 {
state.accepts[slot] = nbio.accept_poly3(sock, t, &state, slot, on_accept, timeout=nbio.NO_TIMEOUT)
}
ev(t, nbio.tick(0), nil)
nbio.dial_poly(ep, t, on_dial)
for state.accepted < 1 {
ev(t, nbio.tick(), nil)
}
for _ in 0..<N-1 {
nbio.dial_poly(ep, t, on_dial)
}
nbio.dial_poly2(ep, t, &state, on_dial)
ev(t, nbio.run(), nil)
e(t, state.recv_done)
ev(t, state.accepted, N)
}
}