@@ -2,7 +2,10 @@ package pool_tests
22
33import list " core:container/intrusive/list"
44import " core:mem"
5+ import " core:sync"
56import " core:testing"
7+ import " core:thread"
8+ import " core:time"
69
710import pool_pkg " ../pool"
811
@@ -394,3 +397,105 @@ test_pool_reset_on_put :: proc(t: ^testing.T) {
394397 free (recycled, recycled.allocator)
395398 }
396399}
400+
401+ // ----------------------------------------------------------------------------
402+ // Timeout tests
403+ // ----------------------------------------------------------------------------
404+
405+ @(test)
406+ test_pool_get_timeout_zero :: proc (t: ^testing.T) {
407+ p: pool_pkg.Pool (Test_Msg)
408+ pool_pkg.init (&p, reset = nil )
409+ defer pool_pkg.destroy (&p)
410+
411+ // Empty pool, .Pool_Only, timeout=0 — must return immediately with .Pool_Empty.
412+ msg, status := pool_pkg.get (&p, .Pool_Only, 0 )
413+ testing.expect (t, msg == nil , " msg should be nil" )
414+ testing.expect (t, status == .Pool_Empty, " status should be .Pool_Empty" )
415+ }
416+
417+ @(test)
418+ test_pool_get_timeout_elapsed :: proc (t: ^testing.T) {
419+ p: pool_pkg.Pool (Test_Msg)
420+ pool_pkg.init (&p, reset = nil )
421+ defer pool_pkg.destroy (&p)
422+
423+ // Empty pool, .Pool_Only, short timeout — nobody puts, should expire with .Pool_Empty.
424+ msg, status := pool_pkg.get (&p, .Pool_Only, time.Millisecond)
425+ testing.expect (t, msg == nil , " msg should be nil after timeout" )
426+ testing.expect (t, status == .Pool_Empty, " status should be .Pool_Empty after timeout" )
427+ }
428+
429+ // _put_wakes_ctx holds shared state for test_pool_get_timeout_put_wakes.
430+ _Put_Wakes_Ctx :: struct {
431+ pool: ^pool_pkg.Pool (Test_Msg),
432+ msg: ^Test_Msg,
433+ ready: sync.Sema,
434+ }
435+
436+ @(test)
437+ test_pool_get_timeout_put_wakes :: proc (t: ^testing.T) {
438+ p: pool_pkg.Pool (Test_Msg)
439+ pool_pkg.init (&p, reset = nil )
440+ defer pool_pkg.destroy (&p)
441+
442+ // Pre-allocate a message to put back from the second thread.
443+ msg, _ := pool_pkg.get (&p)
444+ testing.expect (t, msg != nil , " initial get should return non-nil" )
445+ if msg == nil {
446+ return
447+ }
448+
449+ ctx := _Put_Wakes_Ctx{pool = &p, msg = msg}
450+
451+ th := thread.create_and_start_with_data (&ctx, proc (data: rawptr ) {
452+ c := (^_Put_Wakes_Ctx)(data)
453+ // Signal the waiter that we're ready, then put the message back.
454+ sync.sema_post (&c.ready)
455+ time.sleep (5 * time.Millisecond)
456+ pool_pkg.put (c.pool, c.msg)
457+ })
458+
459+ // Wait until the thread is running, then block on get with a long timeout.
460+ sync.sema_wait (&ctx.ready)
461+ got, status := pool_pkg.get (&p, .Pool_Only, time.Second)
462+ thread.join (th)
463+ thread.destroy (th)
464+
465+ testing.expect (t, got != nil , " get should return non-nil after put wakes it" )
466+ testing.expect (t, status == .Ok, " status should be .Ok" )
467+ if got != nil {
468+ free (got, got.allocator)
469+ }
470+ }
471+
472+ // _destroy_wakes_ctx holds shared state for test_pool_get_timeout_destroy_wakes.
473+ _Destroy_Wakes_Ctx :: struct {
474+ pool: ^pool_pkg.Pool (Test_Msg),
475+ ready: sync.Sema,
476+ }
477+
478+ @(test)
479+ test_pool_get_timeout_destroy_wakes :: proc (t: ^testing.T) {
480+ p: pool_pkg.Pool (Test_Msg)
481+ pool_pkg.init (&p, reset = nil )
482+
483+ ctx := _Destroy_Wakes_Ctx{pool = &p}
484+
485+ th := thread.create_and_start_with_data (&ctx, proc (data: rawptr ) {
486+ c := (^_Destroy_Wakes_Ctx)(data)
487+ // Signal the waiter that we're running, then destroy the pool.
488+ sync.sema_post (&c.ready)
489+ time.sleep (5 * time.Millisecond)
490+ pool_pkg.destroy (c.pool)
491+ })
492+
493+ // Wait until the thread is running, then block on get with infinite timeout.
494+ sync.sema_wait (&ctx.ready)
495+ got, status := pool_pkg.get (&p, .Pool_Only, -1 )
496+ thread.join (th)
497+ thread.destroy (th)
498+
499+ testing.expect (t, got == nil , " get should return nil when pool is destroyed" )
500+ testing.expect (t, status == .Closed, " status should be .Closed" )
501+ }
0 commit comments