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

I'm writing a server where, because of the modeless of the protocol, I'll need to use flock() on files, rather than filehandles. One request will lock a file, another will do a read/write, and then I'll receive an unlock request. The server forks between requests. What's the best way to do this, since flock() operates on filehandles and not files? I'd like to keep this server as portable as possible, although my only real concern is that it works on Linux.

Thanks for your wisdom!

Replies are listed 'Best First'.
Re: Using flock() on files
by perlplexer (Hermit) on Apr 20, 2003 at 06:34 UTC
    • You can't lock a file without having a filehandle that references that file;
    • File locks do not survive fork().

    One possible solution to your problem would be to have a DBF file with a list of all files that are currently locked. The files themselves would not be locked via flock() but access to the DBF file would be gated through flock().
    Advantage: locks will be maintained even if your server/client crashes and is then restarted;
    Disadvantage: having all processes use the same file may create a bottleneck. To minimize the probability of this happening, make sure the DBF file is locked only when necessary.

    Hope this helps.

    --perlplexer
      locks will be maintained even if your server/client crashes and is then restarted

      This is not necessarily an advantage. You need a way to break old, stale locks that might be left behind after a crash.

        I view this as an advantage because I'd rather have the locks stay than have corrupt data. You can easily write a tool for cleaning up old locks; writing a tool for restoring corrupt data, however, may not even be possible depending on what kind of data you're dealing with.

        --perlplexer
Re: Using flock() on files
by benn (Vicar) on Apr 20, 2003 at 11:24 UTC
    Another way may be to use separate lockfiles to hand-roll your own lock - check for the existence of "myfile.lock" - if none, then create it, open "myfile", do stuff, close it, and delete "myfile.lock" all in one go. (Or maybe even just using chmod and some funky rw permission stuff on the file itself)

    I'm sure there must be a few nodes on this lying around the monastery somewhere though, either providing code or trashing the idea (hopefully whilst providing some better solutions) :)

    Cheers,
    Ben
      This creates a race condition. You must wrap it in some atomical locking mechanism. Between the time you check and the time you create, even if you ask sysopen for an exclusive create, several processes may think they possess the lock at the same time.

      not a Good Thing™

      -nuffin
      zz zZ Z Z #!perl
        doh! Ta - I knew that there was something vital missing even as I was pressing submit...
        note to self..don't post before 1st coffee - 'specially on a Sun. morning.
Re: Using flock() on files
by SiMac (Initiate) on Apr 20, 2003 at 14:45 UTC
    I'd like to use standard flock() locks on the files if possible, because of the nature of the application. The files I'm using aren't just internal files; they're files that other processes access too. Using a custom locking scheme would, unfortunately, defeat this mechanism.

    I've come up with two other ideas; unfortunately, neither is particularly portable:

    1. Fork off a separate daemon on startup, and have the request forks pass the names of which files to lock/unlock to it. This daemon would then maintain the locks. Unfortunately, this method would only work on systems where locks are advisory (not Windows or OS/2), since otherwise I couldn't write to the file in the next request.

    2. Fork off a separate daemon on startup, and have the request forks pass file descriptors (using Socket::PassAccessRights) that they've locked, then pass them back for unlocking. Although this method seems more proper, it is no better than method 1 since Socket::PassAccessRights only works on *NIX systems, and according to the docs, doesn't work particularly well because the file descriptor passing code in the kernel is buggy.