ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/jsr166y/LinkedTransferQueue.java
(Generate patch)

Comparing jsr166/src/jsr166y/LinkedTransferQueue.java (file contents):
Revision 1.45 by dl, Wed Oct 21 16:30:40 2009 UTC vs.
Revision 1.49 by jsr166, Thu Oct 22 15:58:44 2009 UTC

# Line 105 | Line 105 | public class LinkedTransferQueue<E> exte
105       * successful atomic operation per enq/deq pair. But it also
106       * enables lower cost variants of queue maintenance mechanics. (A
107       * variation of this idea applies even for non-dual queues that
108 <     * support deletion of embedded elements, such as
108 >     * support deletion of interior elements, such as
109       * j.u.c.ConcurrentLinkedQueue.)
110       *
111 <     * Once a node is matched, its item can never again change.  We
112 <     * may thus arrange that the linked list of them contains a prefix
113 <     * of zero or more matched nodes, followed by a suffix of zero or
114 <     * more unmatched nodes. (Note that we allow both the prefix and
115 <     * suffix to be zero length, which in turn means that we do not
116 <     * use a dummy header.)  If we were not concerned with either time
117 <     * or space efficiency, we could correctly perform enqueue and
118 <     * dequeue operations by traversing from a pointer to the initial
119 <     * node; CASing the item of the first unmatched node on match and
120 <     * CASing the next field of the trailing node on appends.  While
121 <     * this would be a terrible idea in itself, it does have the
122 <     * benefit of not requiring ANY atomic updates on head/tail
123 <     * fields.
111 >     * Once a node is matched, its match status can never again
112 >     * change.  We may thus arrange that the linked list of them
113 >     * contain a prefix of zero or more matched nodes, followed by a
114 >     * suffix of zero or more unmatched nodes. (Note that we allow
115 >     * both the prefix and suffix to be zero length, which in turn
116 >     * means that we do not use a dummy header.)  If we were not
117 >     * concerned with either time or space efficiency, we could
118 >     * correctly perform enqueue and dequeue operations by traversing
119 >     * from a pointer to the initial node; CASing the item of the
120 >     * first unmatched node on match and CASing the next field of the
121 >     * trailing node on appends. (Plus some special-casing when
122 >     * initially empty).  While this would be a terrible idea in
123 >     * itself, it does have the benefit of not requiring ANY atomic
124 >     * updates on head/tail fields.
125       *
126       * We introduce here an approach that lies between the extremes of
127 <     * never versus always updating queue (head and tail) pointers
128 <     * that reflects the tradeoff of sometimes require extra traversal
129 <     * steps to locate the first and/or last unmatched nodes, versus
130 <     * the reduced overhead and contention of fewer updates to queue
131 <     * pointers. For example, a possible snapshot of a queue is:
127 >     * never versus always updating queue (head and tail) pointers.
128 >     * This offers a tradeoff between sometimes requiring extra
129 >     * traversal steps to locate the first and/or last unmatched
130 >     * nodes, versus the reduced overhead and contention of fewer
131 >     * updates to queue pointers. For example, a possible snapshot of
132 >     * a queue is:
133       *
134       *  head           tail
135       *    |              |
# Line 139 | Line 141 | public class LinkedTransferQueue<E> exte
141       * similarly for "tail") is an empirical matter. We have found
142       * that using very small constants in the range of 1-3 work best
143       * over a range of platforms. Larger values introduce increasing
144 <     * costs of cache misses and risks of long traversal chains.
144 >     * costs of cache misses and risks of long traversal chains, while
145 >     * smaller values increase CAS contention and overhead.
146       *
147       * Dual queues with slack differ from plain M&S dual queues by
148       * virtue of only sometimes updating head or tail pointers when
# Line 180 | Line 183 | public class LinkedTransferQueue<E> exte
183       * (Similar issues arise in non-GC environments.)  To cope with
184       * this in our implementation, upon CASing to advance the head
185       * pointer, we set the "next" link of the previous head to point
186 <     * only to itself; thus limiting the length connected dead lists.
186 >     * only to itself; thus limiting the length of connected dead lists.
187       * (We also take similar care to wipe out possibly garbage
188       * retaining values held in other Node fields.)  However, doing so
189       * adds some further complexity to traversal: If any "next"
# Line 203 | Line 206 | public class LinkedTransferQueue<E> exte
206       * additional GC bookkeeping ("write barriers") that are sometimes
207       * more costly than the writes themselves because of contention).
208       *
209 <     * Removal of internal nodes (due to timed out or interrupted
209 >     * Removal of interior nodes (due to timed out or interrupted
210       * waits, or calls to remove or Iterator.remove) uses a scheme
211 <     * roughly similar to that in Scherer, Lea, and Scott
211 >     * roughly similar to that in Scherer, Lea, and Scott's
212       * SynchronousQueue. Given a predecessor, we can unsplice any node
213       * except the (actual) tail of the queue. To avoid build-up of
214       * cancelled trailing nodes, upon a request to remove a trailing
215 <     * node, it is placed in field "cleanMe" to be unspliced later.
215 >     * node, it is placed in field "cleanMe" to be unspliced upon the
216 >     * next call to unsplice any other node.  Situations needing such
217 >     * mechanics are not common but do occur in practice; for example
218 >     * when an unbounded series of short timed calls to poll
219 >     * repeatedly time out but never otherwise fall off the list
220 >     * because of an untimed call to take at the front of the
221 >     * queue. (Note that maintaining field cleanMe does not otherwise
222 >     * much impact garbage retention even if never cleared by some
223 >     * other call because the held node will eventually either
224 >     * directly or indirectly lead to a self-link once off the list.)
225       *
226       * *** Overview of implementation ***
227       *
# Line 217 | Line 229 | public class LinkedTransferQueue<E> exte
229       * slack of two.  The slack value is hard-wired: a path greater
230       * than one is naturally implemented by checking equality of
231       * traversal pointers except when the list has only one element,
232 <     * in which case we keep max slack at one. Avoiding tracking
233 <     * explicit counts across situations slightly simplifies an
232 >     * in which case we keep target slack at one. Avoiding tracking
233 >     * explicit counts across method calls slightly simplifies an
234       * already-messy implementation. Using randomization would
235       * probably work better if there were a low-quality dirt-cheap
236       * per-thread one available, but even ThreadLocalRandom is too
237       * heavy for these purposes.
238       *
239 <     * With such a small slack value, path short-circuiting is rarely
240 <     * worthwhile. However, it is used (in awaitMatch) immediately
241 <     * before a waiting thread starts to block, as a final bit of
242 <     * helping at a point when contention with others is extremely
243 <     * unlikely (since if other threads that could release it are
244 <     * operating, then the current thread wouldn't be blocking).
239 >     * With such a small target slack value, it is rarely worthwhile
240 >     * to augment this with path short-circuiting; i.e., unsplicing
241 >     * nodes between head and the first unmatched node, or similarly
242 >     * for tail, rather than advancing head or tail proper. However,
243 >     * it is used (in awaitMatch) immediately before a waiting thread
244 >     * starts to block, as a final bit of helping at a point when
245 >     * contention with others is extremely unlikely (since if other
246 >     * threads that could release it are operating, then the current
247 >     * thread wouldn't be blocking).
248 >     *
249 >     * We allow both the head and tail fields to be null before any
250 >     * nodes are enqueued; initializing upon first append.  This
251 >     * simplifies some other logic, as well as providing more
252 >     * efficient explicit control paths instead of letting JVMs insert
253 >     * implicit NullPointerExceptions when they are null.  While not
254 >     * currently fully implemented, we also leave open the possibility
255 >     * of re-nulling these fields when empty (which is complicated to
256 >     * arrange, for little benefit.)
257       *
258       * All enqueue/dequeue operations are handled by the single method
259       * "xfer" with parameters indicating whether to act as some form
# Line 249 | Line 273 | public class LinkedTransferQueue<E> exte
273       *    case matching it and returning, also if necessary updating
274       *    head to one past the matched node (or the node itself if the
275       *    list has no other unmatched nodes). If the CAS misses, then
276 <     *    a retry loops until the slack is at most two. Traversals
277 <     *    also check if the initial head is now off-list, in which
278 <     *    case they start at the new head.
276 >     *    a loop retries advancing head by two steps until either
277 >     *    success or the slack is at most two. By requiring that each
278 >     *    attempt advances head by two (if applicable), we ensure that
279 >     *    the slack does not grow without bound. Traversals also check
280 >     *    if the initial head is now off-list, in which case they
281 >     *    start at the new head.
282       *
283       *    If no candidates are found and the call was untimed
284       *    poll/offer, (argument "how" is NOW) return.
# Line 270 | Line 297 | public class LinkedTransferQueue<E> exte
297       *    safely jump to a node on the list by continuing the
298       *    traversal at current head.
299       *
300 <     *    On successful append, if the call was ASYNC, return
300 >     *    On successful append, if the call was ASYNC, return.
301       *
302       * 3. Await match or cancellation (method awaitMatch)
303       *
# Line 322 | Line 349 | public class LinkedTransferQueue<E> exte
349      private static final int CHAINED_SPINS = FRONT_SPINS >>> 2;
350  
351      /**
352 <     * Queue nodes. Uses Object, not E for items to allow forgetting
352 >     * Queue nodes. Uses Object, not E, for items to allow forgetting
353       * them after use.  Relies heavily on Unsafe mechanics to minimize
354 <     * unecessary ordering constraints: Writes that intrinsically
354 >     * unnecessary ordering constraints: Writes that intrinsically
355       * precede or follow CASes use simple relaxed forms.  Other
356       * cleanups use releasing/lazy writes.
357       */
358      static final class Node {
359          final boolean isData;   // false if this is a request node
360 <        volatile Object item;   // initially nonnull if isData; CASed to match
360 >        volatile Object item;   // initially non-null if isData; CASed to match
361          volatile Node next;
362          volatile Thread waiter; // null until waiting
363  
# Line 344 | Line 371 | public class LinkedTransferQueue<E> exte
371          }
372  
373          /**
374 <         * Create a new node. Uses relaxed write because item can only
375 <         * be seen if followed by CAS
374 >         * Creates a new node. Uses relaxed write because item can only
375 >         * be seen if followed by CAS.
376           */
377          Node(Object item, boolean isData) {
378              UNSAFE.putObject(this, itemOffset, item); // relaxed write
# Line 391 | Line 418 | public class LinkedTransferQueue<E> exte
418          }
419  
420          /**
421 <         * Tries to artifically match a data node -- used by remove.
421 >         * Tries to artificially match a data node -- used by remove.
422           */
423          final boolean tryMatchData() {
424              Object x = item;
# Line 449 | Line 476 | public class LinkedTransferQueue<E> exte
476       * Implements all queuing methods. See above for explanation.
477       *
478       * @param e the item or null for take
479 <     * @param haveData true if this is a put else a take
479 >     * @param haveData true if this is a put, else a take
480       * @param how NOW, ASYNC, SYNC, or TIMEOUT
481       * @param nanos timeout in nanosecs, used only if mode is TIMEOUT
482 <     * @return an item if matched, else e;
482 >     * @return an item if matched, else e
483       * @throws NullPointerException if haveData mode but e is null
484       */
485      private Object xfer(Object e, boolean haveData, int how, long nanos) {
# Line 487 | Line 514 | public class LinkedTransferQueue<E> exte
514                      }
515                  }
516                  Node n = p.next;
517 <                p = p != n ? n : (h = head);  // Use head if p offlist
517 >                p = (p != n) ? n : (h = head); // Use head if p offlist
518              }
519  
520              if (how >= ASYNC) {               // No matches available
# Line 504 | Line 531 | public class LinkedTransferQueue<E> exte
531      }
532  
533      /**
534 <     * Tries to append node s as tail
535 <     * @param haveData true if appending in data mode
534 >     * Tries to append node s as tail.
535 >     *
536       * @param s the node to append
537 +     * @param haveData true if appending in data mode
538       * @return null on failure due to losing race with append in
539       * different mode, else s's predecessor, or s itself if no
540       * predecessor
541       */
542      private Node tryAppend(Node s, boolean haveData) {
543 <        for (Node t = tail, p = t;;) { // move p to actual tail and append
543 >        for (Node t = tail, p = t;;) { // move p to last node and append
544              Node n, u;                        // temps for reads of next & tail
545              if (p == null && (p = head) == null) {
546                  if (casHead(null, s))
# Line 520 | Line 548 | public class LinkedTransferQueue<E> exte
548              }
549              else if (p.cannotPrecede(haveData))
550                  return null;                  // lost race vs opposite mode
551 <            else if ((n = p.next) != null)    // Not tail; keep traversing
551 >            else if ((n = p.next) != null)    // not last; keep traversing
552                  p = p != t && t != (u = tail) ? (t = u) : // stale tail
553 <                    p != n ? n : null;        // restart if off list
553 >                    (p != n) ? n : null;      // restart if off list
554              else if (!p.casNext(null, s))
555                  p = p.next;                   // re-read on CAS failure
556              else {
557 <                if (p != t) {                 // Update if slack now >= 2
557 >                if (p != t) {                 // update if slack now >= 2
558                      while ((tail != t || !casTail(t, s)) &&
559                             (t = tail)   != null &&
560                             (s = t.next) != null && // advance and retry
# Line 540 | Line 568 | public class LinkedTransferQueue<E> exte
568      /**
569       * Spins/yields/blocks until node s is matched or caller gives up.
570       *
571 <     * @param pred the predecessor of s or s or null if none
571 >     * @param pred the predecessor of s, or s or null if none
572       * @param s the waiting node
573       * @param e the comparison value for checking match
574       * @param how either SYNC or TIMEOUT
# Line 593 | Line 621 | public class LinkedTransferQueue<E> exte
621      }
622  
623      /**
624 <     * Return spin/yield value for a node with given predecessor and
624 >     * Returns spin/yield value for a node with given predecessor and
625       * data mode. See above for explanation.
626       */
627      private static int spinsFor(Node pred, boolean haveData) {
# Line 633 | Line 661 | public class LinkedTransferQueue<E> exte
661      /* -------------- Traversal methods -------------- */
662  
663      /**
664 <     * Return the first unmatched node of the given mode, or null if
664 >     * Returns the first unmatched node of the given mode, or null if
665       * none.  Used by methods isEmpty, hasWaitingConsumer.
666       */
667      private Node firstOfMode(boolean data) {
668          for (Node p = head; p != null; ) {
669              if (!p.isMatched())
670 <                return p.isData == data? p : null;
670 >                return (p.isData == data) ? p : null;
671              Node n = p.next;
672 <            p = n != p ? n : head;
672 >            p = (n != p) ? n : head;
673          }
674          return null;
675      }
# Line 657 | Line 685 | public class LinkedTransferQueue<E> exte
685              if (item != p && (item != null) == isData)
686                  return isData ? item : null;
687              Node n = p.next;
688 <            p = n != p ? n : head;
688 >            p = (n != p) ? n : head;
689          }
690          return null;
691      }
692  
693      /**
694 <     * Traverse and count nodes of the given mode.
695 <     * Used by methds size and getWaitingConsumerCount.
694 >     * Traverses and counts unmatched nodes of the given mode.
695 >     * Used by methods size and getWaitingConsumerCount.
696       */
697      private int countOfMode(boolean data) {
698          int count = 0;
# Line 711 | Line 739 | public class LinkedTransferQueue<E> exte
739                  else if (item == null)
740                      break;
741                  Node n = p.next;
742 <                p = n != p ? n : head;
742 >                p = (n != p) ? n : head;
743              }
744              nextNode = null;
745          }
# Line 753 | Line 781 | public class LinkedTransferQueue<E> exte
781          s.forgetContents(); // clear unneeded fields
782          /*
783           * At any given time, exactly one node on list cannot be
784 <         * deleted -- the last inserted node. To accommodate this, if
785 <         * we cannot delete s, we save its predecessor as "cleanMe",
784 >         * unlinked -- the last inserted node. To accommodate this, if
785 >         * we cannot unlink s, we save its predecessor as "cleanMe",
786           * processing the previously saved version first. Because only
787           * one node in the list can have a null next, at least one of
788           * node s or the node previously saved can always be
# Line 762 | Line 790 | public class LinkedTransferQueue<E> exte
790           */
791          if (pred != null && pred != s) {
792              while (pred.next == s) {
793 <                Node oldpred = cleanMe == null? null : reclean();
793 >                Node oldpred = (cleanMe == null) ? null : reclean();
794                  Node n = s.next;
795                  if (n != null) {
796                      if (n != s)
# Line 1124 | Line 1152 | public class LinkedTransferQueue<E> exte
1152      }
1153  
1154      /**
1155 <     * Save the state to a stream (that is, serialize it).
1155 >     * Saves the state to a stream (that is, serializes it).
1156       *
1157       * @serialData All of the elements (each an {@code E}) in
1158       * the proper order, followed by a null
# Line 1140 | Line 1168 | public class LinkedTransferQueue<E> exte
1168      }
1169  
1170      /**
1171 <     * Reconstitute the Queue instance from a stream (that is,
1172 <     * deserialize it).
1171 >     * Reconstitutes the Queue instance from a stream (that is,
1172 >     * deserializes it).
1173       *
1174       * @param s the stream
1175       */

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines