Skip to content

Commit b70229d

Browse files
committed
Docs cleanup
1 parent 70ba885 commit b70229d

File tree

14 files changed

+383
-207
lines changed

14 files changed

+383
-207
lines changed

docs/generate.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ rm -rf build
1515
mkdir build
1616

1717
# Generate intermediate binary format for our project
18-
odin doc ../examples/ -all-packages -doc-format -out:odin-mbox.odin-doc
18+
odin doc ../mbox ../mpsc ../pool ../wakeup ../nbio_mbox ../examples -all-packages -doc-format -out:odin-mbox.odin-doc
1919

2020
# Create a temporary config with absolute paths
2121
sed "s|PROJECT_ROOT|$ROOT_DIR|g" odin-doc.json > build/odin-doc.json

examples/negotiation.odin

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ _Worker :: struct {
3535
// - Loop receives the request. Reuses it as the reply (increments data, sends back).
3636
// - Worker receives the reply and frees it.
3737
// - One allocation per round-trip. Worker owns the memory start to finish.
38-
negotiation_example :: proc() -> bool {
38+
negotiation_example :: proc(kind: nbio_mbox.Nbio_Wakeuper_Kind = .UDP) -> bool {
3939
err := nbio.acquire_thread_event_loop()
4040
if err != nil {
4141
return false
@@ -45,7 +45,7 @@ negotiation_example :: proc() -> bool {
4545
loop := nbio.current_thread_event_loop()
4646

4747
// loop_mb receives requests from the worker.
48-
loop_mb, init_err := nbio_mbox.init_nbio_mbox(Msg, loop)
48+
loop_mb, init_err := nbio_mbox.init_nbio_mbox(Msg, loop, kind)
4949
if init_err != .None {
5050
return false
5151
}

mpsc/MPSC-Queue-Implementation-Comparison.md

Lines changed: 0 additions & 69 deletions
This file was deleted.

nbio_mbox/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ when a message is sent from another thread.
99

1010
| Kind | How it works | Notes |
1111
|------|-------------|-------|
12-
| `.UDP` (default) | Sender writes 1 byte to a loopback UDP socket; nbio wakes on receipt | Stable on Linux, macOS, Windows |
13-
| `.Timeout` | Zero-duration nbio timeout; CAS flag prevents queue overflow | 128-slot cross-thread queue limit |
12+
| `.UDP` (default) | Sender writes 1 byte to a loopback UDP socket; nbio wakes on receipt | No queue limit |
13+
| `.Timeout` | Zero-duration nbio timeout; CAS flag prevents queue overflow | Works on all platforms |
1414

1515
## Requirements
1616

nbio_mbox/doc.odin

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ when a message is sent from another thread.
66
77
Two wake mechanisms are available via Nbio_Wakeuper_Kind:
88
9-
.UDP (default) — A loopback UDP socket. The sender writes 1 byte; nbio wakes on
10-
receipt. Stable on Linux, macOS, and Windows. No queue capacity limit.
9+
.UDP (default) — A loopback UDP socket. The sender writes 1 byte; nbio wakes on receipt.
10+
No queue capacity limit.
1111
12-
.Timeout — A zero-duration nbio timeout. Limited by the 128-slot cross-thread
13-
queue; throttled with a CAS flag to prevent overflow under high-frequency sends.
12+
.Timeout — A zero-duration nbio timeout. Works on all platforms.
13+
Throttled with a CAS flag to prevent 128-slot cross-thread queue overflow.
1414
1515
Thread model:
1616

nbio_mbox/nbio_mbox.odin

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,10 @@ _udp_recv_cb :: proc(op: ^nbio.Operation, state: ^_UDP_State) {
163163

164164
// _udp_close cancels the pending recv, closes both sockets, and frees state.
165165
// Must be called from the event-loop thread — nbio.remove panics cross-thread.
166+
//
167+
// nbio.tick(0) after remove drains any pending IOCP cancellation completion on
168+
// Windows before the sockets and state are freed. On Linux/macOS remove is truly
169+
// silent (callback never fires), so tick(0) is a no-op there.
166170
@(private)
167171
_udp_close :: proc(ctx: rawptr) {
168172
if ctx == nil {
@@ -174,6 +178,7 @@ _udp_close :: proc(ctx: rawptr) {
174178
nbio.remove(state.recv_op)
175179
state.recv_op = nil
176180
}
181+
nbio.tick(0) // drain IOCP cancellation completion before freeing buffers
177182
net.close(state.recv_sock)
178183
net.close(state.send_sock)
179184
free(state, state.allocator)
@@ -242,6 +247,8 @@ _init_udp_wakeup :: proc(
242247
// init_nbio_mbox allocates a try_mbox.Mbox wired to the nbio event loop.
243248
//
244249
// kind selects the wake mechanism (default: .UDP).
250+
// Use .Timeout if UDP sockets are unavailable or on Windows where IOCP
251+
// completion packets may interact unexpectedly with UDP at high speed.
245252
//
246253
// Returns (nil, .Invalid_Loop) if loop is nil.
247254
// Returns (nil, .Keepalive_Failed) if the Timeout wakeuper allocation fails.

odin-mbox.odin-doc

4.76 MB
Binary file not shown.

pool_tests/edge_test.odin

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// SPDX-FileCopyrightText: Copyright (c) 2026 g41797
2-
// SPDX-License-Identifier: MIT
1+
//+test
2+
33

44
package pool_tests
55

@@ -94,15 +94,21 @@ test_pool_get_timeout_put_wakes :: proc(t: ^testing.T) {
9494
return
9595
}
9696

97-
ctx := _Put_Wakes_Ctx{pool = &p, msg = msg}
97+
ctx := _Put_Wakes_Ctx {
98+
pool = &p,
99+
msg = msg,
100+
}
98101

99-
th := thread.create_and_start_with_data(&ctx, proc(data: rawptr) {
102+
th := thread.create_and_start_with_data(
103+
&ctx,
104+
proc(data: rawptr) {
100105
c := (^_Put_Wakes_Ctx)(data)
101106
// Signal the waiter that we're ready, then put the message back.
102107
sync.sema_post(&c.ready)
103108
time.sleep(5 * time.Millisecond)
104109
pool_pkg.put(c.pool, c.msg)
105-
})
110+
},
111+
)
106112

107113
// Wait until the thread is running, then block on get with a long timeout.
108114
sync.sema_wait(&ctx.ready)
@@ -122,15 +128,20 @@ test_pool_get_timeout_destroy_wakes :: proc(t: ^testing.T) {
122128
p: pool_pkg.Pool(Test_Msg)
123129
pool_pkg.init(&p, reset = nil)
124130

125-
ctx := _Destroy_Wakes_Ctx{pool = &p}
131+
ctx := _Destroy_Wakes_Ctx {
132+
pool = &p,
133+
}
126134

127-
th := thread.create_and_start_with_data(&ctx, proc(data: rawptr) {
135+
th := thread.create_and_start_with_data(
136+
&ctx,
137+
proc(data: rawptr) {
128138
c := (^_Destroy_Wakes_Ctx)(data)
129139
// Signal the waiter that we're running, then destroy the pool.
130140
sync.sema_post(&c.ready)
131141
time.sleep(5 * time.Millisecond)
132142
pool_pkg.destroy(c.pool)
133-
})
143+
},
144+
)
134145

135146
// Wait until the thread is running, then block on get with infinite timeout.
136147
sync.sema_wait(&ctx.ready)
@@ -158,7 +169,12 @@ test_pool_many_waiters_partial_fill :: proc(t: ^testing.T) {
158169
threads: [N]^thread.Thread
159170

160171
for i in 0 ..< N {
161-
ctxs[i] = _N_Pool_Ctx{pool = &p, idx = i, started = &started, done = &done}
172+
ctxs[i] = _N_Pool_Ctx {
173+
pool = &p,
174+
idx = i,
175+
started = &started,
176+
done = &done,
177+
}
162178
threads[i] = thread.create_and_start_with_data(&ctxs[i], proc(data: rawptr) {
163179
c := (^_N_Pool_Ctx)(data)
164180
sync.sema_post(c.started)
@@ -222,7 +238,12 @@ test_pool_destroy_wakes_all :: proc(t: ^testing.T) {
222238
threads: [N]^thread.Thread
223239

224240
for i in 0 ..< N {
225-
ctxs[i] = _N_Pool_Ctx{pool = &p, idx = i, started = &started, done = &done}
241+
ctxs[i] = _N_Pool_Ctx {
242+
pool = &p,
243+
idx = i,
244+
started = &started,
245+
done = &done,
246+
}
226247
threads[i] = thread.create_and_start_with_data(&ctxs[i], proc(data: rawptr) {
227248
c := (^_N_Pool_Ctx)(data)
228249
sync.sema_post(c.started)
@@ -274,7 +295,11 @@ test_pool_stress_high_volume :: proc(t: ^testing.T) {
274295
threads: [N]^thread.Thread
275296

276297
for i in 0 ..< N {
277-
ctxs[i] = _Stress_Ctx{pool = &p, start = &start, done = &done}
298+
ctxs[i] = _Stress_Ctx {
299+
pool = &p,
300+
start = &start,
301+
done = &done,
302+
}
278303
threads[i] = thread.create_and_start_with_data(&ctxs[i], proc(data: rawptr) {
279304
c := (^_Stress_Ctx)(data)
280305
sync.sema_wait(c.start)
@@ -316,7 +341,11 @@ test_pool_max_limit_racing :: proc(t: ^testing.T) {
316341
threads: [N]^thread.Thread
317342

318343
for i in 0 ..< N {
319-
ctxs[i] = _Max_Race_Ctx{pool = &p, start = &start, done = &done}
344+
ctxs[i] = _Max_Race_Ctx {
345+
pool = &p,
346+
start = &start,
347+
done = &done,
348+
}
320349
threads[i] = thread.create_and_start_with_data(&ctxs[i], proc(data: rawptr) {
321350
c := (^_Max_Race_Ctx)(data)
322351
sync.sema_wait(c.start)
@@ -339,7 +368,11 @@ test_pool_max_limit_racing :: proc(t: ^testing.T) {
339368
thread.destroy(threads[i])
340369
}
341370

342-
testing.expect(t, p.curr_msgs <= 3, "curr_msgs should not exceed max_msgs after concurrent puts")
371+
testing.expect(
372+
t,
373+
p.curr_msgs <= 3,
374+
"curr_msgs should not exceed max_msgs after concurrent puts",
375+
)
343376
pool_pkg.destroy(&p)
344377
}
345378

@@ -357,7 +390,11 @@ test_pool_shutdown_race :: proc(t: ^testing.T) {
357390
threads: [N]^thread.Thread
358391

359392
for i in 0 ..< N {
360-
ctxs[i] = _Shutdown_Ctx{pool = &p, start = &start, done = &done}
393+
ctxs[i] = _Shutdown_Ctx {
394+
pool = &p,
395+
start = &start,
396+
done = &done,
397+
}
361398
threads[i] = thread.create_and_start_with_data(&ctxs[i], proc(data: rawptr) {
362399
c := (^_Shutdown_Ctx)(data)
363400
sync.sema_wait(c.start)
@@ -402,7 +439,10 @@ test_pool_idempotent_destroy :: proc(t: ^testing.T) {
402439
threads: [N]^thread.Thread
403440

404441
for i in 0 ..< N {
405-
ctxs[i] = _Idempotent_Ctx{pool = &p, start = &start}
442+
ctxs[i] = _Idempotent_Ctx {
443+
pool = &p,
444+
start = &start,
445+
}
406446
threads[i] = thread.create_and_start_with_data(&ctxs[i], proc(data: rawptr) {
407447
c := (^_Idempotent_Ctx)(data)
408448
sync.sema_wait(c.start)
@@ -426,8 +466,14 @@ test_pool_idempotent_destroy :: proc(t: ^testing.T) {
426466
// new/free calls, never falling back to context.allocator.
427467
@(test)
428468
test_pool_allocator_integrity :: proc(t: ^testing.T) {
429-
data := Counting_Alloc_Data{max = 10, backing = context.allocator}
430-
counting := mem.Allocator{procedure = _counting_alloc, data = &data}
469+
data := Counting_Alloc_Data {
470+
max = 10,
471+
backing = context.allocator,
472+
}
473+
counting := mem.Allocator {
474+
procedure = _counting_alloc,
475+
data = &data,
476+
}
431477

432478
p: pool_pkg.Pool(Test_Msg)
433479
pool_pkg.init(&p, initial_msgs = 3, reset = nil, allocator = counting)

0 commit comments

Comments
 (0)