- Reasons for Locking Files
- Introducing flock()
- Semaphore Files
- Locking Demonstrated
- Caveats & Modules
Introducing flock()
File locking in Perl is done with the flock() command, which in turn locks the file with the system level flock(2) command. The format of the flock() command is:
flock FILEHANDLE, OPERATION
The first argument is the filehandle that we want to lock. The OPERATION argument is a numeric value of 1, 2, 4 or 8. These mean; shared lock, exclusive lock, non-blocking lock and unlock, respectively. We don't, however, want to pass a literal numeric to the flock() method. Instead, we want to use the Fcntl module, which comes with Perl. The Fcntl module will import your systems values for each of these. The values will be imported as the LOCK_SH, LOCK_EX, LOCK_NB and LOCK_UN constants. They are imported by adding the following to a program:
use Fcntl qw(:flock);
Locking itself is called an 'advisory lock', because it doesn't really stop other programs from accessing a file. But, if you make sure you use flock() in all your programs (when file locking is needed), the programs will see the advisory and do the right thing.
Now, let's take a look our counter program after some basic file locking is added.
01: #!/usr/bin/perl -w 02: use strict; 03: use Fcntl qw(:flock); 04: my $COUNTER = 'count.dat'; 05: print qq{Content-Type: text/html\n\n}; 06: open(DATA, $COUNTER) or die "Can't open $COUNTER ($!)"; 07: flock(DATA, LOCK_EX); 08: my $count = <DATA>; 09: flock(DATA, LOCK_UN); 10: close DATA; 11: $count++; 12: open(DATA, "+<$COUNTER") or die "Can't open $COUNTER ($!)"; 13: flock(DATA, LOCK_EX); 14: print DATA $count; 15: flock(DATA, LOCK_UN); 16: close DATA; 17: print qq{You are number $count!};
Now we are all set, right? Well, no. We have added some file locking, so we are in better shape, but we still have race conditions in the code. Let's take a look at the new flow of the program.
- Open file for reading
- Lock the file
- Read data from file
- Unlock the file
- Close file
- Open file for update
- Lock the file
- Write updated data to file
- Unlock the file
- Close file
We still have a few race conditions. Between steps 1 and 2, 4 and 5, 6 and 7 as well as 9 and 10. Anywhere between those steps, another process could come along (even using flock()) and read or write inaccurate data. Although adding locking has reduced the chances of this happening, you can see it is still there.