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

I've written what's quickly becoming a hefty script and I need to implement file locking since it's a CGI that can be called multiple times - I haven't had it clobber a file yet but I'm gritting my teeth in morbid anticipation.

My question stems from the fact that every example of flock I've been able to find has called die when the lock fails.

My CGI script outputs pages all at once a la HTML::Template rather than piece by piece - if I die in the middle of the script, the user gets an ugly, ugly error message and not an HTML page. I don't want to do this under any circumstances.

Since die isn't an option I've set up the following:

if (sysopen(TRIGGERFILE, "$config->{xmlpath}trigfile.tf", O_WRONLY | O +_EXCL | O_CREAT)) { if (flock (TRIGGERFILE, LOCK_EX | LOCK_NB)) { print TRIGGERFILE "\n"; } else { push (@errors, qq(<p class="message">error message</p>)); } close TRIGGERFILE; } else { push (@errors, qq(<p class="message">error message</p>)); }

My question is about the close. If the lock fails, is the file still open? In other words, do I need a close statement after a failed lock or can I move it just below the statement that prints to the file?

Thanks again for your help, fellow monks.

Replies are listed 'Best First'.
Re: Flock, die and statement order
by Zaxo (Archbishop) on Nov 18, 2002 at 05:14 UTC

    If an operation dies, all the open handles are closed as part of the normal shutdown. Also, with modern perl, you can use a lexical scalar (my $foo;) and the $foo handle will be closed automatically when it goes out of scope.

    It is good policy to check all interaction with the os for errors and die, retry, or otherwise handle the error.

    Your nest of conditionals is unnecessarily complicated, therefore. I'd rewrite it something like this:

    { sysopen my $trigger, $config->{xmlpath}.'trigfile.tf', O_WRONLY | O_EXCL | O_CREAT or die $!; flock $trigger, LOCK_EX | LOCK_NB or die $!; print $trigger $/ or die $!; close $trigger or die $!; }
    At any stage that dies, the file is closed. if you want to continue in spite of errors, you can replace the die clause with warn $! and last; which will have the same effect on the handle, give the same error message, but will continue at the end of the block. I didn't try to duplicate your html output for errors, but it can be inserted in the error message and CGI::Carp can send the errors to the browser.

    After Compline,
    Zaxo

      Unfortunately I can't call any function that will output directly, otherwise I would be using warn or die. If I call warn, I'll end up with something that looks like:

      <p>Error Message</p> <html> <head> <title>Error</title> </head> <body> ... </body> </html>

      As it stands right now, the script pushes a message onto the error stack, which is checked later in the script. The user is then informed of any errors or sent to the next form if there were no errors. It's not clear from the above snippet that errors are handled but rest assured that they are.

        Why is warn() going to the page? It should be going to STDERR and the server's error log. If you are handling errors, you can catch the die with an eval block.
        eval { might_die() }; if ($@) { # handle exception }
Re: Flock, die and statement order
by nothingmuch (Priest) on Nov 18, 2002 at 04:58 UTC
    Yes, the file remains open, and you may read/write. flocks are not mandatory, as you probably no, and they remain that way even if they've been called once.

    -nuffin
    zz zZ Z Z #!perl