in reply to Fcntl() and multiple filehandles

One solution is to implement your own flock in terms of fcntl, and keep track of the lock's state. For example, something like:
our @lockstat; sub readlock { my($fh)=@_; my $fileno = fileno($fh); defined($fileno) or die "Bad filehandle"; if ($lockstat[$fileno] && $lockstat[$fileno]{count}) { $lockstat[$fileno]{count}++; } else { fcntl_lock($fh,F_RDLCK); $lockstat[$fileno]{count}=1; } } sub readunlock { my($fh)=@_; my $fileno = fileno($fh); defined($fileno) or die "Bad filehandle"; return unless $lockstat[$fileno] and $lockstat[$fileno]{count}; if (--$lockstat[$fileno]{count} == 0) { fcntl_lock($fh,F_UNLCK); } }

You can expand this to cover write locks in a similar way (or write a general myflock that does both read, write, and unlocks). If you're using multiple threads, you'll need to use mutexes to protect the data structures from the different threads. You'll also have to think about how to deal with close.

I seem to recall that Perl has a compile-time option to use its own flock implementation based on fcntl; you may want to look into that as well.

Replies are listed 'Best First'.
Re^2: Fcntl() and multiple filehandles
by Anonymous Monk on Sep 20, 2004 at 23:58 UTC
    Not a bad idea. I had thought of something similar. I guess I left out something in my original postb though: in this case, I'm actually working with a couple of different scripts that use the same files (one is a daemon and one is a CGI front end for interacting with it). There will probably be a couple more in the future. So in this case I need several differnt scripts to all adhere to the same locks.

    So does the whole file locking over NFS problem boil down to NFS being poorly designed (or at least being poorly designed for use by more than one client at a time) then? It seems odd to me that NFS has this big achilles heal given how long it's been around and how widely deployed it is.
      So in this case I need several differnt scripts to all adhere to the same locks.
      The behavior you complained about was that when the same process read-locked a file multiple times then unlocked it, all it was unlocked instead of decrementing a lock counter. Solving that problem doesn't require that the different scripts use the same lock discipline.
      So does the whole file locking over NFS problem boil down to NFS being poorly designed (or at least being poorly designed for use by more than one client at a time) then?
      I don't think it's fair to say it's a problem; you just have to use fcntl-locking, which is a POSIX standard. AFAIK, that's always been the case for portable programs, although flock over NFS may have worked on previous versions of RedHat. It's straightforward to implement your own lock-counting code if you want that behavior, and if you think it's useful clean it up and put it on CPAN.

      I don't see any documentation of the lock-counting behavior you describe in the flock(2) manpage. Are you sure you weren't relying on undefined behavior all along?

        I see two scenarios here. One is the one that Annonymous mentioned in his original post, the second is the typical problem of multiple processes. Both are quite valid concerns, but I believe they require two seperate solutions. If closing any file handle to a file clears all locks on it, the there's not much point in using shared locks within the same process. But using shared locks per fcntl can prevent two different processes from stepping on each other's toes. So go ahead and place the locks for the sake of preventing your other scripts from stepping on your toes, but also keep some kind of internal locking mechanism--it has been suggested that a simple hash with file->lock status pairs would do for this. But consider what happens if you have two handles/locks in one process and another process is waiting for an exclusive lock. When one of process A's handles is closed, it clears all of A's locks on the file and B starts working on it, even theough A's second handle is still open. To prevent this, the only solution I see is to be very careful within A in terms of how you design your code. Perhaps you need to keep an internal structure of some sort of filename->glob pairs so you always know when a hanlde is open and can pass the handle from sub to sub rather than needing to open a new handle.