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

Comparing jsr166/src/jsr166e/ConcurrentHashMapV8.java (file contents):
Revision 1.108 by jsr166, Mon Jul 1 19:19:31 2013 UTC vs.
Revision 1.126 by jsr166, Mon Mar 7 23:55:31 2016 UTC

# Line 12 | Line 12 | import java.io.ObjectStreamField;
12   import java.io.Serializable;
13   import java.lang.reflect.ParameterizedType;
14   import java.lang.reflect.Type;
15 + import java.util.AbstractMap;
16   import java.util.Arrays;
17   import java.util.Collection;
17 import java.util.Comparator;
18   import java.util.ConcurrentModificationException;
19   import java.util.Enumeration;
20   import java.util.HashMap;
# Line 114 | Line 114 | import java.util.concurrent.locks.Reentr
114   * objects do not support method {@code setValue}.
115   *
116   * <ul>
117 < * <li> forEach: Perform a given action on each element.
117 > * <li>forEach: Perform a given action on each element.
118   * A variant form applies a given transformation on each element
119 < * before performing the action.</li>
119 > * before performing the action.
120   *
121 < * <li> search: Return the first available non-null result of
121 > * <li>search: Return the first available non-null result of
122   * applying a given function on each element; skipping further
123 < * search when a result is found.</li>
123 > * search when a result is found.
124   *
125 < * <li> reduce: Accumulate each element.  The supplied reduction
125 > * <li>reduce: Accumulate each element.  The supplied reduction
126   * function cannot rely on ordering (more formally, it should be
127   * both associative and commutative).  There are five variants:
128   *
129   * <ul>
130   *
131 < * <li> Plain reductions. (There is not a form of this method for
131 > * <li>Plain reductions. (There is not a form of this method for
132   * (key, value) function arguments since there is no corresponding
133 < * return type.)</li>
133 > * return type.)
134   *
135 < * <li> Mapped reductions that accumulate the results of a given
136 < * function applied to each element.</li>
135 > * <li>Mapped reductions that accumulate the results of a given
136 > * function applied to each element.
137   *
138 < * <li> Reductions to scalar doubles, longs, and ints, using a
139 < * given basis value.</li>
138 > * <li>Reductions to scalar doubles, longs, and ints, using a
139 > * given basis value.
140   *
141   * </ul>
142 * </li>
142   * </ul>
143   *
144   * <p>These bulk operations accept a {@code parallelismThreshold}
# Line 218 | Line 217 | import java.util.concurrent.locks.Reentr
217   * @param <K> the type of keys maintained by this map
218   * @param <V> the type of mapped values
219   */
220 < public class ConcurrentHashMapV8<K,V>
220 > public class ConcurrentHashMapV8<K,V> extends AbstractMap<K,V>
221      implements ConcurrentMap<K,V>, Serializable {
222      private static final long serialVersionUID = 7249069246763182397L;
223  
# Line 275 | Line 274 | public class ConcurrentHashMapV8<K,V>
274      /** Interface describing a function mapping two ints to an int */
275      public interface IntByIntToInt { int apply(int a, int b); }
276  
277 +
278      /*
279       * Overview:
280       *
# Line 380 | Line 380 | public class ConcurrentHashMapV8<K,V>
380       * The table is resized when occupancy exceeds a percentage
381       * threshold (nominally, 0.75, but see below).  Any thread
382       * noticing an overfull bin may assist in resizing after the
383 <     * initiating thread allocates and sets up the replacement
384 <     * array. However, rather than stalling, these other threads may
385 <     * proceed with insertions etc.  The use of TreeBins shields us
386 <     * from the worst case effects of overfilling while resizes are in
383 >     * initiating thread allocates and sets up the replacement array.
384 >     * However, rather than stalling, these other threads may proceed
385 >     * with insertions etc.  The use of TreeBins shields us from the
386 >     * worst case effects of overfilling while resizes are in
387       * progress.  Resizing proceeds by transferring bins, one by one,
388 <     * from the table to the next table. To enable concurrency, the
389 <     * next table must be (incrementally) prefilled with place-holders
390 <     * serving as reverse forwarders to the old table.  Because we are
388 >     * from the table to the next table. However, threads claim small
389 >     * blocks of indices to transfer (via field transferIndex) before
390 >     * doing so, reducing contention.  A generation stamp in field
391 >     * sizeCtl ensures that resizings do not overlap. Because we are
392       * using power-of-two expansion, the elements from each bin must
393       * either stay at same index, or move with a power of two
394       * offset. We eliminate unnecessary node creation by catching
# Line 408 | Line 409 | public class ConcurrentHashMapV8<K,V>
409       * locks, average aggregate waits become shorter as resizing
410       * progresses.  The transfer operation must also ensure that all
411       * accessible bins in both the old and new table are usable by any
412 <     * traversal.  This is arranged by proceeding from the last bin
413 <     * (table.length - 1) up towards the first.  Upon seeing a
414 <     * forwarding node, traversals (see class Traverser) arrange to
415 <     * move to the new table without revisiting nodes.  However, to
416 <     * ensure that no intervening nodes are skipped, bin splitting can
417 <     * only begin after the associated reverse-forwarders are in
418 <     * place.
412 >     * traversal.  This is arranged in part by proceeding from the
413 >     * last bin (table.length - 1) up towards the first.  Upon seeing
414 >     * a forwarding node, traversals (see class Traverser) arrange to
415 >     * move to the new table without revisiting nodes.  To ensure that
416 >     * no intervening nodes are skipped even when moved out of order,
417 >     * a stack (see class TableStack) is created on first encounter of
418 >     * a forwarding node during a traversal, to maintain its place if
419 >     * later processing the current table. The need for these
420 >     * save/restore mechanics is relatively rare, but when one
421 >     * forwarding node is encountered, typically many more will be.
422 >     * So Traversers use a simple caching scheme to avoid creating so
423 >     * many new TableStack nodes. (Thanks to Peter Levart for
424 >     * suggesting use of a stack here.)
425       *
426       * The traversal scheme also applies to partial traversals of
427       * ranges of bins (via an alternate Traverser constructor)
# Line 446 | Line 453 | public class ConcurrentHashMapV8<K,V>
453       * related operations (which is the main reason we cannot use
454       * existing collections such as TreeMaps). TreeBins contain
455       * Comparable elements, but may contain others, as well as
456 <     * elements that are Comparable but not necessarily Comparable
457 <     * for the same T, so we cannot invoke compareTo among them. To
458 <     * handle this, the tree is ordered primarily by hash value, then
459 <     * by Comparable.compareTo order if applicable.  On lookup at a
460 <     * node, if elements are not comparable or compare as 0 then both
461 <     * left and right children may need to be searched in the case of
462 <     * tied hash values. (This corresponds to the full list search
463 <     * that would be necessary if all elements were non-Comparable and
464 <     * had tied hashes.)  The red-black balancing code is updated from
465 <     * pre-jdk-collections
456 >     * elements that are Comparable but not necessarily Comparable for
457 >     * the same T, so we cannot invoke compareTo among them. To handle
458 >     * this, the tree is ordered primarily by hash value, then by
459 >     * Comparable.compareTo order if applicable.  On lookup at a node,
460 >     * if elements are not comparable or compare as 0 then both left
461 >     * and right children may need to be searched in the case of tied
462 >     * hash values. (This corresponds to the full list search that
463 >     * would be necessary if all elements were non-Comparable and had
464 >     * tied hashes.) On insertion, to keep a total ordering (or as
465 >     * close as is required here) across rebalancings, we compare
466 >     * classes and identityHashCodes as tie-breakers. The red-black
467 >     * balancing code is updated from pre-jdk-collections
468       * (http://gee.cs.oswego.edu/dl/classes/collections/RBCell.java)
469       * based in turn on Cormen, Leiserson, and Rivest "Introduction to
470       * Algorithms" (CLR).
# Line 478 | Line 487 | public class ConcurrentHashMapV8<K,V>
487       *
488       * Maintaining API and serialization compatibility with previous
489       * versions of this class introduces several oddities. Mainly: We
490 <     * leave untouched but unused constructor arguments refering to
490 >     * leave untouched but unused constructor arguments referring to
491       * concurrencyLevel. We accept a loadFactor constructor argument,
492       * but apply it only to initial table capacity (which is the only
493       * time that we can guarantee to honor it.) We also declare an
494       * unused "Segment" class that is instantiated in minimal form
495       * only when serializing.
496       *
497 +     * Also, solely for compatibility with previous versions of this
498 +     * class, it extends AbstractMap, even though all of its methods
499 +     * are overridden, so it is just useless baggage.
500 +     *
501       * This file is organized to make things a little easier to follow
502       * while reading than they might otherwise: First the main static
503       * declarations and utilities, then fields, then main public
# Line 565 | Line 578 | public class ConcurrentHashMapV8<K,V>
578       */
579      private static final int MIN_TRANSFER_STRIDE = 16;
580  
581 +    /**
582 +     * The number of bits used for generation stamp in sizeCtl.
583 +     * Must be at least 6 for 32bit arrays.
584 +     */
585 +    private static int RESIZE_STAMP_BITS = 16;
586 +
587 +    /**
588 +     * The maximum number of threads that can help resize.
589 +     * Must fit in 32 - RESIZE_STAMP_BITS bits.
590 +     */
591 +    private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
592 +
593 +    /**
594 +     * The bit shift for recording size stamp in sizeCtl.
595 +     */
596 +    private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
597 +
598      /*
599       * Encodings for Node hash fields. See above for explanation.
600       */
601 <    static final int MOVED     = 0x8fffffff; // (-1) hash for forwarding nodes
602 <    static final int TREEBIN   = 0x80000000; // hash for roots of trees
603 <    static final int RESERVED  = 0x80000001; // hash for transient reservations
601 >    static final int MOVED     = -1; // hash for forwarding nodes
602 >    static final int TREEBIN   = -2; // hash for roots of trees
603 >    static final int RESERVED  = -3; // hash for transient reservations
604      static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash
605  
606      /** Number of CPUS, to place bounds on some sizings */
# Line 597 | Line 627 | public class ConcurrentHashMapV8<K,V>
627          final int hash;
628          final K key;
629          volatile V val;
630 <        Node<K,V> next;
630 >        volatile Node<K,V> next;
631  
632          Node(int hash, K key, V val, Node<K,V> next) {
633              this.hash = hash;
# Line 606 | Line 636 | public class ConcurrentHashMapV8<K,V>
636              this.next = next;
637          }
638  
639 <        public final K getKey()       { return key; }
640 <        public final V getValue()     { return val; }
641 <        public final int hashCode()   { return key.hashCode() ^ val.hashCode(); }
642 <        public final String toString(){ return key + "=" + val; }
639 >        public final K getKey()     { return key; }
640 >        public final V getValue()   { return val; }
641 >        public final int hashCode() { return key.hashCode() ^ val.hashCode(); }
642 >        public final String toString() { return key + "=" + val; }
643          public final V setValue(V value) {
644              throw new UnsupportedOperationException();
645          }
# Line 722 | Line 752 | public class ConcurrentHashMapV8<K,V>
752       * errors by users, these checks must operate on local variables,
753       * which accounts for some odd-looking inline assignments below.
754       * Note that calls to setTabAt always occur within locked regions,
755 <     * and so do not need full volatile semantics, but still require
756 <     * ordering to maintain concurrent readability.
755 >     * and so in principle require only release ordering, not
756 >     * full volatile semantics, but are currently coded as volatile
757 >     * writes to be conservative.
758       */
759  
760      @SuppressWarnings("unchecked")
# Line 737 | Line 768 | public class ConcurrentHashMapV8<K,V>
768      }
769  
770      static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
771 <        U.putOrderedObject(tab, ((long)i << ASHIFT) + ABASE, v);
771 >        U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
772      }
773  
774      /* ---------------- Fields -------------- */
# Line 776 | Line 807 | public class ConcurrentHashMapV8<K,V>
807      private transient volatile int transferIndex;
808  
809      /**
779     * The least available table index to split while resizing.
780     */
781    private transient volatile int transferOrigin;
782
783    /**
810       * Spinlock (locked via CAS) used when resizing and/or creating CounterCells.
811       */
812      private transient volatile int cellsBusy;
# Line 1358 | Line 1384 | public class ConcurrentHashMapV8<K,V>
1384       * Saves the state of the {@code ConcurrentHashMapV8} instance to a
1385       * stream (i.e., serializes it).
1386       * @param s the stream
1387 +     * @throws java.io.IOException if an I/O error occurs
1388       * @serialData
1389       * the key (Object) and value (Object)
1390       * for each key-value mapping, followed by a null pair.
# Line 1400 | Line 1427 | public class ConcurrentHashMapV8<K,V>
1427      /**
1428       * Reconstitutes the instance from a stream (that is, deserializes it).
1429       * @param s the stream
1430 +     * @throws ClassNotFoundException if the class of a serialized object
1431 +     *         could not be found
1432 +     * @throws java.io.IOException if an I/O error occurs
1433       */
1434      private void readObject(java.io.ObjectInputStream s)
1435          throws java.io.IOException, ClassNotFoundException {
# Line 1434 | Line 1464 | public class ConcurrentHashMapV8<K,V>
1464                  int sz = (int)size;
1465                  n = tableSizeFor(sz + (sz >>> 1) + 1);
1466              }
1467 <            @SuppressWarnings({"rawtypes","unchecked"})
1468 <                Node<K,V>[] tab = (Node<K,V>[])new Node[n];
1467 >            @SuppressWarnings("unchecked")
1468 >                Node<K,V>[] tab = (Node<K,V>[])new Node<?,?>[n];
1469              int mask = n - 1;
1470              long added = 0L;
1471              while (p != null) {
# Line 2100 | Line 2130 | public class ConcurrentHashMapV8<K,V>
2130       *
2131       * @param initialCapacity The implementation performs internal
2132       * sizing to accommodate this many elements.
2133 +     * @return the new set
2134       * @throws IllegalArgumentException if the initial capacity of
2135       * elements is negative
2105     * @return the new set
2136       * @since 1.8
2137       */
2138      public static <K> KeySetView<K,Boolean> newKeySet(int initialCapacity) {
# Line 2140 | Line 2170 | public class ConcurrentHashMapV8<K,V>
2170          }
2171  
2172          Node<K,V> find(int h, Object k) {
2173 <            Node<K,V> e; int n;
2174 <            Node<K,V>[] tab = nextTable;
2175 <            if (k != null && tab != null && (n = tab.length) > 0 &&
2176 <                (e = tabAt(tab, (n - 1) & h)) != null) {
2177 <                do {
2173 >            // loop to avoid arbitrarily deep recursion on forwarding nodes
2174 >            outer: for (Node<K,V>[] tab = nextTable;;) {
2175 >                Node<K,V> e; int n;
2176 >                if (k == null || tab == null || (n = tab.length) == 0 ||
2177 >                    (e = tabAt(tab, (n - 1) & h)) == null)
2178 >                    return null;
2179 >                for (;;) {
2180                      int eh; K ek;
2181                      if ((eh = e.hash) == h &&
2182                          ((ek = e.key) == k || (ek != null && k.equals(ek))))
2183                          return e;
2184 <                    if (eh < 0)
2185 <                        return e.find(h, k);
2186 <                } while ((e = e.next) != null);
2184 >                    if (eh < 0) {
2185 >                        if (e instanceof ForwardingNode) {
2186 >                            tab = ((ForwardingNode<K,V>)e).nextTable;
2187 >                            continue outer;
2188 >                        }
2189 >                        else
2190 >                            return e.find(h, k);
2191 >                    }
2192 >                    if ((e = e.next) == null)
2193 >                        return null;
2194 >                }
2195              }
2156            return null;
2196          }
2197      }
2198  
# Line 2173 | Line 2212 | public class ConcurrentHashMapV8<K,V>
2212      /* ---------------- Table Initialization and Resizing -------------- */
2213  
2214      /**
2215 +     * Returns the stamp bits for resizing a table of size n.
2216 +     * Must be negative when shifted left by RESIZE_STAMP_SHIFT.
2217 +     */
2218 +    static final int resizeStamp(int n) {
2219 +        return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));
2220 +    }
2221 +
2222 +    /**
2223       * Initializes table, using the size recorded in sizeCtl.
2224       */
2225      private final Node<K,V>[] initTable() {
# Line 2184 | Line 2231 | public class ConcurrentHashMapV8<K,V>
2231                  try {
2232                      if ((tab = table) == null || tab.length == 0) {
2233                          int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
2234 <                        @SuppressWarnings({"rawtypes","unchecked"})
2235 <                            Node<K,V>[] nt = (Node<K,V>[])new Node[n];
2234 >                        @SuppressWarnings("unchecked")
2235 >                        Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
2236                          table = tab = nt;
2237                          sc = n - (n >>> 2);
2238                      }
# Line 2227 | Line 2274 | public class ConcurrentHashMapV8<K,V>
2274              s = sumCount();
2275          }
2276          if (check >= 0) {
2277 <            Node<K,V>[] tab, nt; int sc;
2277 >            Node<K,V>[] tab, nt; int n, sc;
2278              while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
2279 <                   tab.length < MAXIMUM_CAPACITY) {
2279 >                   (n = tab.length) < MAXIMUM_CAPACITY) {
2280 >                int rs = resizeStamp(n);
2281                  if (sc < 0) {
2282 <                    if (sc == -1 || transferIndex <= transferOrigin ||
2283 <                        (nt = nextTable) == null)
2282 >                    if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
2283 >                        sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
2284 >                        transferIndex <= 0)
2285                          break;
2286 <                    if (U.compareAndSwapInt(this, SIZECTL, sc, sc - 1))
2286 >                    if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
2287                          transfer(tab, nt);
2288                  }
2289 <                else if (U.compareAndSwapInt(this, SIZECTL, sc, -2))
2289 >                else if (U.compareAndSwapInt(this, SIZECTL, sc,
2290 >                                             (rs << RESIZE_STAMP_SHIFT) + 2))
2291                      transfer(tab, null);
2292                  s = sumCount();
2293              }
# Line 2249 | Line 2299 | public class ConcurrentHashMapV8<K,V>
2299       */
2300      final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
2301          Node<K,V>[] nextTab; int sc;
2302 <        if ((f instanceof ForwardingNode) &&
2302 >        if (tab != null && (f instanceof ForwardingNode) &&
2303              (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
2304 <            if (nextTab == nextTable && tab == table &&
2305 <                transferIndex > transferOrigin && (sc = sizeCtl) < -1 &&
2306 <                U.compareAndSwapInt(this, SIZECTL, sc, sc - 1))
2307 <                transfer(tab, nextTab);
2304 >            int rs = resizeStamp(tab.length);
2305 >            while (nextTab == nextTable && table == tab &&
2306 >                   (sc = sizeCtl) < 0) {
2307 >                if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
2308 >                    sc == rs + MAX_RESIZERS || transferIndex <= 0)
2309 >                    break;
2310 >                if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
2311 >                    transfer(tab, nextTab);
2312 >                    break;
2313 >                }
2314 >            }
2315              return nextTab;
2316          }
2317          return table;
# Line 2276 | Line 2333 | public class ConcurrentHashMapV8<K,V>
2333                  if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
2334                      try {
2335                          if (table == tab) {
2336 <                            @SuppressWarnings({"rawtypes","unchecked"})
2337 <                                Node<K,V>[] nt = (Node<K,V>[])new Node[n];
2336 >                            @SuppressWarnings("unchecked")
2337 >                            Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
2338                              table = nt;
2339                              sc = n - (n >>> 2);
2340                          }
# Line 2288 | Line 2345 | public class ConcurrentHashMapV8<K,V>
2345              }
2346              else if (c <= sc || n >= MAXIMUM_CAPACITY)
2347                  break;
2348 <            else if (tab == table &&
2349 <                     U.compareAndSwapInt(this, SIZECTL, sc, -2))
2350 <                transfer(tab, null);
2348 >            else if (tab == table) {
2349 >                int rs = resizeStamp(n);
2350 >                if (sc < 0) {
2351 >                    Node<K,V>[] nt;
2352 >                    if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
2353 >                        sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
2354 >                        transferIndex <= 0)
2355 >                        break;
2356 >                    if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
2357 >                        transfer(tab, nt);
2358 >                }
2359 >                else if (U.compareAndSwapInt(this, SIZECTL, sc,
2360 >                                             (rs << RESIZE_STAMP_SHIFT) + 2))
2361 >                    transfer(tab, null);
2362 >            }
2363          }
2364      }
2365  
# Line 2304 | Line 2373 | public class ConcurrentHashMapV8<K,V>
2373              stride = MIN_TRANSFER_STRIDE; // subdivide range
2374          if (nextTab == null) {            // initiating
2375              try {
2376 <                @SuppressWarnings({"rawtypes","unchecked"})
2377 <                    Node<K,V>[] nt = (Node<K,V>[])new Node[n << 1];
2376 >                @SuppressWarnings("unchecked")
2377 >                Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];
2378                  nextTab = nt;
2379              } catch (Throwable ex) {      // try to cope with OOME
2380                  sizeCtl = Integer.MAX_VALUE;
2381                  return;
2382              }
2383              nextTable = nextTab;
2315            transferOrigin = n;
2384              transferIndex = n;
2317            ForwardingNode<K,V> rev = new ForwardingNode<K,V>(tab);
2318            for (int k = n; k > 0;) {    // progressively reveal ready slots
2319                int nextk = (k > stride) ? k - stride : 0;
2320                for (int m = nextk; m < k; ++m)
2321                    nextTab[m] = rev;
2322                for (int m = n + nextk; m < n + k; ++m)
2323                    nextTab[m] = rev;
2324                U.putOrderedInt(this, TRANSFERORIGIN, k = nextk);
2325            }
2385          }
2386          int nextn = nextTab.length;
2387          ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
2388          boolean advance = true;
2389 +        boolean finishing = false; // to ensure sweep before committing nextTab
2390          for (int i = 0, bound = 0;;) {
2391 <            int nextIndex, nextBound, fh; Node<K,V> f;
2391 >            Node<K,V> f; int fh;
2392              while (advance) {
2393 <                if (--i >= bound)
2393 >                int nextIndex, nextBound;
2394 >                if (--i >= bound || finishing)
2395                      advance = false;
2396 <                else if ((nextIndex = transferIndex) <= transferOrigin) {
2396 >                else if ((nextIndex = transferIndex) <= 0) {
2397                      i = -1;
2398                      advance = false;
2399                  }
# Line 2346 | Line 2407 | public class ConcurrentHashMapV8<K,V>
2407                  }
2408              }
2409              if (i < 0 || i >= n || i + n >= nextn) {
2410 <                for (int sc;;) {
2411 <                    if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, ++sc)) {
2412 <                        if (sc == -1) {
2413 <                            nextTable = null;
2414 <                            table = nextTab;
2415 <                            sizeCtl = (n << 1) - (n >>> 1);
2355 <                        }
2356 <                        return;
2357 <                    }
2410 >                int sc;
2411 >                if (finishing) {
2412 >                    nextTable = null;
2413 >                    table = nextTab;
2414 >                    sizeCtl = (n << 1) - (n >>> 1);
2415 >                    return;
2416                  }
2417 <            }
2418 <            else if ((f = tabAt(tab, i)) == null) {
2419 <                if (casTabAt(tab, i, null, fwd)) {
2420 <                    setTabAt(nextTab, i, null);
2421 <                    setTabAt(nextTab, i + n, null);
2364 <                    advance = true;
2417 >                if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
2418 >                    if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
2419 >                        return;
2420 >                    finishing = advance = true;
2421 >                    i = n; // recheck before commit
2422                  }
2423              }
2424 +            else if ((f = tabAt(tab, i)) == null)
2425 +                advance = casTabAt(tab, i, null, fwd);
2426              else if ((fh = f.hash) == MOVED)
2427                  advance = true; // already processed
2428              else {
# Line 2395 | Line 2454 | public class ConcurrentHashMapV8<K,V>
2454                                  else
2455                                      hn = new Node<K,V>(ph, pk, pv, hn);
2456                              }
2457 +                            setTabAt(nextTab, i, ln);
2458 +                            setTabAt(nextTab, i + n, hn);
2459 +                            setTabAt(tab, i, fwd);
2460 +                            advance = true;
2461                          }
2462                          else if (f instanceof TreeBin) {
2463                              TreeBin<K,V> t = (TreeBin<K,V>)f;
# Line 2426 | Line 2489 | public class ConcurrentHashMapV8<K,V>
2489                                  (hc != 0) ? new TreeBin<K,V>(lo) : t;
2490                              hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :
2491                                  (lc != 0) ? new TreeBin<K,V>(hi) : t;
2492 +                            setTabAt(nextTab, i, ln);
2493 +                            setTabAt(nextTab, i + n, hn);
2494 +                            setTabAt(tab, i, fwd);
2495 +                            advance = true;
2496                          }
2430                        else
2431                            ln = hn = null;
2432                        setTabAt(nextTab, i, ln);
2433                        setTabAt(nextTab, i + n, hn);
2434                        setTabAt(tab, i, fwd);
2435                        advance = true;
2497                      }
2498                  }
2499              }
# Line 2448 | Line 2509 | public class ConcurrentHashMapV8<K,V>
2509      private final void treeifyBin(Node<K,V>[] tab, int index) {
2510          Node<K,V> b; int n, sc;
2511          if (tab != null) {
2512 <            if ((n = tab.length) < MIN_TREEIFY_CAPACITY) {
2513 <                if (tab == table && (sc = sizeCtl) >= 0 &&
2514 <                    U.compareAndSwapInt(this, SIZECTL, sc, -2))
2454 <                    transfer(tab, null);
2455 <            }
2456 <            else if ((b = tabAt(tab, index)) != null) {
2512 >            if ((n = tab.length) < MIN_TREEIFY_CAPACITY)
2513 >                tryPresize(n << 1);
2514 >            else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {
2515                  synchronized (b) {
2516                      if (tabAt(tab, index) == b) {
2517                          TreeNode<K,V> hd = null, tl = null;
# Line 2475 | Line 2533 | public class ConcurrentHashMapV8<K,V>
2533      }
2534  
2535      /**
2536 <     * Returns a list on non-TreeNodes replacing those in given list.
2536 >     * Returns a list of non-TreeNodes replacing those in given list.
2537       */
2538      static <K,V> Node<K,V> untreeify(Node<K,V> b) {
2539          Node<K,V> hd = null, tl = null;
# Line 2519 | Line 2577 | public class ConcurrentHashMapV8<K,V>
2577          final TreeNode<K,V> findTreeNode(int h, Object k, Class<?> kc) {
2578              if (k != null) {
2579                  TreeNode<K,V> p = this;
2580 <                do  {
2580 >                do {
2581                      int ph, dir; K pk; TreeNode<K,V> q;
2582                      TreeNode<K,V> pl = p.left, pr = p.right;
2583                      if ((ph = p.hash) > h)
# Line 2528 | Line 2586 | public class ConcurrentHashMapV8<K,V>
2586                          p = pr;
2587                      else if ((pk = p.key) == k || (pk != null && k.equals(pk)))
2588                          return p;
2589 <                    else if (pl == null && pr == null)
2590 <                        break;
2589 >                    else if (pl == null)
2590 >                        p = pr;
2591 >                    else if (pr == null)
2592 >                        p = pl;
2593                      else if ((kc != null ||
2594                                (kc = comparableClassFor(k)) != null) &&
2595                               (dir = compareComparables(kc, k, pk)) != 0)
2596                          p = (dir < 0) ? pl : pr;
2597 <                    else if (pl == null)
2538 <                        p = pr;
2539 <                    else if (pr == null ||
2540 <                             (q = pr.findTreeNode(h, k, kc)) == null)
2541 <                        p = pl;
2542 <                    else
2597 >                    else if ((q = pr.findTreeNode(h, k, kc)) != null)
2598                          return q;
2599 +                    else
2600 +                        p = pl;
2601                  } while (p != null);
2602              }
2603              return null;
# Line 2567 | Line 2624 | public class ConcurrentHashMapV8<K,V>
2624          static final int READER = 4; // increment value for setting read lock
2625  
2626          /**
2627 +         * Tie-breaking utility for ordering insertions when equal
2628 +         * hashCodes and non-comparable. We don't require a total
2629 +         * order, just a consistent insertion rule to maintain
2630 +         * equivalence across rebalancings. Tie-breaking further than
2631 +         * necessary simplifies testing a bit.
2632 +         */
2633 +        static int tieBreakOrder(Object a, Object b) {
2634 +            int d;
2635 +            if (a == null || b == null ||
2636 +                (d = a.getClass().getName().
2637 +                 compareTo(b.getClass().getName())) == 0)
2638 +                d = (System.identityHashCode(a) <= System.identityHashCode(b) ?
2639 +                     -1 : 1);
2640 +            return d;
2641 +        }
2642 +
2643 +        /**
2644           * Creates bin with initial set of nodes headed by b.
2645           */
2646          TreeBin(TreeNode<K,V> b) {
# Line 2582 | Line 2656 | public class ConcurrentHashMapV8<K,V>
2656                      r = x;
2657                  }
2658                  else {
2659 <                    Object key = x.key;
2660 <                    int hash = x.hash;
2659 >                    K k = x.key;
2660 >                    int h = x.hash;
2661                      Class<?> kc = null;
2662                      for (TreeNode<K,V> p = r;;) {
2663                          int dir, ph;
2664 <                        if ((ph = p.hash) > hash)
2664 >                        K pk = p.key;
2665 >                        if ((ph = p.hash) > h)
2666                              dir = -1;
2667 <                        else if (ph < hash)
2667 >                        else if (ph < h)
2668                              dir = 1;
2669 <                        else if ((kc != null ||
2670 <                                  (kc = comparableClassFor(key)) != null))
2671 <                            dir = compareComparables(kc, key, p.key);
2672 <                        else
2673 <                            dir = 0;
2599 <                        TreeNode<K,V> xp = p;
2669 >                        else if ((kc == null &&
2670 >                                  (kc = comparableClassFor(k)) == null) ||
2671 >                                 (dir = compareComparables(kc, k, pk)) == 0)
2672 >                            dir = tieBreakOrder(k, pk);
2673 >                            TreeNode<K,V> xp = p;
2674                          if ((p = (dir <= 0) ? p.left : p.right) == null) {
2675                              x.parent = xp;
2676                              if (dir <= 0)
# Line 2610 | Line 2684 | public class ConcurrentHashMapV8<K,V>
2684                  }
2685              }
2686              this.root = r;
2687 +            assert checkInvariants(root);
2688          }
2689  
2690          /**
# Line 2633 | Line 2708 | public class ConcurrentHashMapV8<K,V>
2708          private final void contendedLock() {
2709              boolean waiting = false;
2710              for (int s;;) {
2711 <                if (((s = lockState) & WRITER) == 0) {
2711 >                if (((s = lockState) & ~WAITER) == 0) {
2712                      if (U.compareAndSwapInt(this, LOCKSTATE, s, WRITER)) {
2713                          if (waiting)
2714                              waiter = null;
2715                          return;
2716                      }
2717                  }
2718 <                else if ((s | WAITER) == 0) {
2718 >                else if ((s & WAITER) == 0) {
2719                      if (U.compareAndSwapInt(this, LOCKSTATE, s, s | WAITER)) {
2720                          waiting = true;
2721                          waiter = Thread.currentThread();
# Line 2658 | Line 2733 | public class ConcurrentHashMapV8<K,V>
2733           */
2734          final Node<K,V> find(int h, Object k) {
2735              if (k != null) {
2736 <                for (Node<K,V> e = first; e != null; e = e.next) {
2736 >                for (Node<K,V> e = first; e != null; ) {
2737                      int s; K ek;
2738                      if (((s = lockState) & (WAITER|WRITER)) != 0) {
2739                          if (e.hash == h &&
2740                              ((ek = e.key) == k || (ek != null && k.equals(ek))))
2741                              return e;
2742 +                        e = e.next;
2743                      }
2744                      else if (U.compareAndSwapInt(this, LOCKSTATE, s,
2745                                                   s + READER)) {
# Line 2693 | Line 2769 | public class ConcurrentHashMapV8<K,V>
2769           */
2770          final TreeNode<K,V> putTreeVal(int h, K k, V v) {
2771              Class<?> kc = null;
2772 +            boolean searched = false;
2773              for (TreeNode<K,V> p = root;;) {
2774 <                int dir, ph; K pk; TreeNode<K,V> q, pr;
2774 >                int dir, ph; K pk;
2775                  if (p == null) {
2776                      first = root = new TreeNode<K,V>(h, k, v, null, null);
2777                      break;
# Line 2708 | Line 2785 | public class ConcurrentHashMapV8<K,V>
2785                  else if ((kc == null &&
2786                            (kc = comparableClassFor(k)) == null) ||
2787                           (dir = compareComparables(kc, k, pk)) == 0) {
2788 <                    if (p.left == null)
2789 <                        dir = 1;
2790 <                    else if ((pr = p.right) == null ||
2791 <                             (q = pr.findTreeNode(h, k, kc)) == null)
2792 <                        dir = -1;
2793 <                    else
2794 <                        return q;
2788 >                    if (!searched) {
2789 >                        TreeNode<K,V> q, ch;
2790 >                        searched = true;
2791 >                        if (((ch = p.left) != null &&
2792 >                             (q = ch.findTreeNode(h, k, kc)) != null) ||
2793 >                            ((ch = p.right) != null &&
2794 >                             (q = ch.findTreeNode(h, k, kc)) != null))
2795 >                            return q;
2796 >                    }
2797 >                    dir = tieBreakOrder(k, pk);
2798                  }
2799 +
2800                  TreeNode<K,V> xp = p;
2801 <                if ((p = (dir < 0) ? p.left : p.right) == null) {
2801 >                if ((p = (dir <= 0) ? p.left : p.right) == null) {
2802                      TreeNode<K,V> x, f = first;
2803                      first = x = new TreeNode<K,V>(h, k, v, f, xp);
2804                      if (f != null)
2805                          f.prev = x;
2806 <                    if (dir < 0)
2806 >                    if (dir <= 0)
2807                          xp.left = x;
2808                      else
2809                          xp.right = x;
# Line 2945 | Line 3026 | public class ConcurrentHashMapV8<K,V>
3026  
3027          static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,
3028                                                     TreeNode<K,V> x) {
3029 <            for (TreeNode<K,V> xp, xpl, xpr;;)  {
3029 >            for (TreeNode<K,V> xp, xpl, xpr;;) {
3030                  if (x == null || x == root)
3031                      return root;
3032                  else if ((xp = x.parent) == null) {
# Line 3077 | Line 3158 | public class ConcurrentHashMapV8<K,V>
3158      /* ----------------Table Traversal -------------- */
3159  
3160      /**
3161 +     * Records the table, its length, and current traversal index for a
3162 +     * traverser that must process a region of a forwarded table before
3163 +     * proceeding with current table.
3164 +     */
3165 +    static final class TableStack<K,V> {
3166 +        int length;
3167 +        int index;
3168 +        Node<K,V>[] tab;
3169 +        TableStack<K,V> next;
3170 +    }
3171 +
3172 +    /**
3173       * Encapsulates traversal for methods such as containsValue; also
3174       * serves as a base class for other iterators and spliterators.
3175       *
# Line 3100 | Line 3193 | public class ConcurrentHashMapV8<K,V>
3193      static class Traverser<K,V> {
3194          Node<K,V>[] tab;        // current table; updated if resized
3195          Node<K,V> next;         // the next entry to use
3196 +        TableStack<K,V> stack, spare; // to save/restore on ForwardingNodes
3197          int index;              // index of bin to use next
3198          int baseIndex;          // current index of initial table
3199          int baseLimit;          // index bound for initial table
# Line 3121 | Line 3215 | public class ConcurrentHashMapV8<K,V>
3215              if ((e = next) != null)
3216                  e = e.next;
3217              for (;;) {
3218 <                Node<K,V>[] t; int i, n; K ek;  // must use locals in checks
3218 >                Node<K,V>[] t; int i, n;  // must use locals in checks
3219                  if (e != null)
3220                      return next = e;
3221                  if (baseIndex >= baseLimit || (t = tab) == null ||
3222                      (n = t.length) <= (i = index) || i < 0)
3223                      return next = null;
3224 <                if ((e = tabAt(t, index)) != null && e.hash < 0) {
3224 >                if ((e = tabAt(t, i)) != null && e.hash < 0) {
3225                      if (e instanceof ForwardingNode) {
3226                          tab = ((ForwardingNode<K,V>)e).nextTable;
3227                          e = null;
3228 +                        pushState(t, i, n);
3229                          continue;
3230                      }
3231                      else if (e instanceof TreeBin)
# Line 3138 | Line 3233 | public class ConcurrentHashMapV8<K,V>
3233                      else
3234                          e = null;
3235                  }
3236 <                if ((index += baseSize) >= n)
3237 <                    index = ++baseIndex;    // visit upper slots if present
3236 >                if (stack != null)
3237 >                    recoverState(n);
3238 >                else if ((index = i + baseSize) >= n)
3239 >                    index = ++baseIndex; // visit upper slots if present
3240 >            }
3241 >        }
3242 >
3243 >        /**
3244 >         * Saves traversal state upon encountering a forwarding node.
3245 >         */
3246 >        private void pushState(Node<K,V>[] t, int i, int n) {
3247 >            TableStack<K,V> s = spare;  // reuse if possible
3248 >            if (s != null)
3249 >                spare = s.next;
3250 >            else
3251 >                s = new TableStack<K,V>();
3252 >            s.tab = t;
3253 >            s.length = n;
3254 >            s.index = i;
3255 >            s.next = stack;
3256 >            stack = s;
3257 >        }
3258 >
3259 >        /**
3260 >         * Possibly pops traversal state.
3261 >         *
3262 >         * @param n length of current table
3263 >         */
3264 >        private void recoverState(int n) {
3265 >            TableStack<K,V> s; int len;
3266 >            while ((s = stack) != null && (index += (len = s.length)) >= n) {
3267 >                n = len;
3268 >                index = s.index;
3269 >                tab = s.tab;
3270 >                s.tab = null;
3271 >                TableStack<K,V> next = s.next;
3272 >                s.next = spare; // save for reuse
3273 >                stack = next;
3274 >                spare = s;
3275              }
3276 +            if (s == null && (index += baseSize) >= n)
3277 +                index = ++baseIndex;
3278          }
3279      }
3280  
# Line 6108 | Line 6242 | public class ConcurrentHashMapV8<K,V>
6242      private static final sun.misc.Unsafe U;
6243      private static final long SIZECTL;
6244      private static final long TRANSFERINDEX;
6111    private static final long TRANSFERORIGIN;
6245      private static final long BASECOUNT;
6246      private static final long CELLSBUSY;
6247      private static final long CELLVALUE;
# Line 6123 | Line 6256 | public class ConcurrentHashMapV8<K,V>
6256                  (k.getDeclaredField("sizeCtl"));
6257              TRANSFERINDEX = U.objectFieldOffset
6258                  (k.getDeclaredField("transferIndex"));
6126            TRANSFERORIGIN = U.objectFieldOffset
6127                (k.getDeclaredField("transferOrigin"));
6259              BASECOUNT = U.objectFieldOffset
6260                  (k.getDeclaredField("baseCount"));
6261              CELLSBUSY = U.objectFieldOffset

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines