/* This code is part of Freenet. It is distributed under the GNU General
* Public License, version 2 (or at your option any later version). See
* http://www.gnu.org/ for further details of the GPL. */
package freenet.store.saltedhash;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import freenet.support.Logger;
import freenet.support.Logger.LogLevel;
/**
* Lock Manager
*
* Handle locking/unlocking of individual offsets.
*
* @author sdiz
*/
public class LockManager {
private static boolean logDEBUG;
private volatile boolean shutdown;
private Lock entryLock = new ReentrantLock();
private Map<Long, Condition> lockMap = new HashMap<Long, Condition>();
LockManager() {
logDEBUG = Logger.shouldLog(LogLevel.DEBUG, this);
}
/**
* Lock the entry
*
* This lock is <strong>not</strong> re-entrance. No threads except Cleaner should hold more
* then one lock at a time (or deadlock may occur).
*/
Condition lockEntry(long offset) {
if (logDEBUG)
Logger.debug(this, "try locking " + offset, new Exception());
Condition condition;
try {
entryLock.lock();
try {
do {
if (shutdown)
return null;
Condition lockCond = lockMap.get(offset);
if (lockCond != null)
lockCond.await(10, TimeUnit.SECONDS); // 10s for checking shutdown
else
break;
} while (true);
condition = entryLock.newCondition();
lockMap.put(offset, condition);
} finally {
entryLock.unlock();
}
} catch (InterruptedException e) {
Logger.error(this, "lock interrupted", e);
return null;
}
if (logDEBUG)
Logger.debug(this, "locked " + offset, new Exception());
return condition;
}
/**
* Unlock the entry
*/
void unlockEntry(long offset, Condition condition) {
if (logDEBUG)
Logger.debug(this, "unlocking " + offset, new Exception("debug"));
entryLock.lock();
try {
Condition cond = lockMap.remove(offset);
assert cond == condition;
cond.signal();
} finally {
entryLock.unlock();
}
}
/**
* Shutdown and wait for all entries unlocked
*/
void shutdown() {
shutdown = true;
entryLock.lock();
try {
while (!lockMap.isEmpty()) {
Condition cond = lockMap.values().iterator().next();
cond.awaitUninterruptibly();
}
} finally {
entryLock.unlock();
}
}
}