+ lock $locks_down[$id];
+ cond_wait $locks_down[$id] until $locks_down[$id] == 0;
+ }
+
+ {
+ lock $locks_up[$id];
+ $locks_up[$id]++;
+ cond_signal $locks_up[$id];
+ }
+
+ return 1;
+}
+
+for my $first_thread_ends_first (0, 1) {
+ for my $id (0 .. $sync_points - 1) {
+ {
+ lock $locks_down[$id];
+ $locks_down[$id] = 1;
+ }
+ {
+ lock $locks_up[$id];
+ $locks_up[$id] = 0;
+ }
+ }
+
+ my $thr1_end = 'finishes first';
+ my $thr2_end = 'finishes last';
+
+ ($thr1_end, $thr2_end) = ($thr2_end, $thr1_end)
+ unless $first_thread_ends_first;
+
+ SKIP: {
+ my $thr1 = spawn(sub {
+ my $here = "first simultaneous thread ($thr1_end)";
+ sync_slave 0;
+
+ is_loaded 0, "$here, beginning";
+ sync_slave 1;
+
+ do_load;
+ is_loaded 1, "$here, after loading";
+ sync_slave 2;
+ sync_slave 3;
+
+ sync_slave 4;
+ is_loaded 1, "$here, still loaded while also loaded in the other thread";
+ sync_slave 5;
+
+ sync_slave 6 unless $first_thread_ends_first;
+
+ is_loaded 1, "$here, end";
+
+ return 1;
+ });
+
+ skip "$could_not_create_thread (parallel 1)" => (4 * 2) unless defined $thr1;
+
+ my $thr2 = spawn(sub {
+ my $here = "second simultaneous thread ($thr2_end)";
+ sync_slave 0;
+
+ is_loaded 0, "$here, beginning";
+ sync_slave 1;
+
+ sync_slave 2;
+ sync_slave 3;
+ is_loaded 0, "$here, loaded in other thread but not here";
+
+ do_load;
+ is_loaded 1, "$here, after loading";
+ sync_slave 4;
+ sync_slave 5;
+
+ sync_slave 6 if $first_thread_ends_first;
+
+ is_loaded 1, "$here, end";
+
+ return 1;
+ });
+
+ sync_master($_) for 0 .. 5;
+
+ if (defined $thr2) {
+ ($thr2, $thr1) = ($thr1, $thr2) unless $first_thread_ends_first;
+
+ $thr1->join;
+ if (my $err = $thr1->error) {
+ die $err;
+ }
+
+ sync_master(6, 1);
+
+ $thr2->join;
+ if (my $err = $thr1->error) {
+ die $err;
+ }
+ } else {
+ sync_master(6, 1) unless $first_thread_ends_first;
+
+ $thr1->join;
+ if (my $err = $thr1->error) {
+ die $err;
+ }
+
+ skip "$could_not_create_thread (parallel 2)" => (4 * 1);
+ }