package org.infinispan.container.offheap; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Holder for read write locks that provides ability to retrieve them by offset and hashCode * @author wburns * @since 9.0 */ public class StripedLock { private static final int MAXIMUM_CAPACITY = 1 << 30; private static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash private final ReadWriteLock[] locks; public StripedLock() { this(Runtime.getRuntime().availableProcessors() * 2); } public StripedLock(int lockCount) { locks = new ReadWriteLock[tableSizeFor(lockCount)]; for (int i = 0; i< locks.length; ++i) { locks[i] = new ReentrantReadWriteLock(); } } /** * Retrieves the read write lock attributed to the given object using its hashCode for lookup. * @param obj the object to use to find the lock * @return the lock associated with the object */ public ReadWriteLock getLock(Object obj) { return getLockFromHashCode(obj.hashCode()); } /** * Retrieves the lock associated with the given hashCode * @param hashCode the hashCode to retrieve the lock for * @return the lock associated with the given hashCode */ public ReadWriteLock getLockFromHashCode(int hashCode) { int h = spread(hashCode); int offset = h & (locks.length - 1); return locks[offset]; } /** * Retrieves the given lock at a provided offset. Note this is not hashCode based. This method requires care * and the knowledge of how many locks there are. This is useful when iterating over all locks * @param offset the offset of the lock to find * @return the lock at the given offset */ public ReadWriteLock getLockWithOffset(int offset) { if (offset >= locks.length) { throw new ArrayIndexOutOfBoundsException(); } return locks[offset]; } /** * Locks all write locks. Ensure that {@link StripedLock#unlockAll()} is called in a proper finally block */ public void lockAll() { for (int i = 0; i < locks.length; ++i) { locks[i].writeLock().lock(); } } /** * Unlocks all write locks, useful after {@link StripedLock#lockAll()} was invoked. */ void unlockAll() { for (int i = 0; i < locks.length; ++i) { locks[i].writeLock().unlock(); } } private static final int tableSizeFor(int c) { int n = c - 1; n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; } static final int spread(int h) { return (h ^ (h >>> 16)) & HASH_BITS; } }