Thread

  1. Re: On file locking

    Giles Lean <giles@nemeton.com.au> — 2003-02-03T08:19:39Z

    Tom Lane wrote:
    
    > On HPUX 10.20, flock doesn't seem to exist (hasn't got a man page nor
    > any mention in /usr/include).
    
    Correct.  Still isn't there in later releases.
    
    >  lockf says
    > 
    >      All locks for a process are released upon
    >      the first close of the file, even if the process still has the file
    >      opened, and all locks held by a process are released when the process
    >      terminates.
    > 
    > and
    > 
    >      When a file descriptor is closed, all locks on the file from the
    >      calling process are deleted, even if other file descriptors for that
    >      file (obtained through dup() or open(), for example) still exist.
    > 
    > which seems to imply (but doesn't actually say) that HPUX keeps track of
    > exactly which process took out the lock, even if the file is held open
    > by multiple processes.
    
    Having done some testing today, I now understand what the standards
    are trying to say when they talk about locks being "inherited". Or at
    least I think I understand: standards are tricky, locking is subtle,
    and I'm prepared to be corrected if I'm wrong!
    
    All of these lock functions succeed when the same process asks for a
    lock that it already has.  That is:
    
         fcntl(fd, ...);
         fcntl(fd, ...);  /* success -- no error returned */
    
    For flock() only, the lock is inherited by a child process along
    with the file descriptor so the child can re-issue the flock()
    call and that will pass, too:
    
         flock(fd, ...);
         pid = fork();
         if (pid == 0)
             flock(fd, ...);  /* success -- no error returned */
    
    For fcntl() and lockf() the locks are not inherited, and the
    call in a child fails:
    
         fcntl(fd, ...);
         pid = fork();
         if (pid == 0)
             fcntl(fd, ...);  /* will fail and return -1 */
    
    In no case does just closing the file descriptor in the child lose
    the parent's lock.  I rationalise this as follows:
    
    1. flock() is using a "last close" semantic, so closing the file
       descriptor is documented not to lose the lock
    
    2. lockf() and fcntl() use a "first close", but because the locks
       are not inherited by the child process the child can't unlock
       them
    
    > This all doesn't look good for using file locks in the way I had in
    > mind :-( ... but considering that all these man pages seem pretty vague,
    > maybe some direct experimentation is called for.
    
    I conjecture that Tom was looking for a facility to lock a file and
    have it stay locked if the postmaster or any child process was still
    running.  flock() fits the bill, but it's not portable everywhere.
    
    One additional warning: this stuff *is* potentially filesystem
    dependent, per the source code I looked at, which would call
    filesystem specific routines.
    
    I tested with HP-UX 11.00 (VxFS), NetBSD (FFS) and Linux (ext3).  I've
    put the rough and ready test code up for FTP, if anyone wants to check
    my working:
    
        ftp://ftp.nemeton.com.au/pub/pgsql/
    
    Limitations in the testing:
    
    I only used whole file locking (no byte ranges) and didn't prove that
    a lock taken by flock() is still held after a child calls close() as
    it is documented to be.
    
    Regards,
    
    Giles
    
    
  2. Re: On file locking

    Antti Haapala <antti.haapala@iki.fi> — 2003-02-03T12:10:49Z

    > All of these lock functions succeed when the same process asks for a
    > lock that it already has.  That is:
    >
    >      fcntl(fd, ...);
    >      fcntl(fd, ...);  /* success -- no error returned */
    >
    > For flock() only, the lock is inherited by a child process along
    > with the file descriptor so the child can re-issue the flock()
    > call and that will pass, too:
    >
    >      flock(fd, ...);
    >      pid = fork();
    >      if (pid == 0)
    >          flock(fd, ...);  /* success -- no error returned */
    
    True...
    
    > For fcntl() and lockf() the locks are not inherited, and the
    > call in a child fails:
    >
    >      fcntl(fd, ...);
    >      pid = fork();
    >      if (pid == 0)
    >          fcntl(fd, ...);  /* will fail and return -1 */
    >
    > In no case does just closing the file descriptor in the child lose
    > the parent's lock.  I rationalise this as follows:
    >
    > 1. flock() is using a "last close" semantic, so closing the file
    >    descriptor is documented not to lose the lock
    
    Yep.
    
    > 2. lockf() and fcntl() use a "first close", but because the locks
    >    are not inherited by the child process the child can't unlock
    >    them
    
    And at least old linux system call manuals seems to reflect this
    (incorrectly) when they state that file locks are not inherited (should
    be "record locks obtained by fcntl").
    
    > One additional warning: this stuff *is* potentially filesystem
    > dependent, per the source code I looked at, which would call
    > filesystem specific routines.
    >
    > I only used whole file locking (no byte ranges) and didn't prove that
    > a lock taken by flock() is still held after a child calls close() as
    > it is documented to be.
    
    I tested this on Linux 2.4.x ext2 fs and it seems to follow the spec
    exactly. If child is forked and it closes the file, parent still has the
    lock until it's killed or it has also closed the file.
    
    What about having two different lock files: one that would indicate that
    there are some child processes still running and another which would
    indicate that there's postmaster's parent process running? - Using flock
    and fcntl semantics respectively (or flock semantics with children
    immediately closing their fds).
    
    And of course locking is file system dependant, just think NFS on linux
    where virtually no locking semantics actually work :)
    
    -- 
    Antti Haapala