package org.infinispan.util.concurrent.locks.impl; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; import org.infinispan.commons.util.ByRef; import org.infinispan.factories.annotations.Inject; import org.infinispan.util.TimeService; import org.infinispan.util.concurrent.locks.DeadlockChecker; import org.infinispan.util.concurrent.locks.ExtendedLockPromise; /** * A lock container that creates and maintains a new lock per entry. * * @author Pedro Ruivo * @since 8.0 */ public class PerKeyLockContainer implements LockContainer { private static final int INITIAL_CAPACITY = 32; private final ConcurrentMap<Object, InfinispanLock> lockMap; private TimeService timeService; public PerKeyLockContainer() { lockMap = new ConcurrentHashMap<>(INITIAL_CAPACITY); } @Inject public void inject(TimeService timeService) { this.timeService = timeService; for (InfinispanLock lock : lockMap.values()) { lock.setTimeService(timeService); } } @Override public ExtendedLockPromise acquire(Object key, Object lockOwner, long time, TimeUnit timeUnit) { ByRef<ExtendedLockPromise> reference = ByRef.create(null); lockMap.compute(key, (aKey, lock) -> { if (lock == null) { lock = createInfinispanLock(aKey); } reference.set(lock.acquire(lockOwner, time, timeUnit)); return lock; }); return reference.get(); } @Override public InfinispanLock getLock(Object key) { return lockMap.get(key); } @Override public void release(Object key, Object lockOwner) { lockMap.computeIfPresent(key, (ignoredKey, lock) -> { lock.release(lockOwner); return !lock.isLocked() ? null : lock; //remove it if empty }); } @Override public int getNumLocksHeld() { int count = 0; for (InfinispanLock lock : lockMap.values()) { if (lock.isLocked()) { count++; } } return count; } @Override public boolean isLocked(Object key) { InfinispanLock lock = lockMap.get(key); return lock != null && lock.isLocked(); } @Override public int size() { return lockMap.size(); } @Override public void deadlockCheck(DeadlockChecker deadlockChecker) { lockMap.values().forEach(lock -> lock.deadlockCheck(deadlockChecker)); } @Override public String toString() { return "PerKeyLockContainer{" + "locks=" + lockMap + '}'; } private InfinispanLock createInfinispanLock(Object key) { return new InfinispanLock(timeService, () -> lockMap.computeIfPresent(key, (ignoredKey, lock) -> lock.isLocked() ? lock : null)); } }