|
@@ -25,42 +25,156 @@ import org.slf4j.LoggerFactory;
|
|
|
|
|
|
import java.util.Map;
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
+import java.util.function.Consumer;
|
|
|
|
|
|
/**
|
|
|
* Manages the locks on a given resource. A new lock is created for each
|
|
|
* and every unique resource. Uniqueness of resource depends on the
|
|
|
* {@code equals} implementation of it.
|
|
|
*/
|
|
|
-public class LockManager<T> {
|
|
|
+public class LockManager<R> {
|
|
|
|
|
|
private static final Logger LOG = LoggerFactory.getLogger(LockManager.class);
|
|
|
|
|
|
- private final Map<T, ActiveLock> activeLocks = new ConcurrentHashMap<>();
|
|
|
+ private final Map<R, ActiveLock> activeLocks = new ConcurrentHashMap<>();
|
|
|
private final GenericObjectPool<ActiveLock> lockPool =
|
|
|
new GenericObjectPool<>(new PooledLockFactory());
|
|
|
|
|
|
/**
|
|
|
- * Creates new LockManager instance.
|
|
|
+ * Creates new LockManager instance with the given Configuration.
|
|
|
*
|
|
|
* @param conf Configuration object
|
|
|
*/
|
|
|
- public LockManager(Configuration conf) {
|
|
|
- int maxPoolSize = conf.getInt(HddsConfigKeys.HDDS_LOCK_MAX_CONCURRENCY,
|
|
|
+ public LockManager(final Configuration conf) {
|
|
|
+ final int maxPoolSize = conf.getInt(
|
|
|
+ HddsConfigKeys.HDDS_LOCK_MAX_CONCURRENCY,
|
|
|
HddsConfigKeys.HDDS_LOCK_MAX_CONCURRENCY_DEFAULT);
|
|
|
lockPool.setMaxTotal(maxPoolSize);
|
|
|
}
|
|
|
|
|
|
-
|
|
|
/**
|
|
|
* Acquires the lock on given resource.
|
|
|
*
|
|
|
* <p>If the lock is not available then the current thread becomes
|
|
|
* disabled for thread scheduling purposes and lies dormant until the
|
|
|
* lock has been acquired.
|
|
|
+ *
|
|
|
+ * @param resource on which the lock has to be acquired
|
|
|
+ * @deprecated Use {@link LockManager#writeLock} instead
|
|
|
+ */
|
|
|
+ public void lock(final R resource) {
|
|
|
+ writeLock(resource);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Releases the lock on given resource.
|
|
|
+ *
|
|
|
+ * @param resource for which the lock has to be released
|
|
|
+ * @deprecated Use {@link LockManager#writeUnlock} instead
|
|
|
+ */
|
|
|
+ public void unlock(final R resource) {
|
|
|
+ writeUnlock(resource);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Acquires the read lock on given resource.
|
|
|
+ *
|
|
|
+ * <p>Acquires the read lock on resource if the write lock is not held by
|
|
|
+ * another thread and returns immediately.
|
|
|
+ *
|
|
|
+ * <p>If the write lock on resource is held by another thread then
|
|
|
+ * the current thread becomes disabled for thread scheduling
|
|
|
+ * purposes and lies dormant until the read lock has been acquired.
|
|
|
+ *
|
|
|
+ * @param resource on which the read lock has to be acquired
|
|
|
+ */
|
|
|
+ public void readLock(final R resource) {
|
|
|
+ acquire(resource, ActiveLock::readLock);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Releases the read lock on given resource.
|
|
|
+ *
|
|
|
+ * @param resource for which the read lock has to be released
|
|
|
+ * @throws IllegalMonitorStateException if the current thread does not
|
|
|
+ * hold this lock
|
|
|
+ */
|
|
|
+ public void readUnlock(final R resource) throws IllegalMonitorStateException {
|
|
|
+ release(resource, ActiveLock::readUnlock);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Acquires the write lock on given resource.
|
|
|
+ *
|
|
|
+ * <p>Acquires the write lock on resource if neither the read nor write lock
|
|
|
+ * are held by another thread and returns immediately.
|
|
|
+ *
|
|
|
+ * <p>If the current thread already holds the write lock then the
|
|
|
+ * hold count is incremented by one and the method returns
|
|
|
+ * immediately.
|
|
|
+ *
|
|
|
+ * <p>If the lock is held by another thread then the current
|
|
|
+ * thread becomes disabled for thread scheduling purposes and
|
|
|
+ * lies dormant until the write lock has been acquired.
|
|
|
+ *
|
|
|
+ * @param resource on which the lock has to be acquired
|
|
|
*/
|
|
|
- public void lock(T resource) {
|
|
|
- activeLocks.compute(resource, (k, v) -> {
|
|
|
- ActiveLock lock;
|
|
|
+ public void writeLock(final R resource) {
|
|
|
+ acquire(resource, ActiveLock::writeLock);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Releases the write lock on given resource.
|
|
|
+ *
|
|
|
+ * @param resource for which the lock has to be released
|
|
|
+ * @throws IllegalMonitorStateException if the current thread does not
|
|
|
+ * hold this lock
|
|
|
+ */
|
|
|
+ public void writeUnlock(final R resource)
|
|
|
+ throws IllegalMonitorStateException {
|
|
|
+ release(resource, ActiveLock::writeUnlock);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Acquires the lock on given resource using the provided lock function.
|
|
|
+ *
|
|
|
+ * @param resource on which the lock has to be acquired
|
|
|
+ * @param lockFn function to acquire the lock
|
|
|
+ */
|
|
|
+ private void acquire(final R resource, final Consumer<ActiveLock> lockFn) {
|
|
|
+ lockFn.accept(getLockForLocking(resource));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Releases the lock on given resource using the provided release function.
|
|
|
+ *
|
|
|
+ * @param resource for which the lock has to be released
|
|
|
+ * @param releaseFn function to release the lock
|
|
|
+ */
|
|
|
+ private void release(final R resource, final Consumer<ActiveLock> releaseFn) {
|
|
|
+ final ActiveLock lock = getLockForReleasing(resource);
|
|
|
+ releaseFn.accept(lock);
|
|
|
+ decrementActiveLockCount(resource);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns {@link ActiveLock} instance for the given resource,
|
|
|
+ * on which the lock can be acquired.
|
|
|
+ *
|
|
|
+ * @param resource on which the lock has to be acquired
|
|
|
+ * @return {@link ActiveLock} instance
|
|
|
+ */
|
|
|
+ private ActiveLock getLockForLocking(final R resource) {
|
|
|
+ /*
|
|
|
+ * While getting a lock object for locking we should
|
|
|
+ * atomically increment the active count of the lock.
|
|
|
+ *
|
|
|
+ * This is to avoid cases where the selected lock could
|
|
|
+ * be removed from the activeLocks map and returned to
|
|
|
+ * the object pool.
|
|
|
+ */
|
|
|
+ return activeLocks.compute(resource, (k, v) -> {
|
|
|
+ final ActiveLock lock;
|
|
|
try {
|
|
|
if (v == null) {
|
|
|
lock = lockPool.borrowObject();
|
|
@@ -73,22 +187,34 @@ public class LockManager<T> {
|
|
|
throw new RuntimeException(ex);
|
|
|
}
|
|
|
return lock;
|
|
|
- }).lock();
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Releases the lock on given resource.
|
|
|
+ * Returns {@link ActiveLock} instance for the given resource,
|
|
|
+ * for which the lock has to be released.
|
|
|
+ *
|
|
|
+ * @param resource for which the lock has to be released
|
|
|
+ * @return {@link ActiveLock} instance
|
|
|
*/
|
|
|
- public void unlock(T resource) {
|
|
|
- ActiveLock lock = activeLocks.get(resource);
|
|
|
- if (lock == null) {
|
|
|
- // Someone is releasing a lock which was never acquired. Log and return.
|
|
|
- LOG.error("Trying to release the lock on {}, which was never acquired.",
|
|
|
- resource);
|
|
|
- throw new IllegalMonitorStateException("Releasing lock on resource "
|
|
|
- + resource + " without acquiring lock");
|
|
|
+ private ActiveLock getLockForReleasing(final R resource) {
|
|
|
+ if (activeLocks.containsKey(resource)) {
|
|
|
+ return activeLocks.get(resource);
|
|
|
}
|
|
|
- lock.unlock();
|
|
|
+ // Someone is releasing a lock which was never acquired.
|
|
|
+ LOG.error("Trying to release the lock on {}, which was never acquired.",
|
|
|
+ resource);
|
|
|
+ throw new IllegalMonitorStateException("Releasing lock on resource "
|
|
|
+ + resource + " without acquiring lock");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Decrements the active lock count and returns the {@link ActiveLock}
|
|
|
+ * object to pool if the active count is 0.
|
|
|
+ *
|
|
|
+ * @param resource resource to which the ActiveLock is associated
|
|
|
+ */
|
|
|
+ private void decrementActiveLockCount(final R resource) {
|
|
|
activeLocks.computeIfPresent(resource, (k, v) -> {
|
|
|
v.decrementActiveCount();
|
|
|
if (v.getActiveLockCount() != 0) {
|