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

Comparing jsr166/src/jdk7/java/util/concurrent/ConcurrentHashMap.java (file contents):
Revision 1.35 by dl, Wed Sep 11 14:53:48 2013 UTC vs.
Revision 1.36 by dl, Sun Dec 1 13:39:05 2013 UTC

# Line 219 | Line 219 | public class ConcurrentHashMap<K,V> impl
219       * progress.  Resizing proceeds by transferring bins, one by one,
220       * from the table to the next table. However, threads claim small
221       * blocks of indices to transfer (via field transferIndex) before
222 <     * doing so, reducing contention.  Because we are using
223 <     * power-of-two expansion, the elements from each bin must either
224 <     * stay at same index, or move with a power of two offset. We
225 <     * eliminate unnecessary node creation by catching cases where old
226 <     * nodes can be reused because their next fields won't change.  On
227 <     * average, only about one-sixth of them need cloning when a table
228 <     * doubles. The nodes they replace will be garbage collectable as
229 <     * soon as they are no longer referenced by any reader thread that
230 <     * may be in the midst of concurrently traversing table.  Upon
231 <     * transfer, the old table bin contains only a special forwarding
232 <     * node (with hash field "MOVED") that contains the next table as
233 <     * its key. On encountering a forwarding node, access and update
234 <     * operations restart, using the new table.
222 >     * doing so, reducing contention.  A generation stamp in field
223 >     * sizeCtl ensures that resizings do not overlap. Because we are
224 >     * using power-of-two expansion, the elements from each bin must
225 >     * either stay at same index, or move with a power of two
226 >     * offset. We eliminate unnecessary node creation by catching
227 >     * cases where old nodes can be reused because their next fields
228 >     * won't change.  On average, only about one-sixth of them need
229 >     * cloning when a table doubles. The nodes they replace will be
230 >     * garbage collectable as soon as they are no longer referenced by
231 >     * any reader thread that may be in the midst of concurrently
232 >     * traversing table.  Upon transfer, the old table bin contains
233 >     * only a special forwarding node (with hash field "MOVED") that
234 >     * contains the next table as its key. On encountering a
235 >     * forwarding node, access and update operations restart, using
236 >     * the new table.
237       *
238       * Each bin transfer requires its bin lock, which can stall
239       * waiting for locks while resizing. However, because other
# Line 409 | Line 411 | public class ConcurrentHashMap<K,V> impl
411       */
412      private static final int MIN_TRANSFER_STRIDE = 16;
413  
414 +    /**
415 +     * The number of bits used for generation stamp in sizeCtl.
416 +     * Must be at least 6 for 32bit arrays.
417 +     */
418 +    private static int RESIZE_STAMP_BITS = 16;
419 +
420 +    /**
421 +     * The maximum number of threads that can help resize.
422 +     * Must fit in 32 - RESIZE_STAMP_BITS bits.
423 +     */
424 +    private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
425 +
426 +    /**
427 +     * The bit shift for recording size stamp in sizeCtl.
428 +     */
429 +    private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
430 +
431      /*
432       * Encodings for Node hash fields. See above for explanation.
433       */
# Line 1539 | Line 1558 | public class ConcurrentHashMap<K,V> impl
1558      /* ---------------- Table Initialization and Resizing -------------- */
1559  
1560      /**
1561 +     * Returns the stamp bits for resizing a table of size n.
1562 +     * Must be negative when shifted left by RESIZE_STAMP_SHIFT.
1563 +     */
1564 +    static final int resizeStamp(int n) {
1565 +        return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));
1566 +    }
1567 +
1568 +    /**
1569       * Initializes table, using the size recorded in sizeCtl.
1570       */
1571      private final Node<K,V>[] initTable() {
# Line 1551 | Line 1578 | public class ConcurrentHashMap<K,V> impl
1578                      if ((tab = table) == null || tab.length == 0) {
1579                          int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
1580                          @SuppressWarnings("unchecked")
1581 <                            Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
1581 >                        Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
1582                          table = tab = nt;
1583                          sc = n - (n >>> 2);
1584                      }
# Line 1593 | Line 1620 | public class ConcurrentHashMap<K,V> impl
1620              s = sumCount();
1621          }
1622          if (check >= 0) {
1623 <            Node<K,V>[] tab, nt; int sc;
1623 >            Node<K,V>[] tab, nt; int n, sc;
1624              while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
1625 <                   tab.length < MAXIMUM_CAPACITY) {
1625 >                   (n = tab.length) < MAXIMUM_CAPACITY) {
1626 >                int rs = resizeStamp(n);
1627                  if (sc < 0) {
1628 <                    if (sc == -1 || transferIndex <= 0 ||
1629 <                        (nt = nextTable) == null)
1628 >                    if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
1629 >                        sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
1630 >                        transferIndex <= 0)
1631                          break;
1632 <                    if (U.compareAndSwapInt(this, SIZECTL, sc, sc - 1))
1632 >                    if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
1633                          transfer(tab, nt);
1634                  }
1635 <                else if (U.compareAndSwapInt(this, SIZECTL, sc, -2))
1635 >                else if (U.compareAndSwapInt(this, SIZECTL, sc,
1636 >                                             (rs << RESIZE_STAMP_SHIFT) + 2))
1637                      transfer(tab, null);
1638                  s = sumCount();
1639              }
# Line 1615 | Line 1645 | public class ConcurrentHashMap<K,V> impl
1645       */
1646      final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
1647          Node<K,V>[] nextTab; int sc;
1648 <        if ((f instanceof ForwardingNode) &&
1648 >        if (tab != null && (f instanceof ForwardingNode) &&
1649              (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
1650 <            while (transferIndex > 0 && nextTab == nextTable &&
1651 <                   (sc = sizeCtl) < -1) {
1652 <                if (U.compareAndSwapInt(this, SIZECTL, sc, sc - 1)) {
1650 >            int rs = resizeStamp(tab.length);
1651 >            while (nextTab == nextTable && table == tab &&
1652 >                   (sc = sizeCtl) < 0) {
1653 >                if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
1654 >                    sc == rs + MAX_RESIZERS || transferIndex <= 0)
1655 >                    break;
1656 >                if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
1657                      transfer(tab, nextTab);
1658                      break;
1659                  }
# Line 1646 | Line 1680 | public class ConcurrentHashMap<K,V> impl
1680                      try {
1681                          if (table == tab) {
1682                              @SuppressWarnings("unchecked")
1683 <                                Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
1683 >                            Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
1684                              table = nt;
1685                              sc = n - (n >>> 2);
1686                          }
# Line 1657 | Line 1691 | public class ConcurrentHashMap<K,V> impl
1691              }
1692              else if (c <= sc || n >= MAXIMUM_CAPACITY)
1693                  break;
1694 <            else if (tab == table &&
1695 <                     U.compareAndSwapInt(this, SIZECTL, sc, -2))
1696 <                transfer(tab, null);
1694 >            else if (tab == table) {
1695 >                int rs = resizeStamp(n);
1696 >                if (sc < 0) {
1697 >                    Node<K,V>[] nt;
1698 >                    if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
1699 >                        sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
1700 >                        transferIndex <= 0)
1701 >                        break;
1702 >                    if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
1703 >                        transfer(tab, nt);
1704 >                }
1705 >                else if (U.compareAndSwapInt(this, SIZECTL, sc,
1706 >                                             (rs << RESIZE_STAMP_SHIFT) + 2))
1707 >                    transfer(tab, null);
1708 >            }
1709          }
1710      }
1711  
# Line 1714 | Line 1760 | public class ConcurrentHashMap<K,V> impl
1760                      sizeCtl = (n << 1) - (n >>> 1);
1761                      return;
1762                  }
1763 <                if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, ++sc)) {
1764 <                    if (sc != -1)
1763 >                if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
1764 >                    if ((sc - 2) != resizeStamp(n))
1765                          return;
1766                      finishing = advance = true;
1767                      i = n; // recheck before commit
# Line 1809 | Line 1855 | public class ConcurrentHashMap<K,V> impl
1855      private final void treeifyBin(Node<K,V>[] tab, int index) {
1856          Node<K,V> b; int n, sc;
1857          if (tab != null) {
1858 <            if ((n = tab.length) < MIN_TREEIFY_CAPACITY) {
1859 <                if (tab == table && (sc = sizeCtl) >= 0 &&
1860 <                    U.compareAndSwapInt(this, SIZECTL, sc, -2))
1815 <                    transfer(tab, null);
1816 <            }
1817 <            else if ((b = tabAt(tab, index)) != null) {
1858 >            if ((n = tab.length) < MIN_TREEIFY_CAPACITY)
1859 >                tryPresize(n << 1);
1860 >            else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {
1861                  synchronized (b) {
1862                      if (tabAt(tab, index) == b) {
1863                          TreeNode<K,V> hd = null, tl = null;
# Line 2012 | Line 2055 | public class ConcurrentHashMap<K,V> impl
2055          private final void contendedLock() {
2056              boolean waiting = false;
2057              for (int s;;) {
2058 <                if (((s = lockState) & WRITER) == 0) {
2058 >                if (((s = lockState) & ~WAITER) == 0) {
2059                      if (U.compareAndSwapInt(this, LOCKSTATE, s, WRITER)) {
2060                          if (waiting)
2061                              waiter = null;

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines