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.23 by jsr166, Sun Sep 11 04:25:00 2011 UTC vs.
Revision 1.24 by dl, Thu Sep 15 14:25:46 2011 UTC

# Line 6 | Line 6
6  
7   package jsr166e;
8   import jsr166e.LongAdder;
9 + import java.util.Arrays;
10   import java.util.Map;
11   import java.util.Set;
12   import java.util.Collection;
# Line 19 | Line 20 | import java.util.Enumeration;
20   import java.util.ConcurrentModificationException;
21   import java.util.NoSuchElementException;
22   import java.util.concurrent.ConcurrentMap;
23 + import java.util.concurrent.locks.LockSupport;
24   import java.io.Serializable;
25  
26   /**
# Line 54 | Line 56 | import java.io.Serializable;
56   * <p> The table is dynamically expanded when there are too many
57   * collisions (i.e., keys that have distinct hash codes but fall into
58   * the same slot modulo the table size), with the expected average
59 < * effect of maintaining roughly two bins per mapping. There may be
60 < * much variance around this average as mappings are added and
61 < * removed, but overall, this maintains a commonly accepted time/space
62 < * tradeoff for hash tables.  However, resizing this or any other kind
63 < * of hash table may be a relatively slow operation. When possible, it
64 < * is a good idea to provide a size estimate as an optional {@code
59 > * effect of maintaining roughly two bins per mapping (corresponding
60 > * to a 0.75 load factor threshold for resizing). There may be much
61 > * variance around this average as mappings are added and removed, but
62 > * overall, this maintains a commonly accepted time/space tradeoff for
63 > * hash tables.  However, resizing this or any other kind of hash
64 > * table may be a relatively slow operation. When possible, it is a
65 > * good idea to provide a size estimate as an optional {@code
66   * initialCapacity} constructor argument. An additional optional
67   * {@code loadFactor} constructor argument provides a further means of
68   * customizing initial table capacity by specifying the table density
# Line 121 | Line 124 | public class ConcurrentHashMapV8<K, V>
124       * The primary design goal of this hash table is to maintain
125       * concurrent readability (typically method get(), but also
126       * iterators and related methods) while minimizing update
127 <     * contention.
127 >     * contention. Secondary goals are to keep space consumption about
128 >     * the same or better than java.util.HashMap, and to support high
129 >     * initial insertion rates on an empty table by many threads.
130       *
131       * Each key-value mapping is held in a Node.  Because Node fields
132       * can contain special values, they are defined using plain Object
# Line 139 | Line 144 | public class ConcurrentHashMapV8<K, V>
144       * we use intrinsics (sun.misc.Unsafe) operations.  The lists of
145       * nodes within bins are always accurately traversable under
146       * volatile reads, so long as lookups check hash code and
147 <     * non-nullness of value before checking key equality. (All valid
148 <     * hash codes are nonnegative. Negative values are reserved for
149 <     * special forwarding nodes; see below.)
147 >     * non-nullness of value before checking key equality.
148 >     *
149 >     * We use the top two bits of Node hash fields for control
150 >     * purposes -- they are available anyway because of addressing
151 >     * constraints.  As explained further below, these top bits are
152 >     * usd as follows:
153 >     *  00 - Normal
154 >     *  01 - Locked
155 >     *  11 - Locked and may have a thread waiting for lock
156 >     *  10 - Node is a forwarding node
157 >     *
158 >     * The lower 30 bits of each Node's hash field contain a
159 >     * transformation (for better randomization -- method "spread") of
160 >     * the key's hash code, except for forwarding nodes, for which the
161 >     * lower bits are zero (and so always have hash field == "MOVED").
162       *
163       * Insertion (via put or putIfAbsent) of the first node in an
164       * empty bin is performed by just CASing it to the bin.  This is
165 <     * on average by far the most common case for put operations.
166 <     * Other update operations (insert, delete, and replace) require
167 <     * locks.  We do not want to waste the space required to associate
168 <     * a distinct lock object with each bin, so instead use the first
169 <     * node of a bin list itself as a lock, using plain "synchronized"
170 <     * locks. These save space and we can live with block-structured
171 <     * lock/unlock operations. Using the first node of a list as a
172 <     * lock does not by itself suffice though. When a node is locked,
173 <     * any update must first validate that it is still the first node,
174 <     * and retry if not. Because new nodes are always appended to
175 <     * lists, once a node is first in a bin, it remains first until
176 <     * deleted or the bin becomes invalidated.  However, operations
177 <     * that only conditionally update can and sometimes do inspect
178 <     * nodes until the point of update. This is a converse of sorts to
179 <     * the lazy locking technique described by Herlihy & Shavit.
165 >     * by far the most common case for put operations.  Other update
166 >     * operations (insert, delete, and replace) require locks.  We do
167 >     * not want to waste the space required to associate a distinct
168 >     * lock object with each bin, so instead use the first node of a
169 >     * bin list itself as a lock. Blocking support for these locks
170 >     * relies on the builtin "synchronized" monitors.  However, we
171 >     * also need a tryLock construction, so we overlay these by using
172 >     * bits of the Node hash field for lock control (see above), and
173 >     * so normally use builtin monitors only for blocking and
174 >     * signalling using wait/notifyAll constructions. See
175 >     * Node.tryAwaitLock.
176 >     *
177 >     * Using the first node of a list as a lock does not by itself
178 >     * suffice though: When a node is locked, any update must first
179 >     * validate that it is still the first node after locking it, and
180 >     * retry if not. Because new nodes are always appended to lists,
181 >     * once a node is first in a bin, it remains first until deleted
182 >     * or the bin becomes invalidated.  However, operations that only
183 >     * conditionally update may inspect nodes until the point of
184 >     * update. This is a converse of sorts to the lazy locking
185 >     * technique described by Herlihy & Shavit.
186       *
187 <     * The main disadvantage of this approach is that most update
187 >     * The main disadvantage of per-bin locks is that other update
188       * operations on other nodes in a bin list protected by the same
189       * lock can stall, for example when user equals() or mapping
190       * functions take a long time.  However, statistically, this is
# Line 187 | Line 210 | public class ConcurrentHashMapV8<K, V>
210       * that these assumptions hold unless users define exactly the
211       * same value for too many hashCodes.
212       *
213 <     * The table is resized when occupancy exceeds a threshold.  Only
214 <     * a single thread performs the resize (using field "resizing", to
215 <     * arrange exclusion), but the table otherwise remains usable for
216 <     * reads and updates. Resizing proceeds by transferring bins, one
217 <     * by one, from the table to the next table.  Upon transfer, the
218 <     * old table bin contains only a special forwarding node (with
219 <     * negative hash field) that contains the next table as its
220 <     * key. On encountering a forwarding node, access and update
221 <     * operations restart, using the new table. To ensure concurrent
222 <     * readability of traversals, transfers must proceed from the last
223 <     * bin (table.length - 1) up towards the first.  Upon seeing a
224 <     * forwarding node, traversals (see class InternalIterator)
225 <     * arrange to move to the new table for the rest of the traversal
226 <     * without revisiting nodes.  This constrains bin transfers to a
227 <     * particular order, and so can block indefinitely waiting for the
228 <     * next lock, and other threads cannot help with the transfer.
229 <     * However, expected stalls are infrequent enough to not warrant
230 <     * the additional overhead of access and iteration schemes that
231 <     * could admit out-of-order or concurrent bin transfers.
213 >     * The table is resized when occupancy exceeds an occupancy
214 >     * threshold (nominally, 0.75, but see below).  Only a single
215 >     * thread performs the resize (using field "sizeCtl", to arrange
216 >     * exclusion), but the table otherwise remains usable for reads
217 >     * and updates. Resizing proceeds by transferring bins, one by
218 >     * one, from the table to the next table.  Because we are using
219 >     * power-of-two expansion, the elements from each bin must either
220 >     * stay at same index, or move with a power of two offset. We
221 >     * eliminate unnecessary node creation by catching cases where old
222 >     * nodes can be reused because their next fields won't change.  On
223 >     * average, only about one-sixth of them need cloning when a table
224 >     * doubles. The nodes they replace will be garbage collectable as
225 >     * soon as they are no longer referenced by any reader thread that
226 >     * may be in the midst of concurrently traversing table.  Upon
227 >     * transfer, the old table bin contains only a special forwarding
228 >     * node (with hash field "MOVED") that contains the next table as
229 >     * its key. On encountering a forwarding node, access and update
230 >     * operations restart, using the new table.
231 >     *
232 >     * Each bin transfer requires its bin lock. However, unlike other
233 >     * cases, a transfer can skip a bin if it fails to acquire its
234 >     * lock, and revisit it later. Method rebuild maintains a buffer
235 >     * of TRANSFER_BUFFER_SIZE bins that have been skipped because of
236 >     * failure to acquire a lock, and blocks only if none are
237 >     * available (i.e., only very rarely).  The transfer operation
238 >     * must also ensure that all accessible bins in both the old and
239 >     * new table are usable by any traversal.  When there are no lock
240 >     * acquisition failures, this is arranged simply by proceeding
241 >     * from the last bin (table.length - 1) up towards the first.
242 >     * Upon seeing a forwarding node, traversals (see class
243 >     * InternalIterator) arrange to move to the new table without
244 >     * revisiting nodes.  However, when any node is skipped during a
245 >     * transfer, all earlier table bins may have become visible, so
246 >     * are initialized with a reverse-forwarding node back to the old
247 >     * table until the new ones are established. (This sometimes
248 >     * requires transiently locking a forwarding node, which is
249 >     * possible under the above encoding.) These more expensive
250 >     * mechanics trigger only when necessary.
251       *
252 <     * This traversal scheme also applies to partial traversals of
252 >     * The traversal scheme also applies to partial traversals of
253       * ranges of bins (via an alternate InternalIterator constructor)
254       * to support partitioned aggregate operations (that are not
255       * otherwise implemented yet).  Also, read-only operations give up
# Line 218 | Line 260 | public class ConcurrentHashMapV8<K, V>
260       * Lazy table initialization minimizes footprint until first use,
261       * and also avoids resizings when the first operation is from a
262       * putAll, constructor with map argument, or deserialization.
263 <     * These cases attempt to override the targetCapacity used in
264 <     * growTable. These harmlessly fail to take effect in cases of
223 <     * races with other ongoing resizings. Uses of the threshold and
224 <     * targetCapacity during attempted initializations or resizings
225 <     * are racy but fall back on checks to preserve correctness.
263 >     * These cases attempt to override the initial capacity settings,
264 >     * but harmlessly fail to take effect in cases of races.
265       *
266       * The element count is maintained using a LongAdder, which avoids
267       * contention on updates but can encounter cache thrashing if read
# Line 233 | Line 272 | public class ConcurrentHashMapV8<K, V>
272       * is around 13%, meaning that only about 1 in 8 puts check
273       * threshold (and after resizing, many fewer do so). But this
274       * approximation has high variance for small table sizes, so we
275 <     * check on any collision for sizes <= 64.  Further, to increase
237 <     * the probability that a resize occurs soon enough, we offset the
238 <     * threshold (see THRESHOLD_OFFSET) by the expected number of puts
239 <     * between checks.
275 >     * check on any collision for sizes <= 64.
276       *
277       * Maintaining API and serialization compatibility with previous
278       * versions of this class introduces several oddities. Mainly: We
279       * leave untouched but unused constructor arguments refering to
280 <     * concurrencyLevel. We also declare an unused "Segment" class
281 <     * that is instantiated in minimal form only when serializing.
280 >     * concurrencyLevel. We accept a loadFactor constructor argument,
281 >     * but apply it only to initial table capacity (which is the only
282 >     * time that we can guarantee to honor it.) We also declare an
283 >     * unused "Segment" class that is instantiated in minimal form
284 >     * only when serializing.
285       */
286  
287      /* ---------------- Constants -------------- */
# Line 250 | Line 289 | public class ConcurrentHashMapV8<K, V>
289      /**
290       * The largest possible table capacity.  This value must be
291       * exactly 1<<30 to stay within Java array allocation and indexing
292 <     * bounds for power of two table sizes.
292 >     * bounds for power of two table sizes, and is further required
293 >     * because the top two bits of 32bit hash fields are used for
294 >     * control purposes.
295       */
296      private static final int MAXIMUM_CAPACITY = 1 << 30;
297  
# Line 261 | Line 302 | public class ConcurrentHashMapV8<K, V>
302      private static final int DEFAULT_CAPACITY = 16;
303  
304      /**
305 +     * The largest possible (non-power of two) array size.
306 +     * Needed by toArray and related methods.
307 +     */
308 +    static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
309 +
310 +    /**
311 +     * The default concurrency level for this table. Unused but
312 +     * defined for compatibility with previous versions of this class.
313 +     */
314 +    private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
315 +
316 +    /**
317       * The load factor for this table. Overrides of this value in
318       * constructors affect only the initial table capacity.  The
319 <     * actual floating point value isn't normally used, because it is
320 <     * simpler to rely on the expression {@code n - (n >>> 2)} for the
321 <     * associated resizing threshold.
319 >     * actual floating point value isn't normally used -- it is
320 >     * simpler to use expressions such as {@code n - (n >>> 2)} for
321 >     * the associated resizing threshold.
322       */
323      private static final float LOAD_FACTOR = 0.75f;
324  
325      /**
326 <     * The count value to offset thresholds to compensate for checking
327 <     * for the need to resize only when inserting into bins with two
328 <     * or more elements. See above for explanation.
326 >     * The buffer size for skipped bins during transfers. The
327 >     * value is arbitrary but should be large enough to avoid
328 >     * most locking stalls during resizes.
329 >     */
330 >    private static final int TRANSFER_BUFFER_SIZE = 32;
331 >
332 >    /*
333 >     * Encodings for special uses of Node hash fields. See above for
334 >     * explanation.
335       */
336 <    private static final int THRESHOLD_OFFSET = 8;
336 >    static final int MOVED     = 0x80000000; // hash field for fowarding nodes
337 >    static final int LOCKED    = 0x40000000; // set/tested only as a bit
338 >    static final int WAITING   = 0xc0000000; // both bits set/tested together
339 >    static final int HASH_BITS = 0x3fffffff; // usable bits of normal node hash
340 >
341 >    /* ---------------- Fields -------------- */
342  
343      /**
344 <     * The default concurrency level for this table. Unused except as
345 <     * a sizing hint, but defined for compatibility with previous
282 <     * versions of this class.
344 >     * The array of bins. Lazily initialized upon first insertion.
345 >     * Size is always a power of two. Accessed directly by iterators.
346       */
347 <    private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
347 >    transient volatile Node[] table;
348 >
349 >    /**
350 >     * The counter maintaining number of elements.
351 >     */
352 >    private transient final LongAdder counter;
353 >
354 >    /**
355 >     * Table initialization and resizing control.  When negative, the
356 >     * table is being initialized or resized. Otherwise, when table is
357 >     * null, holds the initial table size to use upon creation, or 0
358 >     * for default. After initialization, holds the next element count
359 >     * value upon which to resize the table.
360 >     */
361 >    private transient volatile int sizeCtl;
362 >
363 >    // views
364 >    private transient KeySet<K,V> keySet;
365 >    private transient Values<K,V> values;
366 >    private transient EntrySet<K,V> entrySet;
367 >
368 >    /** For serialization compatibility. Null unless serialized; see below */
369 >    private Segment<K,V>[] segments;
370  
371      /* ---------------- Nodes -------------- */
372  
373      /**
374       * Key-value entry. Note that this is never exported out as a
375 <     * user-visible Map.Entry. Nodes with a negative hash field are
376 <     * special, and do not contain user keys or values.  Otherwise,
377 <     * keys are never null, and null val fields indicate that a node
378 <     * is in the process of being deleted or created. For purposes of
379 <     * read-only access, a key may be read before a val, but can only
380 <     * be used after checking val.  (For an update operation, when a
381 <     * lock is held on a node, order doesn't matter.)
375 >     * user-visible Map.Entry (see WriteThroughEntry and SnapshotEntry
376 >     * below). Nodes with a negative hash field are special, and do
377 >     * not contain user keys or values.  Otherwise, keys are never
378 >     * null, and null val fields indicate that a node is in the
379 >     * process of being deleted or created. For purposes of read-only
380 >     * access, a key may be read before a val, but can only be used
381 >     * after checking val to be non-null.
382       */
383      static final class Node {
384 <        final int hash;
384 >        volatile int hash;
385          final Object key;
386          volatile Object val;
387          volatile Node next;
# Line 307 | Line 392 | public class ConcurrentHashMapV8<K, V>
392              this.val = val;
393              this.next = next;
394          }
310    }
311
312    /**
313     * Sign bit of node hash value indicating to use table in node.key.
314     */
315    private static final int SIGN_BIT = 0x80000000;
395  
396 <    /* ---------------- Fields -------------- */
396 >        /** CompareAndSet the hash field */
397 >        final boolean casHash(int cmp, int val) {
398 >            return UNSAFE.compareAndSwapInt(this, hashOffset, cmp, val);
399 >        }
400  
401 <    /**
402 <     * The array of bins. Lazily initialized upon first insertion.
403 <     * Size is always a power of two. Accessed directly by iterators.
322 <     */
323 <    transient volatile Node[] table;
401 >        /** The number of spins before blocking for a lock */
402 >        static final int MAX_SPINS =
403 >            Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1;
404  
405 <    /** The counter maintaining number of elements. */
406 <    private transient final LongAdder counter;
407 <    /** Nonzero when table is being initialized or resized. Updated via CAS. */
408 <    private transient volatile int resizing;
409 <    /** The next element count value upon which to resize the table. */
410 <    private transient int threshold;
411 <    /** The target capacity; volatile to cover initialization races. */
412 <    private transient volatile int targetCapacity;
405 >        /**
406 >         * Spins a while if LOCKED bit set and this node is the first
407 >         * of its bin, and then sets WAITING bits on hash field and
408 >         * blocks (once) if they are still set.  It is OK for this
409 >         * method to return even if lock is not available upon exit,
410 >         * which enables these simple single-wait mechanics.
411 >         *
412 >         * The corresponding signalling operation is performed within
413 >         * callers: Upon detecting that WAITING has been set when
414 >         * unlocking lock (via a failed CAS from non-waiting LOCKED
415 >         * state), unlockers acquire the sync lock and perform a
416 >         * notifyAll.
417 >         */
418 >        final void tryAwaitLock(Node[] tab, int i) {
419 >            if (tab != null && i >= 0 && i < tab.length) { // bounds check
420 >                int spins = MAX_SPINS, h;
421 >                while (tabAt(tab, i) == this && ((h = hash) & LOCKED) != 0) {
422 >                    if (spins >= 0) {
423 >                        if (--spins == MAX_SPINS >>> 1)
424 >                            Thread.yield();  // heuristically yield mid-way
425 >                    }
426 >                    else if (casHash(h, h | WAITING)) {
427 >                        synchronized(this) {
428 >                            if (tabAt(tab, i) == this &&
429 >                                (hash & WAITING) == WAITING) {
430 >                                try {
431 >                                    wait();
432 >                                } catch (InterruptedException ie) {
433 >                                    Thread.currentThread().interrupt();
434 >                                }
435 >                            }
436 >                            else
437 >                                notifyAll(); // possibly won race vs signaller
438 >                        }
439 >                        break;
440 >                    }
441 >                }
442 >            }
443 >        }
444  
445 <    // views
446 <    private transient KeySet<K,V> keySet;
447 <    private transient Values<K,V> values;
337 <    private transient EntrySet<K,V> entrySet;
445 >        // Unsafe mechanics for casHash
446 >        private static final sun.misc.Unsafe UNSAFE;
447 >        private static final long hashOffset;
448  
449 <    /** For serialization compatibility. Null unless serialized; see below */
450 <    private Segment<K,V>[] segments;
449 >        static {
450 >            try {
451 >                UNSAFE = getUnsafe();
452 >                Class<?> k = Node.class;
453 >                hashOffset = UNSAFE.objectFieldOffset
454 >                    (k.getDeclaredField("hash"));
455 >            } catch (Exception e) {
456 >                throw new Error(e);
457 >            }
458 >        }
459 >    }
460  
461      /* ---------------- Table element access -------------- */
462  
# Line 365 | Line 484 | public class ConcurrentHashMapV8<K, V>
484          UNSAFE.putObjectVolatile(tab, ((long)i<<ASHIFT)+ABASE, v);
485      }
486  
368    /* ----------------Table Initialization and Resizing -------------- */
369
370    /**
371     * Returns a power of two table size for the given desired capacity.
372     * See Hackers Delight, sec 3.2
373     */
374    private static final int tableSizeFor(int c) {
375        int n = c - 1;
376        n |= n >>> 1;
377        n |= n >>> 2;
378        n |= n >>> 4;
379        n |= n >>> 8;
380        n |= n >>> 16;
381        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
382    }
383
384    /**
385     * If not already resizing, initializes or creates next table and
386     * transfers bins. Initial table size uses the capacity recorded
387     * in targetCapacity.  Rechecks occupancy after a transfer to see
388     * if another resize is already needed because resizings are
389     * lagging additions.
390     *
391     * @return current table
392     */
393    private final Node[] growTable() {
394        if (resizing == 0 &&
395            UNSAFE.compareAndSwapInt(this, resizingOffset, 0, 1)) {
396            try {
397                for (;;) {
398                    Node[] tab = table;
399                    int n, c, m;
400                    if (tab == null)
401                        n = (c = targetCapacity) > 0 ? c : DEFAULT_CAPACITY;
402                    else if ((m = tab.length) < MAXIMUM_CAPACITY &&
403                             counter.sum() >= (long)threshold)
404                        n = m << 1;
405                    else
406                        break;
407                    threshold = n - (n >>> 2) - THRESHOLD_OFFSET;
408                    Node[] nextTab = new Node[n];
409                    if (tab != null)
410                        transfer(tab, nextTab,
411                                 new Node(SIGN_BIT, nextTab, null, null));
412                    table = nextTab;
413                    if (tab == null)
414                        break;
415                }
416            } finally {
417                resizing = 0;
418            }
419        }
420        else if (table == null)
421            Thread.yield(); // lost initialization race; just spin
422        return table;
423    }
424
425    /**
426     * Reclassifies nodes in each bin to new table.  Because we are
427     * using power-of-two expansion, the elements from each bin must
428     * either stay at same index, or move with a power of two
429     * offset. We eliminate unnecessary node creation by catching
430     * cases where old nodes can be reused because their next fields
431     * won't change.  Statistically, only about one-sixth of them need
432     * cloning when a table doubles. The nodes they replace will be
433     * garbage collectable as soon as they are no longer referenced by
434     * any reader thread that may be in the midst of concurrently
435     * traversing table.
436     *
437     * Transfers are done from the bottom up to preserve iterator
438     * traversability. On each step, the old bin is locked,
439     * moved/copied, and then replaced with a forwarding node.
440     */
441    private static final void transfer(Node[] tab, Node[] nextTab, Node fwd) {
442        int n = tab.length;
443        Node ignore = nextTab[n + n - 1]; // force bounds check
444        for (int i = n - 1; i >= 0; --i) {
445            for (Node e;;) {
446                if ((e = tabAt(tab, i)) != null) {
447                    boolean validated = false;
448                    synchronized (e) {
449                        if (tabAt(tab, i) == e) {
450                            validated = true;
451                            Node lo = null, hi = null, lastRun = e;
452                            int runBit = e.hash & n;
453                            for (Node p = e.next; p != null; p = p.next) {
454                                int b = p.hash & n;
455                                if (b != runBit) {
456                                    runBit = b;
457                                    lastRun = p;
458                                }
459                            }
460                            if (runBit == 0)
461                                lo = lastRun;
462                            else
463                                hi = lastRun;
464                            for (Node p = e; p != lastRun; p = p.next) {
465                                int ph = p.hash;
466                                Object pk = p.key, pv = p.val;
467                                if ((ph & n) == 0)
468                                    lo = new Node(ph, pk, pv, lo);
469                                else
470                                    hi = new Node(ph, pk, pv, hi);
471                            }
472                            setTabAt(nextTab, i, lo);
473                            setTabAt(nextTab, i + n, hi);
474                            setTabAt(tab, i, fwd);
475                        }
476                    }
477                    if (validated)
478                        break;
479                }
480                else if (casTabAt(tab, i, e, fwd))
481                    break;
482            }
483        }
484    }
485
487      /* ---------------- Internal access and update methods -------------- */
488  
489      /**
490       * Applies a supplemental hash function to a given hashCode, which
491       * defends against poor quality hash functions.  The result must
492 <     * be non-negative, and for reasonable performance must have good
493 <     * avalanche properties; i.e., that each bit of the argument
494 <     * affects each bit (except sign bit) of the result.
492 >     * be have the top 2 bits clear. For reasonable performance, this
493 >     * function must have good avalanche properties; i.e., that each
494 >     * bit of the argument affects each bit of the result. (Although
495 >     * we don't care about the unused top 2 bits.)
496       */
497      private static final int spread(int h) {
498          // Apply base step of MurmurHash; see http://code.google.com/p/smhasher/
# Line 498 | Line 500 | public class ConcurrentHashMapV8<K, V>
500          h *= 0x85ebca6b;
501          h ^= h >>> 13;
502          h *= 0xc2b2ae35;
503 <        return (h >>> 16) ^ (h & 0x7fffffff); // mask out sign bit
503 >        return ((h >>> 16) ^ h) & HASH_BITS; // mask out top bits
504      }
505  
506      /** Implementation for get and containsKey */
507      private final Object internalGet(Object k) {
508          int h = spread(k.hashCode());
509          retry: for (Node[] tab = table; tab != null;) {
510 <            Node e; Object ek, ev; int eh;  // locals to read fields once
510 >            Node e; Object ek, ev; int eh;    // locals to read fields once
511              for (e = tabAt(tab, (tab.length - 1) & h); e != null; e = e.next) {
512 <                if ((eh = e.hash) == h) {
513 <                    if ((ev = e.val) != null &&
512 <                        ((ek = e.key) == k || k.equals(ek)))
513 <                        return ev;
514 <                }
515 <                else if (eh < 0) {          // sign bit set
516 <                    tab = (Node[])e.key;    // bin was moved during resize
512 >                if ((eh = e.hash) == MOVED) {
513 >                    tab = (Node[])e.key;      // restart with new table
514                      continue retry;
515                  }
516 +                if ((eh & HASH_BITS) == h && (ev = e.val) != null &&
517 +                    ((ek = e.key) == k || k.equals(ek)))
518 +                    return ev;
519              }
520              break;
521          }
# Line 527 | Line 527 | public class ConcurrentHashMapV8<K, V>
527          int h = spread(k.hashCode());
528          Object oldVal = null;               // previous value or null if none
529          for (Node[] tab = table;;) {
530 <            Node e; int i; Object ek, ev;
530 >            int i; Node f; int fh; Object fk, fv;
531              if (tab == null)
532 <                tab = growTable();
533 <            else if ((e = tabAt(tab, i = (tab.length - 1) & h)) == null) {
532 >                tab = initTable();
533 >            else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) {
534                  if (casTabAt(tab, i, null, new Node(h, k, v, null)))
535                      break;                   // no lock when adding to empty bin
536              }
537 <            else if (e.hash < 0)             // resized -- restart with new table
538 <                tab = (Node[])e.key;
539 <            else if (!replace && e.hash == h && (ev = e.val) != null &&
540 <                     ((ek = e.key) == k || k.equals(ek))) {
541 <                if (tabAt(tab, i) == e) {    // inspect and validate 1st node
542 <                    oldVal = ev;             // without lock for putIfAbsent
543 <                    break;
544 <                }
537 >            else if ((fh = f.hash) == MOVED)
538 >                tab = (Node[])f.key;
539 >            else if (!replace && (fh & HASH_BITS) == h && (fv = f.val) != null &&
540 >                     ((fk = f.key) == k || k.equals(fk))) {
541 >                oldVal = fv;                // precheck 1st node for putIfAbsent
542 >                break;
543              }
544 <            else {
544 >            else if ((fh & LOCKED) != 0)
545 >                f.tryAwaitLock(tab, i);
546 >            else if (f.casHash(fh, fh | LOCKED)) {
547                  boolean validated = false;
548                  boolean checkSize = false;
549 <                synchronized (e) {           // lock the 1st node of bin list
550 <                    if (tabAt(tab, i) == e) {
549 >                try {
550 >                    if (tabAt(tab, i) == f) {
551                          validated = true;    // retry if 1st already deleted
552 <                        for (Node first = e;;) {
553 <                            if (e.hash == h &&
554 <                                ((ek = e.key) == k || k.equals(ek)) &&
555 <                                (ev = e.val) != null) {
552 >                        for (Node e = f;;) {
553 >                            Object ek, ev;
554 >                            if ((e.hash & HASH_BITS) == h &&
555 >                                (ev = e.val) != null &&
556 >                                ((ek = e.key) == k || k.equals(ek))) {
557                                  oldVal = ev;
558                                  if (replace)
559                                      e.val = v;
# Line 561 | Line 562 | public class ConcurrentHashMapV8<K, V>
562                              Node last = e;
563                              if ((e = e.next) == null) {
564                                  last.next = new Node(h, k, v, null);
565 <                                if (last != first || tab.length <= 64)
565 >                                if (last != f || tab.length <= 64)
566                                      checkSize = true;
567                                  break;
568                              }
569                          }
570                      }
571 +                } finally {                  // unlock and signal if needed
572 +                    if (!f.casHash(fh | LOCKED, fh)) {
573 +                        f.hash = fh;
574 +                        synchronized(f) { f.notifyAll(); };
575 +                    }
576                  }
577                  if (validated) {
578 +                    int sc;
579                      if (checkSize && tab.length < MAXIMUM_CAPACITY &&
580 <                        resizing == 0 && counter.sum() >= (long)threshold)
580 >                        (sc = sizeCtl) >= 0 && counter.sum() >= (long)sc)
581                          growTable();
582                      break;
583                  }
# Line 588 | Line 595 | public class ConcurrentHashMapV8<K, V>
595       */
596      private final Object internalReplace(Object k, Object v, Object cv) {
597          int h = spread(k.hashCode());
598 +        Object oldVal = null;
599          for (Node[] tab = table;;) {
600 <            Node e; int i;
600 >            Node f; int i, fh;
601              if (tab == null ||
602 <                (e = tabAt(tab, i = (tab.length - 1) & h)) == null)
603 <                return null;
604 <            else if (e.hash < 0)
605 <                tab = (Node[])e.key;
606 <            else {
607 <                Object oldVal = null;
602 >                (f = tabAt(tab, i = (tab.length - 1) & h)) == null)
603 >                break;
604 >            else if ((fh = f.hash) == MOVED)
605 >                tab = (Node[])f.key;
606 >            else if ((fh & HASH_BITS) != h && f.next == null) // precheck
607 >                break;                          // rules out possible existence
608 >            else if ((fh & LOCKED) != 0)
609 >                f.tryAwaitLock(tab, i);
610 >            else if (f.casHash(fh, fh | LOCKED)) {
611                  boolean validated = false;
612                  boolean deleted = false;
613 <                synchronized (e) {
614 <                    if (tabAt(tab, i) == e) {
613 >                try {
614 >                    if (tabAt(tab, i) == f) {
615                          validated = true;
616 <                        Node pred = null;
606 <                        do {
616 >                        for (Node e = f, pred = null;;) {
617                              Object ek, ev;
618 <                            if (e.hash == h &&
619 <                                ((ek = e.key) == k || k.equals(ek)) &&
620 <                                ((ev = e.val) != null)) {
618 >                            if ((e.hash & HASH_BITS) == h &&
619 >                                ((ev = e.val) != null) &&
620 >                                ((ek = e.key) == k || k.equals(ek))) {
621                                  if (cv == null || cv == ev || cv.equals(ev)) {
622                                      oldVal = ev;
623                                      if ((e.val = v) == null) {
# Line 621 | Line 631 | public class ConcurrentHashMapV8<K, V>
631                                  }
632                                  break;
633                              }
634 <                        } while ((e = (pred = e).next) != null);
634 >                            pred = e;
635 >                            if ((e = e.next) == null)
636 >                                break;
637 >                        }
638 >                    }
639 >                } finally {
640 >                    if (!f.casHash(fh | LOCKED, fh)) {
641 >                        f.hash = fh;
642 >                        synchronized(f) { f.notifyAll(); };
643                      }
644                  }
645                  if (validated) {
646                      if (deleted)
647                          counter.decrement();
648 <                    return oldVal;
648 >                    break;
649                  }
650              }
651          }
652 +        return oldVal;
653      }
654  
655      /** Implementation for computeIfAbsent and compute. Like put, but messier. */
656 +    // Todo: Somehow reinstate non-termination check
657      @SuppressWarnings("unchecked")
658      private final V internalCompute(K k,
659 <                                    MappingFunction<? super K, ? extends V> f,
659 >                                    MappingFunction<? super K, ? extends V> fn,
660                                      boolean replace) {
661          int h = spread(k.hashCode());
662          V val = null;
663          boolean added = false;
664          Node[] tab = table;
665          outer:for (;;) {
666 <            Node e; int i; Object ek, ev;
666 >            Node f; int i, fh; Object fk, fv;
667              if (tab == null)
668 <                tab = growTable();
669 <            else if ((e = tabAt(tab, i = (tab.length - 1) & h)) == null) {
670 <                Node node = new Node(h, k, null, null);
668 >                tab = initTable();
669 >            else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) {
670 >                Node node = new Node(fh = h | LOCKED, k, null, null);
671                  boolean validated = false;
672 <                synchronized (node) {  // must lock while computing value
673 <                    if (casTabAt(tab, i, null, node)) {
674 <                        validated = true;
675 <                        try {
676 <                            val = f.map(k);
677 <                            if (val != null) {
678 <                                node.val = val;
679 <                                added = true;
680 <                            }
681 <                        } finally {
682 <                            if (!added)
683 <                                setTabAt(tab, i, null);
672 >                if (casTabAt(tab, i, null, node)) {
673 >                    validated = true;
674 >                    try {
675 >                        val = fn.map(k);
676 >                        if (val != null) {
677 >                            node.val = val;
678 >                            added = true;
679 >                        }
680 >                    } finally {
681 >                        if (!added)
682 >                            setTabAt(tab, i, null);
683 >                        if (!node.casHash(fh, h)) {
684 >                            node.hash = fh;
685 >                            synchronized(node) { node.notifyAll(); };
686                          }
687                      }
688                  }
689                  if (validated)
690                      break;
691              }
692 <            else if (e.hash < 0)
693 <                tab = (Node[])e.key;
694 <            else if (!replace && e.hash == h && (ev = e.val) != null &&
695 <                     ((ek = e.key) == k || k.equals(ek))) {
696 <                if (tabAt(tab, i) == e) {
697 <                    val = (V)ev;
692 >            else if ((fh = f.hash) == MOVED)
693 >                tab = (Node[])f.key;
694 >            else if (!replace && (fh & HASH_BITS) == h && (fv = f.val) != null &&
695 >                     ((fk = f.key) == k || k.equals(fk))) {
696 >                if (tabAt(tab, i) == f) {
697 >                    val = (V)fv;
698                      break;
699                  }
700              }
701 <            else if (Thread.holdsLock(e))
702 <                throw new IllegalStateException("Recursive map computation");
703 <            else {
701 >            else if ((fh & LOCKED) != 0)
702 >                f.tryAwaitLock(tab, i);
703 >            else if (f.casHash(fh, fh | LOCKED)) {
704                  boolean validated = false;
705                  boolean checkSize = false;
706 <                synchronized (e) {
707 <                    if (tabAt(tab, i) == e) {
706 >                try {
707 >                    if (tabAt(tab, i) == f) {
708                          validated = true;
709 <                        for (Node first = e;;) {
710 <                            if (e.hash == h &&
711 <                                ((ek = e.key) == k || k.equals(ek)) &&
712 <                                ((ev = e.val) != null)) {
713 <                                Object fv;
714 <                                if (replace && (fv = f.map(k)) != null)
715 <                                    ev = e.val = fv;
709 >                        for (Node e = f;;) {
710 >                            Object ek, ev, v;
711 >                            if ((e.hash & HASH_BITS) == h &&
712 >                                (ev = e.val) != null &&
713 >                                ((ek = e.key) == k || k.equals(ek))) {
714 >                                if (replace && (v = fn.map(k)) != null)
715 >                                    ev = e.val = v;
716                                  val = (V)ev;
717                                  break;
718                              }
719                              Node last = e;
720                              if ((e = e.next) == null) {
721 <                                if ((val = f.map(k)) != null) {
721 >                                if ((val = fn.map(k)) != null) {
722                                      last.next = new Node(h, k, val, null);
723                                      added = true;
724 <                                    if (last != first || tab.length <= 64)
724 >                                    if (last != f || tab.length <= 64)
725                                          checkSize = true;
726                                  }
727                                  break;
728                              }
729                          }
730                      }
731 +                } finally {
732 +                    if (!f.casHash(fh | LOCKED, fh)) {
733 +                        f.hash = fh;
734 +                        synchronized(f) { f.notifyAll(); };
735 +                    }
736                  }
737                  if (validated) {
738 +                    int sc;
739                      if (checkSize && tab.length < MAXIMUM_CAPACITY &&
740 <                        resizing == 0 && counter.sum() >= (long)threshold)
740 >                        (sc = sizeCtl) >= 0 && counter.sum() >= (long)sc)
741                          growTable();
742                      break;
743                  }
# Line 728 | Line 756 | public class ConcurrentHashMapV8<K, V>
756          int i = 0;
757          Node[] tab = table;
758          while (tab != null && i < tab.length) {
759 <            Node e = tabAt(tab, i);
760 <            if (e == null)
759 >            int fh;
760 >            Node f = tabAt(tab, i);
761 >            if (f == null)
762                  ++i;
763 <            else if (e.hash < 0)
764 <                tab = (Node[])e.key;
765 <            else {
763 >            else if ((fh = f.hash) == MOVED)
764 >                tab = (Node[])f.key;
765 >            else if ((fh & LOCKED) != 0)
766 >                f.tryAwaitLock(tab, i);
767 >            else if (f.casHash(fh, fh | LOCKED)) {
768                  boolean validated = false;
769 <                synchronized (e) {
770 <                    if (tabAt(tab, i) == e) {
769 >                try {
770 >                    if (tabAt(tab, i) == f) {
771                          validated = true;
772 <                        Node en;
742 <                        do {
743 <                            en = e.next;
772 >                        for (Node e = f; e != null; e = e.next) {
773                              if (e.val != null) { // currently always true
774                                  e.val = null;
775                                  --delta;
776                              }
777 <                        } while ((e = en) != null);
777 >                        }
778                          setTabAt(tab, i, null);
779                      }
780 +                } finally {
781 +                    if (!f.casHash(fh | LOCKED, fh)) {
782 +                        f.hash = fh;
783 +                        synchronized(f) { f.notifyAll(); };
784 +                    }
785                  }
786                  if (validated)
787                      ++i;
# Line 756 | Line 790 | public class ConcurrentHashMapV8<K, V>
790          counter.add(delta);
791      }
792  
793 +    /* ----------------Table Initialization and Resizing -------------- */
794 +
795 +    /**
796 +     * Returns a power of two table size for the given desired capacity.
797 +     * See Hackers Delight, sec 3.2
798 +     */
799 +    private static final int tableSizeFor(int c) {
800 +        int n = c - 1;
801 +        n |= n >>> 1;
802 +        n |= n >>> 2;
803 +        n |= n >>> 4;
804 +        n |= n >>> 8;
805 +        n |= n >>> 16;
806 +        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
807 +    }
808 +
809 +    /**
810 +     * Initializes table, using the size recorded in sizeCtl.
811 +     */
812 +    private final Node[] initTable() {
813 +        Node[] tab; int sc;
814 +        while ((tab = table) == null) {
815 +            if ((sc = sizeCtl) < 0)
816 +                Thread.yield(); // lost initialization race; just spin
817 +            else if (UNSAFE.compareAndSwapInt(this, sizeCtlOffset, sc, -1)) {
818 +                try {
819 +                    if ((tab = table) == null) {
820 +                        int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
821 +                        tab = table = new Node[n];
822 +                        sc = n - (n >>> 2) - 1;
823 +                    }
824 +                } finally {
825 +                    sizeCtl = sc;
826 +                }
827 +                break;
828 +            }
829 +        }
830 +        return tab;
831 +    }
832 +
833 +    /**
834 +     * If not already resizing, creates next table and transfers bins.
835 +     * Rechecks occupancy after a transfer to see if another resize is
836 +     * already needed because resizings are lagging additions.
837 +     */
838 +    private final void growTable() {
839 +        int sc = sizeCtl;
840 +        if (sc >= 0 && UNSAFE.compareAndSwapInt(this, sizeCtlOffset, sc, -1)) {
841 +            try {
842 +                Node[] tab; int n;
843 +                while ((tab = table) != null &&
844 +                       (n = tab.length) > 0 && n < MAXIMUM_CAPACITY &&
845 +                       counter.sum() >= (long)sc) {
846 +                    table = rebuild(tab);
847 +                    sc = (n << 1) - (n >>> 1) - 1;
848 +                }
849 +            } finally {
850 +                sizeCtl = sc;
851 +            }
852 +        }
853 +    }
854 +
855 +    /*
856 +     * Moves and/or copies the nodes in each bin to new table. See
857 +     * above for explanation.
858 +     *
859 +     * @return the new table
860 +     */
861 +    private static final Node[] rebuild(Node[] tab) {
862 +        int n = tab.length;
863 +        Node[] nextTab = new Node[n << 1];
864 +        Node fwd = new Node(MOVED, nextTab, null, null);
865 +        int[] buffer = null;       // holds bins to revisit; null until needed
866 +        Node rev = null;           // reverse forwarder; null until needed
867 +        int nbuffered = 0;         // the number of bins in buffer list
868 +        int bufferIndex = 0;       // buffer index of current buffered bin
869 +        int bin = n - 1;           // current non-buffered bin or -1 if none
870 +
871 +        for (int i = bin;;) {      // start upwards sweep
872 +            int fh; Node f;
873 +            if ((f = tabAt(tab, i)) == null) {
874 +                if (bin >= 0) {    // no lock needed (or available)
875 +                    if (!casTabAt(tab, i, f, fwd))
876 +                        continue;
877 +                }
878 +                else {             // transiently use a locked forwarding node
879 +                    Node g =  new Node(MOVED|LOCKED, nextTab, null, null);
880 +                    if (!casTabAt(tab, i, f, g))
881 +                        continue;
882 +                    setTabAt(nextTab, i, null);
883 +                    setTabAt(nextTab, i + n, null);
884 +                    setTabAt(tab, i, fwd);
885 +                    if (!g.casHash(MOVED|LOCKED, MOVED)) {
886 +                        g.hash = MOVED;
887 +                        synchronized(g) { g.notifyAll(); }
888 +                    }
889 +                }
890 +            }
891 +            else if (((fh = f.hash) & LOCKED) == 0 && f.casHash(fh, fh|LOCKED)) {
892 +                boolean validated = false;
893 +                try {              // split to lo and hi lists; copying as needed
894 +                    if (tabAt(tab, i) == f) {
895 +                        validated = true;
896 +                        Node e = f, lastRun = f;
897 +                        Node lo = null, hi = null;
898 +                        int runBit = e.hash & n;
899 +                        for (Node p = e.next; p != null; p = p.next) {
900 +                            int b = p.hash & n;
901 +                            if (b != runBit) {
902 +                                runBit = b;
903 +                                lastRun = p;
904 +                            }
905 +                        }
906 +                        if (runBit == 0)
907 +                            lo = lastRun;
908 +                        else
909 +                            hi = lastRun;
910 +                        for (Node p = e; p != lastRun; p = p.next) {
911 +                            int ph = p.hash & HASH_BITS;
912 +                            Object pk = p.key, pv = p.val;
913 +                            if ((ph & n) == 0)
914 +                                lo = new Node(ph, pk, pv, lo);
915 +                            else
916 +                                hi = new Node(ph, pk, pv, hi);
917 +                        }
918 +                        setTabAt(nextTab, i, lo);
919 +                        setTabAt(nextTab, i + n, hi);
920 +                        setTabAt(tab, i, fwd);
921 +                    }
922 +                } finally {
923 +                    if (!f.casHash(fh | LOCKED, fh)) {
924 +                        f.hash = fh;
925 +                        synchronized(f) { f.notifyAll(); };
926 +                    }
927 +                }
928 +                if (!validated)
929 +                    continue;
930 +            }
931 +            else {
932 +                if (buffer == null) // initialize buffer for revisits
933 +                    buffer = new int[TRANSFER_BUFFER_SIZE];
934 +                if (bin < 0 && bufferIndex > 0) {
935 +                    int j = buffer[--bufferIndex];
936 +                    buffer[bufferIndex] = i;
937 +                    i = j;         // swap with another bin
938 +                    continue;
939 +                }
940 +                if (bin < 0 || nbuffered >= TRANSFER_BUFFER_SIZE) {
941 +                    f.tryAwaitLock(tab, i);
942 +                    continue;      // no other options -- block
943 +                }
944 +                if (rev == null)   // initialize reverse-forwarder
945 +                    rev = new Node(MOVED, tab, null, null);
946 +                if (tabAt(tab, i) != f || (f.hash & LOCKED) == 0)
947 +                    continue;      // recheck before adding to list
948 +                buffer[nbuffered++] = i;
949 +                setTabAt(nextTab, i, rev);     // install place-holders
950 +                setTabAt(nextTab, i + n, rev);
951 +            }
952 +
953 +            if (bin > 0)
954 +                i = --bin;
955 +            else if (buffer != null && nbuffered > 0) {
956 +                bin = -1;
957 +                i = buffer[bufferIndex = --nbuffered];
958 +            }
959 +            else
960 +                return nextTab;
961 +        }
962 +    }
963 +
964      /* ----------------Table Traversal -------------- */
965  
966      /**
# Line 830 | Line 1035 | public class ConcurrentHashMapV8<K, V>
1035          final void advance() {
1036              Node e = last = next;
1037              outer: do {
1038 <                if (e != null)                   // pass used or skipped node
1038 >                if (e != null)                  // advance past used/skipped node
1039                      e = e.next;
1040 <                while (e == null) {              // get to next non-null bin
1041 <                    Node[] t; int b, i, n;       // checks must use locals
1040 >                while (e == null) {             // get to next non-null bin
1041 >                    Node[] t; int b, i, n;      // checks must use locals
1042                      if ((b = baseIndex) >= baseLimit || (i = index) < 0 ||
1043                          (t = tab) == null || i >= (n = t.length))
1044                          break outer;
1045 <                    else if ((e = tabAt(t, i)) != null && e.hash < 0)
1046 <                        tab = (Node[])e.key;     // restarts due to null val
1047 <                    else                         // visit upper slots if present
1045 >                    else if ((e = tabAt(t, i)) != null && e.hash == MOVED)
1046 >                        tab = (Node[])e.key;    // restarts due to null val
1047 >                    else                        // visit upper slots if present
1048                          index = (i += baseSize) < n ? i : (baseIndex = b + 1);
1049                  }
1050                  nextKey = e.key;
1051 <            } while ((nextVal = e.val) == null); // skip deleted or special nodes
1051 >            } while ((nextVal = e.val) == null);// skip deleted or special nodes
1052              next = e;
1053          }
1054      }
# Line 855 | Line 1060 | public class ConcurrentHashMapV8<K, V>
1060       */
1061      public ConcurrentHashMapV8() {
1062          this.counter = new LongAdder();
858        this.targetCapacity = DEFAULT_CAPACITY;
1063      }
1064  
1065      /**
# Line 875 | Line 1079 | public class ConcurrentHashMapV8<K, V>
1079                     MAXIMUM_CAPACITY :
1080                     tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
1081          this.counter = new LongAdder();
1082 <        this.targetCapacity = cap;
1082 >        this.sizeCtl = cap;
1083      }
1084  
1085      /**
# Line 885 | Line 1089 | public class ConcurrentHashMapV8<K, V>
1089       */
1090      public ConcurrentHashMapV8(Map<? extends K, ? extends V> m) {
1091          this.counter = new LongAdder();
1092 <        this.targetCapacity = DEFAULT_CAPACITY;
1092 >        this.sizeCtl = DEFAULT_CAPACITY;
1093          putAll(m);
1094      }
1095  
# Line 936 | Line 1140 | public class ConcurrentHashMapV8<K, V>
1140          int cap =  ((size >= (long)MAXIMUM_CAPACITY) ?
1141                      MAXIMUM_CAPACITY: tableSizeFor((int)size));
1142          this.counter = new LongAdder();
1143 <        this.targetCapacity = cap;
1143 >        this.sizeCtl = cap;
1144      }
1145  
1146      /**
# Line 956 | Line 1160 | public class ConcurrentHashMapV8<K, V>
1160                  (int)n);
1161      }
1162  
1163 +    final long longSize() { // accurate version of size needed for views
1164 +        long n = counter.sum();
1165 +        return (n < 0L) ? 0L : n;
1166 +    }
1167 +
1168      /**
1169       * Returns the value to which the specified key is mapped,
1170       * or {@code null} if this map contains no mapping for the key.
# Line 1076 | Line 1285 | public class ConcurrentHashMapV8<K, V>
1285          if (m == null)
1286              throw new NullPointerException();
1287          /*
1288 <         * If uninitialized, try to adjust targetCapacity to
1080 <         * accommodate the given number of elements.
1288 >         * If uninitialized, try to preallocate big enough table
1289           */
1290          if (table == null) {
1291              int size = m.size();
1292 <            int cap = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY :
1292 >            int n = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY :
1293                  tableSizeFor(size + (size >>> 1) + 1);
1294 <            if (cap > targetCapacity)
1295 <                targetCapacity = cap;
1294 >            int sc = sizeCtl;
1295 >            if (n < sc)
1296 >                n = sc;
1297 >            if (sc >= 0 &&
1298 >                UNSAFE.compareAndSwapInt(this, sizeCtlOffset, sc, -1)) {
1299 >                try {
1300 >                    if (table == null) {
1301 >                        table = new Node[n];
1302 >                        sc = n - (n >>> 2) - 1;
1303 >                    }
1304 >                } finally {
1305 >                    sizeCtl = sc;
1306 >                }
1307 >            }
1308 >        }
1309 >        for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
1310 >            Object ek = e.getKey(), ev = e.getValue();
1311 >            if (ek == null || ev == null)
1312 >                throw new NullPointerException();
1313 >            internalPut(ek, ev, true);
1314          }
1089        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
1090            put(e.getKey(), e.getValue());
1315      }
1316  
1317      /**
# Line 1462 | Line 1686 | public class ConcurrentHashMapV8<K, V>
1686              Object k = nextKey;
1687              Object v = nextVal;
1688              advance();
1689 <            return new WriteThroughEntry<K,V>(map, (K)k, (V)v);
1689 >            return new WriteThroughEntry<K,V>((K)k, (V)v, map);
1690 >        }
1691 >    }
1692 >
1693 >    static final class SnapshotEntryIterator<K,V> extends ViewIterator<K,V>
1694 >        implements Iterator<Map.Entry<K,V>> {
1695 >        SnapshotEntryIterator(ConcurrentHashMapV8<K, V> map) { super(map); }
1696 >
1697 >        @SuppressWarnings("unchecked")
1698 >        public final Map.Entry<K,V> next() {
1699 >            if (next == null)
1700 >                throw new NoSuchElementException();
1701 >            Object k = nextKey;
1702 >            Object v = nextVal;
1703 >            advance();
1704 >            return new SnapshotEntry<K,V>((K)k, (V)v);
1705          }
1706      }
1707  
1708      /**
1709 <     * Custom Entry class used by EntryIterator.next(), that relays
1471 <     * setValue changes to the underlying map.
1709 >     * Base of writeThrough and Snapshot entry classes
1710       */
1711 <    static final class WriteThroughEntry<K,V> implements Map.Entry<K, V> {
1474 <        final ConcurrentHashMapV8<K, V> map;
1711 >    static abstract class MapEntry<K,V> implements Map.Entry<K, V> {
1712          final K key; // non-null
1713          V val;       // non-null
1714 <        WriteThroughEntry(ConcurrentHashMapV8<K, V> map, K key, V val) {
1478 <            this.map = map; this.key = key; this.val = val;
1479 <        }
1480 <
1714 >        MapEntry(K key, V val)        { this.key = key; this.val = val; }
1715          public final K getKey()       { return key; }
1716          public final V getValue()     { return val; }
1717          public final int hashCode()   { return key.hashCode() ^ val.hashCode(); }
# Line 1492 | Line 1726 | public class ConcurrentHashMapV8<K, V>
1726                      (v == val || v.equals(val)));
1727          }
1728  
1729 +        public abstract V setValue(V value);
1730 +    }
1731 +
1732 +    /**
1733 +     * Entry used by EntryIterator.next(), that relays setValue
1734 +     * changes to the underlying map.
1735 +     */
1736 +    static final class WriteThroughEntry<K,V> extends MapEntry<K,V>
1737 +        implements Map.Entry<K, V> {
1738 +        final ConcurrentHashMapV8<K, V> map;
1739 +        WriteThroughEntry(K key, V val, ConcurrentHashMapV8<K, V> map) {
1740 +            super(key, val);
1741 +            this.map = map;
1742 +        }
1743 +
1744          /**
1745           * Sets our entry's value and writes through to the map. The
1746           * value to return is somewhat arbitrary here. Since a
# Line 1510 | Line 1759 | public class ConcurrentHashMapV8<K, V>
1759          }
1760      }
1761  
1762 +    /**
1763 +     * Internal version of entry, that doesn't write though changes
1764 +     */
1765 +    static final class SnapshotEntry<K,V> extends MapEntry<K,V>
1766 +        implements Map.Entry<K, V> {
1767 +        SnapshotEntry(K key, V val) { super(key, val); }
1768 +        public final V setValue(V value) { // only locally update
1769 +            if (value == null) throw new NullPointerException();
1770 +            V v = val;
1771 +            val = value;
1772 +            return v;
1773 +        }
1774 +    }
1775 +
1776      /* ----------------Views -------------- */
1777  
1778 <    /*
1779 <     * These currently just extend java.util.AbstractX classes, but
1780 <     * may need a new custom base to support partitioned traversal.
1778 >    /**
1779 >     * Base class for views. This is done mainly to allow adding
1780 >     * customized parallel traversals (not yet implemented.)
1781       */
1782 <
1520 <    static final class KeySet<K,V> extends AbstractSet<K> {
1782 >    static abstract class MapView<K, V> {
1783          final ConcurrentHashMapV8<K, V> map;
1784 <        KeySet(ConcurrentHashMapV8<K, V> map)   { this.map = map; }
1523 <
1784 >        MapView(ConcurrentHashMapV8<K, V> map)  { this.map = map; }
1785          public final int size()                 { return map.size(); }
1786          public final boolean isEmpty()          { return map.isEmpty(); }
1787          public final void clear()               { map.clear(); }
1788 +
1789 +        // implementations below rely on concrete classes supplying these
1790 +        abstract Iterator<?> iter();
1791 +        abstract public boolean contains(Object o);
1792 +        abstract public boolean remove(Object o);
1793 +
1794 +        private static final String oomeMsg = "Required array size too large";
1795 +
1796 +        public final Object[] toArray() {
1797 +            long sz = map.longSize();
1798 +            if (sz > (long)(MAX_ARRAY_SIZE))
1799 +                throw new OutOfMemoryError(oomeMsg);
1800 +            int n = (int)sz;
1801 +            Object[] r = new Object[n];
1802 +            int i = 0;
1803 +            Iterator<?> it = iter();
1804 +            while (it.hasNext()) {
1805 +                if (i == n) {
1806 +                    if (n >= MAX_ARRAY_SIZE)
1807 +                        throw new OutOfMemoryError(oomeMsg);
1808 +                    if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1)
1809 +                        n = MAX_ARRAY_SIZE;
1810 +                    else
1811 +                        n += (n >>> 1) + 1;
1812 +                    r = Arrays.copyOf(r, n);
1813 +                }
1814 +                r[i++] = it.next();
1815 +            }
1816 +            return (i == n) ? r : Arrays.copyOf(r, i);
1817 +        }
1818 +
1819 +        @SuppressWarnings("unchecked")
1820 +        public final <T> T[] toArray(T[] a) {
1821 +            long sz = map.longSize();
1822 +            if (sz > (long)(MAX_ARRAY_SIZE))
1823 +                throw new OutOfMemoryError(oomeMsg);
1824 +            int m = (int)sz;
1825 +            T[] r = (a.length >= m) ? a :
1826 +                (T[])java.lang.reflect.Array
1827 +                .newInstance(a.getClass().getComponentType(), m);
1828 +            int n = r.length;
1829 +            int i = 0;
1830 +            Iterator<?> it = iter();
1831 +            while (it.hasNext()) {
1832 +                if (i == n) {
1833 +                    if (n >= MAX_ARRAY_SIZE)
1834 +                        throw new OutOfMemoryError(oomeMsg);
1835 +                    if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1)
1836 +                        n = MAX_ARRAY_SIZE;
1837 +                    else
1838 +                        n += (n >>> 1) + 1;
1839 +                    r = Arrays.copyOf(r, n);
1840 +                }
1841 +                r[i++] = (T)it.next();
1842 +            }
1843 +            if (a == r && i < n) {
1844 +                r[i] = null; // null-terminate
1845 +                return r;
1846 +            }
1847 +            return (i == n) ? r : Arrays.copyOf(r, i);
1848 +        }
1849 +
1850 +        public final int hashCode() {
1851 +            int h = 0;
1852 +            for (Iterator<?> it = iter(); it.hasNext();)
1853 +                h += it.next().hashCode();
1854 +            return h;
1855 +        }
1856 +
1857 +        public final String toString() {
1858 +            StringBuilder sb = new StringBuilder();
1859 +            sb.append('[');
1860 +            Iterator<?> it = iter();
1861 +            if (it.hasNext()) {
1862 +                for (;;) {
1863 +                    Object e = it.next();
1864 +                    sb.append(e == this ? "(this Collection)" : e);
1865 +                    if (!it.hasNext())
1866 +                        break;
1867 +                    sb.append(',').append(' ');
1868 +                }
1869 +            }
1870 +            return sb.append(']').toString();
1871 +        }
1872 +
1873 +        public final boolean containsAll(Collection<?> c) {
1874 +            if (c != this) {
1875 +                for (Iterator<?> it = c.iterator(); it.hasNext();) {
1876 +                    Object e = it.next();
1877 +                    if (e == null || !contains(e))
1878 +                        return false;
1879 +                }
1880 +            }
1881 +            return true;
1882 +        }
1883 +
1884 +        public final boolean removeAll(Collection c) {
1885 +            boolean modified = false;
1886 +            for (Iterator<?> it = iter(); it.hasNext();) {
1887 +                if (c.contains(it.next())) {
1888 +                    it.remove();
1889 +                    modified = true;
1890 +                }
1891 +            }
1892 +            return modified;
1893 +        }
1894 +
1895 +        public final boolean retainAll(Collection<?> c) {
1896 +            boolean modified = false;
1897 +            for (Iterator<?> it = iter(); it.hasNext();) {
1898 +                if (!c.contains(it.next())) {
1899 +                    it.remove();
1900 +                    modified = true;
1901 +                }
1902 +            }
1903 +            return modified;
1904 +        }
1905 +
1906 +    }
1907 +
1908 +    static final class KeySet<K,V> extends MapView<K,V> implements Set<K> {
1909 +        KeySet(ConcurrentHashMapV8<K, V> map)   { super(map); }
1910          public final boolean contains(Object o) { return map.containsKey(o); }
1911          public final boolean remove(Object o)   { return map.remove(o) != null; }
1912 +
1913          public final Iterator<K> iterator() {
1914              return new KeyIterator<K,V>(map);
1915          }
1916 +        final Iterator<?> iter() {
1917 +            return new KeyIterator<K,V>(map);
1918 +        }
1919 +        public final boolean add(K e) {
1920 +            throw new UnsupportedOperationException();
1921 +        }
1922 +        public final boolean addAll(Collection<? extends K> c) {
1923 +            throw new UnsupportedOperationException();
1924 +        }
1925 +        public boolean equals(Object o) {
1926 +            Set<?> c;
1927 +            return ((o instanceof Set) &&
1928 +                    ((c = (Set<?>)o) == this ||
1929 +                     (containsAll(c) && c.containsAll(this))));
1930 +        }
1931      }
1932  
1933 <    static final class Values<K,V> extends AbstractCollection<V> {
1934 <        final ConcurrentHashMapV8<K, V> map;
1935 <        Values(ConcurrentHashMapV8<K, V> map)   { this.map = map; }
1537 <
1538 <        public final int size()                 { return map.size(); }
1539 <        public final boolean isEmpty()          { return map.isEmpty(); }
1540 <        public final void clear()               { map.clear(); }
1933 >    static final class Values<K,V> extends MapView<K,V>
1934 >        implements Collection<V>  {
1935 >        Values(ConcurrentHashMapV8<K, V> map)   { super(map); }
1936          public final boolean contains(Object o) { return map.containsValue(o); }
1937 +
1938 +        public final boolean remove(Object o) {
1939 +            if (o != null) {
1940 +                Iterator<V> it = new ValueIterator<K,V>(map);
1941 +                while (it.hasNext()) {
1942 +                    if (o.equals(it.next())) {
1943 +                        it.remove();
1944 +                        return true;
1945 +                    }
1946 +                }
1947 +            }
1948 +            return false;
1949 +        }
1950          public final Iterator<V> iterator() {
1951              return new ValueIterator<K,V>(map);
1952          }
1953 +        final Iterator<?> iter() {
1954 +            return new ValueIterator<K,V>(map);
1955 +        }
1956 +        public final boolean add(V e) {
1957 +            throw new UnsupportedOperationException();
1958 +        }
1959 +        public final boolean addAll(Collection<? extends V> c) {
1960 +            throw new UnsupportedOperationException();
1961 +        }
1962      }
1963  
1964 <    static final class EntrySet<K,V> extends AbstractSet<Map.Entry<K,V>> {
1965 <        final ConcurrentHashMapV8<K, V> map;
1966 <        EntrySet(ConcurrentHashMapV8<K, V> map) { this.map = map; }
1550 <
1551 <        public final int size()                 { return map.size(); }
1552 <        public final boolean isEmpty()          { return map.isEmpty(); }
1553 <        public final void clear()               { map.clear(); }
1554 <        public final Iterator<Map.Entry<K,V>> iterator() {
1555 <            return new EntryIterator<K,V>(map);
1556 <        }
1964 >    static final class EntrySet<K,V>  extends MapView<K,V>
1965 >        implements Set<Map.Entry<K,V>> {
1966 >        EntrySet(ConcurrentHashMapV8<K, V> map) { super(map); }
1967  
1968          public final boolean contains(Object o) {
1969              Object k, v, r; Map.Entry<?,?> e;
# Line 1571 | Line 1981 | public class ConcurrentHashMapV8<K, V>
1981                      (v = e.getValue()) != null &&
1982                      map.remove(k, v));
1983          }
1984 +
1985 +        public final Iterator<Map.Entry<K,V>> iterator() {
1986 +            return new EntryIterator<K,V>(map);
1987 +        }
1988 +        final Iterator<?> iter() {
1989 +            return new SnapshotEntryIterator<K,V>(map);
1990 +        }
1991 +        public final boolean add(Entry<K,V> e) {
1992 +            throw new UnsupportedOperationException();
1993 +        }
1994 +        public final boolean addAll(Collection<? extends Entry<K,V>> c) {
1995 +            throw new UnsupportedOperationException();
1996 +        }
1997 +        public boolean equals(Object o) {
1998 +            Set<?> c;
1999 +            return ((o instanceof Set) &&
2000 +                    ((c = (Set<?>)o) == this ||
2001 +                     (containsAll(c) && c.containsAll(this))));
2002 +        }
2003      }
2004  
2005      /* ---------------- Serialization Support -------------- */
# Line 1626 | Line 2055 | public class ConcurrentHashMapV8<K, V>
2055          this.segments = null; // unneeded
2056          // initialize transient final field
2057          UNSAFE.putObjectVolatile(this, counterOffset, new LongAdder());
1629        this.targetCapacity = DEFAULT_CAPACITY;
2058  
2059          // Create all nodes, then place in table once size is known
2060          long size = 0L;
# Line 1643 | Line 2071 | public class ConcurrentHashMapV8<K, V>
2071          }
2072          if (p != null) {
2073              boolean init = false;
2074 <            if (resizing == 0 &&
2075 <                UNSAFE.compareAndSwapInt(this, resizingOffset, 0, 1)) {
2074 >            int n;
2075 >            if (size >= (long)(MAXIMUM_CAPACITY >>> 1))
2076 >                n = MAXIMUM_CAPACITY;
2077 >            else {
2078 >                int sz = (int)size;
2079 >                n = tableSizeFor(sz + (sz >>> 1) + 1);
2080 >            }
2081 >            int sc = sizeCtl;
2082 >            if (n > sc &&
2083 >                UNSAFE.compareAndSwapInt(this, sizeCtlOffset, sc, -1)) {
2084                  try {
2085                      if (table == null) {
2086                          init = true;
1651                        int n;
1652                        if (size >= (long)(MAXIMUM_CAPACITY >>> 1))
1653                            n = MAXIMUM_CAPACITY;
1654                        else {
1655                            int sz = (int)size;
1656                            n = tableSizeFor(sz + (sz >>> 1) + 1);
1657                        }
1658                        threshold = n - (n >>> 2) - THRESHOLD_OFFSET;
2087                          Node[] tab = new Node[n];
2088                          int mask = n - 1;
2089                          while (p != null) {
# Line 1667 | Line 2095 | public class ConcurrentHashMapV8<K, V>
2095                          }
2096                          table = tab;
2097                          counter.add(size);
2098 +                        sc = n - (n >>> 2) - 1;
2099                      }
2100                  } finally {
2101 <                    resizing = 0;
2101 >                    sizeCtl = sc;
2102                  }
2103              }
2104              if (!init) { // Can only happen if unsafely published.
# Line 1684 | Line 2113 | public class ConcurrentHashMapV8<K, V>
2113      // Unsafe mechanics
2114      private static final sun.misc.Unsafe UNSAFE;
2115      private static final long counterOffset;
2116 <    private static final long resizingOffset;
2116 >    private static final long sizeCtlOffset;
2117      private static final long ABASE;
2118      private static final int ASHIFT;
2119  
# Line 1695 | Line 2124 | public class ConcurrentHashMapV8<K, V>
2124              Class<?> k = ConcurrentHashMapV8.class;
2125              counterOffset = UNSAFE.objectFieldOffset
2126                  (k.getDeclaredField("counter"));
2127 <            resizingOffset = UNSAFE.objectFieldOffset
2128 <                (k.getDeclaredField("resizing"));
2127 >            sizeCtlOffset = UNSAFE.objectFieldOffset
2128 >                (k.getDeclaredField("sizeCtl"));
2129              Class<?> sc = Node[].class;
2130              ABASE = UNSAFE.arrayBaseOffset(sc);
2131              ss = UNSAFE.arrayIndexScale(sc);

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines