All Packages This Package Class Hierarchy Class Search Index
Class grendel.storage.UnixDotLock
java.lang.Object | +----grendel.storage.UnixDotLock
- Create a file "FOO.1234" (random unused name.)
- 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.) - Unlink "FOO.1234"
(regardless of whether step 2 succeeded; now either we have a "FOO.lock" file or we don't.) - If we obtained the lock (the link() call in
step #2 succeeded), then we're done.
- 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.)
- Else, the lock is held and current. Wait a second, then goto step #1 and try again.
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:
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
main
public static final void main(String[] arg) throws SecurityException, IOException, InterruptedException
All Packages This Package Class Hierarchy Class Search IndexFreshly brewed Java API Documentation automatically generated with polardoc Version 1.0.4