MCE::Sh...Condvar(3)
π MCE::Shared::Condvar β the condvar helper class, explained by your favorite Unix gremlin π
okay so before we even start β this is from User Contributed Perl Documentation, and the page you're looking at is MCE::Sh...Condvar(3). Yes the title got truncated with dots, no that's not a bug in my brain, that's just how the man page renders it. Anyway. Sit down, grab a strawberry, we're learning condvars. π
β¨ NAME β¨
MCE::Shared::Condvar β it's a Condvar helper class, and honestly it's giving "quiet luxury" because it does so much without making a scene. π
πΈ VERSION πΈ
This document describes MCE::Shared::Condvar version 1.893. Write it down, bestie, iykyk. π
π¦ DESCRIPTION π¦
So this little helper class is for MCE::Shared (the shared-memory bestie) and it hands you three things bundled up together: a Scalar, a Mutex, and the primitives for conditional locking. That's the whole vibe. Scalar + Mutex + condvar primitives = this class. Periodt. π
β SYNOPSIS β
Here's the whole menu, no cap:
use MCE::Shared;
my $cv = MCE::Shared->condvar( 0 );
# OO interface
$val = $cv->set( $val );
$val = $cv->get();
$len = $cv->len();
# conditional locking primitives
$cv->lock();
$cv->unlock();
$cv->broadcast(0.05); # delay before broadcasting
$cv->broadcast();
$cv->signal(0.05); # delay before signaling
$cv->signal();
$cv->timedwait(2.5);
$cv->wait();
# included, sugar methods without having to call set/get explicitly
$val = $cv->append( $string ); # $val .= $string
$val = $cv->decr(); # --$val
$val = $cv->decrby( $number ); # $val -= $number
$val = $cv->getdecr(); # $val--
$val = $cv->getincr(); # $val++
$val = $cv->incr(); # ++$val
$val = $cv->incrby( $number ); # $val += $number
$old = $cv->getset( $new ); # $o = $v, $v = $n, $o
notice how the get/set methods are all just... there, and then the locking primitives are their own little group, and then the sugar methods come with their equivalent one-liner in a comment so you literally cannot get confused. iconic behavior. π§
π EXAMPLE π
This one's a barrier synchronization demo β everyone waits at the barrier until all the workers show up, like a group chat where nobody types until everyone's online. Main character energy fr fr.
use MCE;
use MCE::Shared;
use Time::HiRes qw(usleep);
my $num_workers = 8;
my $count = MCE::Shared->condvar(0);
my $state = MCE::Shared->scalar('ready');
my $microsecs = ( $^O =~ /mswin|mingw|msys|cygwin/i ) ? 0 : 200;
# The lock is released upon entering ->broadcast, ->signal, ->timedwait,
# and ->wait. For performance reasons, the condition variable is *not*
# re-locked prior to exiting the call. Therefore, obtain the lock when
# synchronization is desired subsequently.
sub barrier_sync {
usleep($microsecs) while $state->get eq 'down';
$count->lock;
$state->set('up'), $count->incr;
if ($count->get == $num_workers) {
$count->decr, $state->set('down');
$count->broadcast;
}
else {
$count->wait while $state->get eq 'up';
$count->lock;
$state->set('ready') if $count->decr == 0;
$count->unlock;
}
}
sub user_func {
my $id = MCE->wid;
for (1 .. 400) {
MCE->print("$_: $id\n");
barrier_sync(); # made possible by MCE::Shared::Condvar
# MCE->sync(); # same thing via the MCE-Core API
}
}
my $mce = MCE->new(
max_workers => $num_workers,
user_func => \&user_func
)->run;
# Time taken from a 2.6 GHz machine running Mac OS X.
# threads::shared: 0.207s Perl threads
# forks::shared: 36.426s child processes
# MCE::Shared: 0.353s child processes
# MCE Sync: 0.062s child processes
π look at that timing table.forks::sharedsaid 36.426s.MCE Syncsaid 0.062s. that's not a race, that's a massacre. π
π API DOCUMENTATION π
π MCE::Shared::Condvar->new ( )
This one gets called by MCE::Shared itself when it's constructing a shared-condvar object. You're not really the one calling it β it's the behind-the-scenes bestie. π«Ά
π MCE::Shared->condvar ( [ value ] )
Constructs a brand new condition variable, slay. Its value defaults to 0 when value is not specified.
use MCE::Shared;
$cv = MCE::Shared->condvar( 100 );
$cv = MCE::Shared->condvar;
π set ( value )
Sets the value associated with the "cv" object. The new value is returned in scalar context.
$val = $cv->set( 10 );
$cv->set( 10 );
π get
Returns the value associated with the "cv" object. that's it. that's the whole method. lowkey perfect. β¨
$val = $cv->get;
π len
Returns the length of the value. It returns the undef value if the value is not defined. π«
$len = $var->len;
π lock
Attempts to grab the lock and waits if it's not available. Multiple calls to $cv->lock by the same process or thread is safe. The mutex will remain locked until $cv->unlock is called.
$cv->lock;
π unlock
Releases the lock. Also good to know: a held lock by an exiting process or thread is released automatically β the system's got your back. π
$cv->unlock;
π signal ( [ floating_seconds ] )
Releases a held lock on the variable. Then it unblocks one process or thread that's "wait"ing on that variable. And heads up β the variable is not locked upon return. π¨
Optionally, you can delay floating_seconds before signaling.
$count->signal;
$count->signal( 0.5 );
π broadcast ( [ floating_seconds ] )
"broadcast" works similarly to "signal" β it releases a held lock on the variable. The difference? It unblocks all the processes or threads blocked in a condition "wait" on the variable, rather than only one. Whole group chat, notified. π£ The variable is not locked upon return.
Optionally, delay floating_seconds before broadcasting.
$count->broadcast;
$count->broadcast( 0.5 );
π wait
Releases a held lock on the variable. Then it waits until another thread does a "signal" or "broadcast" for the same variable. Again: the variable is not locked upon return. π
$count->wait() while $state->get() eq "bar";
π timedwait ( floating_seconds )
Releases a held lock on the variable. Then it waits until another thread does a "signal" or "broadcast" for the same variable or if the timeout exceeds floating_seconds.
A false value is returned if the timeout is reached, and a true value otherwise. In either case, the variable is not locked upon return. π°οΈ
$count->timedwait( 10 ) while $state->get() eq "foo";
π§ SUGAR METHODS π§
This module is equipped with sugar methods so you don't have to call "set" and "get" explicitly. And in shared context the benefit is atomicity and a reduction in inter-process communication. It's giving efficient. β‘
The API resembles a subset of the Redis primitives at https://redis.io/commands#strings β except without the key argument. iykyk.
π¬ append ( value )
Appends a value at the end of the current value and returns its new length.
$len = $cv->append( "foo" );
π¬ decr
Decrements the value by one and returns its new value.
$num = $cv->decr;
π¬ decrby ( number )
Decrements the value by the given number and returns its new value.
$num = $cv->decrby( 2 );
π¬ getdecr
Decrements the value by one and returns its old value.
$old = $cv->getdecr;
π¬ getincr
Increments the value by one and returns its old value.
$old = $cv->getincr;
π¬ getset ( value )
Sets the value and returns its old value.
$old = $cv->getset( "baz" );
π¬ incr
Increments the value by one and returns its new value.
$num = $cv->incr;
π¬ incrby ( number )
Increments the value by the given number and returns its new value.
$num = $cv->incrby( 2 );
πΈ CHAMENEOS DEMONSTRATION πΈ
The MCE example (link: https://github.com/marioroy/mce-examples/tree/master/chameneos) is derived from the chameneos example (link: http://benchmarksgame.alioth.debian.org/u64q/program.php?test=chameneosredux&lang=perl&id=4) by Jonathan DePeri and Andrew Rodland. credit where credit's due, periodt. π
This is a big one so brace yourself, bestie:
use 5.010;
use strict;
use warnings;
use MCE::Hobo;
use MCE::Shared;
use Time::HiRes 'time';
die 'No argument given' if not @ARGV;
my $start = time;
my %color = ( blue => 1, red => 2, yellow => 4 );
my ( @colors, @complement );
@colors[values %color] = keys %color;
for my $triple (
[qw(blue blue blue)],
[qw(red red red)],
[qw(yellow yellow yellow)],
[qw(blue red yellow)],
[qw(blue yellow red)],
[qw(red blue yellow)],
[qw(red yellow blue)],
[qw(yellow red blue)],
[qw(yellow blue red)],
) {
$complement[ $color{$triple->[0]} | $color{$triple->[1]} ] =
$color{$triple->[2]};
}
my @numbers = qw(zero one two three four five six seven eight nine);
sub display_complements
{
for my $i (1, 2, 4) {
for my $j (1, 2, 4) {
print "$colors[$i] + $colors[$j] -> $colors[ $complement[$i | $j] ]\n";
}
}
print "\n";
}
sub num2words
{
join ' ', '', map $numbers[$_], split //, shift;
}
# Construct condvars and queues first before other shared objects or in
# any order when IO::FDPass is installed, used by MCE::Shared::Server.
my $meetings = MCE::Shared->condvar();
tie my @creatures, 'MCE::Shared';
tie my $first, 'MCE::Shared', undef;
tie my @met, 'MCE::Shared';
tie my @met_self, 'MCE::Shared';
sub chameneos
{
my $id = shift;
while (1) {
$meetings->lock();
unless ($meetings->get()) {
$meetings->unlock();
last;
}
if (defined $first) {
$creatures[$first] = $creatures[$id] =
$complement[$creatures[$first] | $creatures[$id]];
$met_self[$first]++ if ($first == $id);
$met[$first]++; $met[$id]++;
$meetings->decr();
$first = undef;
# Unlike threads::shared (condvar) which retains the lock
# while in the scope, MCE::Shared signal and wait methods
# must be called prior to leaving the block, due to lock
# being released upon return.
$meetings->signal();
}
else {
$first = $id;
$meetings->wait(); # ditto ^^
}
}
}
sub pall_mall
{
my $N = shift;
@creatures = map $color{$_}, @_;
my @threads;
print " ", join(" ", @_);
$meetings->set($N);
for (0 .. $#creatures) {
$met[$_] = $met_self[$_] = 0;
push @threads, MCE::Hobo->create(\&chameneos, $_);
}
for (@threads) {
$_->join();
}
$meetings->set(0);
for (0 .. $#creatures) {
print "\n$met[$_]", num2words($met_self[$_]);
$meetings->incrby($met[$_]);
}
print "\n", num2words($meetings->get()), "\n\n";
}
display_complements();
pall_mall($ARGV[0], qw(blue red yellow));
pall_mall($ARGV[0], qw(blue red yellow red yellow blue red yellow red blue));
printf "duration: %0.03f\n", time - $start;
π«Ά gentle reminder that inchameneos, the$meetings->signal()and$meetings->wait()calls must happen before leaving the block β because the lock gets released upon return, unlikethreads::sharedwhich keeps the lock while in scope. that comment is there for a reason, don't skip it. π
π CREDITS π
The conditional locking feature is inspired by threads::shared. respectful nods all around. πββοΈ
(part 1 done β but we're not signing off, there's more coming bestie π)
π LIMITATIONS π
okay so bestie, listen up π« β this is the part where we talk about the little gotchas.
Perl must have IO::FDPass for constructing a shared "condvar" or "queue" while the shared-manager process is running. For platforms where IO::FDPass isn't possible, construct "condvar" and "queue" before other classes. On systems without "IO::FDPass", the manager process is delayed until sharing other classes or started explicitly.
use MCE::Shared;
my $has_IO_FDPass = $INC{'IO/FDPass.pm'} ? 1 : 0;
my $cv = MCE::Shared->condvar();
my $que = MCE::Shared->queue();
MCE::Shared->start() unless $has_IO_FDPass;
Regarding mce_open, "IO::FDPass" is needed for constructing a shared-handle from a non-shared handle not yet available inside the shared-manager process. The workaround is to have the non-shared handle made before the shared-manager is started. Passing a file by reference is fine for the three STD* handles.
# The shared-manager knows of \*STDIN, \*STDOUT, \*STDERR.
mce_open my $shared_in, "<", \*STDIN; # ok
mce_open my $shared_out, ">>", \*STDOUT; # ok
mce_open my $shared_err, ">>", \*STDERR; # ok
mce_open my $shared_fh1, "<", "/path/to/sequence.fasta"; # ok
mce_open my $shared_fh2, ">>", "/path/to/results.log"; # ok
mce_open my $shared_fh, ">>", \*NON_SHARED_FH; # requires IO::FDPass
The IO::FDPass module is known to work reliably on most platforms. Install 1.1 or later to rid of limitations described above.
perl -MIO::FDPass -le "print 'Cheers! Perl has IO::FDPass.'"
π INDEX π
MCE, MCE::Hobo, MCE::Shared
π AUTHOR π
Mario E. Roy, <marioeroy AT gmail DOT com>
and that's the whole vibe, bestie β page source/version is perl v5.40.0, dated 2024-09-10, and honestly? iconic. periodt. π
β¨
π¦ where this page comes from
- epel 10: perl-MCE-Shared 1.893-1.el10_0 Β· GPL-1.0-or-later OR Artistic-1.0-Perl
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms