plan(12); my class Semaphore is repr('Semaphore') { } # 9 tests # Single threaded case { my $s := nqp::box_i(3, Semaphore); nqp::semacquire($s); ok(1, 'semacquire of permit 1 of 3 succeeds'); nqp::semacquire($s); ok(1, 'semacquire of permit 2 of 3 succeeds also'); ok( nqp::semtryacquire($s), 'semtryacquire of permit 3 of 3 succeeds'); ok(!nqp::semtryacquire($s), 'semtryacquire underflow fails'); nqp::semrelease($s); ok(1, 'semrelease of permit 3 of 3 succeeds'); ok(nqp::semtryacquire($s), 'semtryacquire of permit 3 of 3 succeeds now'); nqp::semrelease($s); nqp::semrelease($s); nqp::semrelease($s); ok(1, 'semrelease of all three permits succeed'); nqp::semrelease($s); nqp::semacquire($s); nqp::semacquire($s); nqp::semacquire($s); ok( nqp::semtryacquire($s), 'Release overflow adds to available capacity'); ok(!nqp::semtryacquire($s), 'Semaphore does not grant more than overflow level in additional capacity'); } # 3 tests # Multi-threaded case { my $released := 0; my $s := nqp::box_i(3, Semaphore); my $t1 := nqp::newthread({ nqp::semacquire($s); }, 0); my $t2 := nqp::newthread({ nqp::semacquire($s); }, 0); my $t3 := nqp::newthread({ nqp::semacquire($s); }, 0); my $t4 := nqp::newthread({ ok(!nqp::semtryacquire($s), 'Trying fourth acquire before release fails'); }, 0); my $t5 := nqp::newthread({ my $before := nqp::time(); nqp::semacquire($s); my $after := nqp::time(); ok($after - $before > 1000000000, 'Fourth acquire blocks on empty semaphore'); ok($released, 'Fourth acquire succeeds after release in other thread'); }, 0); my $t6 := nqp::newthread({ nqp::sleep(3.0); $released := 1; nqp::semrelease($s); }, 0); # First, exhaust semaphore capacity nqp::threadrun($t1); nqp::threadrun($t2); nqp::threadrun($t3); nqp::threadjoin($t1); nqp::threadjoin($t2); nqp::threadjoin($t3); # Try (and fail) to acquire another permit nqp::threadrun($t4); nqp::threadjoin($t4); # Block on acquire, release in separate thread, succeed nqp::threadrun($t5); nqp::threadrun($t6); nqp::threadjoin($t6); nqp::threadjoin($t5); }