Meet the flocker

B

Bigus

Hi

I am attempting to flock for the very first time - after some surfing
around, the following 2 methods sound like the most viable/robust I can
find:

==== METHOD 1 ====
use strict;
use warnings;

my $file = "flocktest.txt";
open FH, "+<$file" or die "Cannot open $file: $!";
flock FH, 2; # lock file
seek FH, 0, 0; # back to beginning of file
my @lines = <FH>; # get current content
### do stuff to content ###
seek FH, 0, 0; # back to beginning of file
truncate FH, 0; # empty file
print FH @lines; # print new content to file
close FH;


==== METHOD 2 ====
use strict;
use warnings;

my $file = 'flocktest.txt';
my $lockfile = $file.".lock"; # create dummy file to lock
open(LOCK, ">$lockfile") or die "Can't open $lockfile($!)";
flock(LOCK, 2); # lock file
open(FH, $file) or die "Can't open $file ($!)";
my @lines = <FH>; # get current content
close FH;
### do stuff to content ###
open(FH, ">$file") or die "Can't open $file ($!)";
print FH @lines; # print new content to file
close FH;
close LOCK;

==== END SAMPLE CODE =====

Are there any issues with these methods I should be aware of? Which one of
these, or other, methods would you be inclined to choose / think is better?

So far, I'm leaning towards the 2nd one since method 1 uses the seek &
truncate functions which I have not used before. However, only having to
open a file once in read/write mode does sound less "messy" somehow.

One other general thing - if another instance of the above scripts tried to
access the file while it was locked would it wait until it's free or would I
have to include a retry loop in the script?

Thanks
Bigus
 
C

ctcgag

Bigus said:
Hi

I am attempting to flock for the very first time - after some surfing
around, the following 2 methods sound like the most viable/robust I can
find:

==== METHOD 1 ====
use strict;
use warnings;

my $file = "flocktest.txt";
open FH, "+<$file" or die "Cannot open $file: $!";
flock FH, 2; # lock file

flock can return false. You should check the results. Use an "or die $!"
if nothing more appropriate springs to mind.
Also, you should probably import and use LOCK_EX rather than hard-coding 2.
Are there any issues with these methods I should be aware of? Which one
of these, or other, methods would you be inclined to choose / think is
better?

The first one.
So far, I'm leaning towards the 2nd one since method 1 uses the seek &
truncate functions which I have not used before. However, only having to
open a file once in read/write mode does sound less "messy" somehow.

I often take something like the second route out of laziness to learn
seek. But that's not something I'm proud of. Since you've gone to the
trouble of making the first example, you may as well use it. :)

One other general thing - if another instance of the above scripts tried
to access the file while it was locked would it wait until it's free or
would I have to include a retry loop in the script?

It would wait at the flock until it became free. (Unless of course one
version uses method 1, and another version uses method 2, then they would
gladly stomp on each other.)

Xho
 
B

Bigus

flock can return false. You should check the results. Use an "or die $!"
if nothing more appropriate springs to mind.
Also, you should probably import and use LOCK_EX rather than hard-coding 2.

The first one.


I often take something like the second route out of laziness to learn
seek. But that's not something I'm proud of. Since you've gone to the
trouble of making the first example, you may as well use it. :)



It would wait at the flock until it became free. (Unless of course one
version uses method 1, and another version uses method 2, then they would
gladly stomp on each other.)

thanks for the advice. I've been reading up on seek etc and it doesn't seem
too bad :)

Bigus
 
B

Ben Morrow

Quoth "Bigus said:
I am attempting to flock for the very first time - after some surfing
around, the following 2 methods sound like the most viable/robust I can
find:

==== METHOD 1 ====
use strict;
use warnings;

use Fcntl qw/:flock :seek/;
my $file = "flocktest.txt";
open FH, "+<$file" or die "Cannot open $file: $!";
flock FH, 2; # lock file

....or die...;

Always use the constants in Fcntl.pm for the flags to flock and seek.
Hardcoding numbers is both unclear and unportable.
seek FH, 0, 0; # back to beginning of file

No need, you're already there.
my @lines = <FH>; # get current content
### do stuff to content ###
seek FH, 0, 0; # back to beginning of file
truncate FH, 0; # empty file
print FH @lines; # print new content to file
close FH;


==== METHOD 2 ====
use strict;
use warnings;

my $file = 'flocktest.txt';
my $lockfile = $file.".lock"; # create dummy file to lock
open(LOCK, ">$lockfile") or die "Can't open $lockfile($!)";
flock(LOCK, 2); # lock file

This is slightly pointless: about the only reason for creating a
separate lockfile but then using flock on it is when the resource you're
locking is not a regular file, so you can't flock it directly.

A more important consideration can be when the file might be accessed
over the network (NFS, AFS etc.) by several machines at once; in this
case flock(2) won't work and you should simply use the *existence* of a
lockfile as the lock:

sysopen my $LOCK, O_RDWR | O_CREAT | O_EXCL, 0600
or die "can't open lockfile: $!";

On some NFS systems (NFSv2? I think it's fixed in NFSv3) O_EXCL isn't
implemented, so you have to use this instead:

use Sys::Hostname;

# NFSv2-safe file locking
# taken from open(2)
# returns the name of the lockfile for success, false for failure
# will *not* retry on failure
# to unlock, unlink the returned file

sub nfssafe_flock {
my $file = shift;
my $lockfile = "$file.lock";
my $uniqfile = "$lock." . hostname . ".$$";

open my $UNIQ, '>', $uniq
or die "can't create uniq file $uniq: $!";

if (
# link sometimes randomly fails on NFS systems, even when the
# link was created

not link $uniqfile, $lockfile or
(stat $UNIQ)[3] == 2
) {
unlink $uniqfile;
return $lockfile;
}

return;
}

If you need network-safe locking you could also investigate fcntl-based
locks, but they are lost when you fork so may be harder to deal with.

Note that both these methods have the disadvantage that you *must* be
sure to unlock by unlinking the lockfile, unlike a flock lock which the
system will remove when you close the file. This means you probably also
want to test how old the lockfile is, and delete it if it is much older
than the time a process should hold the lock for.

I presume that you understand all these locks are advisory, so if you
have two processes locking the same file with different methods there
you have no protection at all?
open(FH, $file) or die "Can't open $file ($!)";
my @lines = <FH>; # get current content
close FH;
### do stuff to content ###
open(FH, ">$file") or die "Can't open $file ($!)";
print FH @lines; # print new content to file
close FH;
close LOCK;

==== END SAMPLE CODE =====

Are there any issues with these methods I should be aware of? Which one of
these, or other, methods would you be inclined to choose / think is better?

The former, unless it won't work for any of the reasons above.
So far, I'm leaning towards the 2nd one since method 1 uses the seek &
truncate functions which I have not used before.

You seem to have figured out how they work, though :)
One other general thing - if another instance of the above scripts tried to
access the file while it was locked would it wait until it's free or would I
have to include a retry loop in the script?

With either of your methods (locking be calling flock), the script will
block at the flock call until it can make the lock. If you want to
timeout you will have to set an alarm, or pass the LOCK_NB flag to flock
and retry/timeout yourself.

The two methods I showed (locking based on the existence only of a file)
will *not* block, and you would have to retry yourself.

Ben
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,156
Messages
2,570,878
Members
47,408
Latest member
AlenaRay88

Latest Threads

Top