[jboss-cvs] JBossCache/src/org/jboss/cache/lock ...
Jimmy Wilson
jawilson at redhat.com
Sun Jun 24 16:26:17 EDT 2007
User: jawilson
Date: 07/06/24 16:26:17
Added: src/org/jboss/cache/lock Tag: Branch_JBossCache_1_4_0
StripedLock.java
Log:
Fix for [JBCACHE-1103]. Cache Loaders need to control locking when needed, not their interceptor counterpart.
Revision Changes Path
No revision
No revision
1.5.2.2 +162 -0 JBossCache/src/org/jboss/cache/lock/StripedLock.java
(In the diff below, changes in quantity of whitespace are not shown.)
Index: StripedLock.java
===================================================================
RCS file: StripedLock.java
diff -N StripedLock.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ StripedLock.java 24 Jun 2007 20:26:16 -0000 1.5.2.2
@@ -0,0 +1,162 @@
+package org.jboss.cache.lock;
+
+import net.jcip.annotations.ThreadSafe;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.jboss.cache.Fqn;
+
+import java.util.List;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * A simple implementation of lock striping, using Fqns as the keys to lock on, primarily used to help make
+ * {@link org.jboss.cache.loader.CacheLoader} implemtations thread safe.
+ * <p/>
+ * Backed by a set of {@link java.util.concurrent.locks.ReentrantReadWriteLock} instances, and using the {@link org.jboss.cache.Fqn}
+ * hashcodes to determine buckets.
+ * <p/>
+ * Since buckets are used, it doesn't matter that the Fqn in question is not removed from the lock map when no longer in
+ * use, since the Fqn is not referenced in this class. Rather, the hash code is used.
+ * <p/>
+ *
+ * @author <a href="mailto:manik at jboss.org">Manik Surtani</a>
+ * @since 2.0.0
+ */
+ at ThreadSafe
+public class StripedLock
+{
+ private static final int DEFAULT_CONCURRENCY = 20;
+ private int lockSegmentMask;
+ private int lockSegmentShift;
+
+ ReentrantReadWriteLock[] sharedLocks;
+ private Log log = LogFactory.getLog(StripedLock.class);
+
+ /**
+ * 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 fqn the Fqn to lock on
+ * @param exclusive if true, a write (exclusive) lock is attempted, otherwise a read (shared) lock is used.
+ */
+ public void acquireLock(Fqn fqn, boolean exclusive)
+ {
+ ReentrantReadWriteLock lock = getLock(fqn);
+
+ if (exclusive)
+ {
+ // allow for reentrancy
+ lock.writeLock().lock();
+ }
+ else
+ lock.readLock().lock();
+ }
+
+ /**
+ * Releases a lock the caller may be holding. This method is idempotent.
+ *
+ * @param fqn the Fqn to release
+ */
+ public void releaseLock(Fqn fqn)
+ {
+ ReentrantReadWriteLock lock = getLock(fqn);
+ if (lock.isWriteLockedByCurrentThread())
+ {
+ lock.writeLock().unlock();
+ // check that we still don't have a stale WL
+ if (lock.isWriteLockedByCurrentThread() && log.isWarnEnabled())
+ log.warn("Write lock still exists on Fqn " + fqn + " for current thread. Perhaps this was write-locked more than once?");
+ }
+ else
+ {
+ try
+ {
+ lock.readLock().unlock();
+ }
+ catch (IllegalMonitorStateException imse)
+ {
+ // perhaps the RL was already released earlier?
+ }
+ }
+ }
+
+ ReentrantReadWriteLock getLock(Object o)
+ {
+ return sharedLocks[hashToIndex(o)];
+ }
+
+ 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
+ */
+ int hash(Object x)
+ {
+ int h = x.toString().hashCode();
+ h += ~(h << 9);
+ h ^= (h >>> 14);
+ h += (h << 4);
+ h ^= (h >>> 10);
+ return h;
+ }
+
+ /**
+ * Releases locks on all fqns passed in. Makes multiple calls to {@link #releaseLock(org.jboss.cache.Fqn)}. This method is idempotent.
+ *
+ * @param fqns list of fqns
+ * @see #releaseLock(org.jboss.cache.Fqn)
+ */
+ public void releaseAllLocks(List<Fqn> fqns)
+ {
+ for (Fqn f : fqns) releaseLock(f);
+ }
+
+ /**
+ * Acquires locks on all fqns passed in. Makes multiple calls to {@link #acquireLock(org.jboss.cache.Fqn,boolean)}
+ *
+ * @param fqns list of fqns
+ * @param exclusive whether locks are exclusive.
+ * @see #acquireLock(org.jboss.cache.Fqn,boolean)
+ */
+ public void acquireAllLocks(List<Fqn> fqns, boolean exclusive)
+ {
+ for (Fqn f : fqns) acquireLock(f, exclusive);
+ }
+}
More information about the jboss-cvs-commits
mailing list