1MCE::Shared::Condvar(3)User Contributed Perl DocumentatioMnCE::Shared::Condvar(3)
2
3
4
6 MCE::Shared::Condvar - Condvar helper class
7
9 This document describes MCE::Shared::Condvar version 1.873
10
12 This helper class for MCE::Shared provides a "Scalar", "Mutex", and
13 primitives for conditional locking.
14
16 use MCE::Shared;
17
18 my $cv = MCE::Shared->condvar( 0 );
19
20 # OO interface
21
22 $val = $cv->set( $val );
23 $val = $cv->get();
24 $len = $cv->len();
25
26 # conditional locking primitives
27
28 $cv->lock();
29 $cv->unlock();
30 $cv->broadcast(0.05); # delay before broadcasting
31 $cv->broadcast();
32 $cv->signal(0.05); # delay before signaling
33 $cv->signal();
34 $cv->timedwait(2.5);
35 $cv->wait();
36
37 # included, sugar methods without having to call set/get explicitly
38
39 $val = $cv->append( $string ); # $val .= $string
40 $val = $cv->decr(); # --$val
41 $val = $cv->decrby( $number ); # $val -= $number
42 $val = $cv->getdecr(); # $val--
43 $val = $cv->getincr(); # $val++
44 $val = $cv->incr(); # ++$val
45 $val = $cv->incrby( $number ); # $val += $number
46 $old = $cv->getset( $new ); # $o = $v, $v = $n, $o
47
49 The following example demonstrates barrier synchronization.
50
51 use MCE;
52 use MCE::Shared;
53 use Time::HiRes qw(usleep);
54
55 my $num_workers = 8;
56 my $count = MCE::Shared->condvar(0);
57 my $state = MCE::Shared->scalar('ready');
58
59 my $microsecs = ( $^O =~ /mswin|mingw|msys|cygwin/i ) ? 0 : 200;
60
61 # The lock is released upon entering ->broadcast, ->signal, ->timedwait,
62 # and ->wait. For performance reasons, the condition variable is *not*
63 # re-locked prior to exiting the call. Therefore, obtain the lock when
64 # synchronization is desired subsequently.
65
66 sub barrier_sync {
67 usleep($microsecs) while $state->get eq 'down';
68
69 $count->lock;
70 $state->set('up'), $count->incr;
71
72 if ($count->get == $num_workers) {
73 $count->decr, $state->set('down');
74 $count->broadcast;
75 }
76 else {
77 $count->wait while $state->get eq 'up';
78 $count->lock;
79 $state->set('ready') if $count->decr == 0;
80 $count->unlock;
81 }
82 }
83
84 sub user_func {
85 my $id = MCE->wid;
86 for (1 .. 400) {
87 MCE->print("$_: $id\n");
88 barrier_sync(); # made possible by MCE::Shared::Condvar
89 # MCE->sync(); # same thing via the MCE-Core API
90 }
91 }
92
93 my $mce = MCE->new(
94 max_workers => $num_workers,
95 user_func => \&user_func
96 )->run;
97
98 # Time taken from a 2.6 GHz machine running Mac OS X.
99 # threads::shared: 0.207s Perl threads
100 # forks::shared: 36.426s child processes
101 # MCE::Shared: 0.353s child processes
102 # MCE Sync: 0.062s child processes
103
105 MCE::Shared::Condvar->new ( )
106 Called by MCE::Shared for constructing a shared-condvar object.
107
108 MCE::Shared->condvar ( [ value ] )
109 Constructs a new condition variable. Its value defaults to 0 when
110 "value" is not specified.
111
112 use MCE::Shared;
113
114 $cv = MCE::Shared->condvar( 100 );
115 $cv = MCE::Shared->condvar;
116
117 set ( value )
118 Sets the value associated with the "cv" object. The new value is
119 returned in scalar context.
120
121 $val = $cv->set( 10 );
122 $cv->set( 10 );
123
124 get
125 Returns the value associated with the "cv" object.
126
127 $val = $cv->get;
128
129 len
130 Returns the length of the value. It returns the "undef" value if the
131 value is not defined.
132
133 $len = $var->len;
134
135 lock
136 Attempts to grab the lock and waits if not available. Multiple calls to
137 "$cv->lock" by the same process or thread is safe. The mutex will
138 remain locked until "$cv->unlock" is called.
139
140 $cv->lock;
141
142 unlock
143 Releases the lock. A held lock by an exiting process or thread is
144 released automatically.
145
146 $cv->unlock;
147
148 signal ( [ floating_seconds ] )
149 Releases a held lock on the variable. Then, unblocks one process or
150 thread that's "wait"ing on that variable. The variable is *not* locked
151 upon return.
152
153 Optionally, delay "floating_seconds" before signaling.
154
155 $count->signal;
156 $count->signal( 0.5 );
157
158 broadcast ( [ floating_seconds ] )
159 The "broadcast" method works similarly to "signal". It releases a held
160 lock on the variable. Then, unblocks all the processes or threads that
161 are blocked in a condition "wait" on the variable, rather than only
162 one. The variable is *not* locked upon return.
163
164 Optionally, delay "floating_seconds" before broadcasting.
165
166 $count->broadcast;
167 $count->broadcast( 0.5 );
168
169 wait
170 Releases a held lock on the variable. Then, waits until another thread
171 does a "signal" or "broadcast" for the same variable. The variable is
172 *not* locked upon return.
173
174 $count->wait() while $state->get() eq "bar";
175
176 timedwait ( floating_seconds )
177 Releases a held lock on the variable. Then, waits until another thread
178 does a "signal" or "broadcast" for the same variable or if the timeout
179 exceeds "floating_seconds".
180
181 A false value is returned if the timeout is reached, and a true value
182 otherwise. In either case, the variable is *not* locked upon return.
183
184 $count->timedwait( 10 ) while $state->get() eq "foo";
185
187 This module is equipped with sugar methods to not have to call "set"
188 and "get" explicitly. In shared context, the benefit is atomicity and
189 reduction in inter-process communication.
190
191 The API resembles a subset of the Redis primitives
192 <http://redis.io/commands#strings> without the key argument.
193
194 append ( value )
195 Appends a value at the end of the current value and returns its new
196 length.
197
198 $len = $cv->append( "foo" );
199
200 decr
201 Decrements the value by one and returns its new value.
202
203 $num = $cv->decr;
204
205 decrby ( number )
206 Decrements the value by the given number and returns its new value.
207
208 $num = $cv->decrby( 2 );
209
210 getdecr
211 Decrements the value by one and returns its old value.
212
213 $old = $cv->getdecr;
214
215 getincr
216 Increments the value by one and returns its old value.
217
218 $old = $cv->getincr;
219
220 getset ( value )
221 Sets the value and returns its old value.
222
223 $old = $cv->getset( "baz" );
224
225 incr
226 Increments the value by one and returns its new value.
227
228 $num = $cv->incr;
229
230 incrby ( number )
231 Increments the value by the given number and returns its new value.
232
233 $num = $cv->incrby( 2 );
234
236 The MCE example <https://github.com/marioroy/mce-
237 examples/tree/master/chameneos> is derived from the chameneos example
238 <http://benchmarksgame.alioth.debian.org/u64q/program.php?test=chameneosredux&lang=perl&id=4>
239 by Jonathan DePeri and Andrew Rodland.
240
241 use 5.010;
242 use strict;
243 use warnings;
244
245 use MCE::Hobo;
246 use MCE::Shared;
247 use Time::HiRes 'time';
248
249 die 'No argument given' if not @ARGV;
250
251 my $start = time;
252 my %color = ( blue => 1, red => 2, yellow => 4 );
253
254 my ( @colors, @complement );
255
256 @colors[values %color] = keys %color;
257
258 for my $triple (
259 [qw(blue blue blue)],
260 [qw(red red red)],
261 [qw(yellow yellow yellow)],
262 [qw(blue red yellow)],
263 [qw(blue yellow red)],
264 [qw(red blue yellow)],
265 [qw(red yellow blue)],
266 [qw(yellow red blue)],
267 [qw(yellow blue red)],
268 ) {
269 $complement[ $color{$triple->[0]} | $color{$triple->[1]} ] =
270 $color{$triple->[2]};
271 }
272
273 my @numbers = qw(zero one two three four five six seven eight nine);
274
275 sub display_complements
276 {
277 for my $i (1, 2, 4) {
278 for my $j (1, 2, 4) {
279 print "$colors[$i] + $colors[$j] -> $colors[ $complement[$i | $j] ]\n";
280 }
281 }
282 print "\n";
283 }
284
285 sub num2words
286 {
287 join ' ', '', map $numbers[$_], split //, shift;
288 }
289
290 # Construct condvars and queues first before other shared objects or in
291 # any order when IO::FDPass is installed, used by MCE::Shared::Server.
292
293 my $meetings = MCE::Shared->condvar();
294
295 tie my @creatures, 'MCE::Shared';
296 tie my $first, 'MCE::Shared', undef;
297 tie my @met, 'MCE::Shared';
298 tie my @met_self, 'MCE::Shared';
299
300 sub chameneos
301 {
302 my $id = shift;
303
304 while (1) {
305 $meetings->lock();
306
307 unless ($meetings->get()) {
308 $meetings->unlock();
309 last;
310 }
311
312 if (defined $first) {
313 $creatures[$first] = $creatures[$id] =
314 $complement[$creatures[$first] | $creatures[$id]];
315
316 $met_self[$first]++ if ($first == $id);
317 $met[$first]++; $met[$id]++;
318 $meetings->decr();
319 $first = undef;
320
321 # Unlike threads::shared (condvar) which retains the lock
322 # while in the scope, MCE::Shared signal and wait methods
323 # must be called prior to leaving the block, due to lock
324 # being released upon return.
325
326 $meetings->signal();
327 }
328 else {
329 $first = $id;
330 $meetings->wait(); # ditto ^^
331 }
332 }
333 }
334
335 sub pall_mall
336 {
337 my $N = shift;
338 @creatures = map $color{$_}, @_;
339 my @threads;
340
341 print " ", join(" ", @_);
342 $meetings->set($N);
343
344 for (0 .. $#creatures) {
345 $met[$_] = $met_self[$_] = 0;
346 push @threads, MCE::Hobo->create(\&chameneos, $_);
347 }
348 for (@threads) {
349 $_->join();
350 }
351
352 $meetings->set(0);
353
354 for (0 .. $#creatures) {
355 print "\n$met[$_]", num2words($met_self[$_]);
356 $meetings->incrby($met[$_]);
357 }
358
359 print "\n", num2words($meetings->get()), "\n\n";
360 }
361
362 display_complements();
363
364 pall_mall($ARGV[0], qw(blue red yellow));
365 pall_mall($ARGV[0], qw(blue red yellow red yellow blue red yellow red blue));
366
367 printf "duration: %0.03f\n", time - $start;
368
370 The conditional locking feature is inspired by threads::shared.
371
373 Perl must have IO::FDPass for constructing a shared "condvar" or
374 "queue" while the shared-manager process is running. For platforms
375 where IO::FDPass isn't possible, construct "condvar" and "queue" before
376 other classes. On systems without "IO::FDPass", the manager process is
377 delayed until sharing other classes or started explicitly.
378
379 use MCE::Shared;
380
381 my $has_IO_FDPass = $INC{'IO/FDPass.pm'} ? 1 : 0;
382
383 my $cv = MCE::Shared->condvar();
384 my $que = MCE::Shared->queue();
385
386 MCE::Shared->start() unless $has_IO_FDPass;
387
388 Regarding mce_open, "IO::FDPass" is needed for constructing a shared-
389 handle from a non-shared handle not yet available inside the shared-
390 manager process. The workaround is to have the non-shared handle made
391 before the shared-manager is started. Passing a file by reference is
392 fine for the three STD* handles.
393
394 # The shared-manager knows of \*STDIN, \*STDOUT, \*STDERR.
395
396 mce_open my $shared_in, "<", \*STDIN; # ok
397 mce_open my $shared_out, ">>", \*STDOUT; # ok
398 mce_open my $shared_err, ">>", \*STDERR; # ok
399 mce_open my $shared_fh1, "<", "/path/to/sequence.fasta"; # ok
400 mce_open my $shared_fh2, ">>", "/path/to/results.log"; # ok
401
402 mce_open my $shared_fh, ">>", \*NON_SHARED_FH; # requires IO::FDPass
403
404 The IO::FDPass module is known to work reliably on most platforms.
405 Install 1.1 or later to rid of limitations described above.
406
407 perl -MIO::FDPass -le "print 'Cheers! Perl has IO::FDPass.'"
408
410 MCE, MCE::Hobo, MCE::Shared
411
413 Mario E. Roy, <marioeroy AT gmail DOT com>
414
415
416
417perl v5.32.0 2020-08-02 MCE::Shared::Condvar(3)