 |
|
 |
|
Class grendel.storage.UnixDotLock
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:
- 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.
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
private static final boolean debug
Turn this on to cause activity to be logged to System.err.
private static Thread lock_heartbeat_thread
Holds the (one) thread that updates the lock date every <60 seconds.
private static Vector active_locks
List of locked locks. The vector holds UnixDotLock objects.
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.
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.
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.
private File locked_file
The name of the file for which this lock is being held.
Constructors
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
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.
private File makeLockName(File file)
Create a lock file name for the given file (append ".lock" to it.)
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.)
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.
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.
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.
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.
static synchronized void globalHeartbeat()
Call the heartbeat() method on every UnixDotLock in `active_locks'.
private static synchronized void updateLockList(UnixDotLock lock)
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
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
|
|
 |