org.infinispan.util.concurrent.locks.StripedLock Maven / Gradle / Ivy
package org.infinispan.util.concurrent.locks;
import net.jcip.annotations.ThreadSafe;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* A simple implementation of lock striping, using cache entry keys to lock on, primarily used to help make {@link
* org.infinispan.persistence.spi.CacheLoader} implemtations thread safe.
*
* Backed by a set of {@link java.util.concurrent.locks.ReentrantReadWriteLock} instances, and using the key hashcodes
* to determine buckets.
*
* Since buckets are used, it doesn't matter that the key in question is not removed from the lock map when no longer in
* use, since the key is not referenced in this class. Rather, the hash code is used.
*
*
* @author Manik Surtani
* @author [email protected]
* @since 4.0
*/
@ThreadSafe
public class StripedLock {
private static final Log log = LogFactory.getLog(StripedLock.class);
private static final boolean trace = log.isTraceEnabled();
private static final int DEFAULT_CONCURRENCY = 20;
private final int lockSegmentMask;
private final int lockSegmentShift;
final ReentrantReadWriteLock[] sharedLocks;
/**
* This constructor just calls {@link #StripedLock(int)} with a default concurrency value of 20.
*/
public StripedLock() {
this(DEFAULT_CONCURRENCY);
}
/**
* Creates a new StripedLock which uses a certain number of shared locks across all elements that need to be locked.
*
* @param concurrency number of threads expected to use this class concurrently.
*/
public StripedLock(int concurrency) {
int tempLockSegShift = 0;
int numLocks = 1;
while (numLocks < concurrency) {
++tempLockSegShift;
numLocks <<= 1;
}
lockSegmentShift = 32 - tempLockSegShift;
lockSegmentMask = numLocks - 1;
sharedLocks = new ReentrantReadWriteLock[numLocks];
for (int i = 0; i < numLocks; i++) {
sharedLocks[i] = new ReentrantReadWriteLock();
}
}
/**
* Blocks until a lock is acquired.
*
* @param exclusive if true, a write (exclusive) lock is attempted, otherwise a read (shared) lock is used.
*/
public void acquireLock(Object key, boolean exclusive) {
ReentrantReadWriteLock lock = getLock(key);
if (exclusive) {
lock.writeLock().lock();
if (trace) log.tracef("WL acquired for '%s'", key);
} else {
lock.readLock().lock();
if (trace) log.tracef("RL acquired for '%s'", key);
}
}
public boolean acquireLock(Object key, boolean exclusive, long millis) {
ReentrantReadWriteLock lock = getLock(key);
try {
if (exclusive) {
boolean success = lock.writeLock().tryLock(millis, TimeUnit.MILLISECONDS);
if (success && trace) log.tracef("WL acquired for '%s'", key);
return success;
} else {
boolean success = lock.readLock().tryLock(millis, TimeUnit.MILLISECONDS);
if (success && trace) log.tracef("RL acquired for '%s'", key);
return success;
}
} catch (InterruptedException e) {
log.interruptedAcquiringLock(millis, e);
return false;
}
}
/**
* Releases a lock the caller may be holding. This method is idempotent.
*/
public void releaseLock(Object key) {
ReentrantReadWriteLock lock = getLock(key);
if (lock.isWriteLockedByCurrentThread()) {
lock.writeLock().unlock();
if (trace) log.tracef("WL released for '%s'", key);
} else {
lock.readLock().unlock();
if (trace) log.tracef("RL released for '%s'", key);
}
}
public void upgradeLock(Object key) {
ReentrantReadWriteLock lock = getLock(key);
lock.readLock().unlock();
// another thread could come here and take the RL or WL, forcing us to wait
lock.writeLock().lock();
if (trace) log.tracef("RL upgraded to WL for '%s'", key);
}
public void downgradeLock(Object key) {
ReentrantReadWriteLock lock = getLock(key);
lock.readLock().lock();
lock.writeLock().unlock();
if (trace) log.tracef("WL downgraded to RL for '%s'", key);
}
final ReentrantReadWriteLock getLock(Object o) {
return sharedLocks[hashToIndex(o)];
}
final int hashToIndex(Object o) {
return hash(o) >>> lockSegmentShift & lockSegmentMask;
}
/**
* Returns a hash code for non-null Object x. Uses the same hash code spreader as most other java.util hash tables,
* except that this uses the string representation of the object passed in.
*
* @param x the object serving as a key
* @return the hash code
*/
static final int hash(Object x) {
int h = x.hashCode();
h ^= h >>> 20 ^ h >>> 12;
return h ^ h >>> 7 ^ h >>> 4;
}
/**
* Releases locks on all keys passed in. Makes multiple calls to {@link #releaseLock(Object)}. This method is
* idempotent.
*
* @param keys keys to unlock
*/
public void releaseAllLocks(List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy