Mercurial > hg > release > icedtea6-1.12
changeset 2979:7dc0363b60c8
Add backport of 7036559 and ConcurrentHashMap deserialization reliability fix for 8009063.
author | Jon VanAlten <jon.vanalten@redhat.com> |
---|---|
date | Thu, 11 Apr 2013 16:25:24 -0400 |
parents | 623621d29d04 |
children | 43434eedda7d |
files | Makefile.am patches/security/20130416/7036559.patch patches/security/20130416/8009063.patch |
diffstat | 3 files changed, 1506 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile.am Fri Apr 05 09:01:48 2013 -0400 +++ b/Makefile.am Thu Apr 11 16:25:24 2013 -0400 @@ -279,7 +279,9 @@ patches/security/20130219/8006777.patch \ patches/security/20130219/8007688.patch \ patches/security/20130304/8007014.patch \ - patches/security/20130304/8007675.patch + patches/security/20130304/8007675.patch \ + patches/security/20130416/7036559.patch \ + patches/security/20130416/8009063.patch SPECIAL_SECURITY_PATCH = patches/security/20120214/7112642.patch
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/security/20130416/7036559.patch Thu Apr 11 16:25:24 2013 -0400 @@ -0,0 +1,1436 @@ +# HG changeset patch +# User dl +# Date 1303139440 -3600 +# Node ID 005c0c85b0decf18a90ff6c9601d1b9a2c0a3fa4 +# Parent 603e70836e74e5c18fc32279f7e4df5b4c63e0b6 +7036559: ConcurrentHashMap footprint and contention improvements +Reviewed-by: chegar + +diff --git a/src/share/classes/java/util/concurrent/ConcurrentHashMap.java b/src/share/classes/java/util/concurrent/ConcurrentHashMap.java +--- openjdk/jdk/src/share/classes/java/util/concurrent/ConcurrentHashMap.java ++++ openjdk/jdk/src/share/classes/java/util/concurrent/ConcurrentHashMap.java +@@ -105,7 +105,25 @@ + + /* + * The basic strategy is to subdivide the table among Segments, +- * each of which itself is a concurrently readable hash table. ++ * each of which itself is a concurrently readable hash table. To ++ * reduce footprint, all but one segments are constructed only ++ * when first needed (see ensureSegment). To maintain visibility ++ * in the presence of lazy construction, accesses to segments as ++ * well as elements of segment's table must use volatile access, ++ * which is done via Unsafe within methods segmentAt etc ++ * below. These provide the functionality of AtomicReferenceArrays ++ * but reduce the levels of indirection. Additionally, ++ * volatile-writes of table elements and entry "next" fields ++ * within locked operations use the cheaper "lazySet" forms of ++ * writes (via putOrderedObject) because these writes are always ++ * followed by lock releases that maintain sequential consistency ++ * of table updates. ++ * ++ * Historical note: The previous version of this class relied ++ * heavily on "final" fields, which avoided some volatile reads at ++ * the expense of a large initial footprint. Some remnants of ++ * that design (including forced construction of segment 0) exist ++ * to ensure serialization compatibility. + */ + + /* ---------------- Constants -------------- */ +@@ -137,8 +155,15 @@ + static final int MAXIMUM_CAPACITY = 1 << 30; + + /** ++ * The minimum capacity for per-segment tables. Must be a power ++ * of two, at least two to avoid immediate resizing on next use ++ * after lazy construction. ++ */ ++ static final int MIN_SEGMENT_TABLE_CAPACITY = 2; ++ ++ /** + * The maximum number of segments to allow; used to bound +- * constructor arguments. ++ * constructor arguments. Must be power of two less than 1 << 24. + */ + static final int MAX_SEGMENTS = 1 << 16; // slightly conservative + +@@ -164,7 +189,7 @@ + final int segmentShift; + + /** +- * The segments, each of which is a specialized hash table ++ * The segments, each of which is a specialized hash table. + */ + final Segment<K,V>[] segments; + +@@ -172,7 +197,65 @@ + transient Set<Map.Entry<K,V>> entrySet; + transient Collection<V> values; + +- /* ---------------- Small Utilities -------------- */ ++ /** ++ * ConcurrentHashMap list entry. Note that this is never exported ++ * out as a user-visible Map.Entry. ++ */ ++ static final class HashEntry<K,V> { ++ final int hash; ++ final K key; ++ volatile V value; ++ volatile HashEntry<K,V> next; ++ ++ HashEntry(int hash, K key, V value, HashEntry<K,V> next) { ++ this.hash = hash; ++ this.key = key; ++ this.value = value; ++ this.next = next; ++ } ++ ++ /** ++ * Sets next field with volatile write semantics. (See above ++ * about use of putOrderedObject.) ++ */ ++ final void setNext(HashEntry<K,V> n) { ++ UNSAFE.putOrderedObject(this, nextOffset, n); ++ } ++ ++ // Unsafe mechanics ++ static final sun.misc.Unsafe UNSAFE; ++ static final long nextOffset; ++ static { ++ try { ++ UNSAFE = sun.misc.Unsafe.getUnsafe(); ++ Class k = HashEntry.class; ++ nextOffset = UNSAFE.objectFieldOffset ++ (k.getDeclaredField("next")); ++ } catch (Exception e) { ++ throw new Error(e); ++ } ++ } ++ } ++ ++ /** ++ * Gets the ith element of given table (if nonnull) with volatile ++ * read semantics. ++ */ ++ @SuppressWarnings("unchecked") ++ static final <K,V> HashEntry<K,V> entryAt(HashEntry<K,V>[] tab, int i) { ++ return (tab == null) ? null : ++ (HashEntry<K,V>) UNSAFE.getObjectVolatile ++ (tab, ((long)i << TSHIFT) + TBASE); ++ } ++ ++ /** ++ * Sets the ith element of given table, with volatile write ++ * semantics. (See above about use of putOrderedObject.) ++ */ ++ static final <K,V> void setEntryAt(HashEntry<K,V>[] tab, int i, ++ HashEntry<K,V> e) { ++ UNSAFE.putOrderedObject(tab, ((long)i << TSHIFT) + TBASE, e); ++ } + + /** + * Applies a supplemental hash function to a given hashCode, which +@@ -193,104 +276,67 @@ + } + + /** +- * Returns the segment that should be used for key with given hash +- * @param hash the hash code for the key +- * @return the segment +- */ +- final Segment<K,V> segmentFor(int hash) { +- return segments[(hash >>> segmentShift) & segmentMask]; +- } +- +- /* ---------------- Inner Classes -------------- */ +- +- /** +- * ConcurrentHashMap list entry. Note that this is never exported +- * out as a user-visible Map.Entry. +- * +- * Because the value field is volatile, not final, it is legal wrt +- * the Java Memory Model for an unsynchronized reader to see null +- * instead of initial value when read via a data race. Although a +- * reordering leading to this is not likely to ever actually +- * occur, the Segment.readValueUnderLock method is used as a +- * backup in case a null (pre-initialized) value is ever seen in +- * an unsynchronized access method. +- */ +- static final class HashEntry<K,V> { +- final K key; +- final int hash; +- volatile V value; +- final HashEntry<K,V> next; +- +- HashEntry(K key, int hash, HashEntry<K,V> next, V value) { +- this.key = key; +- this.hash = hash; +- this.next = next; +- this.value = value; +- } +- +- @SuppressWarnings("unchecked") +- static final <K,V> HashEntry<K,V>[] newArray(int i) { +- return new HashEntry[i]; +- } +- } +- +- /** + * Segments are specialized versions of hash tables. This + * subclasses from ReentrantLock opportunistically, just to + * simplify some locking and avoid separate construction. + */ + static final class Segment<K,V> extends ReentrantLock implements Serializable { + /* +- * Segments maintain a table of entry lists that are ALWAYS +- * kept in a consistent state, so can be read without locking. +- * Next fields of nodes are immutable (final). All list +- * additions are performed at the front of each bin. This +- * makes it easy to check changes, and also fast to traverse. +- * When nodes would otherwise be changed, new nodes are +- * created to replace them. This works well for hash tables +- * since the bin lists tend to be short. (The average length +- * is less than two for the default load factor threshold.) ++ * Segments maintain a table of entry lists that are always ++ * kept in a consistent state, so can be read (via volatile ++ * reads of segments and tables) without locking. This ++ * requires replicating nodes when necessary during table ++ * resizing, so the old lists can be traversed by readers ++ * still using old version of table. + * +- * Read operations can thus proceed without locking, but rely +- * on selected uses of volatiles to ensure that completed +- * write operations performed by other threads are +- * noticed. For most purposes, the "count" field, tracking the +- * number of elements, serves as that volatile variable +- * ensuring visibility. This is convenient because this field +- * needs to be read in many read operations anyway: +- * +- * - All (unsynchronized) read operations must first read the +- * "count" field, and should not look at table entries if +- * it is 0. +- * +- * - All (synchronized) write operations should write to +- * the "count" field after structurally changing any bin. +- * The operations must not take any action that could even +- * momentarily cause a concurrent read operation to see +- * inconsistent data. This is made easier by the nature of +- * the read operations in Map. For example, no operation +- * can reveal that the table has grown but the threshold +- * has not yet been updated, so there are no atomicity +- * requirements for this with respect to reads. +- * +- * As a guide, all critical volatile reads and writes to the +- * count field are marked in code comments. ++ * This class defines only mutative methods requiring locking. ++ * Except as noted, the methods of this class perform the ++ * per-segment versions of ConcurrentHashMap methods. (Other ++ * methods are integrated directly into ConcurrentHashMap ++ * methods.) These mutative methods use a form of controlled ++ * spinning on contention via methods scanAndLock and ++ * scanAndLockForPut. These intersperse tryLocks with ++ * traversals to locate nodes. The main benefit is to absorb ++ * cache misses (which are very common for hash tables) while ++ * obtaining locks so that traversal is faster once ++ * acquired. We do not actually use the found nodes since they ++ * must be re-acquired under lock anyway to ensure sequential ++ * consistency of updates (and in any case may be undetectably ++ * stale), but they will normally be much faster to re-locate. ++ * Also, scanAndLockForPut speculatively creates a fresh node ++ * to use in put if no node is found. + */ + + private static final long serialVersionUID = 2249069246763182397L; + + /** +- * The number of elements in this segment's region. ++ * The maximum number of times to tryLock in a prescan before ++ * possibly blocking on acquire in preparation for a locked ++ * segment operation. On multiprocessors, using a bounded ++ * number of retries maintains cache acquired while locating ++ * nodes. + */ +- transient volatile int count; ++ static final int MAX_SCAN_RETRIES = ++ Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1; + + /** +- * Number of updates that alter the size of the table. This is +- * used during bulk-read methods to make sure they see a +- * consistent snapshot: If modCounts change during a traversal +- * of segments computing size or checking containsValue, then +- * we might have an inconsistent view of state so (usually) +- * must retry. ++ * The per-segment table. Elements are accessed via ++ * entryAt/setEntryAt providing volatile semantics. ++ */ ++ transient volatile HashEntry<K,V>[] table; ++ ++ /** ++ * The number of elements. Accessed only either within locks ++ * or among other volatile reads that maintain visibility. ++ */ ++ transient int count; ++ ++ /** ++ * The total number of mutative operations in this segment. ++ * Even though this may overflows 32 bits, it provides ++ * sufficient accuracy for stability checks in CHM isEmpty() ++ * and size() methods. Accessed only either within locks or ++ * among other volatile reads that maintain visibility. + */ + transient int modCount; + +@@ -302,11 +348,6 @@ + transient int threshold; + + /** +- * The per-segment table. +- */ +- transient volatile HashEntry<K,V>[] table; +- +- /** + * The load factor for the hash table. Even though this value + * is same for all segments, it is replicated to avoid needing + * links to outer object. +@@ -314,202 +355,94 @@ + */ + final float loadFactor; + +- Segment(int initialCapacity, float lf) { +- loadFactor = lf; +- setTable(HashEntry.<K,V>newArray(initialCapacity)); ++ Segment(float lf, int threshold, HashEntry<K,V>[] tab) { ++ this.loadFactor = lf; ++ this.threshold = threshold; ++ this.table = tab; + } + +- @SuppressWarnings("unchecked") +- static final <K,V> Segment<K,V>[] newArray(int i) { +- return new Segment[i]; ++ final V put(K key, int hash, V value, boolean onlyIfAbsent) { ++ HashEntry<K,V> node = tryLock() ? null : ++ scanAndLockForPut(key, hash, value); ++ V oldValue; ++ try { ++ HashEntry<K,V>[] tab = table; ++ int index = (tab.length - 1) & hash; ++ HashEntry<K,V> first = entryAt(tab, index); ++ for (HashEntry<K,V> e = first;;) { ++ if (e != null) { ++ K k; ++ if ((k = e.key) == key || ++ (e.hash == hash && key.equals(k))) { ++ oldValue = e.value; ++ if (!onlyIfAbsent) { ++ e.value = value; ++ ++modCount; ++ } ++ break; ++ } ++ e = e.next; ++ } ++ else { ++ if (node != null) ++ node.setNext(first); ++ else ++ node = new HashEntry<K,V>(hash, key, value, first); ++ int c = count + 1; ++ if (c > threshold && first != null && ++ tab.length < MAXIMUM_CAPACITY) ++ rehash(node); ++ else ++ setEntryAt(tab, index, node); ++ ++modCount; ++ count = c; ++ oldValue = null; ++ break; ++ } ++ } ++ } finally { ++ unlock(); ++ } ++ return oldValue; + } + + /** +- * Sets table to new HashEntry array. +- * Call only while holding lock or in constructor. ++ * Doubles size of table and repacks entries, also adding the ++ * given node to new table + */ +- void setTable(HashEntry<K,V>[] newTable) { +- threshold = (int)(newTable.length * loadFactor); +- table = newTable; +- } +- +- /** +- * Returns properly casted first entry of bin for given hash. +- */ +- HashEntry<K,V> getFirst(int hash) { +- HashEntry<K,V>[] tab = table; +- return tab[hash & (tab.length - 1)]; +- } +- +- /** +- * Reads value field of an entry under lock. Called if value +- * field ever appears to be null. This is possible only if a +- * compiler happens to reorder a HashEntry initialization with +- * its table assignment, which is legal under memory model +- * but is not known to ever occur. +- */ +- V readValueUnderLock(HashEntry<K,V> e) { +- lock(); +- try { +- return e.value; +- } finally { +- unlock(); +- } +- } +- +- /* Specialized implementations of map methods */ +- +- V get(Object key, int hash) { +- if (count != 0) { // read-volatile +- HashEntry<K,V> e = getFirst(hash); +- while (e != null) { +- if (e.hash == hash && key.equals(e.key)) { +- V v = e.value; +- if (v != null) +- return v; +- return readValueUnderLock(e); // recheck +- } +- e = e.next; +- } +- } +- return null; +- } +- +- boolean containsKey(Object key, int hash) { +- if (count != 0) { // read-volatile +- HashEntry<K,V> e = getFirst(hash); +- while (e != null) { +- if (e.hash == hash && key.equals(e.key)) +- return true; +- e = e.next; +- } +- } +- return false; +- } +- +- boolean containsValue(Object value) { +- if (count != 0) { // read-volatile +- HashEntry<K,V>[] tab = table; +- int len = tab.length; +- for (int i = 0 ; i < len; i++) { +- for (HashEntry<K,V> e = tab[i]; e != null; e = e.next) { +- V v = e.value; +- if (v == null) // recheck +- v = readValueUnderLock(e); +- if (value.equals(v)) +- return true; +- } +- } +- } +- return false; +- } +- +- boolean replace(K key, int hash, V oldValue, V newValue) { +- lock(); +- try { +- HashEntry<K,V> e = getFirst(hash); +- while (e != null && (e.hash != hash || !key.equals(e.key))) +- e = e.next; +- +- boolean replaced = false; +- if (e != null && oldValue.equals(e.value)) { +- replaced = true; +- e.value = newValue; +- } +- return replaced; +- } finally { +- unlock(); +- } +- } +- +- V replace(K key, int hash, V newValue) { +- lock(); +- try { +- HashEntry<K,V> e = getFirst(hash); +- while (e != null && (e.hash != hash || !key.equals(e.key))) +- e = e.next; +- +- V oldValue = null; +- if (e != null) { +- oldValue = e.value; +- e.value = newValue; +- } +- return oldValue; +- } finally { +- unlock(); +- } +- } +- +- +- V put(K key, int hash, V value, boolean onlyIfAbsent) { +- lock(); +- try { +- int c = count; +- if (c++ > threshold) // ensure capacity +- rehash(); +- HashEntry<K,V>[] tab = table; +- int index = hash & (tab.length - 1); +- HashEntry<K,V> first = tab[index]; +- HashEntry<K,V> e = first; +- while (e != null && (e.hash != hash || !key.equals(e.key))) +- e = e.next; +- +- V oldValue; +- if (e != null) { +- oldValue = e.value; +- if (!onlyIfAbsent) +- e.value = value; +- } +- else { +- oldValue = null; +- ++modCount; +- tab[index] = new HashEntry<K,V>(key, hash, first, value); +- count = c; // write-volatile +- } +- return oldValue; +- } finally { +- unlock(); +- } +- } +- +- void rehash() { ++ @SuppressWarnings("unchecked") ++ private void rehash(HashEntry<K,V> node) { ++ /* ++ * Reclassify nodes in each list to new table. Because we ++ * are using power-of-two expansion, the elements from ++ * each bin must either stay at same index, or move with a ++ * power of two offset. We eliminate unnecessary node ++ * creation by catching cases where old nodes can be ++ * reused because their next fields won't change. ++ * Statistically, at the default threshold, only about ++ * one-sixth of them need cloning when a table ++ * doubles. The nodes they replace will be garbage ++ * collectable as soon as they are no longer referenced by ++ * any reader thread that may be in the midst of ++ * concurrently traversing table. Entry accesses use plain ++ * array indexing because they are followed by volatile ++ * table write. ++ */ + HashEntry<K,V>[] oldTable = table; + int oldCapacity = oldTable.length; +- if (oldCapacity >= MAXIMUM_CAPACITY) +- return; +- +- /* +- * Reclassify nodes in each list to new Map. Because we are +- * using power-of-two expansion, the elements from each bin +- * must either stay at same index, or move with a power of two +- * offset. We eliminate unnecessary node creation by catching +- * cases where old nodes can be reused because their next +- * fields won't change. Statistically, at the default +- * threshold, only about one-sixth of them need cloning when +- * a table doubles. The nodes they replace will be garbage +- * collectable as soon as they are no longer referenced by any +- * reader thread that may be in the midst of traversing table +- * right now. +- */ +- +- HashEntry<K,V>[] newTable = HashEntry.newArray(oldCapacity<<1); +- threshold = (int)(newTable.length * loadFactor); +- int sizeMask = newTable.length - 1; ++ int newCapacity = oldCapacity << 1; ++ threshold = (int)(newCapacity * loadFactor); ++ HashEntry<K,V>[] newTable = ++ (HashEntry<K,V>[]) new HashEntry[newCapacity]; ++ int sizeMask = newCapacity - 1; + for (int i = 0; i < oldCapacity ; i++) { +- // We need to guarantee that any existing reads of old Map can +- // proceed. So we cannot yet null out each bin. + HashEntry<K,V> e = oldTable[i]; +- + if (e != null) { + HashEntry<K,V> next = e.next; + int idx = e.hash & sizeMask; +- +- // Single node on list +- if (next == null) ++ if (next == null) // Single node on list + newTable[idx] = e; +- +- else { +- // Reuse trailing consecutive sequence at same slot ++ else { // Reuse consecutive sequence at same slot + HashEntry<K,V> lastRun = e; + int lastIdx = idx; + for (HashEntry<K,V> last = next; +@@ -522,74 +455,259 @@ + } + } + newTable[lastIdx] = lastRun; +- +- // Clone all remaining nodes ++ // Clone remaining nodes + for (HashEntry<K,V> p = e; p != lastRun; p = p.next) { +- int k = p.hash & sizeMask; ++ V v = p.value; ++ int h = p.hash; ++ int k = h & sizeMask; + HashEntry<K,V> n = newTable[k]; +- newTable[k] = new HashEntry<K,V>(p.key, p.hash, +- n, p.value); ++ newTable[k] = new HashEntry<K,V>(h, p.key, v, n); + } + } + } + } ++ int nodeIndex = node.hash & sizeMask; // add the new node ++ node.setNext(newTable[nodeIndex]); ++ newTable[nodeIndex] = node; + table = newTable; + } + + /** ++ * Scans for a node containing given key while trying to ++ * acquire lock, creating and returning one if not found. Upon ++ * return, guarantees that lock is held. UNlike in most ++ * methods, calls to method equals are not screened: Since ++ * traversal speed doesn't matter, we might as well help warm ++ * up the associated code and accesses as well. ++ * ++ * @return a new node if key not found, else null ++ */ ++ private HashEntry<K,V> scanAndLockForPut(K key, int hash, V value) { ++ HashEntry<K,V> first = entryForHash(this, hash); ++ HashEntry<K,V> e = first; ++ HashEntry<K,V> node = null; ++ int retries = -1; // negative while locating node ++ while (!tryLock()) { ++ HashEntry<K,V> f; // to recheck first below ++ if (retries < 0) { ++ if (e == null) { ++ if (node == null) // speculatively create node ++ node = new HashEntry<K,V>(hash, key, value, null); ++ retries = 0; ++ } ++ else if (key.equals(e.key)) ++ retries = 0; ++ else ++ e = e.next; ++ } ++ else if (++retries > MAX_SCAN_RETRIES) { ++ lock(); ++ break; ++ } ++ else if ((retries & 1) == 0 && ++ (f = entryForHash(this, hash)) != first) { ++ e = first = f; // re-traverse if entry changed ++ retries = -1; ++ } ++ } ++ return node; ++ } ++ ++ /** ++ * Scans for a node containing the given key while trying to ++ * acquire lock for a remove or replace operation. Upon ++ * return, guarantees that lock is held. Note that we must ++ * lock even if the key is not found, to ensure sequential ++ * consistency of updates. ++ */ ++ private void scanAndLock(Object key, int hash) { ++ // similar to but simpler than scanAndLockForPut ++ HashEntry<K,V> first = entryForHash(this, hash); ++ HashEntry<K,V> e = first; ++ int retries = -1; ++ while (!tryLock()) { ++ HashEntry<K,V> f; ++ if (retries < 0) { ++ if (e == null || key.equals(e.key)) ++ retries = 0; ++ else ++ e = e.next; ++ } ++ else if (++retries > MAX_SCAN_RETRIES) { ++ lock(); ++ break; ++ } ++ else if ((retries & 1) == 0 && ++ (f = entryForHash(this, hash)) != first) { ++ e = first = f; ++ retries = -1; ++ } ++ } ++ } ++ ++ /** + * Remove; match on key only if value null, else match both. + */ +- V remove(Object key, int hash, Object value) { ++ final V remove(Object key, int hash, Object value) { ++ if (!tryLock()) ++ scanAndLock(key, hash); ++ V oldValue = null; ++ try { ++ HashEntry<K,V>[] tab = table; ++ int index = (tab.length - 1) & hash; ++ HashEntry<K,V> e = entryAt(tab, index); ++ HashEntry<K,V> pred = null; ++ while (e != null) { ++ K k; ++ HashEntry<K,V> next = e.next; ++ if ((k = e.key) == key || ++ (e.hash == hash && key.equals(k))) { ++ V v = e.value; ++ if (value == null || value == v || value.equals(v)) { ++ if (pred == null) ++ setEntryAt(tab, index, next); ++ else ++ pred.setNext(next); ++ ++modCount; ++ --count; ++ oldValue = v; ++ } ++ break; ++ } ++ pred = e; ++ e = next; ++ } ++ } finally { ++ unlock(); ++ } ++ return oldValue; ++ } ++ ++ final boolean replace(K key, int hash, V oldValue, V newValue) { ++ if (!tryLock()) ++ scanAndLock(key, hash); ++ boolean replaced = false; ++ try { ++ HashEntry<K,V> e; ++ for (e = entryForHash(this, hash); e != null; e = e.next) { ++ K k; ++ if ((k = e.key) == key || ++ (e.hash == hash && key.equals(k))) { ++ if (oldValue.equals(e.value)) { ++ e.value = newValue; ++ ++modCount; ++ replaced = true; ++ } ++ break; ++ } ++ } ++ } finally { ++ unlock(); ++ } ++ return replaced; ++ } ++ ++ final V replace(K key, int hash, V value) { ++ if (!tryLock()) ++ scanAndLock(key, hash); ++ V oldValue = null; ++ try { ++ HashEntry<K,V> e; ++ for (e = entryForHash(this, hash); e != null; e = e.next) { ++ K k; ++ if ((k = e.key) == key || ++ (e.hash == hash && key.equals(k))) { ++ oldValue = e.value; ++ e.value = value; ++ ++modCount; ++ break; ++ } ++ } ++ } finally { ++ unlock(); ++ } ++ return oldValue; ++ } ++ ++ final void clear() { + lock(); + try { +- int c = count - 1; + HashEntry<K,V>[] tab = table; +- int index = hash & (tab.length - 1); +- HashEntry<K,V> first = tab[index]; +- HashEntry<K,V> e = first; +- while (e != null && (e.hash != hash || !key.equals(e.key))) +- e = e.next; +- +- V oldValue = null; +- if (e != null) { +- V v = e.value; +- if (value == null || value.equals(v)) { +- oldValue = v; +- // All entries following removed node can stay +- // in list, but all preceding ones need to be +- // cloned. +- ++modCount; +- HashEntry<K,V> newFirst = e.next; +- for (HashEntry<K,V> p = first; p != e; p = p.next) +- newFirst = new HashEntry<K,V>(p.key, p.hash, +- newFirst, p.value); +- tab[index] = newFirst; +- count = c; // write-volatile +- } +- } +- return oldValue; ++ for (int i = 0; i < tab.length ; i++) ++ setEntryAt(tab, i, null); ++ ++modCount; ++ count = 0; + } finally { + unlock(); + } + } ++ } + +- void clear() { +- if (count != 0) { +- lock(); +- try { +- HashEntry<K,V>[] tab = table; +- for (int i = 0; i < tab.length ; i++) +- tab[i] = null; +- ++modCount; +- count = 0; // write-volatile +- } finally { +- unlock(); ++ // Accessing segments ++ ++ /** ++ * Gets the jth element of given segment array (if nonnull) with ++ * volatile element access semantics via Unsafe. ++ */ ++ @SuppressWarnings("unchecked") ++ static final <K,V> Segment<K,V> segmentAt(Segment<K,V>[] ss, int j) { ++ long u = (j << SSHIFT) + SBASE; ++ return ss == null ? null : ++ (Segment<K,V>) UNSAFE.getObjectVolatile(ss, u); ++ } ++ ++ /** ++ * Returns the segment for the given index, creating it and ++ * recording in segment table (via CAS) if not already present. ++ * ++ * @param k the index ++ * @return the segment ++ */ ++ @SuppressWarnings("unchecked") ++ private Segment<K,V> ensureSegment(int k) { ++ final Segment<K,V>[] ss = this.segments; ++ long u = (k << SSHIFT) + SBASE; // raw offset ++ Segment<K,V> seg; ++ if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u)) == null) { ++ Segment<K,V> proto = ss[0]; // use segment 0 as prototype ++ int cap = proto.table.length; ++ float lf = proto.loadFactor; ++ int threshold = (int)(cap * lf); ++ HashEntry<K,V>[] tab = (HashEntry<K,V>[])new HashEntry[cap]; ++ if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u)) ++ == null) { // recheck ++ Segment<K,V> s = new Segment<K,V>(lf, threshold, tab); ++ while ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u)) ++ == null) { ++ if (UNSAFE.compareAndSwapObject(ss, u, null, seg = s)) ++ break; + } + } + } ++ return seg; + } + ++ // Hash-based segment and entry accesses + ++ /** ++ * Get the segment for the given hash ++ */ ++ @SuppressWarnings("unchecked") ++ private Segment<K,V> segmentForHash(int h) { ++ long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE; ++ return (Segment<K,V>) UNSAFE.getObjectVolatile(segments, u); ++ } ++ ++ /** ++ * Gets the table entry for the given segment and hash ++ */ ++ @SuppressWarnings("unchecked") ++ static final <K,V> HashEntry<K,V> entryForHash(Segment<K,V> seg, int h) { ++ HashEntry<K,V>[] tab; ++ return (seg == null || (tab = seg.table) == null) ? null : ++ (HashEntry<K,V>) UNSAFE.getObjectVolatile ++ (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE); ++ } + + /* ---------------- Public operations -------------- */ + +@@ -609,14 +727,13 @@ + * negative or the load factor or concurrencyLevel are + * nonpositive. + */ ++ @SuppressWarnings("unchecked") + public ConcurrentHashMap(int initialCapacity, + float loadFactor, int concurrencyLevel) { + if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0) + throw new IllegalArgumentException(); +- + if (concurrencyLevel > MAX_SEGMENTS) + concurrencyLevel = MAX_SEGMENTS; +- + // Find power-of-two sizes best matching arguments + int sshift = 0; + int ssize = 1; +@@ -624,21 +741,23 @@ + ++sshift; + ssize <<= 1; + } +- segmentShift = 32 - sshift; +- segmentMask = ssize - 1; +- this.segments = Segment.newArray(ssize); +- ++ this.segmentShift = 32 - sshift; ++ this.segmentMask = ssize - 1; + if (initialCapacity > MAXIMUM_CAPACITY) + initialCapacity = MAXIMUM_CAPACITY; + int c = initialCapacity / ssize; + if (c * ssize < initialCapacity) + ++c; +- int cap = 1; ++ int cap = MIN_SEGMENT_TABLE_CAPACITY; + while (cap < c) + cap <<= 1; +- +- for (int i = 0; i < this.segments.length; ++i) +- this.segments[i] = new Segment<K,V>(cap, loadFactor); ++ // create segments and segments[0] ++ Segment<K,V> s0 = ++ new Segment<K,V>(loadFactor, (int)(cap * loadFactor), ++ (HashEntry<K,V>[])new HashEntry[cap]); ++ Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize]; ++ UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0] ++ this.segments = ss; + } + + /** +@@ -701,33 +820,36 @@ + * @return <tt>true</tt> if this map contains no key-value mappings + */ + public boolean isEmpty() { ++ /* ++ * Sum per-segment modCounts to avoid mis-reporting when ++ * elements are concurrently added and removed in one segment ++ * while checking another, in which case the table was never ++ * actually empty at any point. (The sum ensures accuracy up ++ * through at least 1<<31 per-segment modifications before ++ * recheck.) Methods size() and containsValue() use similar ++ * constructions for stability checks. ++ */ ++ long sum = 0L; + final Segment<K,V>[] segments = this.segments; +- /* +- * We keep track of per-segment modCounts to avoid ABA +- * problems in which an element in one segment was added and +- * in another removed during traversal, in which case the +- * table was never actually empty at any point. Note the +- * similar use of modCounts in the size() and containsValue() +- * methods, which are the only other methods also susceptible +- * to ABA problems. +- */ +- int[] mc = new int[segments.length]; +- int mcsum = 0; +- for (int i = 0; i < segments.length; ++i) { +- if (segments[i].count != 0) ++ for (int j = 0; j < segments.length; ++j) { ++ Segment<K,V> seg = segmentAt(segments, j); ++ if (seg != null) { ++ if (seg.count != 0) ++ return false; ++ sum += seg.modCount; ++ } ++ } ++ if (sum != 0L) { // recheck unless no modifications ++ for (int j = 0; j < segments.length; ++j) { ++ Segment<K,V> seg = segmentAt(segments, j); ++ if (seg != null) { ++ if (seg.count != 0) ++ return false; ++ sum -= seg.modCount; ++ } ++ } ++ if (sum != 0L) + return false; +- else +- mcsum += mc[i] = segments[i].modCount; +- } +- // If mcsum happens to be zero, then we know we got a snapshot +- // before any modifications at all were made. This is +- // probably common enough to bother tracking. +- if (mcsum != 0) { +- for (int i = 0; i < segments.length; ++i) { +- if (segments[i].count != 0 || +- mc[i] != segments[i].modCount) +- return false; +- } + } + return true; + } +@@ -740,45 +862,43 @@ + * @return the number of key-value mappings in this map + */ + public int size() { +- final Segment<K,V>[] segments = this.segments; +- long sum = 0; +- long check = 0; +- int[] mc = new int[segments.length]; + // Try a few times to get accurate count. On failure due to + // continuous async changes in table, resort to locking. +- for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) { +- check = 0; +- sum = 0; +- int mcsum = 0; +- for (int i = 0; i < segments.length; ++i) { +- sum += segments[i].count; +- mcsum += mc[i] = segments[i].modCount; +- } +- if (mcsum != 0) { +- for (int i = 0; i < segments.length; ++i) { +- check += segments[i].count; +- if (mc[i] != segments[i].modCount) { +- check = -1; // force retry +- break; ++ final Segment<K,V>[] segments = this.segments; ++ int size; ++ boolean overflow; // true if size overflows 32 bits ++ long sum; // sum of modCounts ++ long last = 0L; // previous sum ++ int retries = -1; // first iteration isn't retry ++ try { ++ for (;;) { ++ if (retries++ == RETRIES_BEFORE_LOCK) { ++ for (int j = 0; j < segments.length; ++j) ++ ensureSegment(j).lock(); // force creation ++ } ++ sum = 0L; ++ size = 0; ++ overflow = false; ++ for (int j = 0; j < segments.length; ++j) { ++ Segment<K,V> seg = segmentAt(segments, j); ++ if (seg != null) { ++ sum += seg.modCount; ++ int c = seg.count; ++ if (c < 0 || (size += c) < 0) ++ overflow = true; + } + } ++ if (sum == last) ++ break; ++ last = sum; + } +- if (check == sum) +- break; ++ } finally { ++ if (retries > RETRIES_BEFORE_LOCK) { ++ for (int j = 0; j < segments.length; ++j) ++ segmentAt(segments, j).unlock(); ++ } + } +- if (check != sum) { // Resort to locking all segments +- sum = 0; +- for (int i = 0; i < segments.length; ++i) +- segments[i].lock(); +- for (int i = 0; i < segments.length; ++i) +- sum += segments[i].count; +- for (int i = 0; i < segments.length; ++i) +- segments[i].unlock(); +- } +- if (sum > Integer.MAX_VALUE) +- return Integer.MAX_VALUE; +- else +- return (int)sum; ++ return overflow ? Integer.MAX_VALUE : size; + } + + /** +@@ -794,7 +914,13 @@ + */ + public V get(Object key) { + int hash = hash(key.hashCode()); +- return segmentFor(hash).get(key, hash); ++ for (HashEntry<K,V> e = entryForHash(segmentForHash(hash), hash); ++ e != null; e = e.next) { ++ K k; ++ if ((k = e.key) == key || (e.hash == hash && key.equals(k))) ++ return e.value; ++ } ++ return null; + } + + /** +@@ -808,7 +934,13 @@ + */ + public boolean containsKey(Object key) { + int hash = hash(key.hashCode()); +- return segmentFor(hash).containsKey(key, hash); ++ for (HashEntry<K,V> e = entryForHash(segmentForHash(hash), hash); ++ e != null; e = e.next) { ++ K k; ++ if ((k = e.key) == key || (e.hash == hash && key.equals(k))) ++ return true; ++ } ++ return false; + } + + /** +@@ -823,51 +955,47 @@ + * @throws NullPointerException if the specified value is null + */ + public boolean containsValue(Object value) { ++ // Same idea as size() + if (value == null) + throw new NullPointerException(); +- +- // See explanation of modCount use above +- + final Segment<K,V>[] segments = this.segments; +- int[] mc = new int[segments.length]; +- +- // Try a few times without locking +- for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) { +- int sum = 0; +- int mcsum = 0; +- for (int i = 0; i < segments.length; ++i) { +- int c = segments[i].count; +- mcsum += mc[i] = segments[i].modCount; +- if (segments[i].containsValue(value)) +- return true; +- } +- boolean cleanSweep = true; +- if (mcsum != 0) { +- for (int i = 0; i < segments.length; ++i) { +- int c = segments[i].count; +- if (mc[i] != segments[i].modCount) { +- cleanSweep = false; +- break; ++ boolean found = false; ++ long last = 0; ++ int retries = -1; ++ try { ++ outer: for (;;) { ++ if (retries++ == RETRIES_BEFORE_LOCK) { ++ for (int j = 0; j < segments.length; ++j) ++ ensureSegment(j).lock(); // force creation ++ } ++ long hashSum = 0L; ++ int sum = 0; ++ for (int j = 0; j < segments.length; ++j) { ++ HashEntry<K,V>[] tab; ++ Segment<K,V> seg = segmentAt(segments, j); ++ if (seg != null && (tab = seg.table) != null) { ++ for (int i = 0 ; i < tab.length; i++) { ++ HashEntry<K,V> e; ++ for (e = entryAt(tab, i); e != null; e = e.next) { ++ V v = e.value; ++ if (v != null && value.equals(v)) { ++ found = true; ++ break outer; ++ } ++ } ++ } ++ sum += seg.modCount; + } + } +- } +- if (cleanSweep) +- return false; +- } +- // Resort to locking all segments +- for (int i = 0; i < segments.length; ++i) +- segments[i].lock(); +- boolean found = false; +- try { +- for (int i = 0; i < segments.length; ++i) { +- if (segments[i].containsValue(value)) { +- found = true; ++ if (retries > 0 && sum == last) + break; +- } ++ last = sum; + } + } finally { +- for (int i = 0; i < segments.length; ++i) +- segments[i].unlock(); ++ if (retries > RETRIES_BEFORE_LOCK) { ++ for (int j = 0; j < segments.length; ++j) ++ segmentAt(segments, j).unlock(); ++ } + } + return found; + } +@@ -908,7 +1036,11 @@ + if (value == null) + throw new NullPointerException(); + int hash = hash(key.hashCode()); +- return segmentFor(hash).put(key, hash, value, false); ++ int j = (hash >>> segmentShift) & segmentMask; ++ Segment<K,V> s = segmentAt(segments, j); ++ if (s == null) ++ s = ensureSegment(j); ++ return s.put(key, hash, value, false); + } + + /** +@@ -922,7 +1054,11 @@ + if (value == null) + throw new NullPointerException(); + int hash = hash(key.hashCode()); +- return segmentFor(hash).put(key, hash, value, true); ++ int j = (hash >>> segmentShift) & segmentMask; ++ Segment<K,V> s = segmentAt(segments, j); ++ if (s == null) ++ s = ensureSegment(j); ++ return s.put(key, hash, value, true); + } + + /** +@@ -948,7 +1084,8 @@ + */ + public V remove(Object key) { + int hash = hash(key.hashCode()); +- return segmentFor(hash).remove(key, hash, null); ++ Segment<K,V> s = segmentForHash(hash); ++ return s == null ? null : s.remove(key, hash, null); + } + + /** +@@ -958,9 +1095,9 @@ + */ + public boolean remove(Object key, Object value) { + int hash = hash(key.hashCode()); +- if (value == null) +- return false; +- return segmentFor(hash).remove(key, hash, value) != null; ++ Segment<K,V> s; ++ return value != null && (s = segmentForHash(hash)) != null && ++ s.remove(key, hash, value) != null; + } + + /** +@@ -969,10 +1106,11 @@ + * @throws NullPointerException if any of the arguments are null + */ + public boolean replace(K key, V oldValue, V newValue) { ++ int hash = hash(key.hashCode()); + if (oldValue == null || newValue == null) + throw new NullPointerException(); +- int hash = hash(key.hashCode()); +- return segmentFor(hash).replace(key, hash, oldValue, newValue); ++ Segment<K,V> s = segmentForHash(hash); ++ return s != null && s.replace(key, hash, oldValue, newValue); + } + + /** +@@ -983,18 +1121,23 @@ + * @throws NullPointerException if the specified key or value is null + */ + public V replace(K key, V value) { ++ int hash = hash(key.hashCode()); + if (value == null) + throw new NullPointerException(); +- int hash = hash(key.hashCode()); +- return segmentFor(hash).replace(key, hash, value); ++ Segment<K,V> s = segmentForHash(hash); ++ return s == null ? null : s.replace(key, hash, value); + } + + /** + * Removes all of the mappings from this map. + */ + public void clear() { +- for (int i = 0; i < segments.length; ++i) +- segments[i].clear(); ++ final Segment<K,V>[] segments = this.segments; ++ for (int j = 0; j < segments.length; ++j) { ++ Segment<K,V> s = segmentAt(segments, j); ++ if (s != null) ++ s.clear(); ++ } + } + + /** +@@ -1095,42 +1238,41 @@ + advance(); + } + +- public boolean hasMoreElements() { return hasNext(); } +- ++ /** ++ * Set nextEntry to first node of next non-empty table ++ * (in backwards order, to simplify checks). ++ */ + final void advance() { +- if (nextEntry != null && (nextEntry = nextEntry.next) != null) +- return; +- +- while (nextTableIndex >= 0) { +- if ( (nextEntry = currentTable[nextTableIndex--]) != null) +- return; +- } +- +- while (nextSegmentIndex >= 0) { +- Segment<K,V> seg = segments[nextSegmentIndex--]; +- if (seg.count != 0) { +- currentTable = seg.table; +- for (int j = currentTable.length - 1; j >= 0; --j) { +- if ( (nextEntry = currentTable[j]) != null) { +- nextTableIndex = j - 1; +- return; +- } +- } ++ for (;;) { ++ if (nextTableIndex >= 0) { ++ if ((nextEntry = entryAt(currentTable, ++ nextTableIndex--)) != null) ++ break; + } ++ else if (nextSegmentIndex >= 0) { ++ Segment<K,V> seg = segmentAt(segments, nextSegmentIndex--); ++ if (seg != null && (currentTable = seg.table) != null) ++ nextTableIndex = currentTable.length - 1; ++ } ++ else ++ break; + } + } + +- public boolean hasNext() { return nextEntry != null; } +- +- HashEntry<K,V> nextEntry() { +- if (nextEntry == null) ++ final HashEntry<K,V> nextEntry() { ++ HashEntry<K,V> e = nextEntry; ++ if (e == null) + throw new NoSuchElementException(); +- lastReturned = nextEntry; +- advance(); +- return lastReturned; ++ lastReturned = e; // cannot assign until after null check ++ if ((nextEntry = e.next) == null) ++ advance(); ++ return e; + } + +- public void remove() { ++ public final boolean hasNext() { return nextEntry != null; } ++ public final boolean hasMoreElements() { return nextEntry != null; } ++ ++ public final void remove() { + if (lastReturned == null) + throw new IllegalStateException(); + ConcurrentHashMap.this.remove(lastReturned.key); +@@ -1142,16 +1284,16 @@ + extends HashIterator + implements Iterator<K>, Enumeration<K> + { +- public K next() { return super.nextEntry().key; } +- public K nextElement() { return super.nextEntry().key; } ++ public final K next() { return super.nextEntry().key; } ++ public final K nextElement() { return super.nextEntry().key; } + } + + final class ValueIterator + extends HashIterator + implements Iterator<V>, Enumeration<V> + { +- public V next() { return super.nextEntry().value; } +- public V nextElement() { return super.nextEntry().value; } ++ public final V next() { return super.nextEntry().value; } ++ public final V nextElement() { return super.nextEntry().value; } + } + + /** +@@ -1271,15 +1413,20 @@ + * The key-value mappings are emitted in no particular order. + */ + private void writeObject(java.io.ObjectOutputStream s) throws IOException { ++ // force all segments for serialization compatibility ++ for (int k = 0; k < segments.length; ++k) ++ ensureSegment(k); + s.defaultWriteObject(); + ++ final Segment<K,V>[] segments = this.segments; + for (int k = 0; k < segments.length; ++k) { +- Segment<K,V> seg = segments[k]; ++ Segment<K,V> seg = segmentAt(segments, k); + seg.lock(); + try { + HashEntry<K,V>[] tab = seg.table; + for (int i = 0; i < tab.length; ++i) { +- for (HashEntry<K,V> e = tab[i]; e != null; e = e.next) { ++ HashEntry<K,V> e; ++ for (e = entryAt(tab, i); e != null; e = e.next) { + s.writeObject(e.key); + s.writeObject(e.value); + } +@@ -1297,13 +1444,20 @@ + * stream (i.e., deserialize it). + * @param s the stream + */ ++ @SuppressWarnings("unchecked") + private void readObject(java.io.ObjectInputStream s) + throws IOException, ClassNotFoundException { + s.defaultReadObject(); + +- // Initialize each segment to be minimally sized, and let grow. +- for (int i = 0; i < segments.length; ++i) { +- segments[i].setTable(new HashEntry[1]); ++ // Re-initialize segments to be minimally sized, and let grow. ++ int cap = MIN_SEGMENT_TABLE_CAPACITY; ++ final Segment<K,V>[] segments = this.segments; ++ for (int k = 0; k < segments.length; ++k) { ++ Segment<K,V> seg = segments[k]; ++ if (seg != null) { ++ seg.threshold = (int)(cap * seg.loadFactor); ++ seg.table = (HashEntry<K,V>[]) new HashEntry[cap]; ++ } + } + + // Read the keys and values, and put the mappings in the table +@@ -1315,4 +1469,31 @@ + put(key, value); + } + } ++ ++ // Unsafe mechanics ++ private static final sun.misc.Unsafe UNSAFE; ++ private static final long SBASE; ++ private static final int SSHIFT; ++ private static final long TBASE; ++ private static final int TSHIFT; ++ ++ static { ++ int ss, ts; ++ try { ++ UNSAFE = sun.misc.Unsafe.getUnsafe(); ++ Class tc = HashEntry[].class; ++ Class sc = Segment[].class; ++ TBASE = UNSAFE.arrayBaseOffset(tc); ++ SBASE = UNSAFE.arrayBaseOffset(sc); ++ ts = UNSAFE.arrayIndexScale(tc); ++ ss = UNSAFE.arrayIndexScale(sc); ++ } catch (Exception e) { ++ throw new Error(e); ++ } ++ if ((ss & (ss-1)) != 0 || (ts & (ts-1)) != 0) ++ throw new Error("data type scale not a power of two"); ++ SSHIFT = 31 - Integer.numberOfLeadingZeros(ss); ++ TSHIFT = 31 - Integer.numberOfLeadingZeros(ts); ++ } ++ + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/security/20130416/8009063.patch Thu Apr 11 16:25:24 2013 -0400 @@ -0,0 +1,67 @@ +# HG changeset patch +# User chegar +# Date 1362305505 0 +# Node ID 98ad2f1e25d13aca196ad77b2f227f85072c9b16 +# Parent 17ac71e7b72087f0f7b7ac793ae93a816ef22d96 +8009063: Improve reliability of ConcurrentHashMap +Reviewed-by: alanb, ahgross + +diff --git a/src/share/classes/java/util/concurrent/ConcurrentHashMap.java b/src/share/classes/java/util/concurrent/ConcurrentHashMap.java +--- openjdk/jdk/src/share/classes/java/util/concurrent/ConcurrentHashMap.java ++++ openjdk/jdk/src/share/classes/java/util/concurrent/ConcurrentHashMap.java +@@ -40,6 +40,7 @@ import java.io.IOException; + import java.io.IOException; + import java.io.ObjectInputStream; + import java.io.ObjectOutputStream; ++import java.io.ObjectStreamField; + + /** + * A hash table supporting full concurrency of retrievals and +@@ -1535,7 +1536,23 @@ public class ConcurrentHashMap<K, V> ext + @SuppressWarnings("unchecked") + private void readObject(java.io.ObjectInputStream s) + throws IOException, ClassNotFoundException { +- s.defaultReadObject(); ++ // Don't call defaultReadObject() ++ ObjectInputStream.GetField oisFields = s.readFields(); ++ final Segment<K,V>[] oisSegments = (Segment<K,V>[])oisFields.get("segments", null); ++ ++ final int ssize = oisSegments.length; ++ if (ssize < 1 || ssize > MAX_SEGMENTS ++ || (ssize & (ssize-1)) != 0 ) // ssize not power of two ++ throw new java.io.InvalidObjectException("Bad number of segments:" ++ + ssize); ++ int sshift = 0, ssizeTmp = ssize; ++ while (ssizeTmp > 1) { ++ ++sshift; ++ ssizeTmp >>>= 1; ++ } ++ UNSAFE.putIntVolatile(this, SEGSHIFT_OFFSET, 32 - sshift); ++ UNSAFE.putIntVolatile(this, SEGMASK_OFFSET, ssize - 1); ++ UNSAFE.putObjectVolatile(this, SEGMENTS_OFFSET, oisSegments); + + // set hashMask + UNSAFE.putIntVolatile(this, HASHSEED_OFFSET, randomHashSeed(this)); +@@ -1568,6 +1585,9 @@ public class ConcurrentHashMap<K, V> ext + private static final int SSHIFT; + private static final long TBASE; + private static final int TSHIFT; ++ private static final long SEGSHIFT_OFFSET; ++ private static final long SEGMASK_OFFSET; ++ private static final long SEGMENTS_OFFSET; + + static { + int ss, ts; +@@ -1581,6 +1601,12 @@ public class ConcurrentHashMap<K, V> ext + SBASE = UNSAFE.arrayBaseOffset(sc); + ts = UNSAFE.arrayIndexScale(tc); + ss = UNSAFE.arrayIndexScale(sc); ++ SEGSHIFT_OFFSET = UNSAFE.objectFieldOffset( ++ ConcurrentHashMap.class.getDeclaredField("segmentShift")); ++ SEGMASK_OFFSET = UNSAFE.objectFieldOffset( ++ ConcurrentHashMap.class.getDeclaredField("segmentMask")); ++ SEGMENTS_OFFSET = UNSAFE.objectFieldOffset( ++ ConcurrentHashMap.class.getDeclaredField("segments")); + } catch (Exception e) { + throw new Error(e); + }