jdk/src/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java
changeset 62 ea448c54b34b
parent 2 90ce3da70b43
child 2431 54a65419300f
--- a/jdk/src/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java	Mon Mar 10 23:23:47 2008 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java	Mon Mar 10 23:23:47 2008 -0700
@@ -222,7 +222,7 @@
     /** Inner class providing writelock */
     private final ReentrantReadWriteLock.WriteLock writerLock;
     /** Performs all synchronization mechanics */
-    private final Sync sync;
+    final Sync sync;
 
     /**
      * Creates a new {@code ReentrantReadWriteLock} with
@@ -239,7 +239,7 @@
      * @param fair {@code true} if this lock should use a fair ordering policy
      */
     public ReentrantReadWriteLock(boolean fair) {
-        sync = (fair)? new FairSync() : new NonfairSync();
+        sync = fair ? new FairSync() : new NonfairSync();
         readerLock = new ReadLock(this);
         writerLock = new WriteLock(this);
     }
@@ -256,8 +256,8 @@
 
         /*
          * Read vs write count extraction constants and functions.
-         * Lock state is logically divided into two shorts: The lower
-         * one representing the exclusive (writer) lock hold count,
+         * Lock state is logically divided into two unsigned shorts:
+         * The lower one representing the exclusive (writer) lock hold count,
          * and the upper the shared (reader) hold count.
          */
 
@@ -279,13 +279,6 @@
             int count;
             // Use id, not reference, to avoid garbage retention
             final long tid = Thread.currentThread().getId();
-            /** Decrement if positive; return previous value */
-            int tryDecrement() {
-                int c = count;
-                if (c > 0)
-                    count = c - 1;
-                return c;
-            }
         }
 
         /**
@@ -303,7 +296,7 @@
          * The number of read locks held by current thread.
          * Initialized only in constructor and readObject.
          */
-        transient ThreadLocalHoldCounter readHolds;
+        private transient ThreadLocalHoldCounter readHolds;
 
         /**
          * The hold count of the last thread to successfully acquire
@@ -312,7 +305,17 @@
          * acquire. This is non-volatile since it is just used
          * as a heuristic, and would be great for threads to cache.
          */
-        transient HoldCounter cachedHoldCounter;
+        private transient HoldCounter cachedHoldCounter;
+
+        /**
+         * firstReader is the first thread to have acquired the read lock.
+         * firstReaderHoldCount is firstReader's hold count.
+         * This allows tracking of read holds for uncontended read
+         * locks to be very cheap.
+         */
+        private final static long INVALID_THREAD_ID = -1;
+        private transient long firstReader = INVALID_THREAD_ID;
+        private transient int firstReaderHoldCount;
 
         Sync() {
             readHolds = new ThreadLocalHoldCounter();
@@ -390,12 +393,25 @@
         }
 
         protected final boolean tryReleaseShared(int unused) {
-            HoldCounter rh = cachedHoldCounter;
-            Thread current = Thread.currentThread();
-            if (rh == null || rh.tid != current.getId())
-                rh = readHolds.get();
-            if (rh.tryDecrement() <= 0)
-                throw new IllegalMonitorStateException();
+            long tid = Thread.currentThread().getId();
+            if (firstReader == tid) {
+                // assert firstReaderHoldCount > 0;
+                if (firstReaderHoldCount == 1)
+                    firstReader = INVALID_THREAD_ID;
+                else
+                    firstReaderHoldCount--;
+            } else {
+                HoldCounter rh = cachedHoldCounter;
+                if (rh == null || rh.tid != tid)
+                    rh = readHolds.get();
+                int count = rh.count;
+                if (count <= 1) {
+                    readHolds.remove();
+                    if (count <= 0)
+                        throw unmatchedUnlockException();
+                }
+                --rh.count;
+            }
             for (;;) {
                 int c = getState();
                 int nextc = c - SHARED_UNIT;
@@ -404,12 +420,16 @@
             }
         }
 
