--- jsr166/src/jsr166y/LinkedTransferQueue.java 2009/10/28 09:28:30 1.57 +++ jsr166/src/jsr166y/LinkedTransferQueue.java 2009/11/02 06:12:02 1.64 @@ -361,14 +361,14 @@ public class LinkedTransferQueue exte * precede or follow CASes use simple relaxed forms. Other * cleanups use releasing/lazy writes. */ - static final class Node { + static final class Node { final boolean isData; // false if this is a request node volatile Object item; // initially non-null if isData; CASed to match - volatile Node next; + volatile Node next; volatile Thread waiter; // null until waiting // CAS methods for fields - final boolean casNext(Node cmp, Node val) { + final boolean casNext(Node cmp, Node val) { return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); } @@ -381,7 +381,7 @@ public class LinkedTransferQueue exte * Creates a new node. Uses relaxed write because item can only * be seen if followed by CAS. */ - Node(E item, boolean isData) { + Node(Object item, boolean isData) { UNSAFE.putObject(this, itemOffset, item); // relaxed write this.isData = isData; } @@ -414,6 +414,13 @@ public class LinkedTransferQueue exte } /** + * Returns true if this is an unmatched request node. + */ + final boolean isUnmatchedRequest() { + return !isData && item == null; + } + + /** * Returns true if a node with the given mode cannot be * appended to this node because this node is unmatched and * has opposite data mode. @@ -428,6 +435,7 @@ public class LinkedTransferQueue exte * Tries to artificially match a data node -- used by remove. */ final boolean tryMatchData() { + assert isData; Object x = item; if (x != null && x != this && casItem(x, null)) { LockSupport.unpark(waiter); @@ -449,30 +457,29 @@ public class LinkedTransferQueue exte } /** head of the queue; null until first enqueue */ - transient volatile Node head; + transient volatile Node head; /** predecessor of dangling unspliceable node */ - private transient volatile Node cleanMe; // decl here reduces contention + private transient volatile Node cleanMe; // decl here reduces contention /** tail of the queue; null until first append */ - private transient volatile Node tail; + private transient volatile Node tail; // CAS methods for fields - private boolean casTail(Node cmp, Node val) { + private boolean casTail(Node cmp, Node val) { return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val); } - private boolean casHead(Node cmp, Node val) { + private boolean casHead(Node cmp, Node val) { return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val); } - private boolean casCleanMe(Node cmp, Node val) { + private boolean casCleanMe(Node cmp, Node val) { return UNSAFE.compareAndSwapObject(this, cleanMeOffset, cmp, val); } /* - * Possible values for "how" argument in xfer method. Beware that - * the order of assigned numerical values matters. + * Possible values for "how" argument in xfer method. */ private static final int NOW = 0; // for untimed poll, tryTransfer private static final int ASYNC = 1; // for offer, put, add @@ -498,20 +505,19 @@ public class LinkedTransferQueue exte 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 + 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 + for (Node h = head, p = h; p != null;) { // find & match first node boolean isData = p.isData; Object item = p.item; if (item != p && (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 head by 2 + for (Node q = p; q != h;) { + Node n = q.next; // update head by 2 if (n != null) // unless singleton q = n; if (head == h && casHead(h, q)) { @@ -526,18 +532,18 @@ public class LinkedTransferQueue exte return this.cast(item); } } - Node n = p.next; + Node n = p.next; p = (p != n) ? n : (h = head); // Use head if p offlist } - if (how >= ASYNC) { // No matches available + if (how != NOW) { // No matches available if (s == null) - s = new Node(e, haveData); - Node pred = tryAppend(s, haveData); + s = new Node(e, haveData); + Node pred = tryAppend(s, haveData); if (pred == null) continue retry; // lost race vs opposite mode - if (how >= SYNC) - return awaitMatch(s, pred, e, how, nanos); + if (how != ASYNC) + return awaitMatch(s, pred, e, (how == TIMEOUT), nanos); } return e; // not waiting } @@ -552,9 +558,9 @@ public class LinkedTransferQueue exte * different mode, else s's predecessor, or s itself if no * predecessor */ - private Node tryAppend(Node s, boolean haveData) { - for (Node t = tail, p = t;;) { // move p to last node and append - Node n, u; // temps for reads of next & tail + private Node tryAppend(Node s, boolean haveData) { + for (Node t = tail, p = t;;) { // move p to last node and append + Node n, u; // temps for reads of next & tail if (p == null && (p = head) == null) { if (casHead(null, s)) return s; // initialize @@ -586,12 +592,12 @@ public class LinkedTransferQueue exte * 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 how either SYNC or TIMEOUT + * @param timed if true, wait only until timeout elapses * @param nanos timeout value * @return matched item, or e if unmatched on interrupt or timeout */ - private E awaitMatch(Node s, Node pred, E e, int how, long nanos) { - long lastTime = (how == TIMEOUT) ? System.nanoTime() : 0L; + private E awaitMatch(Node s, Node pred, E e, boolean timed, long nanos) { + long lastTime = timed ? System.nanoTime() : 0L; Thread w = Thread.currentThread(); int spins = -1; // initialized after first item and cancel checks ThreadLocalRandom randomYields = null; // bound if needed @@ -603,7 +609,7 @@ public class LinkedTransferQueue exte s.forgetContents(); // avoid garbage return this.cast(item); } - if ((w.isInterrupted() || (how == TIMEOUT && nanos <= 0)) && + if ((w.isInterrupted() || (timed && nanos <= 0)) && s.casItem(e, s)) { // cancel unsplice(pred, s); return e; @@ -622,7 +628,7 @@ public class LinkedTransferQueue exte else if (s.waiter == null) { s.waiter = w; // request unpark then recheck } - else if (how == TIMEOUT) { + else if (timed) { long now = System.nanoTime(); if ((nanos -= now - lastTime) > 0) LockSupport.parkNanos(this, nanos); @@ -640,7 +646,7 @@ public class LinkedTransferQueue exte * Returns spin/yield value for a node with given predecessor and * data mode. See above for explanation. */ - private static int spinsFor(Node pred, boolean haveData) { + private static int spinsFor(Node pred, boolean haveData) { if (MP && pred != null) { if (pred.isData != haveData) // phase change return FRONT_SPINS + CHAINED_SPINS; @@ -657,10 +663,10 @@ public class LinkedTransferQueue exte * or trailing node; failing on contention. */ private void shortenHeadPath() { - Node h, hn, p, q; + Node h, hn, p, q; if ((p = h = head) != null && h.isMatched() && (q = hn = h.next) != null) { - Node n; + Node n; while ((n = q.next) != q) { if (n == null || !q.isMatched()) { if (hn != q && h.next == hn) @@ -676,15 +682,23 @@ public class LinkedTransferQueue exte /* -------------- Traversal methods -------------- */ /** + * Returns the successor of p, or the head node if p.next has been + * linked to self, which will only be true if traversing with a + * stale pointer that is now off the list. + */ + final Node succ(Node p) { + Node next = p.next; + return (p == next) ? head : next; + } + + /** * Returns the first unmatched node of the given mode, or null if * none. Used by methods isEmpty, hasWaitingConsumer. */ - private Node firstOfMode(boolean data) { - for (Node p = head; p != null; ) { + private Node firstOfMode(boolean isData) { + for (Node p = head; p != null; p = succ(p)) { if (!p.isMatched()) - return (p.isData == data) ? p : null; - Node n = p.next; - p = (n != p) ? n : head; + return (p.isData == isData) ? p : null; } return null; } @@ -694,13 +708,14 @@ public class LinkedTransferQueue exte * null if none. Used by peek. */ private E firstDataItem() { - for (Node p = head; p != null; ) { - boolean isData = p.isData; + for (Node p = head; p != null; p = succ(p)) { Object item = p.item; - if (item != p && (item != null) == isData) - return isData ? this.cast(item) : null; - Node n = p.next; - p = (n != p) ? n : head; + if (p.isData) { + if (item != null && item != p) + return this.cast(item); + } + else if (item == null) + return null; } return null; } @@ -711,14 +726,14 @@ public class LinkedTransferQueue exte */ private int countOfMode(boolean data) { int count = 0; - for (Node p = head; p != null; ) { + for (Node p = head; p != null; ) { if (!p.isMatched()) { if (p.isData != data) return 0; if (++count == Integer.MAX_VALUE) // saturated break; } - Node n = p.next; + Node n = p.next; if (n != p) p = n; else { @@ -730,19 +745,19 @@ public class LinkedTransferQueue exte } final class Itr implements Iterator { - private Node nextNode; // next node to return item for - private E nextItem; // the corresponding item - private Node lastRet; // last returned node, to support remove + private Node nextNode; // next node to return item for + private E nextItem; // the corresponding item + private Node lastRet; // last returned node, to support remove + private Node lastPred; // predecessor to unlink lastRet /** * Moves to next node after prev, or first node if prev null. */ - private void advance(Node prev) { + private void advance(Node prev) { + lastPred = lastRet; lastRet = prev; - Node p; - if (prev == null || (p = prev.next) == prev) - p = head; - while (p != null) { + for (Node p = (prev == null) ? head : succ(prev); + p != null; p = succ(p)) { Object item = p.item; if (p.isData) { if (item != null && item != p) { @@ -753,8 +768,6 @@ public class LinkedTransferQueue exte } else if (item == null) break; - Node n = p.next; - p = (n != p) ? n : head; } nextNode = null; } @@ -768,7 +781,7 @@ public class LinkedTransferQueue exte } public final E next() { - Node p = nextNode; + Node p = nextNode; if (p == null) throw new NoSuchElementException(); E e = nextItem; advance(p); @@ -776,10 +789,9 @@ public class LinkedTransferQueue exte } public final void remove() { - Node p = lastRet; + Node p = lastRet; if (p == null) throw new IllegalStateException(); - lastRet = null; - findAndRemoveNode(p); + findAndRemoveDataNode(lastPred, p); } } @@ -792,7 +804,7 @@ public class LinkedTransferQueue exte * @param pred predecessor of node to be unspliced * @param s the node to be unspliced */ - private void unsplice(Node pred, Node s) { + private void unsplice(Node pred, Node s) { s.forgetContents(); // clear unneeded fields /* * At any given time, exactly one node on list cannot be @@ -805,16 +817,18 @@ public class LinkedTransferQueue exte */ if (pred != null && pred != s) { while (pred.next == s) { - Node oldpred = (cleanMe == null) ? null : reclean(); - Node n = s.next; + Node oldpred = (cleanMe == null) ? null : reclean(); + Node n = s.next; if (n != null) { if (n != s) pred.casNext(s, n); break; } if (oldpred == pred || // Already saved - (oldpred == null && casCleanMe(null, pred))) - break; // Postpone cleaning + ((oldpred == null || oldpred.next == s) && + casCleanMe(oldpred, pred))) { + break; + } } } } @@ -825,7 +839,7 @@ public class LinkedTransferQueue exte * * @return current cleanMe node (or null) */ - private Node reclean() { + private Node reclean() { /* * cleanMe is, or at one time was, predecessor of a cancelled * node s that was the tail so could not be unspliced. If it @@ -836,10 +850,10 @@ public class LinkedTransferQueue exte * we can (must) clear cleanMe without unsplicing. This can * loop only due to contention. */ - Node pred; + Node pred; while ((pred = cleanMe) != null) { - Node s = pred.next; - Node n; + Node s = pred.next; + Node n; if (s == null || s == pred || !s.isMatched()) casCleanMe(pred, null); // already gone else if ((n = s.next) != null) { @@ -855,23 +869,28 @@ public class LinkedTransferQueue exte /** * Main implementation of Iterator.remove(). Find - * and unsplice the given node. + * and unsplice the given data node. + * @param possiblePred possible predecessor of s + * @param s the node to remove */ - final void findAndRemoveNode(Node s) { + final void findAndRemoveDataNode(Node possiblePred, Node s) { + assert s.isData; if (s.tryMatchData()) { - Node pred = null; - Node p = head; - while (p != null) { - if (p == s) { - unsplice(pred, p); - break; - } - if (!p.isData && !p.isMatched()) - break; - pred = p; - if ((p = p.next) == pred) { // stale - pred = null; - p = head; + if (possiblePred != null && possiblePred.next == s) + unsplice(possiblePred, s); // was actual predecessor + else { + for (Node pred = null, p = head; p != null; ) { + if (p == s) { + unsplice(pred, p); + break; + } + if (p.isUnmatchedRequest()) + break; + pred = p; + if ((p = p.next) == pred) { // stale + pred = null; + p = head; + } } } } @@ -882,9 +901,7 @@ public class LinkedTransferQueue exte */ private boolean findAndRemove(Object e) { if (e != null) { - Node pred = null; - Node p = head; - while (p != null) { + for (Node pred = null, p = head; p != null; ) { Object item = p.item; if (p.isData) { if (item != null && item != p && e.equals(item) && @@ -896,7 +913,7 @@ public class LinkedTransferQueue exte else if (item == null) break; pred = p; - if ((p = p.next) == pred) { + if ((p = p.next) == pred) { // stale pred = null; p = head; }