You are currently viewing a snapshot of www.mozilla.org taken on April 21, 2008. Most of this content is highly out of date (some pages haven't been updated since the project began in 1998) and exists for historical purposes only. If there are any pages on this archive site that you think should be added back to www.mozilla.org, please file a bug.



All Packages  This Package  Class Hierarchy  Class Search  Index

Class grendel.storage.UnixDotLock

java.lang.Object
   |
   +----grendel.storage.UnixDotLock

Implements Unix-style dot-locking (locking file "FOO" by using an atomically-created file in the same directory named "FOO.lock".)

Use it like this:

    UnixDotLock lock = new UnixDotLock(file);  // this blocks
    // ... long-running manipulations of file
    lock.unlock();

All the lock-retrying and lock-date-maintenance happens under the covers.

See also the description of movemail.

Implementation details:

The protocol for getting a lock on some file "FOO" is as follows:

  1. Create a file "FOO.1234" (random unused name.)

  2. Hard-link "FOO.1234" to "FOO.lock".
    (this is the trick, because link() happens to be one of the few atomic, synchronized operations over NFS.)

  3. Unlink "FOO.1234"
    (regardless of whether step 2 succeeded; now either we have a "FOO.lock" file or we don't.)

  4. If we obtained the lock (the link() call in step #2 succeeded), then we're done.

  5. Else if the creation-time of "FOO.lock" is more than 60 seconds in the past, then smash the lock (unlink "FOO.lock") and goto step #1.)

  6. Else, the lock is held and current. Wait a second, then goto step #1 and try again.

One thing implied by this is that if one wants to hold a lock for longer than 60 seconds (which we do in some cases) then one must maintain the lock file: its modification-date must be updated every less-than-60 seconds. We do this by having a ``heartbeat'' thread that wakes up periodically and updates all held locks.


public class  UnixDotLock
     extends java.lang.Object
{
          // Fields 7
     private static final int File_lastModified_scale;
     private static Vector active_locks;
     private static final boolean debug;
     static final int heart_rate;
     private static Thread lock_heartbeat_thread;
     private File locked_file;
     private static final int maximum_lock_age;

          // Constructors 1
     public UnixDotLock(File) throws SecurityException, IOException, InterruptedException;

          // Methods 11
     private void createDiskLock(File) throws SecurityException, IOException, InterruptedException;
     protected synchronized void finalize() throws Throwable;
     private File gentemp(File);
     static synchronized void globalHeartbeat();
     private synchronized void heartbeat() throws IOException;
     public static final void main(String[]) throws SecurityException, IOException, InterruptedException;
     private File makeLockName(File);
     private void removeDiskLock(File);
     private synchronized void setLocked(File) throws InterruptedException;
     public void unlock();
     private static synchronized void updateLockList(UnixDotLock);
}



Fields


debug

   private static final boolean debug

Turn this on to cause activity to be logged to System.err.


lock_heartbeat_thread

   private static Thread lock_heartbeat_thread

Holds the (one) thread that updates the lock date every <60 seconds.


active_locks

   private static Vector active_locks

List of locked locks. The vector holds UnixDotLock objects.


maximum_lock_age

   private static final int maximum_lock_age

After someone else's lock is 60 seconds old, we assume it has been left dangling, and smash it. This is a part of the de-facto dot-locking protocol, folks: I wouldn't make this stuff up. The way you hold a lock for longer than 60 seconds is by periodically updating the modification date of the lock file to prove that you're still alive.


heart_rate

   static final int heart_rate

How often to update the write-date on a lock file. If we want to hold a lock for longer than 60 seconds, we need to update its write date, and this is how often we do that. This should be less than `maximum_lock_age' by enough to be comfortable that system load and thread starvation won't cause the heartbeat thread to fail to update the lock file date in time.


File_lastModified_scale

   private static final int File_lastModified_scale

System dependency: multiply the result of File.lastModified() by this to convert it to seconds. Java doesn't specify the units in which File.lastModified() measures time, but we need to be able to add N seconds to it, to tell when a file is more than N seconds old. It happens that, with JDK 1.1.3 on Irix, this scale is 1000. We must assume that all other Unixen behave the same. If this changes in some future Java implementation, we're fucked.


locked_file

   private File locked_file

The name of the file for which this lock is being held.



Constructors


UnixDotLock

   public UnixDotLock(File file)  throws SecurityException, IOException, InterruptedException

Lock the named file. Unlock it by calling the unlock() method. If you do not call unlock() before discarding the UnixDotLock object, the file will remain locked!

Throws: SecurityException
the lock file could not be created (file permission problems?)
Throws: IOException
a disk I/O error occurred.
Throws: InterruptedException
this thread was killed while waiting for the lock.



Methods


unlock

   public void unlock() 

Unlock the file. This must be called before discarding the UnixDotLock object, and may be called only once. The UnixDotLock object must not be used again after calling this.

Throws: InterruptedException
this thread was killed while waiting for the lock.


makeLockName

   private File makeLockName(File file) 

Create a lock file name for the given file (append ".lock" to it.)



gentemp

   private File gentemp(File prefix) 

Create the name of a new file in the same directory as the given file. This picks a random name and then checks to make sure it doesn't already exist. It doesn't actually create the file (so there's a very slight race here.)



createDiskLock

   private void createDiskLock(File file)  throws SecurityException, IOException, InterruptedException

Attempt to lock the file, using the hairy dot-locking protocol. Does not return until the lock has been obtained.

Throws: SecurityException
the lock file could not be created (file permission problems?).
Throws: IOException
a disk I/O error occurred.
Throws: InterruptedException
this thread was killed while waiting for the lock.


removeDiskLock

   private void removeDiskLock(File file) 

Assumes that this process had at some point obtained a lock on the given file, and removes that lock. Calling this without having obtained the lock will smash someone else's lock, and you don't want to do that.



setLocked

   private synchronized void setLocked(File file)  throws InterruptedException

Marks the object as locked or unlocked. Manages the heartbeat thread (creating or killing it, as appropriate.)

Throws: InterruptedException
this thread was killed while waiting for the lock. This can only be thrown when locking, not when unlocking.


heartbeat

   private synchronized void heartbeat()  throws IOException

If we currently own a lock file, update its modification time. This is a way of informing other processes that this process is still alive, and still desires to hold the lock.

Throws: IOException
a disk I/O error occurred.


globalHeartbeat

   static synchronized void globalHeartbeat() 

Call the heartbeat() method on every UnixDotLock in `active_locks'.



updateLockList

   private static synchronized void updateLockList(UnixDotLock lock) 


finalize

   protected synchronized void finalize()  throws Throwable

If this object becomes reclaimed without unlock() having been called, then call it (thus unlocking the underlying disk file.)

Overrides:
finalize in class Object


main

   public static final void main(String[] arg)  throws SecurityException, IOException, InterruptedException


All Packages  This Package  Class Hierarchy  Class Search  Index
Freshly brewed Java API Documentation automatically generated with polardoc Version 1.0.4