Question

Are these three versions equivalent?

#!/usr/bin/env perl
use warnings;
use strict;
use 5.10.0;
use Fcntl qw(:flock :seek);
my $fh;

sysopen $fh, $file, O_WRONLY | O_CREAT | O_TRUNC or die $!;
flock $fh, LOCK_EX or die $!;
say $fh 'something';
close $fh;

sysopen $fh, $file, O_WRONLY | O_CREAT or die $!;
flock $fh, LOCK_EX or die $!;
seek $fh, 0, SEEK_SET or die $!;
truncate $fh, 0 or die $!;
say $fh 'something';
close $fh;

sysopen $fh, $file, O_WRONLY | O_CREAT or die $!;
flock $fh, LOCK_EX or die $!;
truncate $fh, 0 or die $!;
say $fh 'something';
close $fh;
Was it helpful?

Solution

It seems you want to open a file, lock it, and then truncate it without any possibility that another process that is competing for that lock might see a truncated file. This poses several restrictions:

  • A lock has to be obtained before truncation. This rules out the first solution, as it does the truncation before the lock can be acquired.
  • The file handle cannot be reopened with another mode, or the lock would be lost. This means that the truncation can't be done by reopening.

Thus the ideal solution has the following steps:

  1. The file is opened without truncating, e.g. with sysopen, or the open modes >>, +<. The sysopen option O_TRUNC or the open modes >, +> etc. must not be used here.
  2. A lock is acquired.
  3. Truncation is performed. As open would create a new filehandle, the truncate function must be used here.
  4. If the file was opened in append mode, it is neccessary to seek to the beginning of the file.

Your first solution truncates before aquiring the lock, and can therefore be ruled out.

Your second and third solution are both possible, although the seek is unneccessary.

Other solutions could use regular open:

open my $fh, "+<", $file;  # Achtung: fails if the file doesn't exist
flock $fh, LOCK_EX;
truncate $fh;

# or:
open my $fh, ">>", $file;
flock $fh, LOCK_EX;
truncate $fh;
seek 0, 0;

In many cases, it may be preferable to use an unrelated file as lock, thus avoiding difficulties with reopening. This would look like:

# adapted from perldoc -f flock

sub lock {
  my $file = shift;
  open my $fh, "<", ".$file.lock";
  flock $fh, LOCK_EX;
  return $fh;
}

sub unlock {
  my $fh = shift;
  flock $fh, LOCK_UN;
}

my $lock = lock($file);
open my $fh, ">", $file;
...
unlock($lock);

Of course, all related processes have to honour this interface and create an appropriate lockfile.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top