]> git.vpit.fr Git - perl/modules/indirect.git/blob - t/09-load-threads.t
1f000c15d228d07364667b8839085a25930014e9
[perl/modules/indirect.git] / t / 09-load-threads.t
1 #!perl
2
3 use strict;
4 use warnings;
5
6 use lib 't/lib';
7 use VPIT::TestHelpers;
8
9 BEGIN { delete $ENV{PERL_INDIRECT_PM_DISABLE} }
10
11 my ($module, $thread_safe_var);
12 BEGIN {
13  $module          = 'indirect';
14  $thread_safe_var = 'indirect::I_THREADSAFE()';
15 }
16
17 sub load_test {
18  my $res;
19  if (defined &indirect::msg) {
20   local $@;
21   eval 'BEGIN { indirect->unimport(":fatal") if defined &indirect::msg } return; my $x = new X;';
22   $res = $@;
23  }
24  if (defined $res and $res =~ /^Indirect call of method/) {
25   return 1;
26  } elsif (not defined $res or $res eq '') {
27   return 0;
28  } else {
29   return $res;
30  }
31 }
32
33 # Keep the rest of the file untouched
34
35 BEGIN {
36  my $is_threadsafe;
37
38  if (defined $thread_safe_var) {
39   my $stat = run_perl "require POSIX; require $module; exit($thread_safe_var ? POSIX::EXIT_SUCCESS() : POSIX::EXIT_FAILURE())";
40   if (defined $stat) {
41    require POSIX;
42    my $res  = $stat >> 8;
43    if ($res == POSIX::EXIT_SUCCESS()) {
44     $is_threadsafe = 1;
45    } elsif ($res == POSIX::EXIT_FAILURE()) {
46     $is_threadsafe = !1;
47    }
48   }
49   if (not defined $is_threadsafe) {
50    skip_all "Could not detect if $module is thread safe or not";
51   }
52  }
53
54  VPIT::TestHelpers->import(
55   threads => [ $module => $is_threadsafe ],
56  )
57 }
58
59 my $could_not_create_thread = 'Could not create thread';
60
61 use Test::Leaner tests => 1 + (2 + 2 * 2) + 6 + (2 * 4) + 2;
62
63 sub is_loaded {
64  my ($affirmative, $desc) = @_;
65
66  my $res = load_test();
67
68  if ($affirmative) {
69   is $res, 1, "$desc: module loaded";
70  } else {
71   is $res, 0, "$desc: module not loaded";
72  }
73 }
74
75 BEGIN {
76  local $@;
77  my $code = eval "sub { require $module }";
78  die $@ if $@;
79  *do_load = $code;
80 }
81
82 is_loaded 0, 'main body, beginning';
83
84 # Test serial loadings
85
86 SKIP: {
87  my $thr = spawn(sub {
88   my $here = "first serial thread";
89   is_loaded 0, "$here, beginning";
90
91   do_load;
92   is_loaded 1, "$here, after loading";
93
94   return;
95  });
96
97  skip "$could_not_create_thread (serial 1)" => 2 unless defined $thr;
98
99  $thr->join;
100  if (my $err = $thr->error) {
101   die $err;
102  }
103 }
104
105 is_loaded 0, 'main body, in between serial loadings';
106
107 SKIP: {
108  my $thr = spawn(sub {
109   my $here = "second serial thread";
110   is_loaded 0, "$here, beginning";
111
112   do_load;
113   is_loaded 1, "$here, after loading";
114
115   return;
116  });
117
118  skip "$could_not_create_thread (serial 2)" => 2 unless defined $thr;
119
120  $thr->join;
121  if (my $err = $thr->error) {
122   die $err;
123  }
124 }
125
126 is_loaded 0, 'main body, after serial loadings';
127
128 # Test nested loadings
129
130 SKIP: {
131  my $thr = spawn(sub {
132   my $here = 'parent thread';
133   is_loaded 0, "$here, beginning";
134
135   SKIP: {
136    my $kid = spawn(sub {
137     my $here = 'child thread';
138     is_loaded 0, "$here, beginning";
139
140     do_load;
141     is_loaded 1, "$here, after loading";
142
143     return;
144    });
145
146    skip "$could_not_create_thread (nested child)" => 2 unless defined $kid;
147
148    $kid->join;
149    if (my $err = $kid->error) {
150     die "in child thread: $err\n";
151    }
152   }
153
154   is_loaded 0, "$here, after child terminated";
155
156   do_load;
157   is_loaded 1, "$here, after loading";
158
159   return;
160  });
161
162  skip "$could_not_create_thread (nested parent)" => (3 + 2) unless defined $thr;
163
164  $thr->join;
165  if (my $err = $thr->error) {
166   die $err;
167  }
168 }
169
170 is_loaded 0, 'main body, after nested loadings';
171
172 # Test parallel loadings
173
174 use threads;
175 use threads::shared;
176
177 my @locks_down = (1) x 5;
178 my @locks_up   = (0) x scalar @locks_down;
179 share($_) for @locks_down, @locks_up;
180
181 my $peers = 2;
182
183 sub sync_master {
184  my ($id) = @_;
185
186  {
187   lock $locks_down[$id];
188   $locks_down[$id] = 0;
189   cond_broadcast $locks_down[$id];
190  }
191
192  {
193   lock $locks_up[$id];
194   cond_wait $locks_up[$id] until $locks_up[$id] == $peers;
195  }
196 }
197
198 sub sync_slave {
199  my ($id) = @_;
200
201  {
202   lock $locks_down[$id];
203   cond_wait $locks_down[$id] until $locks_down[$id] == 0;
204  }
205
206  {
207   lock $locks_up[$id];
208   $locks_up[$id]++;
209   cond_signal $locks_up[$id];
210  }
211 }
212
213 SKIP: {
214  my $thr1 = spawn(sub {
215   my $here = 'first simultaneous thread';
216   is_loaded 0, "$here, beginning";
217   sync_slave 0;
218
219   do_load;
220   is_loaded 1, "$here, after loading";
221   sync_slave 1;
222   sync_slave 2;
223
224   sync_slave 3;
225   is_loaded 1, "$here, still loaded while also loaded in the other thread";
226   sync_slave 4;
227
228   is_loaded 1, "$here, end";
229
230   return;
231  });
232
233  skip "$could_not_create_thread (parallel 1)" => (4 * 2) unless defined $thr1;
234
235  my $thr2 = spawn(sub {
236   my $here = 'second simultaneous thread';
237   is_loaded 0, "$here, beginning";
238   sync_slave 0;
239
240   sync_slave 1;
241   is_loaded 0, "$here, loaded in other thread but not here";
242   sync_slave 2;
243
244   do_load;
245   is_loaded 1, "$here, after loading";
246   sync_slave 3;
247   sync_slave 4;
248
249   is_loaded 1, "$here, end";
250
251   return;
252  });
253
254  sync_master($_) for 0 .. $#locks_down;
255
256  $thr1->join;
257  if (my $err = $thr1->error) {
258   die $err;
259  }
260
261  skip "$could_not_create_thread (parallel 2)" => (4 * 1) unless defined $thr2;
262
263  $thr2->join;
264  if (my $err = $thr2->error) {
265   die $err;
266  }
267 }
268
269 is_loaded 0, 'main body, after simultaneous threads';
270
271 do_load;
272 is_loaded 1, 'main body, loaded at end';