in reply to Safe Counter

Hello Wise Monks! I have since modified my counter upon the suggestion of various wise monks. However, my problem still persists: my counter resets to zero whenever 10 or more instances of it are run at the same time. What's still wrong with my code? It's true I could just give up and use an existing perl module to handle this, but my modified code below should be good enough, right?

$completeadd = "sacrifice.txt"; open(SAC, "$completeadd") || die "sacrificial file open failed: $!\n"; flock(SAC, LOCK_EX) || die "sacrificial Lock failed: $!"; $completeadd = "counter.txt"; open(MFILE, "$completeadd") || die "file open failed: $!\n"; flock(MFILE, LOCK_EX) || die "Lock failed: $!"; @filedata1=<MFILE>; chomp @filedata1; close(MFILE); if ($filedata1[0]) { $filedata1[0]=$filedata1[0] + 1; } else { $filedata1[0] = 1; } open(MFILE, ">$completeadd") || die "file open failed: $!\n"; flock(MFILE, LOCK_EX) || die "Lock failed: $!"; print MFILE "$filedata1[0]"; close(MFILE); close(SAC);

Replies are listed 'Best First'.
Re: Safe Counter Follow Up # 2
by davido (Cardinal) on May 07, 2004 at 03:04 UTC
    I've pretty much just taken peoples' word for the following statement, but it has worked out ok so far: File writes in append mode are atomic for most operating systems.

    That being the case, one thing you could do is to, instead of keeping a count, keep a tally. Open the file for append, and add "HIT\n".

    Once a day (adjust to taste) a cron job could then be used to add up the tallies. The cron job's script would add all the tallies, plus a number you've stored in the first line of this file. Then rewrite the file (using a temp file) with only the total number at the top. rename() the temp file back over the original, and you've now got a fresh file.

    For example:

    • At midnight the job runs and resets the file to look like this:
      234428\n

    • By 2:00am the file has accumulated some hits and looks like this:
      234428\n HIT\n HIT\n HIT\n HIT\n

    • At the following midnight the cron job runs again and adds up all "HIT\n"s. Let's say there were 1000. So it writes a temp file that looks like:
      235428\n

      It then renames the tempfile over the original file, and starts a new day.

    Kind of elaborate, but if your existing course of action isn't working out for you this may be an alternative, and it's certanly less prone to race conditions. You won't have accurate up to the minute stats, but you can have up to date stats as often as you set your job to run.


    Dave

Re: Safe Counter Follow Up # 2
by Zaxo (Archbishop) on May 07, 2004 at 02:08 UTC

    You have a race condition [but see update below] by closing and dropping the lock between reading and writing the file. Do both within the same lock with the '+<' mode of open.

    $completeadd = "counter.txt"; open MFILE, '+<', $completeadd or die $!; flock(MFILE, LOCK_EX) || die $!; chomp($filedata=<MFILE>); $filedata++; seek MFILE, 0, 0; print MFILE $filedata, $/ or die $!; close MFILE or die $!;

    You seem to be trying to set up the sacrifice file as a semaphore, but once in place you don't use it for anything.

    Update: Oops, now I see the semaphore ought to be effective. Your symptoms indicate that it isn't, however. Is the counter opened elsewhere by something that doesn't honor the semaphore?? Thanks, ++davido.

    After Compline,
    Zaxo

      Nope. This is the only place where the counter is opened. Could it be that the semaphore isn't working?