I/O

flock#

Place an advisory lock on an open file.

flock is Perl’s portable file-locking primitive. It calls the underlying flock(2), fcntl(2), or lockf(3) — whichever the platform provides — on FILEHANDLE and blocks by default until the requested lock can be granted. Locks cover the whole file, never a byte range, and are advisory: they coordinate only among processes that also call flock on the same file. Code that ignores the lock can still read or modify the contents.

Synopsis#

flock FILEHANDLE, OPERATION

What you get back#

True on success, false on failure (with $! set). Always check the return value — with LOCK_NB a failure is the normal “someone else holds it” signal, not an error:

flock($fh, LOCK_EX | LOCK_NB)
    or die "already locked: $!";

On a platform that implements none of flock(2), fcntl(2) locking, or lockf(3), flock raises a fatal error rather than returning false. On Linux (pperl’s only supported platform) this never happens.

OPERATION values#

OPERATION is one of the mutually exclusive request types, optionally bitwise-or’ed with the non-blocking flag. Traditional numeric values are 1, 2, 4, 8 but portable code imports the symbolic names from Fcntl, either individually or as a group via the :flock tag:

use Fcntl qw(:flock);
  • LOCK_SH — shared lock. Multiple processes may hold a shared lock on the same file simultaneously. Used for readers.

  • LOCK_EX — exclusive lock. Only one process may hold it; no other process may hold any lock on the file at the same time. Used for writers.

  • LOCK_UN — release whatever lock this filehandle currently holds.

  • LOCK_NB — bitwise-or with LOCK_SH or LOCK_EX to make the call non-blocking; flock returns false immediately if the lock cannot be granted, instead of waiting.

An flock call with a new mode atomically replaces an existing lock on the same filehandle (for example, upgrading LOCK_SH to LOCK_EX).

Global state it touches#

  • Sets $! on failure.

  • Flushes FILEHANDLE’s output buffer before locking or unlocking, so locked-region data hits the OS before another process can see the lock state change.

Examples#

Exclusive lock for a writer, blocking until granted:

use Fcntl qw(:flock);
open my $fh, ">>", "counter.log" or die $!;
flock($fh, LOCK_EX)               or die "lock failed: $!";
print $fh "tick\n";
flock($fh, LOCK_UN)               or die "unlock failed: $!";
close $fh;

Non-blocking attempt — skip the work if another process is already running:

use Fcntl qw(:flock);
open my $fh, ">", "/var/run/myjob.lock" or die $!;
flock($fh, LOCK_EX | LOCK_NB)
    or do { warn "another instance running\n"; exit 0 };
# ... work ...
# lock auto-released when $fh is closed / program exits

Classic mailbox appender. Note the seek to SEEK_END after locking — on very old systems >> append mode is not atomic, so the write position must be re-established once the lock is held:

use Fcntl qw(:flock SEEK_END);

sub lock_mbox   { flock($_[0], LOCK_EX) or die "lock: $!" }
sub unlock_mbox { flock($_[0], LOCK_UN) or die "unlock: $!" }

open my $mbox, ">>", "/var/mail/$ENV{USER}" or die $!;
lock_mbox($mbox);
seek($mbox, 0, SEEK_END) or die "seek: $!";
print $mbox $msg, "\n\n";
unlock_mbox($mbox);
close $mbox;

Shared read lock while loading a config file, letting other readers proceed concurrently:

use Fcntl qw(:flock);
open my $fh, "<", "config.dat" or die $!;
flock($fh, LOCK_SH)             or die "lock: $!";
my @lines = <$fh>;
close $fh;                      # releases the lock

Upgrading a shared lock to exclusive — the second flock atomically swaps modes:

flock($fh, LOCK_SH) or die $!;
# ... inspect contents ...
flock($fh, LOCK_EX) or die $!;  # now we intend to write

Edge cases#

  • Locks are per-filehandle, not per-file. Two independent opens of the same path each have their own lock slot. This is why locking your own file twice from the same process through different handles can deadlock or behave surprisingly on fcntl(2)-based systems.

  • Closing the filehandle releases the lock. There is no separate “unlock” step required at program exit; LOCK_UN is useful only when you want to keep the handle open after.

  • LOCK_NB failure is not a croak. It returns false and sets $! to EWOULDBLOCK. Treat it as an expected outcome, not an error.

  • Advisory only. A process that never calls flock sees no locks. Use flock as a cooperation protocol among your own programs, not as a security mechanism.

  • Whole files only. There is no byte-range locking through flock. For record-level locking use fcntl with F_SETLK / F_SETLKW.

  • NFS and other network filesystems. Some older NFS implementations cannot honour flock across hosts; the call may silently become a no-op or return success without providing exclusion. Use fcntl locking if network coordination matters.

  • Fork inheritance. When the platform uses real flock(2) (Linux does), locks are inherited across fork; the child shares them with the parent. On fcntl(2)-emulated platforms locks do not survive the fork, which makes writing locking servers considerably harder.

  • Buffer flushing. Perl flushes FILEHANDLE’s output buffer on every flock call. Code that mixes buffered print with locking does not need an explicit flush before the lock boundary.

  • Interaction with dup-style opens. An open of the form open(my $A, ">>&=", $B) shares the underlying file descriptor with $B, so flock($A) and flock($B) act on the same lock slot. A >>& dup gives each handle its own descriptor and its own lock slot.

Differences from upstream#

Fully compatible with upstream Perl 5.42. pperl runs on Linux only, where flock(2) is always available, so the “platform has no locking primitive — fatal error” path documented upstream never triggers in practice.

See also#

  • fcntl — byte-range and advisory locking with finer control; use it when whole-file granularity is too coarse or when network filesystems require it

  • open — produces the filehandle flock operates on; remember that the required open mode (< vs >) matters for fcntl-emulated LOCK_SH / LOCK_EX

  • close — releases any flock still held on the handle

  • seek — pair with flock when appending to files that other processes may have extended while you waited for the lock

  • fork — lock inheritance across child processes depends on the underlying locking primitive

  • Fcntl — source of the LOCK_SH, LOCK_EX, LOCK_UN, LOCK_NB constants and the :flock import tag