/** * If node non-null, forces cancel status and unsplices it from queue if possible and wakes up any * cowaiters (of the node, or group, as applicable), and in any case helps release current first * waiter if lock is free. (Calling with null arguments serves as a conditional form of release, * which is not currently needed but may be needed under possible future cancellation policies). * This is a variant of cancellation methods in AbstractQueuedSynchronizer (see its detailed * explanation in AQS internal documentation). * * @param node if nonnull, the waiter * @param group either node or the group node is cowaiting with * @param interrupted if already interrupted * @return INTERRUPTED if interrupted or Thread.interrupted, else zero */ private long cancelWaiter(WNode node, WNode group, boolean interrupted) { if (node != null && group != null) { Thread w; node.status = CANCELLED; // unsplice cancelled nodes from group for (WNode p = group, q; (q = p.cowait) != null; ) { if (q.status == CANCELLED) { U.compareAndSwapObject(p, WCOWAIT, q, q.cowait); p = group; // restart } else p = q; } if (group == node) { for (WNode r = group.cowait; r != null; r = r.cowait) { if ((w = r.thread) != null) U.unpark(w); // wake up uncancelled co-waiters } for (WNode pred = node.prev; pred != null; ) { // unsplice WNode succ, pp; // find valid successor while ((succ = node.next) == null || succ.status == CANCELLED) { WNode q = null; // find successor the slow way for (WNode t = wtail; t != null && t != node; t = t.prev) if (t.status != CANCELLED) q = t; // don't link if succ cancelled if (succ == q || // ensure accurate successor U.compareAndSwapObject(node, WNEXT, succ, succ = q)) { if (succ == null && node == wtail) U.compareAndSwapObject(this, WTAIL, node, pred); break; } } if (pred.next == node) // unsplice pred link U.compareAndSwapObject(pred, WNEXT, node, succ); if (succ != null && (w = succ.thread) != null) { succ.thread = null; U.unpark(w); // wake up succ to observe new pred } if (pred.status != CANCELLED || (pp = pred.prev) == null) break; node.prev = pp; // repeat if new pred wrong/cancelled U.compareAndSwapObject(pp, WNEXT, pred, succ); pred = pp; } } } WNode h; // Possibly release first waiter while ((h = whead) != null) { long s; WNode q; // similar to release() but check eligibility if ((q = h.next) == null || q.status == CANCELLED) { for (WNode t = wtail; t != null && t != h; t = t.prev) if (t.status <= 0) q = t; } if (h == whead) { if (q != null && h.status == 0 && ((s = state) & ABITS) != WBIT && // waiter is eligible (s == 0L || q.mode == RMODE)) release(h); break; } } return (interrupted || Thread.interrupted()) ? INTERRUPTED : 0L; }
/** * See above for explanation. * * @param interruptible true if should check interrupts and if so return INTERRUPTED * @param deadline if nonzero, the System.nanoTime value to timeout at (and return zero) * @return next state, or INTERRUPTED */ private long acquireRead(boolean interruptible, long deadline) { WNode node = null, p; boolean interrupted = false; for (int spins = -1; ; ) { WNode h; if ((h = whead) == (p = wtail)) { for (long m, s, ns; ; ) { if ((m = (s = state) & ABITS) < RFULL ? U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) : (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) { if (interrupted) Thread.currentThread().interrupt(); return ns; } else if (m >= WBIT) { if (spins > 0) { if (LockSupport.nextSecondarySeed() >= 0) --spins; } else { if (spins == 0) { WNode nh = whead, np = wtail; if ((nh == h && np == p) || (h = nh) != (p = np)) break; } spins = SPINS; } } } } if (p == null) { // initialize queue WNode hd = new WNode(WMODE, null); if (U.compareAndSwapObject(this, WHEAD, null, hd)) wtail = hd; } else if (node == null) node = new WNode(RMODE, p); else if (h == p || p.mode != RMODE) { if (node.prev != p) node.prev = p; else if (U.compareAndSwapObject(this, WTAIL, p, node)) { p.next = node; break; } } else if (!U.compareAndSwapObject(p, WCOWAIT, node.cowait = p.cowait, node)) node.cowait = null; else { for (; ; ) { WNode pp, c; Thread w; if ((h = whead) != null && (c = h.cowait) != null && U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) && (w = c.thread) != null) // help release U.unpark(w); if (h == (pp = p.prev) || h == p || pp == null) { long m, s, ns; do { if ((m = (s = state) & ABITS) < RFULL ? U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) : (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) { if (interrupted) Thread.currentThread().interrupt(); return ns; } } while (m < WBIT); } if (whead == h && p.prev == pp) { long time; if (pp == null || h == p || p.status > 0) { node = null; // throw away break; } if (deadline == 0L) time = 0L; else if ((time = deadline - System.nanoTime()) <= 0L) return cancelWaiter(node, p, false); Thread wt = Thread.currentThread(); U.putObject(wt, PARKBLOCKER, this); node.thread = wt; if ((h != pp || (state & ABITS) == WBIT) && whead == h && p.prev == pp) U.park(false, time); node.thread = null; U.putObject(wt, PARKBLOCKER, null); // if (interruptible && Thread.interrupted()) // return cancelWaiter(node, p, true); if (Thread.interrupted()) { if (interruptible) return cancelWaiter(node, p, true); else interrupted = true; } } } } } for (int spins = -1; ; ) { WNode h, np, pp; int ps; if ((h = whead) == p) { if (spins < 0) spins = HEAD_SPINS; else if (spins < MAX_HEAD_SPINS) spins <<= 1; for (int k = spins; ; ) { // spin at head long m, s, ns; if ((m = (s = state) & ABITS) < RFULL ? U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) : (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) { WNode c; Thread w; whead = node; node.prev = null; while ((c = node.cowait) != null) { if (U.compareAndSwapObject(node, WCOWAIT, c, c.cowait) && (w = c.thread) != null) U.unpark(w); } if (interrupted) Thread.currentThread().interrupt(); return ns; } else if (m >= WBIT && LockSupport.nextSecondarySeed() >= 0 && --k <= 0) break; } } else if (h != null) { WNode c; Thread w; while ((c = h.cowait) != null) { if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) && (w = c.thread) != null) U.unpark(w); } } if (whead == h) { if ((np = node.prev) != p) { if (np != null) (p = np).next = node; // stale } else if ((ps = p.status) == 0) U.compareAndSwapInt(p, WSTATUS, 0, WAITING); else if (ps == CANCELLED) { if ((pp = p.prev) != null) { node.prev = pp; pp.next = node; } } else { long time; if (deadline == 0L) time = 0L; else if ((time = deadline - System.nanoTime()) <= 0L) return cancelWaiter(node, node, false); Thread wt = Thread.currentThread(); U.putObject(wt, PARKBLOCKER, this); node.thread = wt; if (p.status < 0 && (p != h || (state & ABITS) == WBIT) && whead == h && node.prev == p) U.park(false, time); node.thread = null; U.putObject(wt, PARKBLOCKER, null); // if (interruptible && Thread.interrupted()) // return cancelWaiter(node, node, true); if (Thread.interrupted()) { if (interruptible) return cancelWaiter(node, p, true); else interrupted = true; } } } } }
/** * See above for explanation. * * @param interruptible true if should check interrupts and if so return INTERRUPTED * @param deadline if nonzero, the System.nanoTime value to timeout at (and return zero) * @return next state, or INTERRUPTED */ private long acquireWrite(boolean interruptible, long deadline) { WNode node = null, p; boolean interrupted = false; for (int spins = -1; ; ) { // spin while enqueuing long m, s, ns; if ((m = (s = state) & ABITS) == 0L) { if (U.compareAndSwapLong(this, STATE, s, ns = s + WBIT)) { if (interrupted) Thread.currentThread().interrupt(); return ns; } } else if (spins < 0) spins = (m == WBIT && wtail == whead) ? SPINS : 0; else if (spins > 0) { if (LockSupport.nextSecondarySeed() >= 0) --spins; } else if ((p = wtail) == null) { // initialize queue WNode hd = new WNode(WMODE, null); if (U.compareAndSwapObject(this, WHEAD, null, hd)) wtail = hd; } else if (node == null) node = new WNode(WMODE, p); else if (node.prev != p) node.prev = p; else if (U.compareAndSwapObject(this, WTAIL, p, node)) { p.next = node; break; } } for (int spins = -1; ; ) { WNode h, np, pp; int ps; if ((h = whead) == p) { if (spins < 0) spins = HEAD_SPINS; else if (spins < MAX_HEAD_SPINS) spins <<= 1; for (int k = spins; ; ) { // spin at head long s, ns; if (((s = state) & ABITS) == 0L) { if (U.compareAndSwapLong(this, STATE, s, ns = s + WBIT)) { whead = node; node.prev = null; if (interrupted) Thread.currentThread().interrupt(); return ns; } } else if (LockSupport.nextSecondarySeed() >= 0 && --k <= 0) break; } } else if (h != null) { // help release stale waiters WNode c; Thread w; while ((c = h.cowait) != null) { if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) && (w = c.thread) != null) U.unpark(w); } } if (whead == h) { if ((np = node.prev) != p) { if (np != null) (p = np).next = node; // stale } else if ((ps = p.status) == 0) U.compareAndSwapInt(p, WSTATUS, 0, WAITING); else if (ps == CANCELLED) { if ((pp = p.prev) != null) { node.prev = pp; pp.next = node; } } else { long time; // 0 argument to park means no timeout if (deadline == 0L) time = 0L; else if ((time = deadline - System.nanoTime()) <= 0L) return cancelWaiter(node, node, false); Thread wt = Thread.currentThread(); U.putObject(wt, PARKBLOCKER, this); node.thread = wt; if (p.status < 0 && (p != h || (state & ABITS) != 0L) && whead == h && node.prev == p) U.park(false, time); // emulate LockSupport.park node.thread = null; U.putObject(wt, PARKBLOCKER, null); // if (interruptible && Thread.interrupted()) // return cancelWaiter(node, node, true); if (Thread.interrupted()) { if (interruptible) return cancelWaiter(node, node, true); else interrupted = true; } } } } }