733 lines
18 KiB
V
733 lines
18 KiB
V
import context
|
|
import time
|
|
import x.async as xasync
|
|
|
|
fn test_pool_rejects_invalid_config() {
|
|
xasync.new_pool(workers: 0, queue_size: 1) or {
|
|
assert err.msg() == 'async: pool worker count must be positive'
|
|
return
|
|
}
|
|
assert false
|
|
}
|
|
|
|
fn test_pool_rejects_invalid_queue_size() {
|
|
xasync.new_pool(workers: 1, queue_size: 0) or {
|
|
assert err.msg() == 'async: pool queue size must be positive'
|
|
return
|
|
}
|
|
assert false
|
|
}
|
|
|
|
fn test_pool_respects_worker_count() {
|
|
mut pool := xasync.new_pool(workers: 2, queue_size: 4)!
|
|
started := chan bool{cap: 4}
|
|
release := chan bool{cap: 4}
|
|
for _ in 0 .. 4 {
|
|
pool.try_submit(fn [started, release] (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
started <- true
|
|
_ := <-release
|
|
})!
|
|
}
|
|
|
|
wait_for_bool_signal(started, 'first pool job did not start')
|
|
wait_for_bool_signal(started, 'second pool job did not start')
|
|
select {
|
|
_ := <-started {
|
|
assert false, 'pool started more jobs than worker count before release'
|
|
}
|
|
100 * time.millisecond {}
|
|
}
|
|
|
|
for _ in 0 .. 4 {
|
|
release <- true
|
|
}
|
|
pool.close()!
|
|
}
|
|
|
|
fn test_pool_queue_full_returns_backpressure_error() {
|
|
mut pool := xasync.new_pool(workers: 1, queue_size: 1)!
|
|
started := chan bool{cap: 1}
|
|
release := chan bool{cap: 2}
|
|
blocking_job := fn [started, release] (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
started <- true
|
|
_ := <-release
|
|
}
|
|
|
|
pool.try_submit(blocking_job)!
|
|
wait_for_bool_signal(started, 'blocking pool job did not start')
|
|
pool.try_submit(blocking_job)!
|
|
pool.try_submit(blocking_job) or {
|
|
assert err.msg() == 'async: pool queue is full'
|
|
release <- true
|
|
release <- true
|
|
pool.close()!
|
|
return
|
|
}
|
|
assert false
|
|
}
|
|
|
|
fn test_pool_submit_with_context_waits_until_capacity_is_available() {
|
|
mut pool := xasync.new_pool(workers: 1, queue_size: 1)!
|
|
started := chan bool{cap: 2}
|
|
release := chan bool{cap: 2}
|
|
attempting := chan bool{cap: 1}
|
|
accepted := chan bool{cap: 1}
|
|
ran := chan bool{cap: 1}
|
|
blocking_job := fn [started, release] (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
started <- true
|
|
_ := <-release
|
|
}
|
|
|
|
pool.try_submit(blocking_job)!
|
|
wait_for_bool_signal(started, 'first blocking pool job did not start')
|
|
pool.try_submit(blocking_job)!
|
|
|
|
submit_thread := spawn fn [mut pool, attempting, accepted, ran] () {
|
|
attempting <- true
|
|
pool.submit_with_context(context.background(), fn [ran] (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
ran <- true
|
|
}) or {
|
|
accepted <- false
|
|
return
|
|
}
|
|
accepted <- true
|
|
}()
|
|
wait_for_bool_signal(attempting, 'bounded submit thread did not start')
|
|
select {
|
|
_ := <-accepted {
|
|
assert false, 'bounded submit returned while pool was full'
|
|
}
|
|
50 * time.millisecond {}
|
|
}
|
|
|
|
release <- true
|
|
wait_for_bool_signal(accepted, 'bounded submit did not accept after capacity opened')
|
|
release <- true
|
|
wait_for_bool_signal(ran, 'accepted bounded submit job did not run')
|
|
pool.close()!
|
|
submit_thread.wait()
|
|
}
|
|
|
|
fn test_pool_submit_with_timeout_returns_timeout_without_leaking_acceptance() {
|
|
mut pool := xasync.new_pool(workers: 1, queue_size: 1)!
|
|
started := chan bool{cap: 2}
|
|
release := chan bool{cap: 2}
|
|
finished := chan bool{cap: 2}
|
|
blocking_job := fn [started, release, finished] (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
started <- true
|
|
_ := <-release
|
|
finished <- true
|
|
}
|
|
|
|
pool.try_submit(blocking_job)!
|
|
wait_for_bool_signal(started, 'first blocking pool job did not start')
|
|
pool.try_submit(blocking_job)!
|
|
|
|
pool.submit_with_timeout(20 * time.millisecond, fn (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
}) or {
|
|
assert err.msg() == 'async: timeout'
|
|
release <- true
|
|
wait_for_bool_signal(finished, 'first blocking pool job did not finish')
|
|
pool.submit_with_timeout(1 * time.second, fn (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
})!
|
|
release <- true
|
|
pool.close()!
|
|
return
|
|
}
|
|
assert false
|
|
}
|
|
|
|
fn test_pool_submit_with_context_parent_cancel_does_not_accept_job() {
|
|
mut pool := xasync.new_pool(workers: 1, queue_size: 1)!
|
|
started := chan bool{cap: 1}
|
|
release := chan bool{cap: 2}
|
|
attempting := chan bool{cap: 1}
|
|
would_run := chan bool{cap: 1}
|
|
result := chan string{cap: 1}
|
|
parent_ctx, cancel := xasync.with_cancel()
|
|
blocking_job := fn [started, release] (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
started <- true
|
|
_ := <-release
|
|
}
|
|
|
|
pool.try_submit(blocking_job)!
|
|
wait_for_bool_signal(started, 'blocking pool job did not start')
|
|
pool.try_submit(blocking_job)!
|
|
|
|
submit_thread := spawn fn [mut pool, parent_ctx, attempting, would_run, result] () {
|
|
attempting <- true
|
|
pool.submit_with_context(parent_ctx, fn [would_run] (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
would_run <- true
|
|
}) or {
|
|
result <- err.msg()
|
|
return
|
|
}
|
|
result <- 'accepted'
|
|
}()
|
|
wait_for_bool_signal(attempting, 'bounded submit thread did not start')
|
|
select {
|
|
msg := <-result {
|
|
assert false, 'bounded submit returned before parent cancellation: ${msg}'
|
|
}
|
|
50 * time.millisecond {}
|
|
}
|
|
|
|
cancel()
|
|
select {
|
|
msg := <-result {
|
|
assert msg == 'context canceled'
|
|
}
|
|
1 * time.second {
|
|
assert false, 'bounded submit did not return after parent cancellation'
|
|
}
|
|
}
|
|
release <- true
|
|
release <- true
|
|
pool.close()!
|
|
assert_no_bool_signal(would_run, 'job was accepted after parent cancellation')
|
|
submit_thread.wait()
|
|
}
|
|
|
|
fn test_pool_close_wakes_waiting_submitter_without_accepting_job() {
|
|
mut pool := xasync.new_pool(workers: 1, queue_size: 1)!
|
|
started := chan bool{cap: 1}
|
|
release := chan bool{cap: 2}
|
|
attempting := chan bool{cap: 1}
|
|
would_run := chan bool{cap: 1}
|
|
result := chan string{cap: 1}
|
|
closed := chan bool{cap: 1}
|
|
blocking_job := fn [started, release] (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
started <- true
|
|
_ := <-release
|
|
}
|
|
|
|
pool.try_submit(blocking_job)!
|
|
wait_for_bool_signal(started, 'blocking pool job did not start')
|
|
pool.try_submit(blocking_job)!
|
|
|
|
submit_thread := spawn fn [mut pool, attempting, would_run, result] () {
|
|
attempting <- true
|
|
pool.submit_with_context(context.background(), fn [would_run] (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
would_run <- true
|
|
}) or {
|
|
result <- err.msg()
|
|
return
|
|
}
|
|
result <- 'accepted'
|
|
}()
|
|
wait_for_bool_signal(attempting, 'bounded submit thread did not start')
|
|
select {
|
|
msg := <-result {
|
|
assert false, 'bounded submit returned before close: ${msg}'
|
|
}
|
|
50 * time.millisecond {}
|
|
}
|
|
|
|
close_thread := spawn fn [mut pool, closed] () {
|
|
pool.close() or {
|
|
closed <- false
|
|
return
|
|
}
|
|
closed <- true
|
|
}()
|
|
select {
|
|
msg := <-result {
|
|
assert msg == 'async: pool is closed'
|
|
}
|
|
1 * time.second {
|
|
assert false, 'bounded submit did not return after pool close started'
|
|
}
|
|
}
|
|
release <- true
|
|
release <- true
|
|
wait_for_bool_signal(closed, 'pool close did not finish after accepted jobs were released')
|
|
assert_no_bool_signal(would_run, 'job was accepted after pool close started')
|
|
submit_thread.wait()
|
|
close_thread.wait()
|
|
}
|
|
|
|
fn test_pool_bounded_submit_rejects_nil_job() {
|
|
mut pool := xasync.new_pool(workers: 1, queue_size: 1)!
|
|
nil_job := unsafe { xasync.JobFn(nil) }
|
|
pool.submit_with_context(context.background(), nil_job) or {
|
|
assert err.msg() == 'async: job function is nil'
|
|
pool.submit_with_timeout(20 * time.millisecond, nil_job) or {
|
|
assert err.msg() == 'async: job function is nil'
|
|
pool.close()!
|
|
return
|
|
}
|
|
assert false
|
|
}
|
|
assert false
|
|
}
|
|
|
|
fn test_pool_submit_after_close_is_refused() {
|
|
mut pool := xasync.new_pool(workers: 1, queue_size: 1)!
|
|
pool.close()!
|
|
pool.try_submit(fn (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
}) or {
|
|
assert err.msg() == 'async: pool is closed'
|
|
return
|
|
}
|
|
assert false
|
|
}
|
|
|
|
fn test_pool_wait_is_one_shot() {
|
|
mut pool := xasync.new_pool(workers: 1, queue_size: 1)!
|
|
pool.wait()!
|
|
pool.wait() or {
|
|
assert err.msg() == 'async: pool wait was already called'
|
|
return
|
|
}
|
|
assert false
|
|
}
|
|
|
|
fn test_pool_refuses_nil_job() {
|
|
mut pool := xasync.new_pool(workers: 1, queue_size: 1)!
|
|
nil_job := unsafe { xasync.JobFn(nil) }
|
|
pool.try_submit(nil_job) or {
|
|
assert err.msg() == 'async: job function is nil'
|
|
pool.close()!
|
|
return
|
|
}
|
|
assert false
|
|
}
|
|
|
|
fn test_pool_close_waits_for_accepted_jobs() {
|
|
mut pool := xasync.new_pool(workers: 1, queue_size: 1)!
|
|
release := chan bool{cap: 1}
|
|
closed := chan bool{cap: 1}
|
|
pool.try_submit(fn [release] (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
_ := <-release
|
|
})!
|
|
|
|
close_thread := spawn fn [mut pool, closed] () {
|
|
pool.close() or {
|
|
closed <- false
|
|
return
|
|
}
|
|
closed <- true
|
|
}()
|
|
select {
|
|
_ := <-closed {
|
|
assert false, 'pool close returned before accepted job completed'
|
|
}
|
|
100 * time.millisecond {}
|
|
}
|
|
release <- true
|
|
select {
|
|
ok := <-closed {
|
|
assert ok
|
|
}
|
|
1 * time.second {
|
|
assert false, 'pool close did not return after accepted job completed'
|
|
}
|
|
}
|
|
close_thread.wait()
|
|
}
|
|
|
|
fn test_pool_first_error_is_propagated() {
|
|
mut pool := xasync.new_pool(workers: 1, queue_size: 2)!
|
|
started := chan bool{cap: 1}
|
|
release := chan bool{cap: 1}
|
|
pool.try_submit(fn [started, release] (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
started <- true
|
|
_ := <-release
|
|
return error('first pool failure')
|
|
})!
|
|
wait_for_bool_signal(started, 'first pool failure job did not start')
|
|
pool.try_submit(fn (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
return error('second pool failure')
|
|
})!
|
|
release <- true
|
|
pool.close() or {
|
|
assert err.msg() == 'first pool failure'
|
|
return
|
|
}
|
|
assert false
|
|
}
|
|
|
|
fn test_pool_error_does_not_drop_accepted_jobs() {
|
|
mut pool := xasync.new_pool(workers: 1, queue_size: 1)!
|
|
accepted_job_ran := chan bool{cap: 1}
|
|
pool.try_submit(fn (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
return error('first pool failure')
|
|
})!
|
|
pool.try_submit(fn [accepted_job_ran] (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
accepted_job_ran <- true
|
|
})!
|
|
pool.close() or {
|
|
assert err.msg() == 'first pool failure'
|
|
select {
|
|
did_run := <-accepted_job_ran {
|
|
assert did_run
|
|
}
|
|
1 * time.second {
|
|
assert false, 'accepted pool job did not run after earlier error'
|
|
}
|
|
}
|
|
return
|
|
}
|
|
assert false
|
|
}
|
|
|
|
fn test_pool_concurrent_errors_return_one_error_and_drain_accepted_jobs() {
|
|
error_jobs := 4
|
|
ok_jobs := 8
|
|
mut pool := xasync.new_pool(workers: error_jobs, queue_size: ok_jobs)!
|
|
started := chan bool{cap: error_jobs}
|
|
release := chan bool{cap: error_jobs}
|
|
completed_ok := chan bool{cap: ok_jobs}
|
|
for i in 0 .. error_jobs {
|
|
pool.try_submit(fn [started, release, i] (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
started <- true
|
|
_ := <-release
|
|
return error('pool concurrent failure ${i}')
|
|
})!
|
|
}
|
|
for _ in 0 .. ok_jobs {
|
|
pool.try_submit(fn [completed_ok] (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
completed_ok <- true
|
|
})!
|
|
}
|
|
for _ in 0 .. error_jobs {
|
|
wait_for_bool_signal(started, 'pool error job did not start')
|
|
}
|
|
for _ in 0 .. error_jobs {
|
|
release <- true
|
|
}
|
|
pool.close() or {
|
|
assert err.msg().starts_with('pool concurrent failure ')
|
|
for _ in 0 .. ok_jobs {
|
|
wait_for_bool_signal(completed_ok, 'accepted ok pool job did not complete')
|
|
}
|
|
return
|
|
}
|
|
assert false
|
|
}
|
|
|
|
fn test_pool_close_drains_many_accepted_jobs_while_finishing() {
|
|
jobs := 12
|
|
workers := 3
|
|
mut pool := xasync.new_pool(workers: workers, queue_size: jobs - workers)!
|
|
started := chan bool{cap: jobs}
|
|
release := chan bool{cap: jobs}
|
|
finished := chan bool{cap: jobs}
|
|
closed := chan bool{cap: 1}
|
|
for _ in 0 .. jobs {
|
|
pool.try_submit(fn [started, release, finished] (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
started <- true
|
|
_ := <-release
|
|
finished <- true
|
|
})!
|
|
}
|
|
for _ in 0 .. workers {
|
|
wait_for_bool_signal(started, 'initial pool job did not start')
|
|
}
|
|
close_thread := spawn fn [mut pool, closed] () {
|
|
pool.close() or {
|
|
closed <- false
|
|
return
|
|
}
|
|
closed <- true
|
|
}()
|
|
wait_until_pool_rejects_as_closed(mut pool)
|
|
assert_no_bool_signal(closed, 'pool close returned while accepted jobs were still blocked')
|
|
|
|
for _ in 0 .. jobs - 1 {
|
|
release <- true
|
|
}
|
|
for _ in 0 .. jobs - 1 {
|
|
wait_for_bool_signal(finished, 'accepted pool job did not finish')
|
|
}
|
|
assert_no_bool_signal(closed, 'pool close returned before the last accepted job finished')
|
|
|
|
release <- true
|
|
wait_for_bool_signal(finished, 'last accepted pool job did not finish')
|
|
wait_for_bool_signal(closed, 'pool close did not drain accepted jobs')
|
|
close_thread.wait()
|
|
}
|
|
|
|
fn test_pool_parent_cancellation_is_observed_by_cooperative_job() {
|
|
parent_ctx, cancel := xasync.with_cancel()
|
|
mut pool := xasync.new_pool_with_context(parent_ctx, workers: 1, queue_size: 1)!
|
|
started := chan bool{cap: 1}
|
|
pool.try_submit(fn [started] (mut ctx context.Context) ! {
|
|
started <- true
|
|
done := ctx.done()
|
|
select {
|
|
_ := <-done {
|
|
return ctx.err()
|
|
}
|
|
1 * time.second {
|
|
return error('pool job did not observe parent cancellation')
|
|
}
|
|
}
|
|
return error('unreachable')
|
|
})!
|
|
wait_for_bool_signal(started, 'pool job did not start before parent cancellation')
|
|
cancel()
|
|
pool.close() or {
|
|
assert err.msg() == 'context canceled'
|
|
return
|
|
}
|
|
assert false
|
|
}
|
|
|
|
fn test_pool_non_cooperative_job_finishes_naturally_after_parent_cancel() {
|
|
parent_ctx, cancel := xasync.with_cancel()
|
|
mut pool := xasync.new_pool_with_context(parent_ctx, workers: 1, queue_size: 1)!
|
|
finished := chan bool{cap: 1}
|
|
pool.try_submit(fn [finished] (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
time.sleep(20 * time.millisecond)
|
|
finished <- true
|
|
})!
|
|
cancel()
|
|
pool.close()!
|
|
assert <-finished
|
|
}
|
|
|
|
fn test_pool_short_stress_many_jobs() {
|
|
jobs := 100
|
|
mut pool := xasync.new_pool(workers: 4, queue_size: jobs)!
|
|
done := chan int{cap: jobs}
|
|
for i in 0 .. jobs {
|
|
pool.try_submit(fn [done, i] (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
done <- i
|
|
})!
|
|
}
|
|
pool.close()!
|
|
|
|
mut seen := []bool{len: jobs}
|
|
for _ in 0 .. jobs {
|
|
i := <-done
|
|
assert i >= 0
|
|
assert i < jobs
|
|
assert !seen[i]
|
|
seen[i] = true
|
|
}
|
|
for was_seen in seen {
|
|
assert was_seen
|
|
}
|
|
}
|
|
|
|
fn test_pool_bounded_submit_stress_waiting_submitters_are_released() {
|
|
workers := 2
|
|
queue_size := 2
|
|
initial_jobs := workers + queue_size
|
|
submitters := 8
|
|
mut pool := xasync.new_pool(workers: workers, queue_size: queue_size)!
|
|
started := chan bool{cap: initial_jobs}
|
|
release := chan bool{cap: initial_jobs}
|
|
attempting := chan bool{cap: submitters}
|
|
accepted := chan int{cap: submitters}
|
|
ran := chan int{cap: submitters}
|
|
blocking_job := fn [started, release] (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
started <- true
|
|
_ := <-release
|
|
}
|
|
|
|
for _ in 0 .. initial_jobs {
|
|
pool.try_submit(blocking_job)!
|
|
}
|
|
for _ in 0 .. workers {
|
|
wait_for_bool_signal(started, 'initial pool worker did not start')
|
|
}
|
|
|
|
mut submit_threads := []thread{}
|
|
for i in 0 .. submitters {
|
|
submit_threads << spawn fn [mut pool, attempting, accepted, ran, i] () {
|
|
attempting <- true
|
|
pool.submit_with_context(context.background(), fn [ran, i] (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
ran <- i
|
|
}) or {
|
|
failed_index := -1 - i
|
|
accepted <- failed_index
|
|
return
|
|
}
|
|
accepted <- i
|
|
}()
|
|
}
|
|
for _ in 0 .. submitters {
|
|
wait_for_bool_signal(attempting, 'bounded submitter did not start')
|
|
}
|
|
|
|
for _ in 0 .. initial_jobs {
|
|
release <- true
|
|
}
|
|
|
|
mut seen_accepted := []bool{len: submitters}
|
|
for _ in 0 .. submitters {
|
|
i := wait_for_int_signal(accepted, 'bounded submitter was not accepted')
|
|
assert i >= 0
|
|
assert i < submitters
|
|
assert !seen_accepted[i]
|
|
seen_accepted[i] = true
|
|
}
|
|
mut seen_ran := []bool{len: submitters}
|
|
for _ in 0 .. submitters {
|
|
i := wait_for_int_signal(ran, 'accepted bounded submitter job did not run')
|
|
assert i >= 0
|
|
assert i < submitters
|
|
assert !seen_ran[i]
|
|
seen_ran[i] = true
|
|
}
|
|
|
|
pool.close()!
|
|
for t in submit_threads {
|
|
t.wait()
|
|
}
|
|
}
|
|
|
|
fn test_pool_bounded_submit_stress_waiting_submitters_rejected_on_close() {
|
|
workers := 2
|
|
queue_size := 2
|
|
initial_jobs := workers + queue_size
|
|
submitters := 8
|
|
mut pool := xasync.new_pool(workers: workers, queue_size: queue_size)!
|
|
started := chan bool{cap: initial_jobs}
|
|
release := chan bool{cap: initial_jobs}
|
|
attempting := chan bool{cap: submitters}
|
|
result := chan string{cap: submitters}
|
|
ran := chan bool{cap: submitters}
|
|
closed := chan bool{cap: 1}
|
|
blocking_job := fn [started, release] (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
started <- true
|
|
_ := <-release
|
|
}
|
|
|
|
for _ in 0 .. initial_jobs {
|
|
pool.try_submit(blocking_job)!
|
|
}
|
|
for _ in 0 .. workers {
|
|
wait_for_bool_signal(started, 'initial pool worker did not start')
|
|
}
|
|
|
|
mut submit_threads := []thread{}
|
|
for i in 0 .. submitters {
|
|
submit_threads << spawn fn [mut pool, attempting, result, ran, i] () {
|
|
attempting <- true
|
|
pool.submit_with_context(context.background(), fn [ran] (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
ran <- true
|
|
}) or {
|
|
result <- err.msg()
|
|
return
|
|
}
|
|
result <- 'accepted ${i}'
|
|
}()
|
|
}
|
|
for _ in 0 .. submitters {
|
|
wait_for_bool_signal(attempting, 'bounded submitter did not start')
|
|
}
|
|
|
|
close_thread := spawn fn [mut pool, closed] () {
|
|
pool.close() or {
|
|
closed <- false
|
|
return
|
|
}
|
|
closed <- true
|
|
}()
|
|
for _ in 0 .. submitters {
|
|
msg := wait_for_string_signal(result, 'bounded submitter did not return after close')
|
|
assert msg == 'async: pool is closed'
|
|
}
|
|
assert_no_bool_signal(ran, 'bounded submitter job ran after close started')
|
|
|
|
for _ in 0 .. initial_jobs {
|
|
release <- true
|
|
}
|
|
wait_for_bool_signal(closed, 'pool close did not finish after releasing accepted jobs')
|
|
for t in submit_threads {
|
|
t.wait()
|
|
}
|
|
close_thread.wait()
|
|
}
|
|
|
|
fn wait_for_bool_signal(signal chan bool, message string) {
|
|
select {
|
|
ok := <-signal {
|
|
assert ok
|
|
}
|
|
1 * time.second {
|
|
assert false, message
|
|
}
|
|
}
|
|
}
|
|
|
|
fn wait_for_int_signal(signal chan int, message string) int {
|
|
select {
|
|
value := <-signal {
|
|
return value
|
|
}
|
|
1 * time.second {
|
|
assert false, message
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
fn wait_for_string_signal(signal chan string, message string) string {
|
|
select {
|
|
value := <-signal {
|
|
return value
|
|
}
|
|
1 * time.second {
|
|
assert false, message
|
|
}
|
|
}
|
|
return ''
|
|
}
|
|
|
|
fn assert_no_bool_signal(signal chan bool, message string) {
|
|
select {
|
|
_ := <-signal {
|
|
assert false, message
|
|
}
|
|
else {}
|
|
}
|
|
}
|
|
|
|
fn wait_until_pool_rejects_as_closed(mut pool xasync.Pool) {
|
|
probe := fn (mut ctx context.Context) ! {
|
|
_ = ctx
|
|
}
|
|
for _ in 0 .. 100 {
|
|
pool.try_submit(probe) or {
|
|
if err.msg() == 'async: pool is closed' {
|
|
return
|
|
}
|
|
assert err.msg() == 'async: pool queue is full'
|
|
time.sleep(1 * time.millisecond)
|
|
continue
|
|
}
|
|
assert false, 'pool accepted probe while backlog should be full'
|
|
}
|
|
assert false, 'pool close did not start'
|
|
}
|