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.31 by dl, Thu Jan 24 21:35:03 2013 UTC vs.
Revision 1.33 by dl, Sat Jan 26 15:00:15 2013 UTC

# Line 40 | Line 40 | import java.util.concurrent.locks.LockSu
40   *  <li><b>Optimistic Reading.</b> Method {@link #tryOptimisticRead}
41   *   returns a non-zero stamp only if the lock is not currently held
42   *   in write mode. Method {@link #validate} returns true if the lock
43 < *   has not since been acquired in write mode. This mode can be
44 < *   thought of as an extremely weak version of a read-lock, that can
45 < *   be broken by a writer at any time.  The use of optimistic mode
46 < *   for short read-only code segments often reduces contention and
47 < *   improves throughput.  However, its use is inherently fragile.
48 < *   Optimistic read sections should only read fields and hold them in
49 < *   local variables for later use after validation. Fields read while
50 < *   in optimistic mode may be wildly inconsistent, so usage applies
51 < *   only when you are familiar enough with data representations to
52 < *   check consistency and/or repeatedly invoke method {@code
53 < *   validate()}.  For example, such steps are typically required when
54 < *   first reading an object or array reference, and then accessing
55 < *   one of its fields, elements or methods. </li>
43 > *   has not been acquired in write mode since obtaining a given
44 > *   stamp.  This mode can be thought of as an extremely weak version
45 > *   of a read-lock, that can be broken by a writer at any time.  The
46 > *   use of optimistic mode for short read-only code segments often
47 > *   reduces contention and improves throughput.  However, its use is
48 > *   inherently fragile.  Optimistic read sections should only read
49 > *   fields and hold them in local variables for later use after
50 > *   validation. Fields read while in optimistic mode may be wildly
51 > *   inconsistent, so usage applies only when you are familiar enough
52 > *   with data representations to check consistency and/or repeatedly
53 > *   invoke method {@code validate()}.  For example, such steps are
54 > *   typically required when first reading an object or array
55 > *   reference, and then accessing one of its fields, elements or
56 > *   methods. </li>
57   *
58   * </ul>
59   *
# Line 252 | Line 253 | public class StampedLock implements java
253       * motivation to further spread out contended locations, but might
254       * be subject to future improvements.
255       */
256 +
257      private static final long serialVersionUID = -6001602636862214147L;
258  
259      /** Number of processors, for spin control */
# Line 442 | Line 444 | public class StampedLock implements java
444       */
445      public long tryReadLock(long time, TimeUnit unit)
446          throws InterruptedException {
447 <        long next, deadline;
447 >        long s, m, next, deadline;
448          long nanos = unit.toNanos(time);
449          if (!Thread.interrupted()) {
450 <            if ((next = tryReadLock()) != 0L)
451 <                return next;
450 >            if ((m = (s = state) & ABITS) != WBIT) {
451 >                if (m < RFULL) {
452 >                    if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
453 >                        return next;
454 >                }
455 >                else if ((next = tryIncReaderOverflow(s)) != 0L)
456 >                    return next;
457 >            }
458              if (nanos <= 0L)
459                  return 0L;
460              if ((deadline = System.nanoTime() + nanos) == 0L)
# Line 490 | Line 498 | public class StampedLock implements java
498       * Returns true if the lock has not been exclusively acquired
499       * since issuance of the given stamp. Always returns false if the
500       * stamp is zero. Always returns true if the stamp represents a
501 <     * currently held lock.
501 >     * currently held lock. Invoking this method with a value not
502 >     * obtained from {@link #tryOptimisticRead} or a locking method
503 >     * for this lock has no defined effect or result.
504       *
505       * @return true if the lock has not been exclusively acquired
506       * since issuance of the given stamp; else false
# Line 1030 | Line 1040 | public class StampedLock implements java
1040                  if (deadline == 0L)
1041                      time = 0L;
1042                  else if ((time = deadline - System.nanoTime()) <= 0L)
1043 <                    return cancelWaiter(node, null, false);
1043 >                    return cancelWaiter(node, node, false);
1044                  node.thread = Thread.currentThread();
1045                  if (node.prev == p && p.status == WAITING && // recheck
1046                      (p != whead || (state & ABITS) != 0L))
1047                      U.park(false, time);
1048                  node.thread = null;
1049                  if (interruptible && Thread.interrupted())
1050 <                    return cancelWaiter(node, null, true);
1050 >                    return cancelWaiter(node, node, true);
1051              }
1052          }
1053      }
# Line 1170 | Line 1180 | public class StampedLock implements java
1180                  if (deadline == 0L)
1181                      time = 0L;
1182                  else if ((time = deadline - System.nanoTime()) <= 0L)
1183 <                    return cancelWaiter(node, null, false);
1183 >                    return cancelWaiter(node, node, false);
1184                  node.thread = Thread.currentThread();
1185                  if (node.prev == p && p.status == WAITING &&
1186                      (p != whead || (state & ABITS) != WBIT))
1187                      U.park(false, time);
1188                  node.thread = null;
1189                  if (interruptible && Thread.interrupted())
1190 <                    return cancelWaiter(node, null, true);
1190 >                    return cancelWaiter(node, node, true);
1191 >            }
1192 >        }
1193 >    }
1194 >
1195 >    /**
1196 >     * If node non-null, forces cancel status and unsplices it from
1197 >     * queue if possible and wakes up any cowaiters (of the node, or
1198 >     * group, as applicable), and in any case helps release current
1199 >     * first waiter if lock is free. (Calling with null arguments
1200 >     * serves as a conditional form of release, which is not currently
1201 >     * needed but may be needed under possible future cancellation
1202 >     * policies). This is a variant of cancellation methods in
1203 >     * AbstractQueuedSynchronizer (see its detailed explanation in AQS
1204 >     * internal documentation).
1205 >     *
1206 >     * @param node if nonnull, the waiter
1207 >     * @param group, either node or the group node is cowaiting with
1208 >     * @param interrupted if already interrupted
1209 >     * @return INTERRUPTED if interrupted or Thread.interrupted, else zero
1210 >     */
1211 >    private long cancelWaiter(WNode node, WNode group, boolean interrupted) {
1212 >        if (node != null && group != null) {
1213 >            Thread w;
1214 >            node.status = CANCELLED;
1215 >            node.thread = null;
1216 >            // unsplice cancelled nodes from group
1217 >            for (WNode p = group, q; (q = p.cowait) != null;) {
1218 >                if (q.status == CANCELLED)
1219 >                    U.compareAndSwapObject(p, WNEXT, q, q.next);
1220 >                else
1221 >                    p = q;
1222 >            }
1223 >            if (group == node) {
1224 >                WNode r; // detach and wake up uncancelled co-waiters
1225 >                while ((r = node.cowait) != null) {
1226 >                    if (U.compareAndSwapObject(node, WCOWAIT, r, r.cowait) &&
1227 >                        (w = r.thread) != null) {
1228 >                        r.thread = null;
1229 >                        U.unpark(w);
1230 >                    }
1231 >                }
1232 >                for (WNode pred = node.prev; pred != null; ) { // unsplice
1233 >                    WNode succ, pp;        // find valid successor
1234 >                    while ((succ = node.next) == null ||
1235 >                           succ.status == CANCELLED) {
1236 >                        WNode q = null;    // find successor the slow way
1237 >                        for (WNode t = wtail; t != null && t != node; t = t.prev)
1238 >                            if (t.status != CANCELLED)
1239 >                                q = t;     // don't link if succ cancelled
1240 >                        if (succ == q ||   // ensure accurate successor
1241 >                            U.compareAndSwapObject(node, WNEXT,
1242 >                                                   succ, succ = q)) {
1243 >                            if (succ == null && node == wtail)
1244 >                                U.compareAndSwapObject(this, WTAIL, node, pred);
1245 >                            break;
1246 >                        }
1247 >                    }
1248 >                    if (pred.next == node) // unsplice pred link
1249 >                        U.compareAndSwapObject(pred, WNEXT, node, succ);
1250 >                    if (succ != null && (w = succ.thread) != null) {
1251 >                        succ.thread = null;
1252 >                        U.unpark(w);       // wake up succ to observe new pred
1253 >                    }
1254 >                    if (pred.status != CANCELLED || (pp = pred.prev) == null)
1255 >                        break;
1256 >                    node.prev = pp;        // repeat if new pred wrong/cancelled
1257 >                    U.compareAndSwapObject(pp, WNEXT, pred, succ);
1258 >                    pred = pp;
1259 >                }
1260              }
1261          }
1262 +        WNode h; // Possibly release first waiter
1263 +        while ((h = whead) != null) {
1264 +            long s; WNode q; // similar to release() but check eligibility
1265 +            if ((q = h.next) == null || q.status == CANCELLED) {
1266 +                for (WNode t = wtail; t != null && t != h; t = t.prev)
1267 +                    if (t.status <= 0)
1268 +                        q = t;
1269 +            }
1270 +            if (h == whead) {
1271 +                if (q != null && h.status == 0 &&
1272 +                    ((s = state) & ABITS) != WBIT && // waiter is eligible
1273 +                    (s == 0L || q.mode == RMODE))
1274 +                    release(h);
1275 +                break;
1276 +            }
1277 +        }
1278 +        return (interrupted || Thread.interrupted()) ? INTERRUPTED : 0L;
1279 +    }
1280 +
1281 +    // Unsafe mechanics
1282 +    private static final sun.misc.Unsafe U;
1283 +    private static final long STATE;
1284 +    private static final long WHEAD;
1285 +    private static final long WTAIL;
1286 +    private static final long WNEXT;
1287 +    private static final long WSTATUS;
1288 +    private static final long WCOWAIT;
1289 +
1290 +    static {
1291 +        try {
1292 +            U = getUnsafe();
1293 +            Class<?> k = StampedLock.class;
1294 +            Class<?> wk = WNode.class;
1295 +            STATE = U.objectFieldOffset
1296 +                (k.getDeclaredField("state"));
1297 +            WHEAD = U.objectFieldOffset
1298 +                (k.getDeclaredField("whead"));
1299 +            WTAIL = U.objectFieldOffset
1300 +                (k.getDeclaredField("wtail"));
1301 +            WSTATUS = U.objectFieldOffset
1302 +                (wk.getDeclaredField("status"));
1303 +            WNEXT = U.objectFieldOffset
1304 +                (wk.getDeclaredField("next"));
1305 +            WCOWAIT = U.objectFieldOffset
1306 +                (wk.getDeclaredField("cowait"));
1307 +
1308 +        } catch (Exception e) {
1309 +            throw new Error(e);
1310 +        }
1311      }
1312  
1313      /**

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines