Clover coverage report -
Coverage timestamp: Thu Jul 5 2007 20:02:32 EDT
file stats: LOC: 162   Methods: 9
NCLOC: 89   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
StripedLock.java 90% 85.3% 77.8% 84.9%
coverage coverage
 1    package org.jboss.cache.lock;
 2   
 3    import net.jcip.annotations.ThreadSafe;
 4    import org.apache.commons.logging.Log;
 5    import org.apache.commons.logging.LogFactory;
 6    import org.jboss.cache.Fqn;
 7   
 8    import java.util.List;
 9    import java.util.concurrent.locks.ReentrantReadWriteLock;
 10   
 11    /**
 12    * A simple implementation of lock striping, using Fqns as the keys to lock on, primarily used to help make
 13    * {@link org.jboss.cache.loader.CacheLoader} implemtations thread safe.
 14    * <p/>
 15    * Backed by a set of {@link java.util.concurrent.locks.ReentrantReadWriteLock} instances, and using the {@link org.jboss.cache.Fqn}
 16    * hashcodes to determine buckets.
 17    * <p/>
 18    * Since buckets are used, it doesn't matter that the Fqn in question is not removed from the lock map when no longer in
 19    * use, since the Fqn is not referenced in this class. Rather, the hash code is used.
 20    * <p/>
 21    *
 22    * @author <a href="mailto:manik@jboss.org">Manik Surtani</a>
 23    * @since 2.0.0
 24    */
 25    @ThreadSafe
 26    public class StripedLock
 27    {
 28    private static final int DEFAULT_CONCURRENCY = 20;
 29    private int lockSegmentMask;
 30    private int lockSegmentShift;
 31   
 32    ReentrantReadWriteLock[] sharedLocks;
 33    private Log log = LogFactory.getLog(StripedLock.class);
 34   
 35    /**
 36    * This constructor just calls {@link #StripedLock(int)} with a default concurrency value of 20.
 37    */
 38  653 public StripedLock()
 39    {
 40  653 this(DEFAULT_CONCURRENCY);
 41    }
 42   
 43    /**
 44    * Creates a new StripedLock which uses a certain number of shared locks across all elements that need to be locked.
 45    *
 46    * @param concurrency number of threads expected to use this class concurrently.
 47    */
 48  653 public StripedLock(int concurrency)
 49    {
 50  653 int tempLockSegShift = 0;
 51  653 int numLocks = 1;
 52  653 while (numLocks < concurrency)
 53    {
 54  3265 ++tempLockSegShift;
 55  3265 numLocks <<= 1;
 56    }
 57  653 lockSegmentShift = 32 - tempLockSegShift;
 58  653 lockSegmentMask = numLocks - 1;
 59   
 60  653 sharedLocks = new ReentrantReadWriteLock[numLocks];
 61   
 62  20896 for (int i = 0; i < numLocks; i++) sharedLocks[i] = new ReentrantReadWriteLock();
 63    }
 64   
 65    /**
 66    * Blocks until a lock is acquired.
 67    *
 68    * @param fqn the Fqn to lock on
 69    * @param exclusive if true, a write (exclusive) lock is attempted, otherwise a read (shared) lock is used.
 70    */
 71  247191 public void acquireLock(Fqn fqn, boolean exclusive)
 72    {
 73  247191 ReentrantReadWriteLock lock = getLock(fqn);
 74   
 75  247191 if (exclusive)
 76    {
 77    // allow for reentrancy
 78  227889 lock.writeLock().lock();
 79    }
 80    else
 81  19302 lock.readLock().lock();
 82    }
 83   
 84    /**
 85    * Releases a lock the caller may be holding. This method is idempotent.
 86    *
 87    * @param fqn the Fqn to release
 88    */
 89  247191 public void releaseLock(Fqn fqn)
 90    {
 91  247191 ReentrantReadWriteLock lock = getLock(fqn);
 92  247191 if (lock.isWriteLockedByCurrentThread())
 93    {
 94  227889 lock.writeLock().unlock();
 95    // check that we still don't have a stale WL
 96  227889 if (lock.isWriteLockedByCurrentThread() && log.isWarnEnabled())
 97  0 log.warn("Write lock still exists on Fqn " + fqn + " for current thread. Perhaps this was write-locked more than once?");
 98    }
 99    else
 100    {
 101  19302 try
 102    {
 103  19302 lock.readLock().unlock();
 104    }
 105    catch (IllegalMonitorStateException imse)
 106    {
 107    // perhaps the RL was already released earlier?
 108    }
 109    }
 110    }
 111   
 112  495382 ReentrantReadWriteLock getLock(Object o)
 113    {
 114  495378 return sharedLocks[hashToIndex(o)];
 115    }
 116   
 117  495382 int hashToIndex(Object o)
 118    {
 119  495382 return (hash(o) >>> lockSegmentShift) & lockSegmentMask;
 120    }
 121   
 122    /**
 123    * Returns a hash code for non-null Object x.
 124    * Uses the same hash code spreader as most other java.util hash tables, except that this uses the string representation
 125    * of the object passed in.
 126    *
 127    * @param x the object serving as a key
 128    * @return the hash code
 129    */
 130  495382 int hash(Object x)
 131    {
 132  495382 int h = x.toString().hashCode();
 133  495382 h += ~(h << 9);
 134  495380 h ^= (h >>> 14);
 135  495382 h += (h << 4);
 136  495382 h ^= (h >>> 10);
 137  495382 return h;
 138    }
 139   
 140    /**
 141    * Releases locks on all fqns passed in. Makes multiple calls to {@link #releaseLock(org.jboss.cache.Fqn)}. This method is idempotent.
 142    *
 143    * @param fqns list of fqns
 144    * @see #releaseLock(org.jboss.cache.Fqn)
 145    */
 146  0 public void releaseAllLocks(List<Fqn> fqns)
 147    {
 148  0 for (Fqn f : fqns) releaseLock(f);
 149    }
 150   
 151    /**
 152    * Acquires locks on all fqns passed in. Makes multiple calls to {@link #acquireLock(org.jboss.cache.Fqn,boolean)}
 153    *
 154    * @param fqns list of fqns
 155    * @param exclusive whether locks are exclusive.
 156    * @see #acquireLock(org.jboss.cache.Fqn,boolean)
 157    */
 158  0 public void acquireAllLocks(List<Fqn> fqns, boolean exclusive)
 159    {
 160  0 for (Fqn f : fqns) acquireLock(f, exclusive);
 161    }
 162    }