+        private IllegalMonitorStateException unmatchedUnlockException() {
+            return new IllegalMonitorStateException(
+                "attempt to unlock read lock, not locked by current thread");
+        }
+
         protected final int tryAcquireShared(int unused) {
             /*
              * Walkthrough:
              * 1. If write lock held by another thread, fail.
-             * 2. If count saturated, throw error.
-             * 3. Otherwise, this thread is eligible for
+             * 2. Otherwise, this thread is eligible for
              *    lock wrt state, so ask if it should block
              *    because of queue policy. If not, try
              *    to grant by CASing state and updating count.
@@ -417,23 +437,33 @@
              *    acquires, which is postponed to full version
              *    to avoid having to check hold count in
              *    the more typical non-reentrant case.
-             * 4. If step 3 fails either because thread
-             *    apparently not eligible or CAS fails,
-             *    chain to version with full retry loop.
+             * 3. If step 2 fails either because thread
+             *    apparently not eligible or CAS fails or count
+             *    saturated, chain to version with full retry loop.
              */
             Thread current = Thread.currentThread();
             int c = getState();
             if (exclusiveCount(c) != 0 &&
                 getExclusiveOwnerThread() != current)
                 return -1;
-            if (sharedCount(c) == MAX_COUNT)
-                throw new Error("Maximum lock count exceeded");
+            int r = sharedCount(c);
             if (!readerShouldBlock() &&
+                r < MAX_COUNT &&
                 compareAndSetState(c, c + SHARED_UNIT)) {
-                HoldCounter rh = cachedHoldCounter;
-                if (rh == null || rh.tid != current.getId())
-                    cachedHoldCounter = rh = readHolds.get();
-                rh.count++;
+                long tid = current.getId();
+                if (r == 0) {
+                    firstReader = tid;
+                    firstReaderHoldCount = 1;
+                } else if (firstReader == tid) {
+                    firstReaderHoldCount++;
+                } else {
+                    HoldCounter rh = cachedHoldCounter;
+                    if (rh == null || rh.tid != tid)
+                        cachedHoldCounter = rh = readHolds.get();
+                    else if (rh.count == 0)
+                        readHolds.set(rh);
+                    rh.count++;
+                }
                 return 1;
             }
             return fullTryAcquireShared(current);
@@ -450,20 +480,56 @@
              * complicating tryAcquireShared with interactions between
              * retries and lazily reading hold counts.
              */
-            HoldCounter rh = cachedHoldCounter;
-            if (rh == null || rh.tid != current.getId())
-                rh = readHolds.get();
+            HoldCounter rh = null;
             for (;;) {
                 int c = getState();
-                int w = exclusiveCount(c);
-                if ((w != 0 && getExclusiveOwnerThread() != current) ||
-                    ((rh.count | w) == 0 && readerShouldBlock()))
-                    return -1;
+                if (exclusiveCount(c) != 0) {
+                    if (getExclusiveOwnerThread() != current)
+                        //if (removeNeeded) readHolds.remove();
+                        return -1;
+                    // else we hold the exclusive lock; blocking here
+                    // would cause deadlock.
+                } else if (readerShouldBlock()) {
+                    // Make sure we're not acquiring read lock reentrantly
+                    long tid = current.getId();
+                    if (firstReader == tid) {
+                        // assert firstReaderHoldCount > 0;
+                    } else {
+                        if (rh == null) {
+                            rh = cachedHoldCounter;
+                            if (rh == null || rh.tid != tid) {
+                                rh = readHolds.get();
+                                if (rh.count == 0)
+                                    readHolds.remove();
+                            }
+                        }
+                        if (rh.count == 0)
+                            return -1;
+                    }
+                }
                 if (sharedCount(c) == MAX_COUNT)
                     throw new Error("Maximum lock count exceeded");
                 if (compareAndSetState(c, c + SHARED_UNIT)) {
-                    cachedHoldCounter = rh; // cache for release
-                    rh.count++;
+                    long tid = current.getId();
+                    if (sharedCount(c) == 0) {
+                        firstReader = tid;
+                        firstReaderHoldCount = 1;
+                    } else if (firstReader == tid) {
+                        firstReaderHoldCount++;
+                    } else {
+                        if (rh == null) {
+                            rh = cachedHoldCounter;
+                            if (rh != null && rh.tid == tid) {
+                                if (rh.count == 0)
+                                    readHolds.set(rh);
+                            } else {
+                                rh = readHolds.get();
+                            }
+                        } else if (rh.count == 0)
+                            readHolds.set(rh);
+                        cachedHoldCounter = rh; // cache for release
+                        rh.count++;
+                    }
                     return 1;
                 }
             }
@@ -472,14 +538,14 @@
         /**
          * Performs tryLock for write, enabling barging in both modes.
          * This is identical in effect to tryAcquire except for lack
-         * of calls to writerShouldBlock
+         * of calls to writerShouldBlock.
          */
         final boolean tryWriteLock() {
             Thread current = Thread.currentThread();
             int c = getState();
             if (c != 0) {
                 int w = exclusiveCount(c);
-                if (w == 0 ||current != getExclusiveOwnerThread())
+                if (w == 0 || current != getExclusiveOwnerThread())
                     return false;
                 if (w == MAX_COUNT)
                     throw new Error("Maximum lock count exceeded");
@@ -493,7 +559,7 @@
         /**
          * Performs tryLock for read, enabling barging in both modes.
          * This is identical in effect to tryAcquireShared except for
-         * lack of calls to readerShouldBlock
+         * lack of calls to readerShouldBlock.
          */
         final boolean tryReadLock() {
             Thread current = Thread.currentThread();
@@ -502,13 +568,24 @@
                 if (exclusiveCount(c) != 0 &&
                     getExclusiveOwnerThread() != current)
                     return false;
-                if (sharedCount(c) == MAX_COUNT)
+                int r = sharedCount(c);
+                if (r == MAX_COUNT)
                     throw new Error("Maximum lock count exceeded");
                 if (compareAndSetState(c, c + SHARED_UNIT)) {
-                    HoldCounter rh = cachedHoldCounter;
-                    if (rh == null || rh.tid != current.getId())
-                        cachedHoldCounter = rh = readHolds.get();
-                    rh.count++;
+                    long tid = current.getId();
+                    if (r == 0) {
+                        firstReader = tid;
+                        firstReaderHoldCount = 1;
+                    } else if (firstReader == tid) {
+                        firstReaderHoldCount++;
+                    } else {
+                        HoldCounter rh = cachedHoldCounter;
+                        if (rh == null || rh.tid != tid)
+                            cachedHoldCounter = rh = readHolds.get();
+                        else if (rh.count == 0)
+                            readHolds.set(rh);
+                        rh.count++;
+                    }
                     return true;
                 }
             }
@@ -546,7 +623,20 @@
         }
 
         final int getReadHoldCount() {
-            return getReadLockCount() == 0? 0 : readHolds.get().count;
+            if (getReadLockCount() == 0)
+                return 0;
+
+            long tid = Thread.currentThread().getId();
+            if (firstReader == tid)
+                return firstReaderHoldCount;
+
+            HoldCounter rh = cachedHoldCounter;
+            if (rh != null && rh.tid == tid)
+                return rh.count;
+
+            int count = readHolds.get().count;
+            if (count == 0) readHolds.remove();
+            return count;
         }
 
         /**
@@ -557,6 +647,7 @@
             throws java.io.IOException, ClassNotFoundException {
             s.defaultReadObject();
             readHolds = new ThreadLocalHoldCounter();
+            firstReader = INVALID_THREAD_ID;
             setState(0); // reset to unlocked state
         }