/** * Spins/yields/blocks until node s is matched or caller gives up. * * @param s the waiting node * @param pred the predecessor of s, or s itself if it has no predecessor, or null if unknown (the * null case does not occur in any current calls but may in possible future extensions) * @param e the comparison value for checking match * @param timed if true, wait only until timeout elapses * @param nanos timeout in nanosecs, used only if timed is true * @return matched item, or e if unmatched on interrupt or timeout */ private E awaitMatch(Node s, Node pred, E e, boolean timed, long nanos) { final long deadline = timed ? System.nanoTime() + nanos : 0L; Thread w = Thread.currentThread(); int spins = -1; // initialized after first item and cancel checks ThreadLocalRandom randomYields = null; // bound if needed for (; ; ) { Object item = s.item; if (item != e) { // matched // assert item != s; s.forgetContents(); // avoid garbage return LinkedTransferQueue.<E>cast(item); } if ((w.isInterrupted() || (timed && nanos <= 0)) && s.casItem(e, FORGOTTEN)) { // cancel unsplice(pred, s); return e; } if (spins < 0) { // establish spins at/near front if ((spins = spinsFor(pred, s.isData)) > 0) randomYields = ThreadLocalRandom.current(); } else if (spins > 0) { // spin --spins; if (randomYields.nextInt(CHAINED_SPINS) == 0) Thread.yield(); // occasionally yield } else if (s.waiter == null) { s.waiter = w; // request unpark then recheck } else if (timed) { nanos = deadline - System.nanoTime(); if (nanos > 0L) LockSupport.parkNanos(this, nanos); } else { LockSupport.park(this); } } }
/** Returns the item in the first unmatched node with isData; or null if none. Used by peek. */ private E firstDataItem() { for (Node p = head; p != null; p = succ(p)) { Object item = p.item; if (p.isData) { if (item != null && item != FORGOTTEN) return LinkedTransferQueue.<E>cast(item); } else if (item == null) return null; } return null; }
/** Moves to next node after prev, or first node if prev null. */ private void advance(Node prev) { /* * To track and avoid buildup of deleted nodes in the face * of calls to both Queue.remove and Itr.remove, we must * include variants of unsplice and sweep upon each * advance: Upon Itr.remove, we may need to catch up links * from lastPred, and upon other removes, we might need to * skip ahead from stale nodes and unsplice deleted ones * found while advancing. */ Node r, b; // reset lastPred upon possible deletion of lastRet if ((r = lastRet) != null && !r.isMatched()) lastPred = r; // next lastPred is old lastRet else if ((b = lastPred) == null || b.isMatched()) lastPred = null; // at start of list else { Node s, n; // help with removal of lastPred.next while ((s = b.next) != null && s != UNLINKED && s.isMatched() && (n = s.next) != null && n != UNLINKED) b.casNext(s, n); } this.lastRet = prev; for (Node p = prev, s, n; ; ) { s = (p == null) ? head : p.next; if (s == null) break; else if (s == UNLINKED) { p = null; continue; } Object item = s.item; if (s.isData) { if (item != null && item != FORGOTTEN) { nextItem = LinkedTransferQueue.<E>cast(item); nextNode = s; return; } } else if (item == null) break; // assert s.isMatched(); if (p == null) p = s; else if ((n = s.next) == null) break; else if (UNLINKED == n) p = null; else p.casNext(s, n); } nextNode = null; nextItem = null; }
/** * Implements all queuing methods. See above for explanation. * * @param e the item or null for take * @param haveData true if this is a put, else a take * @param how NOW, ASYNC, SYNC, or TIMED * @param nanos timeout in nanosecs, used only if mode is TIMED * @return an item if matched, else e * @throws NullPointerException if haveData mode but e is null */ private E xfer(E e, boolean haveData, int how, long nanos) { if (haveData && (e == null)) throw new NullPointerException(); Node s = null; // the node to append, if needed retry: for (; ; ) { // restart on append race for (Node h = head, p = h; p != null; ) { // find & match first node boolean isData = p.isData; Object item = p.item; if (item != FORGOTTEN && (item != null) == isData) { // unmatched if (isData == haveData) // can't match break; if (p.casItem(item, e)) { // match for (Node q = p; q != h; ) { Node n = q.next; // update by 2 unless singleton if (head == h && casHead(h, n == null ? q : n)) { h.forgetNext(); break; } // advance and retry if ((h = head) == null || (q = h.next) == null || !q.isMatched()) break; // unless slack < 2 } LockSupport.unpark(p.waiter); return LinkedTransferQueue.<E>cast(item); } } Node n = p.next; p = (UNLINKED != n) ? n : (h = head); // Use head if p offlist } if (how != NOW) { // No matches available if (s == null) s = new Node(e, haveData); Node pred = tryAppend(s, haveData); if (pred == null) continue retry; // lost race vs opposite mode if (how != ASYNC) return awaitMatch(s, pred, e, (how == TIMED), nanos); } return e; // not waiting } }