1. Perl
  2. builtin functions
  3. here

flock function - exclusive control of files

You can use the flock function for exclusive control of files. This time, we will actually use the flock function to read and write to the file in order. (Generally, it is called exclusive processing.)

The following is an example that makes you realize that you are actually reading and writing in order. Save it as example_20080814.pl.

use strict;
use warnings;
use Fcntl qw /:DEFAULT :flock :seek/;

# Be careful as it will be overwritten
my $file = "data_20080814.txt";

# Open in read/write mode (not very secure)
# If there is no file with O_CREAT, create it.
sysopen(my $fh, $file, O_RDWR | O_CREAT)
  or die "Cannot open $file:$!";

# Exclusive lock (exclusive lock because it reads and writes)
flock($fh, LOCK_EX)
  or die "Cannot lock $file:$!";

my $cnt;
$cnt = <$fh>;
if (! Defined $cnt && $!) {Die "Cannot read $file:$!";}

$cnt ++;

# Move the file pointer to the beginning
seek ($fh, 0, SEEK_SET)
  or die "Cannot seek $file:$!";

# write in.
print {$fh} $cnt
  or die "Cannot Write $file:$!";

# Truncate the file to write size
truncate ($fh, length $cnt);

# The file lock is released at the time of close.
close $fh
  or die "Cannot close $file:$!";

print "Current count: $cnt\n";

A script that repeats the above script at random time intervals. Save it as write_loop_20080814.pl.

use strict;
use warnings;
use FindBin;
use Time::HiRes 'usleep';

my $script = "$FindBin::Bin/example_20080814.pl";

while (1) {
  # Repeat the above script.
  system("perl $script");

  # Random number less than 100,000 (1 second)
  my $random = rand 1000_000;
  usleep $random;
}

(Reference) FindBin, Time::HiRes

The script is called repeatedly using while statement.

Launch about 3 command prompts and execute write_loop_20080814.pl at all prompts. Read data_20080814.txt, count up and write back in 3 parallels.

If the order is not followed, the written content will be duplicated or the file will be corrupted. If the order is followed, it should count up correctly.

Kill all three processes with Ctrl + C and make sure there are no duplicate counts. If the counts do not overlap, you can see that you are reading and writing in order. (Flock doesn't work on some operating systems (such as Winows98), but it will work on most operating systems.)

Code explanation

Fcntl - Read constants for file locking

Fcntl module allows you to use the file locking constants used by flock.

use Fcntl qw /:DEFAULT :flock :seek/;

Use use Fcntl qw(:flock) to read the constants used by flock. There are three constants read by the:flock tag:LOCK_SH, LOCK_EX, and LOCK_UN.

LOCK_SH Shared lock
LOCK_EX Exclusive lock
LOCK_NB Non-block mode

LOCK_NB is used with LOCK_SH LOCK_EX to return control to the program without waiting for the lock to be released if the lock has already been applied.

As a side note:DEFAULT uses his SEEK_SET in the seek function to use O_RDWR and O_CREAT in sysopen function. Therefore, it is specified.

(2) If the file does not exist, create it and open it in read/write mode

# Open in read/write mode (not very secure)
# If there is no file with O_CREAT, create it.
sysopen(my $fh, $file, O_RDWR | O_CREAT)
  or die "Cannot open $file:$!";

In sysopen, specify O_RDWR and O_CREAT for the open flags.

(3) Exclusive lock is performed with the flock function

# Exclusive lock (exclusive lock because it reads and writes)
flock($fh, LOCK_EX)
  or die "Cannot lock $file:$!";

This time, we will use an exclusive lock (write lock) because we will read the data, count it up, and write it back. To perform an exclusive lock, specify LOCK_EX in the second argument of the flock function. If you just want to read, specify LOCK_SH.

Note that the first argument is an open filehandle, not a file. When all processes access a file, they can access the file in order by keeping the promise of acquiring the lock with flock.

(4) Count up and write

my $cnt;
$cnt = <$fh>;
if (! Defined $cnt && $!) {Die "Cannot read $file:$!";}

$cnt ++;

# Move the file pointer to the beginning
seek ($fh, 0, SEEK_SET)
  or die "Cannot seek $file:$!";

# write in.
print {$fh} $cnt
  or die "Cannot Write $file:$!";

# Truncate the file to write size
truncate ($fh, length $cnt);

Read from the file and count up if there are no errors. Since the file pointer is advanced at the time of reading, use the seek function to return it to the beginning. Write with the print function and truncate to the written size with the truncate function. (This time, the numbers are just going up, so there is no need for truncate.)

(5) Unlock

# The file lock is released at the time of close.
close $fh
  or die "Cannot close $file:$!";

Calling the close function unlocks the file.

A brief explanation of the loop script

use FindBin;
use Time::HiRes 'usleep';

my $script = "$FindBin::Bin/example_20080814.pl";

while (1) {
  # Repeat the above script.
  system("perl $script");

  # Random number less than 100,000 (1 second)
  my $random = rand 1000_000;
  usleep $random;
}

FindBin is a module that allows you to know the path of a script. The system function is a function that can execute a specified command. The rand function is a function for generating a random number. The usleep function is a function that stops the script for a certain period of time. Unlike sleep function, you can specify the number of seconds in microseconds.

The usleep function is given a random value of 1 second or less, and when this process is run in parallel, it is devised so that a file conflict occurs.

Related Informatrion