ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/jsr166e/StampedLock.java
(Generate patch)

Comparing jsr166/src/jsr166e/StampedLock.java (file contents):
Revision 1.28 by dl, Tue Jan 22 15:42:28 2013 UTC vs.
Revision 1.37 by jsr166, Sun Jul 14 19:55:05 2013 UTC

# Line 5 | Line 5
5   */
6  
7   package jsr166e;
8
9 import java.util.concurrent.ThreadLocalRandom;
8   import java.util.concurrent.TimeUnit;
9 < import java.util.concurrent.locks.*;
9 > import java.util.concurrent.locks.Lock;
10 > import java.util.concurrent.locks.Condition;
11 > import java.util.concurrent.locks.ReadWriteLock;
12 > import java.util.concurrent.locks.LockSupport;
13  
14   /**
15   * A capability-based lock with three modes for controlling read/write
# Line 37 | Line 38 | import java.util.concurrent.locks.*;
38   *  <li><b>Optimistic Reading.</b> Method {@link #tryOptimisticRead}
39   *   returns a non-zero stamp only if the lock is not currently held
40   *   in write mode. Method {@link #validate} returns true if the lock
41 < *   has not since been acquired in write mode. This mode can be
42 < *   thought of as an extremely weak version of a read-lock, that can
43 < *   be broken by a writer at any time.  The use of optimistic mode
44 < *   for short read-only code segments often reduces contention and
45 < *   improves throughput.  However, its use is inherently fragile.
46 < *   Optimistic read sections should only read fields and hold them in
47 < *   local variables for later use after validation. Fields read while
48 < *   in optimistic mode may be wildly inconsistent, so usage applies
49 < *   only when you are familiar enough with data representations to
50 < *   check consistency and/or repeatedly invoke method {@code
51 < *   validate()}.  For example, such steps are typically required when
52 < *   first reading an object or array reference, and then accessing
53 < *   one of its fields, elements or methods. </li>
41 > *   has not been acquired in write mode since obtaining a given
42 > *   stamp.  This mode can be thought of as an extremely weak version
43 > *   of a read-lock, that can be broken by a writer at any time.  The
44 > *   use of optimistic mode for short read-only code segments often
45 > *   reduces contention and improves throughput.  However, its use is
46 > *   inherently fragile.  Optimistic read sections should only read
47 > *   fields and hold them in local variables for later use after
48 > *   validation. Fields read while in optimistic mode may be wildly
49 > *   inconsistent, so usage applies only when you are familiar enough
50 > *   with data representations to check consistency and/or repeatedly
51 > *   invoke method {@code validate()}.  For example, such steps are
52 > *   typically required when first reading an object or array
53 > *   reference, and then accessing one of its fields, elements or
54 > *   methods. </li>
55   *
56   * </ul>
57   *
# Line 115 | Line 117 | import java.util.concurrent.locks.*;
117   *     }
118   *   }
119   *
120 < *   double distanceFromOriginV1() { // A read-only method
121 < *     long stamp;
122 < *     if ((stamp = sl.tryOptimisticRead()) != 0L) { // optimistic
123 < *       double currentX = x;
124 < *       double currentY = y;
125 < *       if (sl.validate(stamp))
126 < *         return Math.sqrt(currentX * currentX + currentY * currentY);
127 < *     }
128 < *     stamp = sl.readLock(); // fall back to read lock
129 < *     try {
130 < *       double currentX = x;
129 < *       double currentY = y;
130 < *         return Math.sqrt(currentX * currentX + currentY * currentY);
131 < *     } finally {
132 < *       sl.unlockRead(stamp);
133 < *     }
134 < *   }
135 < *
136 < *   double distanceFromOriginV2() { // combines code paths
137 < *     double currentX = 0.0, currentY = 0.0;
138 < *     for (long stamp = sl.tryOptimisticRead(); ; stamp = sl.readLock()) {
139 < *       try {
140 < *         currentX = x;
141 < *         currentY = y;
142 < *       } finally {
143 < *         if (sl.tryConvertToOptimisticRead(stamp) != 0L) // unlock or validate
144 < *           break;
145 < *       }
120 > *   double distanceFromOrigin() { // A read-only method
121 > *     long stamp = sl.tryOptimisticRead();
122 > *     double currentX = x, currentY = y;
123 > *     if (!sl.validate(stamp)) {
124 > *        stamp = sl.readLock();
125 > *        try {
126 > *          currentX = x;
127 > *          currentY = y;
128 > *        } finally {
129 > *           sl.unlockRead(stamp);
130 > *        }
131   *     }
132   *     return Math.sqrt(currentX * currentX + currentY * currentY);
133   *   }
# Line 198 | Line 183 | public class StampedLock implements java
183       *
184       * Waiters use a modified form of CLH lock used in
185       * AbstractQueuedSynchronizer (see its internal documentation for
186 <     * a fuller account), where each node it tagged (field mode) as
186 >     * a fuller account), where each node is tagged (field mode) as
187       * either a reader or writer. Sets of waiting readers are grouped
188       * (linked) under a common node (field cowait) so act as a single
189 <     * node with respect to most CLH mechanics.  By virtue of its
190 <     * structure, wait nodes need not actually carry sequence numbers;
191 <     * we know each is >= its predecessor.  These queue mechanics
192 <     * simplify the scheduling policy to a mainly-FIFO scheme that
189 >     * node with respect to most CLH mechanics.  By virtue of the
190 >     * queue structure, wait nodes need not actually carry sequence
191 >     * numbers; we know each is greater than its predecessor.  This
192 >     * simplifies the scheduling policy to a mainly-FIFO scheme that
193       * incorporates elements of Phase-Fair locks (see Brandenburg &
194       * Anderson, especially http://www.cs.unc.edu/~bbb/diss/).  In
195       * particular, we use the phase-fair anti-barging rule: If an
# Line 229 | Line 214 | public class StampedLock implements java
214       *
215       * Nearly all of these mechanics are carried out in methods
216       * acquireWrite and acquireRead, that, as typical of such code,
217 <     * sprawl out because actions and retries rely on consitent sets
218 <     * of locally cahced reads.
217 >     * sprawl out because actions and retries rely on consistent sets
218 >     * of locally cached reads.
219       *
220       * As noted in Boehm's paper (above), sequence validation (mainly
221       * method validate()) requires stricter ordering rules than apply
# Line 329 | Line 314 | public class StampedLock implements java
314       * @return a stamp that can be used to unlock or convert mode
315       */
316      public long writeLock() {
317 <        long s, next;  // bypass acquireWrite in fully onlocked case only
317 >        long s, next;  // bypass acquireWrite in fully unlocked case only
318          return ((((s = state) & ABITS) == 0L &&
319                   U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
320                  next : acquireWrite(false, 0L));
# Line 354 | Line 339 | public class StampedLock implements java
339       * Behavior under timeout and interruption matches that specified
340       * for method {@link Lock#tryLock(long,TimeUnit)}.
341       *
342 +     * @param time the maximum time to wait for the lock
343 +     * @param unit the time unit of the {@code time} argument
344       * @return a stamp that can be used to unlock or convert mode,
345       * or zero if the lock is not available
346       * @throws InterruptedException if the current thread is interrupted
# Line 364 | Line 351 | public class StampedLock implements java
351          long nanos = unit.toNanos(time);
352          if (!Thread.interrupted()) {
353              long next, deadline;
354 <            if ((next = tryWriteLock()) != 0)
354 >            if ((next = tryWriteLock()) != 0L)
355                  return next;
356              if (nanos <= 0L)
357                  return 0L;
# Line 401 | Line 388 | public class StampedLock implements java
388       * @return a stamp that can be used to unlock or convert mode
389       */
390      public long readLock() {
391 <        long s, next;  // bypass acquireRead on fully onlocked case only
391 >        long s, next;  // bypass acquireRead on fully unlocked case only
392          return ((((s = state) & ABITS) == 0L &&
393                   U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) ?
394                  next : acquireRead(false, 0L));
# Line 433 | Line 420 | public class StampedLock implements java
420       * Behavior under timeout and interruption matches that specified
421       * for method {@link Lock#tryLock(long,TimeUnit)}.
422       *
423 +     * @param time the maximum time to wait for the lock
424 +     * @param unit the time unit of the {@code time} argument
425       * @return a stamp that can be used to unlock or convert mode,
426       * or zero if the lock is not available
427       * @throws InterruptedException if the current thread is interrupted
# Line 440 | Line 429 | public class StampedLock implements java
429       */
430      public long tryReadLock(long time, TimeUnit unit)
431          throws InterruptedException {
432 <        long next, deadline;
432 >        long s, m, next, deadline;
433          long nanos = unit.toNanos(time);
434          if (!Thread.interrupted()) {
435 <            if ((next = tryReadLock()) != 0)
436 <                return next;
435 >            if ((m = (s = state) & ABITS) != WBIT) {
436 >                if (m < RFULL) {
437 >                    if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
438 >                        return next;
439 >                }
440 >                else if ((next = tryIncReaderOverflow(s)) != 0L)
441 >                    return next;
442 >            }
443              if (nanos <= 0L)
444                  return 0L;
445              if ((deadline = System.nanoTime() + nanos) == 0L)
# Line 488 | Line 483 | public class StampedLock implements java
483       * Returns true if the lock has not been exclusively acquired
484       * since issuance of the given stamp. Always returns false if the
485       * stamp is zero. Always returns true if the stamp represents a
486 <     * currently held lock.
486 >     * currently held lock. Invoking this method with a value not
487 >     * obtained from {@link #tryOptimisticRead} or a locking method
488 >     * for this lock has no defined effect or result.
489       *
490 <     * @return true if the lock has not been exclusively acquired
490 >     * @param stamp a stamp
491 >     * @return {@code true} if the lock has not been exclusively acquired
492       * since issuance of the given stamp; else false
493       */
494      public boolean validate(long stamp) {
# Line 524 | Line 522 | public class StampedLock implements java
522       * not match the current state of this lock
523       */
524      public void unlockRead(long stamp) {
525 <        long s, m;  WNode h;
526 <        if ((stamp & RBITS) != 0L) {
527 <            while (((s = state) & SBITS) == (stamp & SBITS)) {
528 <                if ((m = s & ABITS) == 0L)
525 >        long s, m; WNode h;
526 >        for (;;) {
527 >            if (((s = state) & SBITS) != (stamp & SBITS) ||
528 >                (stamp & ABITS) == 0L || (m = s & ABITS) == 0L || m == WBIT)
529 >                throw new IllegalMonitorStateException();
530 >            if (m < RFULL) {
531 >                if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
532 >                    if (m == RUNIT && (h = whead) != null && h.status != 0)
533 >                        release(h);
534                      break;
532                else if (m < RFULL) {
533                    if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
534                        if (m == RUNIT && (h = whead) != null && h.status != 0)
535                            release(h);
536                        return;
537                    }
535                  }
539                else if (m >= WBIT)
540                    break;
541                else if (tryDecReaderOverflow(s) != 0L)
542                    return;
536              }
537 +            else if (tryDecReaderOverflow(s) != 0L)
538 +                break;
539          }
545        throw new IllegalMonitorStateException();
540      }
541  
542      /**
# Line 707 | Line 701 | public class StampedLock implements java
701       * stamp value. This method may be useful for recovery after
702       * errors.
703       *
704 <     * @return true if the lock was held, else false
704 >     * @return {@code true} if the lock was held, else false
705       */
706      public boolean tryUnlockWrite() {
707          long s; WNode h;
# Line 725 | Line 719 | public class StampedLock implements java
719       * requiring a stamp value. This method may be useful for recovery
720       * after errors.
721       *
722 <     * @return true if the read lock was held, else false
722 >     * @return {@code true} if the read lock was held, else false
723       */
724      public boolean tryUnlockRead() {
725          long s, m; WNode h;
# Line 743 | Line 737 | public class StampedLock implements java
737          return false;
738      }
739  
740 +    // status monitoring methods
741 +
742      /**
743 <     * Returns true if the lock is currently held exclusively.
743 >     * Returns combined state-held and overflow read count for given
744 >     * state s.
745 >     */
746 >    private int getReadLockCount(long s) {
747 >        long readers;
748 >        if ((readers = s & RBITS) >= RFULL)
749 >            readers = RFULL + readerOverflow;
750 >        return (int) readers;
751 >    }
752 >
753 >    /**
754 >     * Returns {@code true} if the lock is currently held exclusively.
755       *
756 <     * @return true if the lock is currently held exclusively
756 >     * @return {@code true} if the lock is currently held exclusively
757       */
758      public boolean isWriteLocked() {
759          return (state & WBIT) != 0L;
760      }
761  
762      /**
763 <     * Returns true if the lock is currently held non-exclusively.
763 >     * Returns {@code true} if the lock is currently held non-exclusively.
764       *
765 <     * @return true if the lock is currently held non-exclusively
765 >     * @return {@code true} if the lock is currently held non-exclusively
766       */
767      public boolean isReadLocked() {
768          return (state & RBITS) != 0L;
769      }
770  
771 <    private void readObject(java.io.ObjectInputStream s)
772 <        throws java.io.IOException, ClassNotFoundException {
773 <        s.defaultReadObject();
774 <        state = ORIGIN; // reset to unlocked state
771 >    /**
772 >     * Queries the number of read locks held for this lock. This
773 >     * method is designed for use in monitoring system state, not for
774 >     * synchronization control.
775 >     * @return the number of read locks held
776 >     */
777 >    public int getReadLockCount() {
778 >        return getReadLockCount(state);
779 >    }
780 >
781 >    /**
782 >     * Returns a string identifying this lock, as well as its lock
783 >     * state.  The state, in brackets, includes the String {@code
784 >     * "Unlocked"} or the String {@code "Write-locked"} or the String
785 >     * {@code "Read-locks:"} followed by the current number of
786 >     * read-locks held.
787 >     *
788 >     * @return a string identifying this lock, as well as its lock state
789 >     */
790 >    public String toString() {
791 >        long s = state;
792 >        return super.toString() +
793 >            ((s & ABITS) == 0L ? "[Unlocked]" :
794 >             (s & WBIT) != 0L ? "[Write-locked]" :
795 >             "[Read-locks:" + getReadLockCount(s) + "]");
796      }
797  
798 +    // views
799 +
800      /**
801       * Returns a plain {@link Lock} view of this StampedLock in which
802       * the {@link Lock#lock} method is mapped to {@link #readLock},
# Line 825 | Line 855 | public class StampedLock implements java
855              throws InterruptedException {
856              return tryReadLock(time, unit) != 0L;
857          }
858 <        // note that we give up ability to check mode so just use current state
829 <        public void unlock() { unlockRead(state); }
858 >        public void unlock() { unstampedUnlockRead(); }
859          public Condition newCondition() {
860              throw new UnsupportedOperationException();
861          }
# Line 842 | Line 871 | public class StampedLock implements java
871              throws InterruptedException {
872              return tryWriteLock(time, unit) != 0L;
873          }
874 <        public void unlock() { unlockWrite(state); }
874 >        public void unlock() { unstampedUnlockWrite(); }
875          public Condition newCondition() {
876              throw new UnsupportedOperationException();
877          }
# Line 853 | Line 882 | public class StampedLock implements java
882          public Lock writeLock() { return asWriteLock(); }
883      }
884  
885 +    // Unlock methods without stamp argument checks for view classes.
886 +    // Needed because view-class lock methods throw away stamps.
887 +
888 +    final void unstampedUnlockWrite() {
889 +        WNode h; long s;
890 +        if (((s = state) & WBIT) == 0L)
891 +            throw new IllegalMonitorStateException();
892 +        state = (s += WBIT) == 0L ? ORIGIN : s;
893 +        if ((h = whead) != null && h.status != 0)
894 +            release(h);
895 +    }
896 +
897 +    final void unstampedUnlockRead() {
898 +        for (;;) {
899 +            long s, m; WNode h;
900 +            if ((m = (s = state) & ABITS) == 0L || m >= WBIT)
901 +                throw new IllegalMonitorStateException();
902 +            else if (m < RFULL) {
903 +                if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
904 +                    if (m == RUNIT && (h = whead) != null && h.status != 0)
905 +                        release(h);
906 +                    break;
907 +                }
908 +            }
909 +            else if (tryDecReaderOverflow(s) != 0L)
910 +                break;
911 +        }
912 +    }
913 +
914 +    private void readObject(java.io.ObjectInputStream s)
915 +        throws java.io.IOException, ClassNotFoundException {
916 +        s.defaultReadObject();
917 +        state = ORIGIN; // reset to unlocked state
918 +    }
919 +
920      // internals
921  
922      /**
# Line 860 | Line 924 | public class StampedLock implements java
924       * access bits value to RBITS, indicating hold of spinlock,
925       * then updating, then releasing.
926       *
927 <     * @param s, assumed that (s & ABITS) >= RFULL
927 >     * @param s a reader overflow stamp: (s & ABITS) >= RFULL
928       * @return new stamp on success, else zero
929       */
930      private long tryIncReaderOverflow(long s) {
931 +        // assert (s & ABITS) >= RFULL;
932          if ((s & ABITS) == RFULL) {
933              if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
934                  ++readerOverflow;
# Line 880 | Line 945 | public class StampedLock implements java
945      /**
946       * Tries to decrement readerOverflow.
947       *
948 <     * @param s, assumed that (s & ABITS) >= RFULL
948 >     * @param s a reader overflow stamp: (s & ABITS) >= RFULL
949       * @return new stamp on success, else zero
950       */
951      private long tryDecReaderOverflow(long s) {
952 +        // assert (s & ABITS) >= RFULL;
953          if ((s & ABITS) == RFULL) {
954              if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
955                  int r; long next;
# Line 903 | Line 969 | public class StampedLock implements java
969          return 0L;
970      }
971  
972 <    /*
972 >    /**
973       * Wakes up the successor of h (normally whead). This is normally
974       * just h.next, but may require traversal from wtail if next
975       * pointers are lagging. This may fail to wake up an acquiring
# Line 977 | Line 1043 | public class StampedLock implements java
1043                  (p = np).next = node;   // stale
1044              if (whead == p) {
1045                  for (int k = spins;;) { // spin at head
1046 <                    if (((s = state) & ABITS) == 0L &&
1047 <                        U.compareAndSwapLong(this, STATE, s, ns = s + WBIT)) {
1048 <                        whead = node;
1049 <                        node.prev = null;
1050 <                        return ns;
1046 >                    if (((s = state) & ABITS) == 0L) {
1047 >                        if (U.compareAndSwapLong(this, STATE, s, ns = s+WBIT)) {
1048 >                            whead = node;
1049 >                            node.prev = null;
1050 >                            return ns;
1051 >                        }
1052                      }
1053                      else if (ThreadLocalRandom.current().nextInt() >= 0 &&
1054                               --k <= 0)
# Line 1003 | Line 1070 | public class StampedLock implements java
1070                  if (deadline == 0L)
1071                      time = 0L;
1072                  else if ((time = deadline - System.nanoTime()) <= 0L)
1073 <                    return cancelWaiter(node, null, false);
1074 <                node.thread = Thread.currentThread();
1073 >                    return cancelWaiter(node, node, false);
1074 >                Thread wt = Thread.currentThread();
1075 >                U.putObject(wt, PARKBLOCKER, this); // emulate LockSupport.park
1076 >                node.thread = wt;
1077                  if (node.prev == p && p.status == WAITING && // recheck
1078 <                    (p != whead || (state & ABITS) != 0L)) {
1078 >                    (p != whead || (state & ABITS) != 0L))
1079                      U.park(false, time);
1011                    if (interruptible && Thread.interrupted())
1012                        return cancelWaiter(node, null, true);
1013                }
1080                  node.thread = null;
1081 +                U.putObject(wt, PARKBLOCKER, null);
1082 +                if (interruptible && Thread.interrupted())
1083 +                    return cancelWaiter(node, node, true);
1084              }
1085          }
1086      }
# Line 1033 | Line 1102 | public class StampedLock implements java
1102                  if (group == null && (h = whead) != null &&
1103                      (q = h.next) != null && q.mode != RMODE)
1104                      break;
1105 <                if ((m = (s = state) & ABITS) == WBIT)
1037 <                    break;
1038 <                if (m < RFULL ?
1105 >                if ((m = (s = state) & ABITS) < RFULL ?
1106                      U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
1107 <                    (ns = tryIncReaderOverflow(s)) != 0L) {
1107 >                    (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) {
1108                      if (group != null) {  // help release others
1109                          for (WNode r = group;;) {
1110                              if ((w = r.thread) != null) {
# Line 1051 | Line 1118 | public class StampedLock implements java
1118                      }
1119                      return ns;
1120                  }
1121 +                if (m >= WBIT)
1122 +                    break;
1123              }
1124              if (spins > 0) {
1125                  if (ThreadLocalRandom.current().nextInt() >= 0)
# Line 1085 | Line 1154 | public class StampedLock implements java
1154                              node.thread = null;
1155                              break;
1156                          }
1157 +                        Thread wt = Thread.currentThread();
1158 +                        U.putObject(wt, PARKBLOCKER, this);
1159                          if (node.thread == null) // must recheck
1160                              break;
1161                          U.park(false, time);
1162 +                        U.putObject(wt, PARKBLOCKER, null);
1163                          if (interruptible && Thread.interrupted())
1164                              return cancelWaiter(node, p, true);
1165                      }
# Line 1144 | Line 1216 | public class StampedLock implements java
1216                  if (deadline == 0L)
1217                      time = 0L;
1218                  else if ((time = deadline - System.nanoTime()) <= 0L)
1219 <                    return cancelWaiter(node, null, false);
1220 <                node.thread = Thread.currentThread();
1219 >                    return cancelWaiter(node, node, false);
1220 >                Thread wt = Thread.currentThread();
1221 >                U.putObject(wt, PARKBLOCKER, this);
1222 >                node.thread = wt;
1223                  if (node.prev == p && p.status == WAITING &&
1224 <                    (p != whead || (state & ABITS) != WBIT)) {
1224 >                    (p != whead || (state & ABITS) != WBIT))
1225                      U.park(false, time);
1152                    if (interruptible && Thread.interrupted())
1153                        return cancelWaiter(node, null, true);
1154                }
1226                  node.thread = null;
1227 +                U.putObject(wt, PARKBLOCKER, null);
1228 +                if (interruptible && Thread.interrupted())
1229 +                    return cancelWaiter(node, node, true);
1230              }
1231          }
1232      }
1233  
1234      /**
1235 <     * If node non-null, forces cancel status and unsplices from queue
1236 <     * if possible. This is a variant of cancellation methods in
1235 >     * If node non-null, forces cancel status and unsplices it from
1236 >     * queue if possible and wakes up any cowaiters (of the node, or
1237 >     * group, as applicable), and in any case helps release current
1238 >     * first waiter if lock is free. (Calling with null arguments
1239 >     * serves as a conditional form of release, which is not currently
1240 >     * needed but may be needed under possible future cancellation
1241 >     * policies). This is a variant of cancellation methods in
1242       * AbstractQueuedSynchronizer (see its detailed explanation in AQS
1243 <     * internal documentation) that more conservatively wakes up other
1244 <     * threads that may have had their links changed, so as to preserve
1245 <     * liveness in the main signalling methods.
1243 >     * internal documentation).
1244 >     *
1245 >     * @param node if nonnull, the waiter
1246 >     * @param group either node or the group node is cowaiting with
1247 >     * @param interrupted if already interrupted
1248 >     * @return INTERRUPTED if interrupted or Thread.interrupted, else zero
1249       */
1250      private long cancelWaiter(WNode node, WNode group, boolean interrupted) {
1251 <        if (node != null) {
1252 <            node.thread = null;
1251 >        if (node != null && group != null) {
1252 >            Thread w;
1253              node.status = CANCELLED;
1254 <            if (group != null) {
1255 <                for (WNode p = group, q; p != null; p = q) {
1256 <                    if ((q = p.cowait) != null && q.status == CANCELLED) {
1257 <                        U.compareAndSwapObject(p, WCOWAIT, q, q.cowait);
1258 <                        break;
1254 >            node.thread = null;
1255 >            // unsplice cancelled nodes from group
1256 >            for (WNode p = group, q; (q = p.cowait) != null;) {
1257 >                if (q.status == CANCELLED)
1258 >                    U.compareAndSwapObject(p, WNEXT, q, q.next);
1259 >                else
1260 >                    p = q;
1261 >            }
1262 >            if (group == node) {
1263 >                WNode r; // detach and wake up uncancelled co-waiters
1264 >                while ((r = node.cowait) != null) {
1265 >                    if (U.compareAndSwapObject(node, WCOWAIT, r, r.cowait) &&
1266 >                        (w = r.thread) != null) {
1267 >                        r.thread = null;
1268 >                        U.unpark(w);
1269                      }
1270                  }
1271 <            }
1272 <            else {
1181 <                for (WNode pred = node.prev; pred != null; ) {
1182 <                    WNode succ, pp; Thread w;
1271 >                for (WNode pred = node.prev; pred != null; ) { // unsplice
1272 >                    WNode succ, pp;        // find valid successor
1273                      while ((succ = node.next) == null ||
1274                             succ.status == CANCELLED) {
1275 <                        WNode q = null;
1275 >                        WNode q = null;    // find successor the slow way
1276                          for (WNode t = wtail; t != null && t != node; t = t.prev)
1277                              if (t.status != CANCELLED)
1278 <                                q = t;
1279 <                        if (succ == q ||
1278 >                                q = t;     // don't link if succ cancelled
1279 >                        if (succ == q ||   // ensure accurate successor
1280                              U.compareAndSwapObject(node, WNEXT,
1281                                                     succ, succ = q)) {
1282                              if (succ == null && node == wtail)
# Line 1194 | Line 1284 | public class StampedLock implements java
1284                              break;
1285                          }
1286                      }
1287 <                    if (pred.next == node)
1287 >                    if (pred.next == node) // unsplice pred link
1288                          U.compareAndSwapObject(pred, WNEXT, node, succ);
1289 <                    if (succ != null && (w = succ.thread) != null)
1290 <                        U.unpark(w);
1289 >                    if (succ != null && (w = succ.thread) != null) {
1290 >                        succ.thread = null;
1291 >                        U.unpark(w);       // wake up succ to observe new pred
1292 >                    }
1293                      if (pred.status != CANCELLED || (pp = pred.prev) == null)
1294                          break;
1295 <                    node.prev = pp; // repeat for new pred
1295 >                    node.prev = pp;        // repeat if new pred wrong/cancelled
1296                      U.compareAndSwapObject(pp, WNEXT, pred, succ);
1297                      pred = pp;
1298                  }
1299              }
1300          }
1301 <        release(whead);
1301 >        WNode h; // Possibly release first waiter
1302 >        while ((h = whead) != null) {
1303 >            long s; WNode q; // similar to release() but check eligibility
1304 >            if ((q = h.next) == null || q.status == CANCELLED) {
1305 >                for (WNode t = wtail; t != null && t != h; t = t.prev)
1306 >                    if (t.status <= 0)
1307 >                        q = t;
1308 >            }
1309 >            if (h == whead) {
1310 >                if (q != null && h.status == 0 &&
1311 >                    ((s = state) & ABITS) != WBIT && // waiter is eligible
1312 >                    (s == 0L || q.mode == RMODE))
1313 >                    release(h);
1314 >                break;
1315 >            }
1316 >        }
1317          return (interrupted || Thread.interrupted()) ? INTERRUPTED : 0L;
1318      }
1319  
# Line 1218 | Line 1325 | public class StampedLock implements java
1325      private static final long WNEXT;
1326      private static final long WSTATUS;
1327      private static final long WCOWAIT;
1328 +    private static final long PARKBLOCKER;
1329  
1330      static {
1331          try {
# Line 1236 | Line 1344 | public class StampedLock implements java
1344                  (wk.getDeclaredField("next"));
1345              WCOWAIT = U.objectFieldOffset
1346                  (wk.getDeclaredField("cowait"));
1347 +            Class<?> tk = Thread.class;
1348 +            PARKBLOCKER = U.objectFieldOffset
1349 +                (tk.getDeclaredField("parkBlocker"));
1350  
1351          } catch (Exception e) {
1352              throw new Error(e);

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines