Gorby has asked for the wisdom of the Perl Monks concerning the following question:

Hello Wise Monks. I've been having trouble with the following code especially when several people run my script at the same time. What happens is that sometimes, when I view the file named "counter", it reflects a number lower than what it was the previous day, perhaps because the file was deleted somehow. But I don't see any way the file would be deleted. Please help?
sub get_lock { open(SEM, ">$semaphore_file") || die "Cannot create semaphore $semap +hore_file: $!"; flock(SEM, LOCK_EX) || die "Lock failed: $!"; } sub release_lock { close(SEM); } sub readdata { my @filedata; @filedata=(); open(MFILE, ">>$completeadd") || die "file open2 failed at $complete +add: $!\n"; close(MFILE); open(MFILE, "$completeadd") || die "file open81 failed: $!\n"; @filedata=<MFILE>; chomp @filedata; close(MFILE); return(@filedata); } sub writedata { my @filedata; @filedata=(); @filedata=@_; open(MFILE, ">$completeadd") || die "file open3 failed: $!\n"; foreach(@filedata) { print MFILE "$_\n"; } close(MFILE); } $completeadd = "counter"; $semaphore_file = "counterLOCK"; get_lock(); if (-e $completeadd) { open(MFILE, "$completeadd") || die "file open4 failed: $!\n"; @filedata1=<MFILE>; chomp @filedata1; close(MFILE); } else { @filedata1=readdata(); } $hitcount=$filedata1[0]; if ($hitcount) { $hitcount=$hitcount + 1; } else { $hitcount = 1; } $filedata1[0]=$hitcount; writedata(@filedata1); release_lock(); close(CNTLCK);

Replies are listed 'Best First'.
•Re: Disappearing File
by merlyn (Sage) on Jan 13, 2004 at 02:28 UTC
Re: Disappearing File
by Zaxo (Archbishop) on Jan 13, 2004 at 02:45 UTC

    Your attempt at making a semaphore file is not having any effect. The race condition caused by reading and writing separately goes like this:
    Process AProcess B
    1readdata()
    2readdata()
    3writedata()
    4writedata()
    Process B's work is overwritten.

    You should open the file once to read and write, flock it (blocking), read, process, and write. Close releases the lock.

    To use a semaphore file, you should do a blocking sysopen with O_EXCL, and unlink the file when you're done. There's no need to use both methods.

    I think you also have a problem with autovivifying globals in your subroutines, then expecting later lexicals to appear in their place. use warnings; use strict;.

    After Compline,
    Zaxo

      Is there a chance for a race condition to occur in my code? Reading and writing of data happens between get_lock() and release_lock(). If this is not sufficient, what is lacking? I just want to understand the problem. Thanks in advance. Gorby

        Some platforms/perl versions don't support flock. Mod_perl will give you trouble with all those global variables and unlocalized handles - each connection will think it has the lock (I withdraw the "lexical" word). Your locking code will work on the right system under ideal conditions, but there are many things that can fail.

        You haven't shown your error logs and you don't say what platform, perl version, whether threading is in use, or whether different connections have a new environment.

        My advice was to simplify the locking and isolate variables' scope.

        After Compline,
        Zaxo

Re: Disappearing File
by pg (Canon) on Jan 13, 2004 at 04:11 UTC

    I don't think this has anything to do with locking.

    In this case, without proper locking, the worst thing can happen is that you don't get the correct increment, but there is no chance that your counter will be decreased.

    For example, your currect counter = 100, both process A and B try to increase the counter by 1. Somehow your locking failed, both A and B read 100, and then write 101 back. Your counter still increases, although it only increased by 1, instead of the right increment 2.

    The fact your counter is going down, must be caused by some other defect(s).

      What you have said is true. There really should be no case where the counter will decrease, even if the lock fails. My question is, what's causing the file to be deleted? The code I provided is the only code that edits the "counter" file. Gorby
        Just a thought, but in all the times I've written flocking apps, I've always used append mode instead of create mode. Perhaps I do this because somewhere I've got bitten by create mode removing the file to create it empty, and that of course would have another locker still holding the old (unlinked) file locked, even though I can now lock the new one.

        Try changing your lockfile open to an append, and rerun your tests. I apologize if that doesn't fix it, but it is suspicious.

        -- Randal L. Schwartz, Perl hacker
        Be sure to read my standard disclaimer if this is a reply.