http://qs321.pair.com?node_id=28955

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

I just need a few more eyes to look at this code and help me determine just what in the world could possibly be going wrong here. this is a CGI script, by the way.
my $tempfile="/tmp/$$.tmp"; open(DBA, "+< $datafile") or die "Could not read $datafile: $!"; flock(DBA, 2); seek(DBA, 0, 0); open(TMPA, "+> $tempfile") or die "Could not open tempfile: $!"; flock(TMPA, 2) or die "ERROR: $!"; # it dies here? seek(TMPA, 0, 0); while(<DBA>){ # more code here, etc etc ...
Of course, this is only a small piece of a MUCH larger script I've already written. I'm opening a datafile and comparing it to some user input, and then opening a tempfile to write any lines that should be preserved from DBA. Then I'd rewind both files and write from TMPA to DBA and then truncate, and close. Anyhow, the real problem comes in when I try to flock the temp file. I've put debugging lines in the code with messages that say things like "I've got this far" or whatever, after each line. And it gets all the way up to that flock call to the TMPA filehandle just fine. But if I let the script try to execute that line, the browser just sits there and hangs... like it's in a loop or something.

What might be happening here? The first flock call to the DBA filehandle works just fine. I'm pulling my hair out after staring at this code for over an hour now. Please help.
*sobs*

Replies are listed 'Best First'.
Re: More Eyes
by tye (Sage) on Aug 22, 2000 at 07:36 UTC

    When flock() hangs and you haven't specified LOCK_NB, then it is very, very likely that another process holds a lock on that file and you are waiting for that lock to clear.

    Since the file is named "/tmp/$$.tmp", I can't think what other process would be likely to hold a lock on it. Try the following:

    use Fcntl qw( :flock ); #[...] open(TMPA, "+> $tempfile") or die "Could not open tempfile: $!"; flock(TMPA, LOCK_EX|LOCK_NB) or die "ERROR: $!";

    FYI, saying "it dies here" makes it sound like the "or die" is triggered and you see the value of $!.

            - tye (but my friends call me "Tye")
      tye, Thanks for the input. I tried your suggestion, and when configured as you suggest, the flock dies with the message "Resource temporarily unavailable". Wow. What does that mean? I mean, it says what it says, yeah. But what does it really mean, in terms of flocking? It's quite strange. When I made a temp directory of my own "/home/username/htdocs/tmp" and made it chmod 777, it was able to flock just fine.
      So is there an issue with flocking files which are not in your own userspace (i.e. /tmp or something), perhaps?

      Update: Incidentally, I got this very same result with the script on two different machines: one running Slack 7, and the other running FreeBSD 3.1
      Update 2: I just had to try the simplest version, so I did this:
      #!/usr/bin/perl use Fcntl qw( :flock ); my $tempfile="/tmp/$$.temp"; print "Content-type: text/html\n\n"; print "Testing flocking now.<BR>\n"; open(TMP, "+> $tempfile") or die "Could not open tempfile: $!"; flock(TMP, LOCK_EX|LOCK_NB) or die "no lock: $!"; seek(TMP, 0, 0); print TMP "Blah blah blah"; # just to test close(TMP); unlink $tempfile; print "That seemed to go just fine.\n";
      And it ran just fine. Go figure. I'm quite puzzled.
        Hmmm. Interesting, in some versions of Unix /tmp is held in memory I haven't access to your ?nixs but I wonder if that has a bearing on things?
Re: More Eyes
by merlyn (Sage) on Aug 22, 2000 at 15:36 UTC
    There is really no point in flocking a temp file that you've just destroyed. If someone else was using it, too bad, you've just killed their data.

    Once you've flocked the input file, are you now the only one that is doing the operation? If so, no more flocking is needed. Just create your temp file, and rename over it, or whatever you were going to do at the end.

    However, flocking a file you will be renaming over the top is a bit problematic. I've posted other code here that shows a template for that at Exclusively updating a file that continues to be repeatedly read.

    -- Randal L. Schwartz, Perl hacker

      *laugh*

      At the exact moment merlyn was noticing that you were destroying the temp file on open, le and I were discussing a problem that turned out to be his not being able to flock a file he had destroyed on open.

      With your original code change how you open the temp file and see if that makes it work for you. If it does then I definitely learned something interesting today. :-)

Re(tilly) 1: More Eyes
by tilly (Archbishop) on Aug 22, 2000 at 06:14 UTC
    Your code works for me, but instead of messing around in /tmp yourself you should try installing File::Temp and using that instead.
I must thank you all, and deeply apologize
by Kozz (Friar) on Aug 22, 2000 at 19:05 UTC
    I should have taken tilly's suggestion from the VERY start and used Temp::File. And now I feel quite foolish, but perhaps a little bit wiser, and I must acknowledge my folly to the Perlmonks who've endured me.

    Especially since the solution which would solve this mystery was in none of the pieces of code I posted here. I realized that the original block of code I posted in the parent thread was in a subroutine which had been called by ANOTHER subroutine, and in THAT subroutine I had already opened and flocked a tempfile which had been created moments ago with the exact same naming scheme.

    So the most obvious solution was invisible to me late at night: "The file exists, and that's why you can't get a lock on it, dumbass!"
    Thankfully things were clearer in the morning. I feel sooo silly.
RE: More Eyes, Please
by turnstep (Parson) on Aug 22, 2000 at 18:03 UTC
    The original example uses:
    open(DBA, "+< $datafile") ...
    but the next 2 code examples use:
    open(TMPA, "+> $tempfile") ...
    Note the direction of the mode indicator - in the first example, you are opening the file for read/write access, or, to be specific, you are opening the file for input, and want read/write access. In the next examples, you are opening it for output (and clobbering the contents in the process), and want read/write access. The second is not very useful: if you have already opened it for writing, and just set it to zero length, there is nothing to read! If you are using flock, you want to use the first example, so that any changes made to the file (like, say, truncating it to 0 bytes) is made *after* you have sucessfully flocked the file. You could possibly use the second in cases where you do not care about what is in the file, but merely about whether it is locked or not (e.g. a semaphore).