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.116 by dl, Wed Sep 11 14:53:38 2013 UTC vs.
Revision 1.117 by dl, Sun Dec 1 13:39:02 2013 UTC

# Line 389 | Line 389 | public class ConcurrentHashMapV8<K,V> ex
389       * progress.  Resizing proceeds by transferring bins, one by one,
390       * from the table to the next table. However, threads claim small
391       * blocks of indices to transfer (via field transferIndex) before
392 <     * doing so, reducing contention.  Because we are using
393 <     * power-of-two expansion, the elements from each bin must either
394 <     * stay at same index, or move with a power of two offset. We
395 <     * eliminate unnecessary node creation by catching cases where old
396 <     * nodes can be reused because their next fields won't change.  On
397 <     * average, only about one-sixth of them need cloning when a table
398 <     * doubles. The nodes they replace will be garbage collectable as
399 <     * soon as they are no longer referenced by any reader thread that
400 <     * may be in the midst of concurrently traversing table.  Upon
401 <     * transfer, the old table bin contains only a special forwarding
402 <     * node (with hash field "MOVED") that contains the next table as
403 <     * its key. On encountering a forwarding node, access and update
404 <     * operations restart, using the new table.
392 >     * doing so, reducing contention.  A generation stamp in field
393 >     * sizeCtl ensures that resizings do not overlap. Because we are
394 >     * using power-of-two expansion, the elements from each bin must
395 >     * either stay at same index, or move with a power of two
396 >     * offset. We eliminate unnecessary node creation by catching
397 >     * cases where old nodes can be reused because their next fields
398 >     * won't change.  On average, only about one-sixth of them need
399 >     * cloning when a table doubles. The nodes they replace will be
400 >     * garbage collectable as soon as they are no longer referenced by
401 >     * any reader thread that may be in the midst of concurrently
402 >     * traversing table.  Upon transfer, the old table bin contains
403 >     * only a special forwarding node (with hash field "MOVED") that
404 >     * contains the next table as its key. On encountering a
405 >     * forwarding node, access and update operations restart, using
406 >     * the new table.
407       *
408       * Each bin transfer requires its bin lock, which can stall
409       * waiting for locks while resizing. However, because other
# Line 578 | Line 580 | public class ConcurrentHashMapV8<K,V> ex
580       */
581      private static final int MIN_TRANSFER_STRIDE = 16;
582  
583 +    /**
584 +     * The number of bits used for generation stamp in sizeCtl.
585 +     * Must be at least 6 for 32bit arrays.
586 +     */
587 +    private static int RESIZE_STAMP_BITS = 16;
588 +
589 +    /**
590 +     * The maximum number of threads that can help resize.
591 +     * Must fit in 32 - RESIZE_STAMP_BITS bits.
592 +     */
593 +    private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
594 +
595 +    /**
596 +     * The bit shift for recording size stamp in sizeCtl.
597 +     */
598 +    private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
599 +
600      /*
601       * Encodings for Node hash fields. See above for explanation.
602       */
# Line 735 | Line 754 | public class ConcurrentHashMapV8<K,V> ex
754       * errors by users, these checks must operate on local variables,
755       * which accounts for some odd-looking inline assignments below.
756       * Note that calls to setTabAt always occur within locked regions,
757 <     * and so in principle require only release ordering, not need
757 >     * and so in principle require only release ordering, not
758       * full volatile semantics, but are currently coded as volatile
759       * writes to be conservative.
760       */
# Line 2195 | Line 2214 | public class ConcurrentHashMapV8<K,V> ex
2214      /* ---------------- Table Initialization and Resizing -------------- */
2215  
2216      /**
2217 +     * Returns the stamp bits for resizing a table of size n.
2218 +     * Must be negative when shifted left by RESIZE_STAMP_SHIFT.
2219 +     */
2220 +    static final int resizeStamp(int n) {
2221 +        return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));
2222 +    }
2223 +
2224 +    /**
2225       * Initializes table, using the size recorded in sizeCtl.
2226       */
2227      private final Node<K,V>[] initTable() {
# Line 2207 | Line 2234 | public class ConcurrentHashMapV8<K,V> ex
2234                      if ((tab = table) == null || tab.length == 0) {
2235                          int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
2236                          @SuppressWarnings("unchecked")
2237 <                            Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
2237 >                        Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
2238                          table = tab = nt;
2239                          sc = n - (n >>> 2);
2240                      }
# Line 2249 | Line 2276 | public class ConcurrentHashMapV8<K,V> ex
2276              s = sumCount();
2277          }
2278          if (check >= 0) {
2279 <            Node<K,V>[] tab, nt; int sc;
2279 >            Node<K,V>[] tab, nt; int n, sc;
2280              while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
2281 <                   tab.length < MAXIMUM_CAPACITY) {
2281 >                   (n = tab.length) < MAXIMUM_CAPACITY) {
2282 >                int rs = resizeStamp(n);
2283                  if (sc < 0) {
2284 <                    if (sc == -1 || transferIndex <= 0 ||
2285 <                        (nt = nextTable) == null)
2284 >                    if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
2285 >                        sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
2286 >                        transferIndex <= 0)
2287                          break;
2288 <                    if (U.compareAndSwapInt(this, SIZECTL, sc, sc - 1))
2288 >                    if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
2289                          transfer(tab, nt);
2290                  }
2291 <                else if (U.compareAndSwapInt(this, SIZECTL, sc, -2))
2291 >                else if (U.compareAndSwapInt(this, SIZECTL, sc,
2292 >                                             (rs << RESIZE_STAMP_SHIFT) + 2))
2293                      transfer(tab, null);
2294                  s = sumCount();
2295              }
# Line 2271 | Line 2301 | public class ConcurrentHashMapV8<K,V> ex
2301       */
2302      final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
2303          Node<K,V>[] nextTab; int sc;
2304 <        if ((f instanceof ForwardingNode) &&
2304 >        if (tab != null && (f instanceof ForwardingNode) &&
2305              (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
2306 <            while (transferIndex > 0 && nextTab == nextTable &&
2307 <                   (sc = sizeCtl) < -1) {
2308 <                if (U.compareAndSwapInt(this, SIZECTL, sc, sc - 1)) {
2306 >            int rs = resizeStamp(tab.length);
2307 >            while (nextTab == nextTable && table == tab &&
2308 >                   (sc = sizeCtl) < 0) {
2309 >                if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
2310 >                    sc == rs + MAX_RESIZERS || transferIndex <= 0)
2311 >                    break;
2312 >                if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
2313                      transfer(tab, nextTab);
2314                      break;
2315                  }
# Line 2302 | Line 2336 | public class ConcurrentHashMapV8<K,V> ex
2336                      try {
2337                          if (table == tab) {
2338                              @SuppressWarnings("unchecked")
2339 <                                Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
2339 >                            Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
2340                              table = nt;
2341                              sc = n - (n >>> 2);
2342                          }
# Line 2313 | Line 2347 | public class ConcurrentHashMapV8<K,V> ex
2347              }
2348              else if (c <= sc || n >= MAXIMUM_CAPACITY)
2349                  break;
2350 <            else if (tab == table &&
2351 <                     U.compareAndSwapInt(this, SIZECTL, sc, -2))
2352 <                transfer(tab, null);
2350 >            else if (tab == table) {
2351 >                int rs = resizeStamp(n);
2352 >                if (sc < 0) {
2353 >                    Node<K,V>[] nt;
2354 >                    if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
2355 >                        sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
2356 >                        transferIndex <= 0)
2357 >                        break;
2358 >                    if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
2359 >                        transfer(tab, nt);
2360 >                }
2361 >                else if (U.compareAndSwapInt(this, SIZECTL, sc,
2362 >                                             (rs << RESIZE_STAMP_SHIFT) + 2))
2363 >                    transfer(tab, null);
2364 >            }
2365          }
2366      }
2367  
# Line 2370 | Line 2416 | public class ConcurrentHashMapV8<K,V> ex
2416                      sizeCtl = (n << 1) - (n >>> 1);
2417                      return;
2418                  }
2419 <                if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, ++sc)) {
2420 <                    if (sc != -1)
2419 >                if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
2420 >                    if ((sc - 2) != resizeStamp(n))
2421                          return;
2422                      finishing = advance = true;
2423                      i = n; // recheck before commit
# Line 2465 | Line 2511 | public class ConcurrentHashMapV8<K,V> ex
2511      private final void treeifyBin(Node<K,V>[] tab, int index) {
2512          Node<K,V> b; int n, sc;
2513          if (tab != null) {
2514 <            if ((n = tab.length) < MIN_TREEIFY_CAPACITY) {
2515 <                if (tab == table && (sc = sizeCtl) >= 0 &&
2470 <                    U.compareAndSwapInt(this, SIZECTL, sc, -2))
2471 <                    transfer(tab, null);
2472 <            }
2514 >            if ((n = tab.length) < MIN_TREEIFY_CAPACITY)
2515 >                tryPresize(n << 1);
2516              else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {
2517                  synchronized (b) {
2518                      if (tabAt(tab, index) == b) {
# Line 2490 | Line 2533 | public class ConcurrentHashMapV8<K,V> ex
2533              }
2534          }
2535      }
2493
2536      /**
2537       * Returns a list on non-TreeNodes replacing those in given list.
2538       */
# Line 2667 | Line 2709 | public class ConcurrentHashMapV8<K,V> ex
2709          private final void contendedLock() {
2710              boolean waiting = false;
2711              for (int s;;) {
2712 <                if (((s = lockState) & WRITER) == 0) {
2712 >                if (((s = lockState) & ~WAITER) == 0) {
2713                      if (U.compareAndSwapInt(this, LOCKSTATE, s, WRITER)) {
2714                          if (waiting)
2715                              waiter = null;

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines