lock#
Place an advisory lock on a shared variable, array, hash, or subroutine until the lock goes out of scope.
lock is the synchronisation primitive of Perl’s interpreter-threads
model (use threads; use threads::shared). When THING is a variable
that has been declared :shared — or a reference to a shared
aggregate — lock acquires an advisory mutex on that datum for the
rest of the enclosing block. When the block exits, the lock is
released. Any other thread calling lock on the same datum blocks
until the holder releases it.
The lock is advisory: it stops other threads that also call lock
on the same variable, nothing else. A thread that just reads or
writes the shared variable without locking it is not blocked and not
serialised. lock is a convention, not a barrier.
Synopsis#
lock $shared_scalar
lock @shared_array
lock %shared_hash
lock &shared_sub
lock $ref_to_shared_aggregate
What you get back#
The argument itself:
For a scalar, the scalar value.
For an array, hash, or subroutine, a reference to it.
The return value is rarely used. lock is called for its side effect
(acquiring the mutex), not for its value.
Lock is scoped, not statement-scoped#
The lock is released when the innermost enclosing block exits, not
when the lock statement finishes. Two consequences:
lockinside a{ ... }block serialises only that block’s duration. Exiting the block — by fall-through,return,last,die, whatever — releases the lock.You cannot release a lock early. There is no
unlock. If you need a shorter critical section, introduce a tighter{ ... }block:{ lock %shared_hash; $shared_hash{$key} = $value; } # lock released here do_unlocked_work();
Weak keyword#
lock is a weak keyword. If a subroutine named lock is in scope
at the point of the call (declared or imported before the call site),
that subroutine is called instead of the built-in. This makes lock
safe to use as a method or function name in code that does not use
threads. To force the built-in when a same-named sub is in scope,
call it via its package: CORE::lock($thing).
Examples#
Serialise a hash update between threads:
use threads;
use threads::shared;
my %counts :shared;
sub bump {
my $key = shift;
lock %counts;
$counts{$key}++;
}
Lock an aggregate through a reference. lock follows one level of
reference to find the datum to lock:
my @queue :shared;
my $queue_ref = \@queue;
sub enqueue {
my $item = shift;
lock $queue_ref; # locks @queue
push @queue, $item;
}
Narrow the critical section with an inner block so unrelated work runs unlocked:
my $result;
{
lock %cache;
$result = $cache{$key};
}
expensive_post_processing($result); # runs without the lock
Without use threads::shared, lock is a no-op. This makes it
harmless to leave in code that may run single-threaded:
# no `use threads::shared` here
lock $x; # does nothing, does not warn
Edge cases#
Unshared variable:
lockon a variable that was not declared:sharedand is not a reference to shared data does nothing useful. Under upstream this may croak in some cases; under others it is silently a no-op. Do not rely onlockto catch the “forgot to share” bug.Reference vs aggregate:
lock \%handlock %hboth lock%h.lockdereferences one level to find the shared datum.Recursive locking within one thread: the same thread may re-acquire a lock it already holds; the mutex is recursive per thread. Another thread still blocks.
No
unlock: releasing before block exit requires restructuring into a tighter scope.undef $lock_guardpatterns from other languages do not apply —lockis not a value you store.Weak-keyword ambiguity: a
sub lockdeclared or imported earlier wins over the built-in. UseCORE::lockto disambiguate.Not cross-process:
lockcoordinates threads within one interpreter process. For file-level locking across processes useflock; for advisory filesystem locks they are separate mechanisms with no interaction.
Differences from upstream#
pperl does not implement interpreter threads. The
lockopcode is recognised by the parser but has no runtime effect — it behaves exactly as upstreamlockdoes under a build withoutthreads::shared: a silent no-op returning its argument. Code that useslockdefensively (as the upstream documentation recommends for single-threaded compatibility) runs unchanged. Code that depends onlockfor actual synchronisation will not be synchronised under pperl.The companion distribution
threads::sharedis not available under pperl;use threads::sharedfails at compile time. Parallelism in pperl is exposed through JIT auto-parallelisation (Rayon) on specific loop shapes, not through user-visible ithreads.
See also#
flock— advisory lock on a filehandle, coordinating between processes rather than threadswait— reap a child process; the process-level equivalent of waiting for another unit of executionfork— the other concurrency primitive in core Perl, with no shared memory by defaultour— declare a package-scoped variable; commonly paired with:sharedin threaded codelocal— dynamic-scope binding, sometimes confused withlockbecause both are scope-bounded and both restore on block exit