xref: /openbsd-src/gnu/usr.bin/perl/dist/threads-shared/t/cond.t (revision b39c515898423c8d899e35282f4b395f7cad3298)
1*b39c5158Smillertuse strict;
2*b39c5158Smillertuse warnings;
3*b39c5158Smillert
4*b39c5158SmillertBEGIN {
5*b39c5158Smillert    use Config;
6*b39c5158Smillert    if (! $Config{'useithreads'}) {
7*b39c5158Smillert        print("1..0 # SKIP Perl not compiled with 'useithreads'\n");
8*b39c5158Smillert        exit(0);
9*b39c5158Smillert    }
10*b39c5158Smillert}
11*b39c5158Smillert
12*b39c5158Smillertuse ExtUtils::testlib;
13*b39c5158Smillert
14*b39c5158Smillertmy $Base = 0;
15*b39c5158Smillertsub ok {
16*b39c5158Smillert    my ($id, $ok, $name) = @_;
17*b39c5158Smillert    $id += $Base;
18*b39c5158Smillert
19*b39c5158Smillert    # You have to do it this way or VMS will get confused.
20*b39c5158Smillert    if ($ok) {
21*b39c5158Smillert        print("ok $id - $name\n");
22*b39c5158Smillert    } else {
23*b39c5158Smillert        print("not ok $id - $name\n");
24*b39c5158Smillert        printf("# Failed test at line %d\n", (caller)[2]);
25*b39c5158Smillert    }
26*b39c5158Smillert
27*b39c5158Smillert    return ($ok);
28*b39c5158Smillert}
29*b39c5158Smillert
30*b39c5158SmillertBEGIN {
31*b39c5158Smillert    $| = 1;
32*b39c5158Smillert    print("1..32\n");   ### Number of tests that will be run ###
33*b39c5158Smillert};
34*b39c5158Smillert
35*b39c5158Smillertuse threads;
36*b39c5158Smillertuse threads::shared;
37*b39c5158Smillertok(1, 1, 'Loaded');
38*b39c5158Smillert$Base++;
39*b39c5158Smillert
40*b39c5158Smillert### Start of Testing ###
41*b39c5158Smillert
42*b39c5158Smillert# test locking
43*b39c5158Smillert{
44*b39c5158Smillert    my $lock : shared;
45*b39c5158Smillert    my $tr;
46*b39c5158Smillert
47*b39c5158Smillert    # test that a subthread can't lock until parent thread has unlocked
48*b39c5158Smillert
49*b39c5158Smillert    {
50*b39c5158Smillert        lock($lock);
51*b39c5158Smillert        ok(1, 1, "set first lock");
52*b39c5158Smillert        $tr = async {
53*b39c5158Smillert            lock($lock);
54*b39c5158Smillert            ok(3, 1, "set lock in subthread");
55*b39c5158Smillert        };
56*b39c5158Smillert        threads->yield;
57*b39c5158Smillert        ok(2, 1, "still got lock");
58*b39c5158Smillert    }
59*b39c5158Smillert    $tr->join;
60*b39c5158Smillert
61*b39c5158Smillert    $Base += 3;
62*b39c5158Smillert
63*b39c5158Smillert    # ditto with ref to thread
64*b39c5158Smillert
65*b39c5158Smillert    {
66*b39c5158Smillert        my $lockref = \$lock;
67*b39c5158Smillert        lock($lockref);
68*b39c5158Smillert        ok(1,1,"set first lockref");
69*b39c5158Smillert        $tr = async {
70*b39c5158Smillert            lock($lockref);
71*b39c5158Smillert            ok(3,1,"set lockref in subthread");
72*b39c5158Smillert        };
73*b39c5158Smillert        threads->yield;
74*b39c5158Smillert        ok(2,1,"still got lockref");
75*b39c5158Smillert    }
76*b39c5158Smillert    $tr->join;
77*b39c5158Smillert
78*b39c5158Smillert    $Base += 3;
79*b39c5158Smillert
80*b39c5158Smillert    # make sure recursive locks unlock at the right place
81*b39c5158Smillert    {
82*b39c5158Smillert        lock($lock);
83*b39c5158Smillert        ok(1,1,"set first recursive lock");
84*b39c5158Smillert        lock($lock);
85*b39c5158Smillert        threads->yield;
86*b39c5158Smillert        {
87*b39c5158Smillert            lock($lock);
88*b39c5158Smillert            threads->yield;
89*b39c5158Smillert        }
90*b39c5158Smillert        $tr = async {
91*b39c5158Smillert            lock($lock);
92*b39c5158Smillert            ok(3,1,"set recursive lock in subthread");
93*b39c5158Smillert        };
94*b39c5158Smillert        {
95*b39c5158Smillert            lock($lock);
96*b39c5158Smillert            threads->yield;
97*b39c5158Smillert            {
98*b39c5158Smillert                lock($lock);
99*b39c5158Smillert                threads->yield;
100*b39c5158Smillert                lock($lock);
101*b39c5158Smillert                threads->yield;
102*b39c5158Smillert            }
103*b39c5158Smillert        }
104*b39c5158Smillert        ok(2,1,"still got recursive lock");
105*b39c5158Smillert    }
106*b39c5158Smillert    $tr->join;
107*b39c5158Smillert
108*b39c5158Smillert    $Base += 3;
109*b39c5158Smillert
110*b39c5158Smillert    # Make sure a lock factory gives out fresh locks each time
111*b39c5158Smillert    # for both attribute and run-time shares
112*b39c5158Smillert
113*b39c5158Smillert    sub lock_factory1 { my $lock : shared; return \$lock; }
114*b39c5158Smillert    sub lock_factory2 { my $lock; share($lock); return \$lock; }
115*b39c5158Smillert
116*b39c5158Smillert    my (@locks1, @locks2);
117*b39c5158Smillert    push @locks1, lock_factory1() for 1..2;
118*b39c5158Smillert    push @locks1, lock_factory2() for 1..2;
119*b39c5158Smillert    push @locks2, lock_factory1() for 1..2;
120*b39c5158Smillert    push @locks2, lock_factory2() for 1..2;
121*b39c5158Smillert
122*b39c5158Smillert    ok(1,1,"lock factory: locking all locks");
123*b39c5158Smillert    lock $locks1[0];
124*b39c5158Smillert    lock $locks1[1];
125*b39c5158Smillert    lock $locks1[2];
126*b39c5158Smillert    lock $locks1[3];
127*b39c5158Smillert    ok(2,1,"lock factory: locked all locks");
128*b39c5158Smillert    $tr = async {
129*b39c5158Smillert        ok(3,1,"lock factory: child: locking all locks");
130*b39c5158Smillert        lock $locks2[0];
131*b39c5158Smillert        lock $locks2[1];
132*b39c5158Smillert        lock $locks2[2];
133*b39c5158Smillert        lock $locks2[3];
134*b39c5158Smillert        ok(4,1,"lock factory: child: locked all locks");
135*b39c5158Smillert    };
136*b39c5158Smillert    $tr->join;
137*b39c5158Smillert
138*b39c5158Smillert    $Base += 4;
139*b39c5158Smillert}
140*b39c5158Smillert
141*b39c5158Smillert
142*b39c5158Smillert# test cond_signal()
143*b39c5158Smillert{
144*b39c5158Smillert    my $lock : shared;
145*b39c5158Smillert
146*b39c5158Smillert    sub foo {
147*b39c5158Smillert        lock($lock);
148*b39c5158Smillert        ok(1,1,"cond_signal: created first lock");
149*b39c5158Smillert        my $tr2 = threads->create(\&bar);
150*b39c5158Smillert        cond_wait($lock);
151*b39c5158Smillert        $tr2->join();
152*b39c5158Smillert        ok(5,1,"cond_signal: joined");
153*b39c5158Smillert    }
154*b39c5158Smillert
155*b39c5158Smillert    sub bar {
156*b39c5158Smillert        ok(2,1,"cond_signal: child before lock");
157*b39c5158Smillert        lock($lock);
158*b39c5158Smillert        ok(3,1,"cond_signal: child locked");
159*b39c5158Smillert        cond_signal($lock);
160*b39c5158Smillert        ok(4,1,"cond_signal: signalled");
161*b39c5158Smillert    }
162*b39c5158Smillert
163*b39c5158Smillert    my $tr  = threads->create(\&foo);
164*b39c5158Smillert    $tr->join();
165*b39c5158Smillert
166*b39c5158Smillert    $Base += 5;
167*b39c5158Smillert
168*b39c5158Smillert    # ditto, but with lockrefs
169*b39c5158Smillert
170*b39c5158Smillert    my $lockref = \$lock;
171*b39c5158Smillert    sub foo2 {
172*b39c5158Smillert        lock($lockref);
173*b39c5158Smillert        ok(1,1,"cond_signal: ref: created first lock");
174*b39c5158Smillert        my $tr2 = threads->create(\&bar2);
175*b39c5158Smillert        cond_wait($lockref);
176*b39c5158Smillert        $tr2->join();
177*b39c5158Smillert        ok(5,1,"cond_signal: ref: joined");
178*b39c5158Smillert    }
179*b39c5158Smillert
180*b39c5158Smillert    sub bar2 {
181*b39c5158Smillert        ok(2,1,"cond_signal: ref: child before lock");
182*b39c5158Smillert        lock($lockref);
183*b39c5158Smillert        ok(3,1,"cond_signal: ref: child locked");
184*b39c5158Smillert        cond_signal($lockref);
185*b39c5158Smillert        ok(4,1,"cond_signal: ref: signalled");
186*b39c5158Smillert    }
187*b39c5158Smillert
188*b39c5158Smillert    $tr  = threads->create(\&foo2);
189*b39c5158Smillert    $tr->join();
190*b39c5158Smillert
191*b39c5158Smillert    $Base += 5;
192*b39c5158Smillert}
193*b39c5158Smillert
194*b39c5158Smillert
195*b39c5158Smillert# test cond_broadcast()
196*b39c5158Smillert{
197*b39c5158Smillert    my $counter : shared = 0;
198*b39c5158Smillert
199*b39c5158Smillert    # broad(N) forks off broad(N-1) and goes into a wait, in such a way
200*b39c5158Smillert    # that it's guaranteed to reach the wait before its child enters the
201*b39c5158Smillert    # locked region. When N reaches 0, the child instead does a
202*b39c5158Smillert    # cond_broadcast to wake all its ancestors.
203*b39c5158Smillert
204*b39c5158Smillert    sub broad {
205*b39c5158Smillert        my $n = shift;
206*b39c5158Smillert        my $th;
207*b39c5158Smillert        {
208*b39c5158Smillert            lock($counter);
209*b39c5158Smillert            if ($n > 0) {
210*b39c5158Smillert                $counter++;
211*b39c5158Smillert                $th = threads->create(\&broad, $n-1);
212*b39c5158Smillert                cond_wait($counter);
213*b39c5158Smillert                $counter += 10;
214*b39c5158Smillert            }
215*b39c5158Smillert            else {
216*b39c5158Smillert                ok(1, $counter == 3, "cond_broadcast: all three waiting");
217*b39c5158Smillert                cond_broadcast($counter);
218*b39c5158Smillert            }
219*b39c5158Smillert        }
220*b39c5158Smillert        $th->join if $th;
221*b39c5158Smillert    }
222*b39c5158Smillert
223*b39c5158Smillert    threads->create(\&broad, 3)->join;
224*b39c5158Smillert    ok(2, $counter == 33, "cond_broadcast: all three threads woken");
225*b39c5158Smillert
226*b39c5158Smillert    $Base += 2;
227*b39c5158Smillert
228*b39c5158Smillert
229*b39c5158Smillert    # ditto, but with refs and shared()
230*b39c5158Smillert
231*b39c5158Smillert    my $counter2 = 0;
232*b39c5158Smillert    share($counter2);
233*b39c5158Smillert    my $r = \$counter2;
234*b39c5158Smillert
235*b39c5158Smillert    sub broad2 {
236*b39c5158Smillert        my $n = shift;
237*b39c5158Smillert        my $th;
238*b39c5158Smillert        {
239*b39c5158Smillert            lock($r);
240*b39c5158Smillert            if ($n > 0) {
241*b39c5158Smillert                $$r++;
242*b39c5158Smillert                $th = threads->create(\&broad2, $n-1);
243*b39c5158Smillert                cond_wait($r);
244*b39c5158Smillert                $$r += 10;
245*b39c5158Smillert            }
246*b39c5158Smillert            else {
247*b39c5158Smillert                ok(1, $$r == 3, "cond_broadcast: ref: all three waiting");
248*b39c5158Smillert                cond_broadcast($r);
249*b39c5158Smillert            }
250*b39c5158Smillert        }
251*b39c5158Smillert        $th->join if $th;
252*b39c5158Smillert    }
253*b39c5158Smillert
254*b39c5158Smillert    threads->create(\&broad2, 3)->join;;
255*b39c5158Smillert    ok(2, $$r == 33, "cond_broadcast: ref: all three threads woken");
256*b39c5158Smillert
257*b39c5158Smillert    $Base += 2;
258*b39c5158Smillert}
259*b39c5158Smillert
260*b39c5158Smillert
261*b39c5158Smillert# test warnings;
262*b39c5158Smillert{
263*b39c5158Smillert    my $warncount = 0;
264*b39c5158Smillert    local $SIG{__WARN__} = sub { $warncount++ };
265*b39c5158Smillert
266*b39c5158Smillert    my $lock : shared;
267*b39c5158Smillert
268*b39c5158Smillert    cond_signal($lock);
269*b39c5158Smillert    ok(1, $warncount == 1, 'get warning on cond_signal');
270*b39c5158Smillert    cond_broadcast($lock);
271*b39c5158Smillert    ok(2, $warncount == 2, 'get warning on cond_broadcast');
272*b39c5158Smillert    no warnings 'threads';
273*b39c5158Smillert    cond_signal($lock);
274*b39c5158Smillert    ok(3, $warncount == 2, 'get no warning on cond_signal');
275*b39c5158Smillert    cond_broadcast($lock);
276*b39c5158Smillert    ok(4, $warncount == 2, 'get no warning on cond_broadcast');
277*b39c5158Smillert
278*b39c5158Smillert    $Base += 4;
279*b39c5158Smillert}
280*b39c5158Smillert
281*b39c5158Smillertexit(0);
282*b39c5158Smillert
283*b39c5158Smillert# EOF
284