From: friedman@gnu.ai.mit.edu (Noah Friedman)
Newsgroups: comp.unix.shell
Subject: Re: stale lock removal
Date: 30 Jul 92 20:29:14 GMT
Organization: Free Software Foundation, 675 Mass Ave. Cambridge, MA 02139
In-reply-to: dattier@ddsw1.mcs.com's message of 29 Jul 92 16:14:17 GMT
X-Disclaimer: The article, except for quotations, is public domain.

In article <1992Jul29.161417.2473@ddsw1.mcs.com> dattier@ddsw1.mcs.com (David W. Tamkin) writes:
>Say a process finds that another process has the lock and it's going to wait
>a bit and then try again.  How can it determine whether the process that has
>the lock is actively using it or has stalled?

   Most programs simply don't.  They'll wait forever (either by checking
periodically, implementing some sort of semaphore, or whatever) until the
lock goes away.  After all, there are lots of reasons why a program may be
wedged (not merely zombified).  Perhaps in some circumstances you can use
some empirical guestimates for how long a process can reasonably hold a
lock, and then steal it if it takes longer than that.  Depends entirely on
the nature of the data and the programs, I suppose (and you may still be
subject to nasty but unlikely race conditions).

   Emacs has an advisory locking mechanism similar to the one you described
later in your article.  The lockfile contains the process-ID of the emacs
process locking the file.  When emacs notices that a file has a lock, it
checks to see if the other process still exists (using a kill 0 and looking
at the resulting errno).  This implementation fails miserably with
NFS-mounted files, because they could easily be edited on remote machines.
We've been thinking informally about a new locking mechanism, but no solid
ideas have been proposed.  My guess is that emacs 19 will use the same
fairly brain-dead locking mechanism which was fine when everyone worked on
a single vax without networked file systems, but is now obviously
inadequate.  *sigh*.  Anyone who has ideas, please send them to me!

>Potentially process A could find that process B had the lock and go into these
>tests; B could end and process C could get the lock but not yet have created
>the signature file by the time A tests for a signature file to find out which
>process has the lock.

   You should never assume you have a lock until you have successfully
negotiated whatever protocol is necessary to demonstrate you have it.

>Finally, is there some good way simply to tell the age of a lock (if not one
>made with /etc/link, at least one make with mknod p or with mkdir)?  If a
>lock is older than a certain age, one can figure that it is stale.  Various
>ls options will display last inode modification time or access time, but how
>does one compare them to the current time efficiently?
>
>Any suggestions?  This will have to be done with shell scripts or existing
>utilities.

   I wrote the following for bash, though I admit I cheated by using perl
to get at various system calls.  :-)

   To use these, you basically just stat the file and get the mtime (or
atime, whatever), and use "time2" to get the current system time.  Then you
can just do some simple arithmetic using expr or builtin bash routines to
get the age of a file. 

   Getting the age of locks that were acquired with lockd may be more
difficult.  :-)


# dev ino mode nlink uid gid rdev size atime mtime ctime blksize blocks
#
# Converts mode to canonical 3-digit octal number (type bits can be gotten
# with "test" anyway)
function stat ()
{
    perl - "$@" <<- '__EOF__'
	for ($i = 0; $i <= $#ARGV; $i += 1)
	  {
	    @st = stat("$ARGV[$i]");
	    $st[2] = $st[2] & 0777; 
	    printf ("%ld %u %o %d %lu %lu %ld %lu %lu %lu %lu %lu %lu\n", @st);
	  }
	__EOF__
}

# WARNING: In order to optimize computations after the first call to this
# function, the `SECONDS' magic shell variable is used to compute changes
# from the previous value.  If this variable is ever assigned (or unset),
# time2 will no longer provide the correct value.
#:end docstring:

function time2 ()
{
    # Calling an external program is slow
    if [ "${_time_seconds+set}" != "set" ]; then
       _time_seconds=$(perl -e 'print(time());')
    fi

    echo $[ _time_seconds + SECONDS ]
}
Newsgroups: comp.unix.shell