--- name: lock signature: 'lock THING' since: 5.005 status: documented categories: ["Misc"] --- ```{index} single: lock; Perl built-in ``` *[Misc](../perlfunc-by-category)* # 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 ```perl 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: - `lock` inside 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: ```perl { 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: ```perl 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: ```perl 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: ```perl 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: ```perl # no `use threads::shared` here lock $x; # does nothing, does not warn ``` ## Edge cases - **Unshared variable**: `lock` on a variable that was not declared `:shared` and 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 on `lock` to catch the "forgot to share" bug. - **Reference vs aggregate**: `lock \%h` and `lock %h` both lock `%h`. `lock` dereferences 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_guard` patterns from other languages do not apply — `lock` is not a value you store. - **Weak-keyword ambiguity**: a `sub lock` declared or imported earlier wins over the built-in. Use `CORE::lock` to disambiguate. - **Not cross-process**: `lock` coordinates threads within one interpreter process. For file-level locking across processes use [`flock`](flock); for advisory filesystem locks they are separate mechanisms with no interaction. ## Differences from upstream - pperl does not implement interpreter threads. The `lock` opcode is recognised by the parser but has no runtime effect — it behaves exactly as upstream `lock` does under a build without `threads::shared`: a silent no-op returning its argument. Code that uses `lock` defensively (as the upstream documentation recommends for single-threaded compatibility) runs unchanged. Code that depends on `lock` for actual synchronisation will not be synchronised under pperl. - The companion distribution `threads::shared` is not available under pperl; `use threads::shared` fails 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`](flock) — advisory lock on a filehandle, coordinating between processes rather than threads - [`wait`](wait) — reap a child process; the process-level equivalent of waiting for another unit of execution - [`fork`](fork) — the other concurrency primitive in core Perl, with no shared memory by default - [`our`](our) — declare a package-scoped variable; commonly paired with `:shared` in threaded code - [`local`](local) — dynamic-scope binding, sometimes confused with `lock` because both are scope-bounded and both restore on block exit