use warnings;
BEGIN {
chdir 't' if -d 't';
push @INC ,'../lib';
require Config; import Config;
unless ($Config{'useithreads'}) {
print "1..0 # Skip: no threads\n";
exit 0;
}
}
$|++;
print "1..31\n";
use strict;
use threads;
use threads::shared;
# We can't use the normal ok() type stuff here, as part of the test is
# to check that the numbers get printed in the right order. Instead, we
# set a 'base' number for each part of the test and specify the ok()
# number as an offset from that base.
my $Base = 0;
sub ok {
my ($offset, $bool, $text) = @_;
print "not " unless $bool;
print "ok ", $Base + $offset, " - $text\n";
}
# test locking
{
my $lock : shared;
my $tr;
# test that a subthread can't lock until parent thread has unlocked
{
lock($lock);
ok(1,1,"set first lock");
$tr = async {
lock($lock);
ok(3,1,"set lock in subthread");
};
threads->yield;
ok(2,1,"still got lock");
}
$tr->join;
$Base += 3;
# ditto with ref to thread
{
my $lockref = \$lock;
lock($lockref);
ok(1,1,"set first lockref");
$tr = async {
lock($lockref);
ok(3,1,"set lockref in subthread");
};
threads->yield;
ok(2,1,"still got lockref");
}
$tr->join;
$Base += 3;
# make sure recursive locks unlock at the right place
{
lock($lock);
ok(1,1,"set first recursive lock");
lock($lock);
threads->yield;
{
lock($lock);
threads->yield;
}
$tr = async {
lock($lock);
ok(3,1,"set recursive lock in subthread");
};
{
lock($lock);
threads->yield;
{
lock($lock);
threads->yield;
lock($lock);
threads->yield;
}
}
ok(2,1,"still got recursive lock");
}
$tr->join;
$Base += 3;
# Make sure a lock factory gives out fresh locks each time
# for both attribute and run-time shares
sub lock_factory1 { my $lock : shared; return \$lock; }
sub lock_factory2 { my $lock; share($lock); return \$lock; }
my (@locks1, @locks2);
push @locks1, lock_factory1() for 1..2;
push @locks1, lock_factory2() for 1..2;
push @locks2, lock_factory1() for 1..2;
push @locks2, lock_factory2() for 1..2;
ok(1,1,"lock factory: locking all locks");
lock $locks1[0];
lock $locks1[1];
lock $locks1[2];
lock $locks1[3];
ok(2,1,"lock factory: locked all locks");
$tr = async {
ok(3,1,"lock factory: child: locking all locks");
lock $locks2[0];
lock $locks2[1];
lock $locks2[2];
lock $locks2[3];
ok(4,1,"lock factory: child: locked all locks");
};
$tr->join;
$Base += 4;
}
# test cond_signal()
{
my $lock : shared;
sub foo {
lock($lock);
ok(1,1,"cond_signal: created first lock");
my $tr2 = threads->create(\&bar);
cond_wait($lock);
$tr2->join();
ok(5,1,"cond_signal: joined");
}
sub bar {
ok(2,1,"cond_signal: child before lock");
lock($lock);
ok(3,1,"cond_signal: child locked");
cond_signal($lock);
ok(4,1,"cond_signal: signalled");
}
my $tr = threads->create(\&foo);
$tr->join();
$Base += 5;
# ditto, but with lockrefs
my $lockref = \$lock;
sub foo2 {
lock($lockref);
ok(1,1,"cond_signal: ref: created first lock");
my $tr2 = threads->create(\&bar2);
cond_wait($lockref);
$tr2->join();
ok(5,1,"cond_signal: ref: joined");
}
sub bar2 {
ok(2,1,"cond_signal: ref: child before lock");
lock($lockref);
ok(3,1,"cond_signal: ref: child locked");
cond_signal($lockref);
ok(4,1,"cond_signal: ref: signalled");
}
$tr = threads->create(\&foo2);
$tr->join();
$Base += 5;
}
# test cond_broadcast()
{
my $counter : shared = 0;
# broad(N) forks off broad(N-1) and goes into a wait, in such a way
# that it's guaranteed to reach the wait before its child enters the
# locked region. When N reaches 0, the child instead does a
# cond_broadcast to wake all its ancestors.
sub broad {
my $n = shift;
my $th;
{
lock($counter);
if ($n > 0) {
$counter++;
$th = threads->new(\&broad, $n-1);
cond_wait($counter);
$counter += 10;
}
else {
ok(1, $counter == 3, "cond_broadcast: all three waiting");
cond_broadcast($counter);
}
}
$th->join if $th;
}
threads->new(\&broad, 3)->join;
ok(2, $counter == 33, "cond_broadcast: all three threads woken");
print "# counter=$counter\n";
$Base += 2;
# ditto, but with refs and shared()
my $counter2 = 0;
share($counter2);
my $r = \$counter2;
sub broad2 {
my $n = shift;
my $th;
{
lock($r);
if ($n > 0) {
$$r++;
$th = threads->new(\&broad2, $n-1);
cond_wait($r);
$$r += 10;
}
else {
ok(1, $$r == 3, "cond_broadcast: ref: all three waiting");
cond_broadcast($r);
}
}
$th->join if $th;
}
threads->new(\&broad2, 3)->join;;
ok(2, $$r == 33, "cond_broadcast: ref: all three threads woken");
print "# counter=$$r\n";
$Base += 2;
}
# test warnings;
{
my $warncount = 0;
local $SIG{__WARN__} = sub { $warncount++ };
my $lock : shared;
cond_signal($lock);
ok(1, $warncount == 1, 'get warning on cond_signal');
cond_broadcast($lock);
ok(2, $warncount == 2, 'get warning on cond_broadcast');
no warnings 'threads';
cond_signal($lock);
ok(3, $warncount == 2, 'get no warning on cond_signal');
cond_broadcast($lock);
ok(4, $warncount == 2, 'get no warning on cond_broadcast');
$Base += 4;
}
|