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.2 by jsr166, Fri Oct 12 16:22:40 2012 UTC vs.
Revision 1.8 by dl, Sat Oct 13 11:51:12 2012 UTC

# Line 26 | Line 26 | import java.util.concurrent.TimeUnit;
26   *   in method {@link #unlockWrite} to release the lock. Untimed and
27   *   timed versions of {@code tryWriteLock} are also provided. When
28   *   the lock is held in write mode, no read locks may be obtained,
29 < *   and all observer validations will fail.  </li>
29 > *   and all optimistic read validations will fail.  </li>
30   *
31   *  <li><b>Reading.</b> Method {@link #readLock} possibly blocks
32   *   waiting for non-exclusive access, returning a stamp that can be
# Line 76 | Line 76 | import java.util.concurrent.TimeUnit;
76   * into initial unlocked state, so they are not useful for remote
77   * locking.
78   *
79 < * <p>The scheduling policy of StampedLock does not consistently prefer
80 < * readers over writers or vice versa.
79 > * <p>The scheduling policy of StampedLock does not consistently
80 > * prefer readers over writers or vice versa.  A zero return from any
81 > * "try" method for acquiring or converting locks does not carry any
82 > * information about the state of the lock; a subsequent invocation
83 > * may succeed.
84   *
85   * <p><b>Sample Usage.</b> The following illustrates some usage idioms
86   * in a class that maintains simple two-dimensional points. The sample
# Line 87 | Line 90 | import java.util.concurrent.TimeUnit;
90   *
91   *  <pre>{@code
92   * class Point {
93 < *   private volatile double x, y;
93 > *   private double x, y;
94   *   private final StampedLock sl = new StampedLock();
95   *
96   *   void move(double deltaX, double deltaY) { // an exclusively locked method
# Line 119 | Line 122 | import java.util.concurrent.TimeUnit;
122   *   }
123   *
124   *   double distanceFromOriginV2() { // combines code paths
125 < *     for (long stamp = sl.optimisticRead(); ; stamp = sl.readLock()) {
125 > *     for (long stamp = sl.tryOptimisticRead(); ; stamp = sl.readLock()) {
126   *       double currentX, currentY;
127   *       try {
128   *         currentX = x;
# Line 177 | Line 180 | public class StampedLock implements java
180       * read-locked.  The read count is ignored when validating
181       * "optimistic" seqlock-reader-style stamps.  Because we must use
182       * a small finite number of bits (currently 7) for readers, a
183 <     * supplementatry reader overflow word is used when then number of
183 >     * supplementary reader overflow word is used when then number of
184       * readers exceeds the count field. We do this by treating the max
185       * reader count value (RBITS) as a spinlock protecting overflow
186       * updates.
# Line 213 | Line 216 | public class StampedLock implements java
216       * in-progress spins/signals, and others do not account for
217       * cancellations.
218       *
219 +     * Controlled, randomized spinning is used in the two await
220 +     * methods to reduce (increasingly expensive) context switching
221 +     * while also avoiding sustained memory thrashing among many
222 +     * threads.  Both await methods use a similar spin strategy: If
223 +     * the associated queue appears to be empty, then the thread
224 +     * spin-waits up to SPINS times (where each iteration decreases
225 +     * spin count with 50% probablility) before enqueing, and then, if
226 +     * it is the first thread to be enqueued, spins again up to SPINS
227 +     * times before blocking. If, upon wakening it fails to obtain
228 +     * lock, and is still (or becomes) the first waiting thread (which
229 +     * indicates that some other thread barged and obtained lock), it
230 +     * escalates spins (up to MAX_HEAD_SPINS) to reduce the likelihood
231 +     * of continually losing to barging threads.
232 +     *
233       * As noted in Boehm's paper (above), sequence validation (mainly
234       * method validate()) requires stricter ordering rules than apply
235       * to normal volatile reads (of "state").  In the absence of (but
# Line 235 | Line 252 | public class StampedLock implements java
252      private static final int NCPU = Runtime.getRuntime().availableProcessors();
253  
254      /** Maximum number of retries before blocking on acquisition */
255 <    private static final int SPINS = NCPU > 1 ? 1 << 6 : 1;
255 >    private static final int SPINS = (NCPU > 1) ? 1 << 6 : 1;
256  
257 <    /** Maximum number of retries before re-blocking on write lock */
258 <    private static final int MAX_HEAD_SPINS = NCPU > 1 ? 1 << 12 : 1;
257 >    /** Maximum number of retries before re-blocking */
258 >    private static final int MAX_HEAD_SPINS = (NCPU > 1) ? 1 << 12 : 1;
259  
260      /** The period for yielding when waiting for overflow spinlock */
261      private static final int OVERFLOW_YIELD_RATE = 7; // must be power 2 - 1
# Line 303 | Line 320 | public class StampedLock implements java
320       * Exclusively acquires the lock, blocking if necessary
321       * until available.
322       *
323 <     * @return a stamp that can be used to unlock or convert mode.
323 >     * @return a stamp that can be used to unlock or convert mode
324       */
325      public long writeLock() {
326          long s, next;
# Line 329 | Line 346 | public class StampedLock implements java
346  
347      /**
348       * Exclusively acquires the lock if it is available within the
349 <     * given time and the current thread has not been interrupted
349 >     * given time and the current thread has not been interrupted.
350       *
351       * @return a stamp that can be used to unlock or convert mode,
352 <     * or zero if the lock is not available.
352 >     * or zero if the lock is not available
353       * @throws InterruptedException if the current thread is interrupted
354 <     * before acquiring the lock.
354 >     * before acquiring the lock
355       */
356      public long tryWriteLock(long time, TimeUnit unit)
357          throws InterruptedException {
358 <        long nanos =  unit.toNanos(time);
358 >        long nanos = unit.toNanos(time);
359          if (!Thread.interrupted()) {
360              long s, next, deadline;
361              if (((s = state) & ABITS) == 0L &&
# Line 358 | Line 375 | public class StampedLock implements java
375       * Exclusively acquires the lock, blocking if necessary
376       * until available or the current thread is interrupted.
377       *
378 <     * @return a stamp that can be used to unlock or convert mode.
378 >     * @return a stamp that can be used to unlock or convert mode
379       * @throws InterruptedException if the current thread is interrupted
380 <     * before acquiring the lock.
380 >     * before acquiring the lock
381       */
382      public long writeLockInterruptibly() throws InterruptedException {
383          if (!Thread.interrupted()) {
# Line 378 | Line 395 | public class StampedLock implements java
395       * Non-exclusively acquires the lock, blocking if necessary
396       * until available.
397       *
398 <     * @return a stamp that can be used to unlock or convert mode.
398 >     * @return a stamp that can be used to unlock or convert mode
399       */
400      public long readLock() {
401          for (;;) {
# Line 401 | Line 418 | public class StampedLock implements java
418       * Non-exclusively acquires the lock if it is immediately available.
419       *
420       * @return a stamp that can be used to unlock or convert mode,
421 <     * or zero if the lock is not available.
421 >     * or zero if the lock is not available
422       */
423      public long tryReadLock() {
424          for (;;) {
# Line 419 | Line 436 | public class StampedLock implements java
436  
437      /**
438       * Non-exclusively acquires the lock if it is available within the
439 <     * given time and the current thread has not been interrupted
439 >     * given time and the current thread has not been interrupted.
440       *
441       * @return a stamp that can be used to unlock or convert mode,
442 <     * or zero if the lock is not available.
442 >     * or zero if the lock is not available
443       * @throws InterruptedException if the current thread is interrupted
444 <     * before acquiring the lock.
444 >     * before acquiring the lock
445       */
446      public long tryReadLock(long time, TimeUnit unit)
447          throws InterruptedException {
# Line 457 | Line 474 | public class StampedLock implements java
474       * Non-exclusively acquires the lock, blocking if necessary
475       * until available or the current thread is interrupted.
476       *
477 <     * @return a stamp that can be used to unlock or convert mode.
477 >     * @return a stamp that can be used to unlock or convert mode
478       * @throws InterruptedException if the current thread is interrupted
479 <     * before acquiring the lock.
479 >     * before acquiring the lock
480       */
481      public long readLockInterruptibly() throws InterruptedException {
482          if (!Thread.interrupted()) {
# Line 512 | Line 529 | public class StampedLock implements java
529       *
530       * @param stamp a stamp returned by a write-lock operation
531       * @throws IllegalMonitorStateException if the stamp does
532 <     * not match the current state of this lock.
532 >     * not match the current state of this lock
533       */
534      public void unlockWrite(long stamp) {
535          if (state != stamp || (stamp & WBIT) == 0L)
# Line 527 | Line 544 | public class StampedLock implements java
544       *
545       * @param stamp a stamp returned by a read-lock operation
546       * @throws IllegalMonitorStateException if the stamp does
547 <     * not match the current state of this lock.
547 >     * not match the current state of this lock
548       */
549      public void unlockRead(long stamp) {
550          long s, m;
# Line 557 | Line 574 | public class StampedLock implements java
574       *
575       * @param stamp a stamp returned by a lock operation
576       * @throws IllegalMonitorStateException if the stamp does
577 <     * not match the current state of this lock.
577 >     * not match the current state of this lock
578       */
579      public void unlock(long stamp) {
580          long a = stamp & ABITS, m, s;
# Line 707 | Line 724 | public class StampedLock implements java
724       * stamp value. This method may be useful for recovery after
725       * errors.
726       *
727 <     * @return true if the lock was held, else false.
727 >     * @return true if the lock was held, else false
728       */
729      public boolean tryUnlockWrite() {
730          long s;
# Line 724 | Line 741 | public class StampedLock implements java
741       * requiring a stamp value. This method may be useful for recovery
742       * after errors.
743       *
744 <     * @return true if the read lock was held, else false.
744 >     * @return true if the read lock was held, else false
745       */
746      public boolean tryUnlockRead() {
747          long s, m;
# Line 773 | Line 790 | public class StampedLock implements java
790       * Tries to increment readerOverflow by first setting state
791       * access bits value to RBITS, indicating hold of spinlock,
792       * then updating, then releasing.
793 +     *
794       * @param stamp, assumed that (stamp & ABITS) >= RFULL
795       * @return new stamp on success, else zero
796       */
# Line 792 | Line 810 | public class StampedLock implements java
810  
811      /**
812       * Tries to decrement readerOverflow.
813 +     *
814       * @param stamp, assumed that (stamp & ABITS) >= RFULL
815       * @return new stamp on success, else zero
816       */
# Line 889 | Line 908 | public class StampedLock implements java
908      /**
909       * RNG for local spins. The first call from await{Read,Write}
910       * produces a thread-local value. Unless zero, subsequent calls
911 <     * use an xorShift to further reduce memory traffic.  Both await
893 <     * methods use a similar spin strategy: If associated queue
894 <     * appears to be empty, then the thread spin-waits up to SPINS
895 <     * times before enqueing, and then, if the first thread to be
896 <     * enqueued, spins again up to SPINS times before blocking. If,
897 <     * upon wakening it fails to obtain lock, and is still (or
898 <     * becomes) the first waiting thread (which indicates that some
899 <     * other thread barged and obtained lock), it escalates spins (up
900 <     * to MAX_HEAD_SPINS) to reduce the likelihood of continually
901 <     * losing to barging threads.
911 >     * use an xorShift to further reduce memory traffic.
912       */
913      private static int nextRandom(int r) {
914          if (r == 0)
# Line 911 | Line 921 | public class StampedLock implements java
921  
922      /**
923       * Possibly spins trying to obtain write lock, then enqueues and
924 <     * blocks while not head of write queue or cannot aquire lock,
924 >     * blocks while not head of write queue or cannot acquire lock,
925       * possibly spinning when at head; cancelling on timeout or
926       * interrupt.
927       *
928       * @param interruptible true if should check interrupts and if so
929       * return INTERRUPTED
930       * @param deadline if nonzero, the System.nanoTime value to timeout
931 <     * at (and return zero).
931 >     * at (and return zero)
932       */
933      private long awaitWrite(boolean interruptible, long deadline) {
934          WNode node = null;
# Line 947 | Line 957 | public class StampedLock implements java
957                  p.next = node;
958                  for (int headSpins = SPINS;;) {
959                      WNode np; int ps;
960 <                    if ((np = node.prev) != p && np != null)
961 <                        (p = np).next = node; // stale
960 >                    if ((np = node.prev) != p && np != null &&
961 >                        (p = np).next != node)
962 >                        p.next = node; // stale
963                      if (p == whead) {
964                          for (int k = headSpins;;) {
965                              if (((s = state) & ABITS) == 0L) {
# Line 1005 | Line 1016 | public class StampedLock implements java
1016              WNode predNext = pred.next;
1017              node.status = CANCELLED;
1018              if (predNext != null) {
1019 <                Thread w = null;
1019 >                Thread w;
1020                  WNode succ = node.next;
1021 <                while (succ != null && succ.status == CANCELLED)
1022 <                    succ = succ.next;
1023 <                if (succ != null)
1024 <                    w = succ.thread;
1025 <                else if (node == wtail)
1026 <                    U.compareAndSwapObject(this, WTAIL, node, pred);
1021 >                if (succ == null || succ.status == CANCELLED) {
1022 >                    succ = null;
1023 >                    for (WNode t = wtail; t != null && t != node; t = t.prev)
1024 >                        if (t.status <= 0)
1025 >                            succ = t;
1026 >                    if (succ == null && node == wtail)
1027 >                        U.compareAndSwapObject(this, WTAIL, node, pred);
1028 >                }
1029                  U.compareAndSwapObject(pred, WNEXT, predNext, succ);
1030 <                if (w != null)
1030 >                if (succ != null && (w = succ.thread) != null)
1031                      U.unpark(w);
1032              }
1033          }
1034          writerPrefSignal();
1035 <        return (interrupted || Thread.interrupted())? INTERRUPTED : 0L;
1035 >        return (interrupted || Thread.interrupted()) ? INTERRUPTED : 0L;
1036      }
1037  
1038 <    /*
1038 >    /**
1039       * Waits for read lock or timeout or interrupt. The form of
1040       * awaitRead differs from awaitWrite mainly because it must
1041       * restart (with a new wait node) if the thread was unqueued and
# Line 1101 | Line 1114 | public class StampedLock implements java
1114      /**
1115       * If node non-null, forces cancel status and unsplices from queue
1116       * if possible, by traversing entire queue looking for cancelled
1117 <     * nodes, cleaning out all at head, but stopping upon first
1105 <     * encounter otherwise.
1117 >     * nodes.
1118       */
1119      private long cancelReader(RNode node, boolean interrupted) {
1120          Thread w;
# Line 1117 | Line 1129 | public class StampedLock implements java
1129                      }
1130                      else {
1131                          U.compareAndSwapObject(pred, RNEXT, p, q);
1132 <                        break;
1132 >                        p = pred.next;
1133                      }
1134                  }
1135                  else {
# Line 1127 | Line 1139 | public class StampedLock implements java
1139              }
1140          }
1141          readerPrefSignal();
1142 <        return (interrupted || Thread.interrupted())? INTERRUPTED : 0L;
1142 >        return (interrupted || Thread.interrupted()) ? INTERRUPTED : 0L;
1143      }
1144  
1145      // Unsafe mechanics
# Line 1201 | Line 1213 | public class StampedLock implements java
1213      }
1214  
1215   }
1204

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines