ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/concurrent/ConcurrentHashMap.java
(Generate patch)

Comparing jsr166/src/main/java/util/concurrent/ConcurrentHashMap.java (file contents):
Revision 1.251 by dl, Wed Sep 4 00:02:46 2013 UTC vs.
Revision 1.252 by dl, Sun Dec 1 13:38:58 2013 UTC

# Line 351 | Line 351 | public class ConcurrentHashMap<K,V> exte
351       * progress.  Resizing proceeds by transferring bins, one by one,
352       * from the table to the next table. However, threads claim small
353       * blocks of indices to transfer (via field transferIndex) before
354 <     * doing so, reducing contention.  Because we are using
355 <     * power-of-two expansion, the elements from each bin must either
356 <     * stay at same index, or move with a power of two offset. We
357 <     * eliminate unnecessary node creation by catching cases where old
358 <     * nodes can be reused because their next fields won't change.  On
359 <     * average, only about one-sixth of them need cloning when a table
360 <     * doubles. The nodes they replace will be garbage collectable as
361 <     * soon as they are no longer referenced by any reader thread that
362 <     * may be in the midst of concurrently traversing table.  Upon
363 <     * transfer, the old table bin contains only a special forwarding
364 <     * node (with hash field "MOVED") that contains the next table as
365 <     * its key. On encountering a forwarding node, access and update
366 <     * operations restart, using the new table.
354 >     * doing so, reducing contention.  A generation stamp in field
355 >     * sizeCtl ensures that resizings do not overlap. Because we are
356 >     * using power-of-two expansion, the elements from each bin must
357 >     * either stay at same index, or move with a power of two
358 >     * offset. We eliminate unnecessary node creation by catching
359 >     * cases where old nodes can be reused because their next fields
360 >     * won't change.  On average, only about one-sixth of them need
361 >     * cloning when a table doubles. The nodes they replace will be
362 >     * garbage collectable as soon as they are no longer referenced by
363 >     * any reader thread that may be in the midst of concurrently
364 >     * traversing table.  Upon transfer, the old table bin contains
365 >     * only a special forwarding node (with hash field "MOVED") that
366 >     * contains the next table as its key. On encountering a
367 >     * forwarding node, access and update operations restart, using
368 >     * the new table.
369       *
370       * Each bin transfer requires its bin lock, which can stall
371       * waiting for locks while resizing. However, because other
# Line 540 | Line 542 | public class ConcurrentHashMap<K,V> exte
542       */
543      private static final int MIN_TRANSFER_STRIDE = 16;
544  
545 +    /**
546 +     * The number of bits used for generation stamp in sizeCtl.
547 +     * Must be at least 6 for 32bit arrays.
548 +     */
549 +    private static int RESIZE_STAMP_BITS = 16;
550 +
551 +    /**
552 +     * The maximum number of threads that can help resize.
553 +     * Must fit in 32 - RESIZE_STAMP_BITS bits.
554 +     */
555 +    private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
556 +
557 +    /**
558 +     * The bit shift for recording size stamp in sizeCtl.
559 +     */
560 +    private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
561 +
562      /*
563       * Encodings for Node hash fields. See above for explanation.
564       */
# Line 697 | Line 716 | public class ConcurrentHashMap<K,V> exte
716       * errors by users, these checks must operate on local variables,
717       * which accounts for some odd-looking inline assignments below.
718       * Note that calls to setTabAt always occur within locked regions,
719 <     * and so in principle require only release ordering, not need
719 >     * and so in principle require only release ordering, not
720       * full volatile semantics, but are currently coded as volatile
721       * writes to be conservative.
722       */
# Line 2165 | Line 2184 | public class ConcurrentHashMap<K,V> exte
2184      /* ---------------- Table Initialization and Resizing -------------- */
2185  
2186      /**
2187 +     * Returns the stamp bits for resizing a table of size n.
2188 +     * Must be negative when shifted left by RESIZE_STAMP_SHIFT.
2189 +     */
2190 +    static final int resizeStamp(int n) {
2191 +        return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));
2192 +    }
2193 +
2194 +    /**
2195       * Initializes table, using the size recorded in sizeCtl.
2196       */
2197      private final Node<K,V>[] initTable() {
# Line 2218 | Line 2245 | public class ConcurrentHashMap<K,V> exte
2245              s = sumCount();
2246          }
2247          if (check >= 0) {
2248 <            Node<K,V>[] tab, nt; int sc;
2248 >            Node<K,V>[] tab, nt; int n, sc;
2249              while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
2250 <                   tab.length < MAXIMUM_CAPACITY) {
2250 >                   (n = tab.length) < MAXIMUM_CAPACITY) {
2251 >                int rs = resizeStamp(n);
2252                  if (sc < 0) {
2253 <                    if (sc == -1 || transferIndex <= 0 ||
2254 <                        (nt = nextTable) == null)
2253 >                    if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
2254 >                        sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
2255 >                        transferIndex <= 0)
2256                          break;
2257 <                    if (U.compareAndSwapInt(this, SIZECTL, sc, sc - 1))
2257 >                    if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
2258                          transfer(tab, nt);
2259                  }
2260 <                else if (U.compareAndSwapInt(this, SIZECTL, sc, -2))
2260 >                else if (U.compareAndSwapInt(this, SIZECTL, sc,
2261 >                                             (rs << RESIZE_STAMP_SHIFT) + 2))
2262                      transfer(tab, null);
2263                  s = sumCount();
2264              }
# Line 2240 | Line 2270 | public class ConcurrentHashMap<K,V> exte
2270       */
2271      final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
2272          Node<K,V>[] nextTab; int sc;
2273 <        if ((f instanceof ForwardingNode) &&
2273 >        if (tab != null && (f instanceof ForwardingNode) &&
2274              (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
2275 <            while (transferIndex > 0 && nextTab == nextTable &&
2276 <                   (sc = sizeCtl) < -1) {
2277 <                if (U.compareAndSwapInt(this, SIZECTL, sc, sc - 1)) {
2275 >            int rs = resizeStamp(tab.length);
2276 >            while (nextTab == nextTable && table == tab &&
2277 >                   (sc = sizeCtl) < 0) {
2278 >                if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
2279 >                    sc == rs + MAX_RESIZERS || transferIndex <= 0)
2280 >                    break;
2281 >                if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
2282                      transfer(tab, nextTab);
2283                      break;
2284                  }
# Line 2282 | Line 2316 | public class ConcurrentHashMap<K,V> exte
2316              }
2317              else if (c <= sc || n >= MAXIMUM_CAPACITY)
2318                  break;
2319 <            else if (tab == table &&
2320 <                     U.compareAndSwapInt(this, SIZECTL, sc, -2))
2321 <                transfer(tab, null);
2319 >            else if (tab == table) {
2320 >                int rs = resizeStamp(n);
2321 >                if (sc < 0) {
2322 >                    Node<K,V>[] nt;
2323 >                    if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
2324 >                        sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
2325 >                        transferIndex <= 0)
2326 >                        break;
2327 >                    if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
2328 >                        transfer(tab, nt);
2329 >                }
2330 >                else if (U.compareAndSwapInt(this, SIZECTL, sc,
2331 >                                             (rs << RESIZE_STAMP_SHIFT) + 2))
2332 >                    transfer(tab, null);
2333 >            }
2334          }
2335      }
2336  
# Line 2339 | Line 2385 | public class ConcurrentHashMap<K,V> exte
2385                      sizeCtl = (n << 1) - (n >>> 1);
2386                      return;
2387                  }
2388 <                if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, ++sc)) {
2389 <                    if (sc != -1)
2388 >                if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
2389 >                    if ((sc - 2) != resizeStamp(n))
2390                          return;
2391                      finishing = advance = true;
2392                      i = n; // recheck before commit
# Line 2539 | Line 2585 | public class ConcurrentHashMap<K,V> exte
2585      private final void treeifyBin(Node<K,V>[] tab, int index) {
2586          Node<K,V> b; int n, sc;
2587          if (tab != null) {
2588 <            if ((n = tab.length) < MIN_TREEIFY_CAPACITY) {
2589 <                if (tab == table && (sc = sizeCtl) >= 0 &&
2544 <                    U.compareAndSwapInt(this, SIZECTL, sc, -2))
2545 <                    transfer(tab, null);
2546 <            }
2588 >            if ((n = tab.length) < MIN_TREEIFY_CAPACITY)
2589 >                tryPresize(n << 1);
2590              else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {
2591                  synchronized (b) {
2592                      if (tabAt(tab, index) == b) {
# Line 2741 | Line 2784 | public class ConcurrentHashMap<K,V> exte
2784          private final void contendedLock() {
2785              boolean waiting = false;
2786              for (int s;;) {
2787 <                if (((s = lockState) & WRITER) == 0) {
2787 >                if (((s = lockState) & ~WAITER) == 0) {
2788                      if (U.compareAndSwapInt(this, LOCKSTATE, s, WRITER)) {
2789                          if (waiting)
2790                              waiter = null;

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines