Re: I can't flock, it returns 0, any ideas.

From: Ben Morrow (usenet_at_morrow.me.uk)
Date: 12/01/03


Date: Mon, 1 Dec 2003 17:22:53 +0000 (UTC)


"Guy" <someone@somewhere.nb.ca> wrote:
> I can't flock, it returns 0, any ideas.

man flock

| RETURN VALUE
| On success, zero is returned. On error, -1 is returned and
| errno is set appropriately.

This is what the syscall returns. Perl maps these values (0 and -1) to
"0 but true" and undef respectively. The upshot of this is that if you
say 'if (flock(...) == 0)' it will always be true: "0 but true" numifies
to 0, as does undef. The correct test is 'if (flock(...))': undef is
false, and "0 but true" is true (that's why Perl uses that special
string rather than the number 0).

> I wanted to run this script to see if it would affect my other perl script
> whi is trying to access the same file.

Note that flock() locks are advisory, so it won't affect your other
script unless that also uses flock. The rules governing locks are:

1. Locks don't affect anything except other locks: they don't stop
   anyone opening, reading, writing, deleting, etc. the file.

2. You can only hold a lock on an open file. (The details of what
   happens when you have several handles open on the same file depend
   on the underlying system: don't go there. :)

3. If someone holds a LOCK_EX lock, all attempts to get a lock will
   fail. (LOCK_EX means 'exclusive lock'.)

4. If someone holds a LOCK_SH lock, attempts to get a LOCK_EX lock
   will fail but attempts to get another LOCK_SH lock will
   succeed. (LOCK_SH means 'shared lock'.)

5. If you can't get the lock you are trying for, then if you specify
   LOCK_NB (use | rather than +, i.e. 'flock $FH, LOCK_SH | LOCK_NB)
   the flock call will fail, and if you don't flock will just not
   return until you can.

> From what I read, I'm assuming that when you close a file, it also unlocks
> it, so I'm guessing that you don't really have to unlock it before closing
> it.

That's right. Most uses of LOCK_UN are wrong.

> I tried 1,2, and 8, but it doesn't appear to lock the file, and the flock
> function returns 0.

Don't do this. Use the constants from Fcntl, both for readability and
portability:

> #!/usr/local/bin/perl -w
>
> use CGI qw(:standard);

use Fcntl qw/:flock/;

> no warnings;

Why? Using '-w' and then 'no warnings' is particularly perverse... You
should have both these lines:
  use warnings;
  use strict;
and then you don't need '-w'.

> print header();
>
> $fd="../data";

  my $fd = "../data";

Insert 'my' similarly for all the variables 'strict' complains about.

> $lockfil="$fd/lock.txt";
>
> $temp=$ENV{'QUERY_STRING'};
> @pairs=split(/&/,$temp);
>
> foreach $item(@pairs)
> {
> ($key,$content)=split(/=/,$item,2);$CONFILTERED=~tr/+/
> /;$CONFILTERED=~s/%(..)/pack("c",hex($1))/ge;
> if($key eq "f"){$f=$content;}
> if($key eq "l"){$l=$content;}
> }

NO. Since you're using CGI.pm, *USE* it. Read the docs if you don't
know how: perldoc CGI.

> print"<HTML><BODY>\n";
>
> $gchk=locksys(); # Lock file access

[Indentation sorted out: you'll find code much easier to work with if
you indent properly]

> if($gchk==0) {

So here you just want
  if($gchk) {

> while($f>0) {
> $f--;
> $t=64000;
> while($t>0) {
> $t--;
> $a = 1234;
> $b = 31;
> $c = $a * $b / 3;
> }
> }

AARGH! No no no, this is not how you pause the script!
See 'perldoc -f sleep'.

> &unlocksys;

Don't use &sub unless you know what it does and why you need it:
  unlocksys();

> }
>
> print"Done!<BR></BODY></HTML>\n";
>

[more indentation-adjusting]

> sub locksys{
> my $chk=3840;
> if(-d "$fd") {
> $chk=3584;
> if(open(LCKFILE,"$lockfil")) {

This will fail if lock.txt doesn't exist. Since you're just using it
for locking, you can just nuke it each time with
  open LCKFILE, ">$lockfil";
.

> $chk=flock LCKFILE,$l;

This is silly. You don't need to pass the type of lock to aquire in as
a CGI parameter. Those programs which only read whatever it is you are
locking should use
  $chk = flock LCKFILE, LOCK_SH;
and those that write should use
  $chk = flock LCKFILE, LOCK_EX;
. The rules given above were designed with this in mind. If you are
just trying to make sure you have only one copy of your script running
at a time, use LOCK_EX.

> }

If the open failed, don't you want to know why? Perl will tell you if
you ask it nicely:
        else {
            print "Open failed: $!";
        }

I would be very much inclined to structure this whole thing rather
differently. I would have:

sub locksys {
    my $file = shift;
    
    open my $FH, ">", $file or die "can't open lockfile: $!";
    flock $FH, LOCK_EX or die "can't aquire lock: $!";

    return $FH;
}

There are several points here:

1. 'my $file = shift' means we take the file to lock as an argument.

2. 'my $FH' creates a what is called a 'lexical filehandle'. Basically
   this means that the file is automatically closed as soon as all
   references to it go out of scope, so you don't need &unlocksys any
   more.

3. We return this filehandle from the sub, so that the file isn't just
   closed straight away (that would be rather silly). You use it like
   this:

#...program...

{
    my $LOCK = locksys("../data/lock.txt");

    #... do stuff that relies on having the lock
}
#... $LOCK goes out of scope here, so the file is closed and the lock
# lost.

4. If any of the syscalls fails, we 'die'. This will terminate the
   program with an error message. You can use the CGI::Carp module
   (see its docs) to get the error sent somewhere sensible when
   running CGI scripts.

   This is really a matter of style: you could just as well choose to
   return undef or something instead, and write your own error message
   with $! up in the main script. You may find this approach easier if
   you want to fail softly rather than aborting the script and don't
   feel like messing with eval {}.

5. I assume here that if flock fails it is a fatal error. If flock may
   fail non-fatally (either because you used LOCK_NB or because your
   system doesn't restart syscalls if you catch a signal) you will
   need to investigte the Errno module to find the reason for its
   failure, and take appropriate action.

Ben

-- 
perl -e'print map {/.(.)/s} sort unpack "a2"x26, pack "N"x13,
qw/1632265075 1651865445 1685354798 1696626283 1752131169 1769237618
1801808488 1830841936 1886550130 1914728293 1936225377 1969451372
2047502190/'                                                 # ben@morrow.me.uk


Relevant Pages

  • Re: [PHP] usage of flock
    ... > I am confused with the flock function and its usage. ... > hoping the first job_controller will lock it-self, ... if the server crash down the "surviving" lock file ... locked file from an "exit" or killed script, and then you have to know ...
    (php.general)
  • Re: [PHP] usage of flock
    ... to use flock. ... hoping the first job_controller will lock it-self, ... I also thought of writing in the lock file the PID of the first ... So how could prevent multiple instance of the same script? ...
    (php.general)
  • Re: I cant flock, it returns 0, any ideas.
    ... # returns zero when I try to flock ... >> I wanted to run this script to see if it would affect my other perl ... If someone holds a LOCK_EX lock, all attempts to get a lock will ... > the flock call will fail, and if you don't flock will just not ...
    (comp.lang.perl.misc)
  • Re: PHP instances writing to the same file?
    ... to lock a file if you are running your script on a server with an OS ... Here is a flock sample from: ... In my case a single PHP script appends text strings to an existing text ...
    (comp.lang.php)
  • Re: UW imap-2006b: 64 bit problem
    ... UW imapd is obliged> to do considerable more work on SVR4 systems than it does on BSD and> Linux systems which offer flock() locking as an alternative to POSIX> locking. ... IEEE Std 1003.1-1988 requires that all fcntl() locks associated with a file for a given process are removed when *any* file descriptor for that file is closed by that process. ... Put another way, before any library routine opens a file, it must be aware of what files the application and any other libraries have open and locked; otherwise the library routine will remove the lock unexpectedly when it closes the file. ...
    (comp.mail.imap)