You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sb...@apache.org on 2015/06/10 17:04:26 UTC
[1/7] incubator-ignite git commit: ignite-sprint-6: merge from
ignite-545
Repository: incubator-ignite
Updated Branches:
refs/heads/ignite-sprint-6 a12302eed -> 95b7147fe
http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/455b96fc/modules/core/src/main/java/org/jsr166/ConcurrentLinkedDeque8.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/jsr166/ConcurrentLinkedDeque8.java b/modules/core/src/main/java/org/jsr166/ConcurrentLinkedDeque8.java
index 630db1c..9c8c2db 100644
--- a/modules/core/src/main/java/org/jsr166/ConcurrentLinkedDeque8.java
+++ b/modules/core/src/main/java/org/jsr166/ConcurrentLinkedDeque8.java
@@ -5,8 +5,12 @@
*/
/*
- * The initial version of this file was copied from JSR-166:
- * http://gee.cs.oswego.edu/dl/concurrency-interest/
+ * The latest version of the file corresponds to the following CVS commit:
+ * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/main/java/util/concurrent/
+ * ConcurrentLinkedDeque.java?pathrev=1.33
+ *
+ * The later versions use JDK 8 specific classes that are unavailable in JDK 7.
+ * Thus those commits can't be imported.
*/
package org.jsr166;
@@ -18,6 +22,7 @@ import java.security.*;
import java.util.*;
import java.util.Queue;
+
/**
* An unbounded concurrent {@linkplain Deque deque} based on linked nodes.
* Concurrent insertion, removal, and access operations execute safely
@@ -55,13 +60,21 @@ import java.util.Queue;
* <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
* actions subsequent to the access or removal of that element from
* the {@code ConcurrentLinkedDeque} in another thread.
- * <p>
- * Written by Doug Lea and Martin Buchholz with assistance from members of
- * JCP JSR-166 Expert Group and released to the public domain, as explained
- * at http://creativecommons.org/publicdomain/zero/1.0/
+ *
+ * <p>This class is a member of the
+ * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * Java Collections Framework</a>.
+ *
+ * @since 1.7
+ * @author Doug Lea
+ * @author Martin Buchholz
+ * @param <E> the type of elements held in this collection
*/
-@SuppressWarnings( {"ALL"})
-public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements Deque<E> {
+@SuppressWarnings("ALL")
+public class ConcurrentLinkedDeque8<E>
+ extends AbstractCollection<E>
+ implements Deque<E>, java.io.Serializable {
+
/*
* This is an implementation of a concurrent lock-free deque
* supporting interior removes but not interior insertions, as
@@ -213,6 +226,8 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
* good as we can hope for.
*/
+ private static final long serialVersionUID = 876323262645176354L;
+
/**
* A node from which the first node on list (that is, the unique node p
* with p.prev == null && p.next != p) can be reached in O(1) time.
@@ -226,7 +241,7 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
* - head.item may or may not be null
* - head may not be reachable from the first or last node, or from tail
*/
- private volatile Node<E> head;
+ private transient volatile Node<E> head;
/**
* A node from which the last node on list (that is, the unique node p
@@ -240,12 +255,11 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
* - tail.item may or may not be null
* - tail may not be reachable from the first or last node, or from head
*/
- private volatile Node<E> tail;
+ private transient volatile Node<E> tail;
/** */
private final LongAdder8 size = new LongAdder8();
- /** Previous and next terminators. */
private static final Node<Object> PREV_TERMINATOR, NEXT_TERMINATOR;
@SuppressWarnings("unchecked")
@@ -258,29 +272,17 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
return (Node<E>) NEXT_TERMINATOR;
}
- /**
- * Internal node element.
- *
- * @param <E> Node item.
- */
- @SuppressWarnings( {"PackageVisibleField", "PackageVisibleInnerClass"})
public static final class Node<E> {
volatile Node<E> prev;
volatile E item;
volatile Node<E> next;
- /**
- * Default constructor for NEXT_TERMINATOR, PREV_TERMINATOR.
- */
- Node() {
- // No-op.
+ Node() { // default constructor for NEXT_TERMINATOR, PREV_TERMINATOR
}
/**
* Constructs a new node. Uses relaxed write because item can
* only be seen after publication via casNext or casPrev.
- *
- * @param item Item to initialize.
*/
Node(E item) {
UNSAFE.putObject(this, itemOffset, item);
@@ -293,73 +295,44 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
return item;
}
- /**
- * @param cmp Compare value.
- * @param val New value.
- * @return {@code True} if set.
- */
boolean casItem(E cmp, E val) {
return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
}
- /**
- * @param val New value.
- */
void lazySetNext(Node<E> val) {
UNSAFE.putOrderedObject(this, nextOffset, val);
}
- /**
- * @param cmp Compare value.
- * @param val New value.
- * @return {@code True} if set.
- */
boolean casNext(Node<E> cmp, Node<E> val) {
return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
- /**
- * @param val New value.
- */
void lazySetPrev(Node<E> val) {
UNSAFE.putOrderedObject(this, prevOffset, val);
}
- /**
- * @param cmp Compare value.
- * @param val New value.
- * @return {@code True} if set.
- */
boolean casPrev(Node<E> cmp, Node<E> val) {
return UNSAFE.compareAndSwapObject(this, prevOffset, cmp, val);
}
- /** Unsafe. */
- private static final Unsafe UNSAFE;
+ // Unsafe mechanics
- /** Previous field offset. */
+ private static final sun.misc.Unsafe UNSAFE;
private static final long prevOffset;
-
- /** Item field offset. */
private static final long itemOffset;
-
- /** Next field offset. */
private static final long nextOffset;
- /**
- * Initialize offsets.
- */
static {
try {
UNSAFE = unsafe();
-
- Class k = Node.class;
-
- prevOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("prev"));
- itemOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("item"));
- nextOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("next"));
- }
- catch (Exception e) {
+ Class<?> k = Node.class;
+ prevOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("prev"));
+ itemOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("item"));
+ nextOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("next"));
+ } catch (Exception e) {
throw new Error(e);
}
}
@@ -376,9 +349,10 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
final Node<E> newNode = new Node<E>(e);
restartFromHead:
- for (;;) {
+ for (;;)
for (Node<E> h = head, p = h, q;;) {
- if ((q = p.prev) != null && (q = (p = q).prev) != null)
+ if ((q = p.prev) != null &&
+ (q = (p = q).prev) != null)
// Check for head updates every other hop.
// If p == q, we are sure to follow head instead.
p = (h != (h = head)) ? h : q;
@@ -386,21 +360,18 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
continue restartFromHead;
else {
// p is first node
- newNode.lazySetNext(p); // CAS piggyback.
-
+ newNode.lazySetNext(p); // CAS piggyback
if (p.casPrev(null, newNode)) {
// Successful CAS is the linearization point
// for e to become an element of this deque,
// and for newNode to become "live".
if (p != h) // hop two nodes at a time
casHead(h, newNode); // Failure is OK.
-
return;
}
// Lost CAS race to another thread; re-read prev
}
}
- }
}
/**
@@ -446,8 +417,6 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
/**
* Links e as last element.
- *
- * @param e Element to link.
*/
private void linkLast(E e) {
checkNotNull(e);
@@ -457,9 +426,10 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
final Node<E> newNode = new Node<E>(e);
restartFromTail:
- for (;;) {
+ for (;;)
for (Node<E> t = tail, p = t, q;;) {
- if ((q = p.next) != null && (q = (p = q).next) != null)
+ if ((q = p.next) != null &&
+ (q = (p = q).next) != null)
// Check for tail updates every other hop.
// If p == q, we are sure to follow tail instead.
p = (t != (t = tail)) ? t : q;
@@ -467,21 +437,18 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
continue restartFromTail;
else {
// p is last node
- newNode.lazySetPrev(p); // CAS piggyback.
-
+ newNode.lazySetPrev(p); // CAS piggyback
if (p.casNext(null, newNode)) {
// Successful CAS is the linearization point
// for e to become an element of this deque,
// and for newNode to become "live".
if (p != t) // hop two nodes at a time
casTail(t, newNode); // Failure is OK.
-
return;
}
// Lost CAS race to another thread; re-read next
}
}
- }
}
/**
@@ -563,7 +530,6 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
}
}
- /** Number of HOPs before unlinking head or tail. */
private static final int HOPS = 2;
/**
@@ -589,7 +555,7 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
/**
* Unlinks non-null node x.
*/
- private void unlink(Node<E> x) {
+ void unlink(Node<E> x) {
// assert x != null;
// assert x.item == null;
// assert x != PREV_TERMINATOR;
@@ -601,11 +567,11 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
// Unlink should not be called twice for the same node.
size.decrement();
- if (prev == null)
+ if (prev == null) {
unlinkFirst(x, next);
- else if (next == null)
+ } else if (next == null) {
unlinkLast(x, prev);
- else {
+ } else {
// Unlink interior node.
//
// This is the common case, since a series of polls at the
@@ -626,31 +592,22 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
// tail/head, before setting x's prev/next links to their
// logical approximate replacements, self/TERMINATOR.
Node<E> activePred, activeSucc;
-
boolean isFirst, isLast;
-
int hops = 1;
// Find active predecessor
for (Node<E> p = prev; ; ++hops) {
if (p.item != null) {
activePred = p;
-
isFirst = false;
-
break;
}
-
Node<E> q = p.prev;
-
if (q == null) {
if (p.next == p)
return;
-
activePred = p;
-
isFirst = true;
-
break;
}
else if (p == q)
@@ -663,22 +620,15 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
for (Node<E> p = next; ; ++hops) {
if (p.item != null) {
activeSucc = p;
-
isLast = false;
-
break;
}
-
Node<E> q = p.next;
-
if (q == null) {
if (p.prev == p)
return;
-
activeSucc = p;
-
isLast = true;
-
break;
}
else if (p == q)
@@ -688,8 +638,9 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
}
// TODO: better HOP heuristics
- // Always squeeze out interior deleted nodes.
- if (hops < HOPS && (isFirst | isLast))
+ if (hops < HOPS
+ // always squeeze out interior deleted nodes
+ && (isFirst | isLast))
return;
// Squeeze out deleted nodes between activePred and
@@ -699,6 +650,7 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
// Try to gc-unlink, if possible
if ((isFirst | isLast) &&
+
// Recheck expected state of predecessor and successor
(activePred.next == activeSucc) &&
(activeSucc.prev == activePred) &&
@@ -793,11 +745,11 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
// Either head already points to an active node, or we keep
// trying to cas it to the first node until it does.
Node<E> h, p, q;
-
restartFromHead:
while ((h = head).item == null && (p = h.prev) != null) {
for (;;) {
- if ((q = p.prev) == null || (q = (p = q).prev) == null) {
+ if ((q = p.prev) == null ||
+ (q = (p = q).prev) == null) {
// It is possible that p is PREV_TERMINATOR,
// but if so, the CAS is guaranteed to fail.
if (casHead(h, p))
@@ -823,11 +775,11 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
// Either tail already points to an active node, or we keep
// trying to cas it to the last node until it does.
Node<E> t, p, q;
-
restartFromTail:
while ((t = tail).item == null && (p = t.next) != null) {
for (;;) {
- if ((q = p.next) == null || (q = (p = q).next) == null) {
+ if ((q = p.next) == null ||
+ (q = (p = q).next) == null) {
// It is possible that p is NEXT_TERMINATOR,
// but if so, the CAS is guaranteed to fail.
if (casTail(t, p))
@@ -843,9 +795,6 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
}
}
- /**
- * @param x Node to start from.
- */
private void skipDeletedPredecessors(Node<E> x) {
whileActive:
do {
@@ -854,18 +803,14 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
// assert x != NEXT_TERMINATOR;
// assert x != PREV_TERMINATOR;
Node<E> p = prev;
-
findActive:
for (;;) {
if (p.item != null)
break findActive;
-
Node<E> q = p.prev;
-
if (q == null) {
if (p.next == p)
continue whileActive;
-
break findActive;
}
else if (p == q)
@@ -881,9 +826,6 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
} while (x.item != null || x.next == null);
}
- /**
- * @param x Node to start from.
- */
private void skipDeletedSuccessors(Node<E> x) {
whileActive:
do {
@@ -892,19 +834,14 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
// assert x != NEXT_TERMINATOR;
// assert x != PREV_TERMINATOR;
Node<E> p = next;
-
findActive:
-
for (;;) {
if (p.item != null)
break findActive;
-
Node<E> q = p.next;
-
if (q == null) {
if (p.prev == p)
continue whileActive;
-
break findActive;
}
else if (p == q)
@@ -917,22 +854,17 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
if (next == p || x.casNext(next, p))
return;
- }
- while (x.item != null || x.prev == null);
+ } while (x.item != null || x.prev == null);
}
/**
* Returns the successor of p, or the first node if p.next has been
* linked to self, which will only be true if traversing with a
* stale pointer that is now off the list.
- *
- * @param p Node to find successor for.
- * @return Successor node.
*/
- final Node<E> successor(Node<E> p) {
+ final Node<E> succ(Node<E> p) {
// TODO: should we skip deleted nodes here?
Node<E> q = p.next;
-
return (p == q) ? first() : q;
}
@@ -940,11 +872,8 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
* Returns the predecessor of p, or the last node if p.prev has been
* linked to self, which will only be true if traversing with a
* stale pointer that is now off the list.
- *
- * @param p Node to find predecessor for.
- * @return Predecessor node.
*/
- final Node<E> predecessor(Node<E> p) {
+ final Node<E> pred(Node<E> p) {
Node<E> q = p.prev;
return (p == q) ? last() : q;
}
@@ -954,10 +883,7 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
* p.prev == null && p.next != p
* The returned node may or may not be logically deleted.
* Guarantees that head is set to the returned node.
- *
- * @return First node.
*/
- @SuppressWarnings( {"TooBroadScope"})
Node<E> first() {
restartFromHead:
for (;;)
@@ -982,10 +908,7 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
* p.next == null && p.prev != p
* The returned node may or may not be logically deleted.
* Guarantees that tail is set to the returned node.
- *
- * @return Last node.
*/
- @SuppressWarnings( {"TooBroadScope"})
Node<E> last() {
restartFromTail:
for (;;)
@@ -1005,6 +928,8 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
}
}
+ // Minor convenience utilities
+
/**
* Throws NullPointerException if argument is null.
*
@@ -1025,7 +950,6 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
private E screenNullResult(E v) {
if (v == null)
throw new NoSuchElementException();
-
return v;
}
@@ -1033,18 +957,15 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
* Creates an array list and fills it with elements of this list.
* Used by toArray.
*
- * @return the arrayList
+ * @return the array list
*/
private ArrayList<E> toArrayList() {
ArrayList<E> list = new ArrayList<E>();
-
- for (Node<E> p = first(); p != null; p = successor(p)) {
+ for (Node<E> p = first(); p != null; p = succ(p)) {
E item = p.item;
-
if (item != null)
list.add(item);
}
-
return list;
}
@@ -1052,7 +973,7 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
* Constructs an empty deque.
*/
public ConcurrentLinkedDeque8() {
- head = tail = new Node<E>();
+ head = tail = new Node<E>(null);
}
/**
@@ -1064,15 +985,12 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
* @throws NullPointerException if the specified collection or any
* of its elements are null
*/
- public ConcurrentLinkedDeque8(Iterable<? extends E> c) {
+ public ConcurrentLinkedDeque8(Collection<? extends E> c) {
// Copy c into a private chain of Nodes
Node<E> h = null, t = null;
-
for (E e : c) {
checkNotNull(e);
-
Node<E> newNode = new Node<E>(e);
-
if (h == null)
h = t = newNode;
else {
@@ -1081,15 +999,11 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
t = newNode;
}
}
-
initHeadTail(h, t);
}
/**
* Initializes head and tail, ensuring invariants hold.
- *
- * @param h Head.
- * @param t Tail.
*/
private void initHeadTail(Node<E> h, Node<E> t) {
if (h == t) {
@@ -1098,15 +1012,11 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
else {
// Avoid edge case of a single Node with non-null item.
Node<E> newNode = new Node<E>(null);
-
t.lazySetNext(newNode);
-
newNode.lazySetPrev(t);
-
t = newNode;
}
}
-
head = h;
tail = t;
}
@@ -1118,21 +1028,11 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
*
* @throws NullPointerException if the specified element is null
*/
- @Override public void addFirst(E e) {
+ public void addFirst(E e) {
linkFirst(e);
}
/**
- * Same as {@link #addFirst(Object)}, but returns new node.
- *
- * @param e Element to add.
- * @return New node.
- */
- public Node<E> addFirstx(E e) {
- return linkFirstx(e);
- }
-
- /**
* Inserts the specified element at the end of this deque.
* As the deque is unbounded, this method will never throw
* {@link IllegalStateException}.
@@ -1141,7 +1041,7 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
*
* @throws NullPointerException if the specified element is null
*/
- @Override public void addLast(E e) {
+ public void addLast(E e) {
linkLast(e);
}
@@ -1162,9 +1062,8 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
* @return {@code true} (as specified by {@link Deque#offerFirst})
* @throws NullPointerException if the specified element is null
*/
- @Override public boolean offerFirst(E e) {
+ public boolean offerFirst(E e) {
linkFirst(e);
-
return true;
}
@@ -1187,9 +1086,8 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
* @return {@code true} (as specified by {@link Deque#offerLast})
* @throws NullPointerException if the specified element is null
*/
- @Override public boolean offerLast(E e) {
+ public boolean offerLast(E e) {
linkLast(e);
-
return true;
}
@@ -1203,15 +1101,12 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
return linkLastx(e);
}
- /** {@inheritDoc} */
- @Override public E peekFirst() {
- for (Node<E> p = first(); p != null; p = successor(p)) {
+ public E peekFirst() {
+ for (Node<E> p = first(); p != null; p = succ(p)) {
E item = p.item;
-
if (item != null)
return item;
}
-
return null;
}
@@ -1222,7 +1117,7 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
* @return The header node of this deque, or <tt>null</tt> if this deque is empty
*/
public Node<E> peekFirstx() {
- for (Node<E> p = first(); p != null; p = successor(p)) {
+ for (Node<E> p = first(); p != null; p = succ(p)) {
E item = p.item;
if (item != null)
@@ -1232,76 +1127,67 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
return null;
}
- /** {@inheritDoc} */
- @Override public E peekLast() {
- for (Node<E> p = last(); p != null; p = predecessor(p)) {
+ public E peekLast() {
+ for (Node<E> p = last(); p != null; p = pred(p)) {
E item = p.item;
-
if (item != null)
return item;
}
-
return null;
}
/**
* @throws NoSuchElementException {@inheritDoc}
*/
- @Override public E getFirst() {
+ public E getFirst() {
return screenNullResult(peekFirst());
}
/**
* @throws NoSuchElementException {@inheritDoc}
*/
- @Override public E getLast() {
+ public E getLast() {
return screenNullResult(peekLast());
}
- /** {@inheritDoc} */
- @Override public E pollFirst() {
- for (Node<E> p = first(); p != null; p = successor(p)) {
+ public E pollFirst() {
+ for (Node<E> p = first(); p != null; p = succ(p)) {
E item = p.item;
-
if (item != null && p.casItem(item, null)) {
unlink(p);
-
return item;
}
}
-
return null;
}
- /** {@inheritDoc} */
- @Override public E pollLast() {
- for (Node<E> p = last(); p != null; p = predecessor(p)) {
+ public E pollLast() {
+ for (Node<E> p = last(); p != null; p = pred(p)) {
E item = p.item;
-
if (item != null && p.casItem(item, null)) {
unlink(p);
-
return item;
}
}
-
return null;
}
/**
* @throws NoSuchElementException {@inheritDoc}
*/
- @Override public E removeFirst() {
+ public E removeFirst() {
return screenNullResult(pollFirst());
}
/**
* @throws NoSuchElementException {@inheritDoc}
*/
- @Override public E removeLast() {
+ public E removeLast() {
return screenNullResult(pollLast());
}
+ // *** Queue and stack methods ***
+
/**
* Inserts the specified element at the tail of this deque.
* As the deque is unbounded, this method will never return {@code false}.
@@ -1309,21 +1195,11 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
* @return {@code true} (as specified by {@link Queue#offer})
* @throws NullPointerException if the specified element is null
*/
- @Override public boolean offer(E e) {
+ public boolean offer(E e) {
return offerLast(e);
}
/**
- * Same as {@link #offer(Object)}, but returns new {@link Node}.
- *
- * @param e Element to add.
- * @return New node.
- */
- public Node<E> offerx(E e) {
- return offerLastx(e);
- }
-
- /**
* Inserts the specified element at the tail of this deque.
* As the deque is unbounded, this method will never throw
* {@link IllegalStateException} or return {@code false}.
@@ -1331,7 +1207,7 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
* @return {@code true} (as specified by {@link Collection#add})
* @throws NullPointerException if the specified element is null
*/
- @Override public boolean add(E e) {
+ public boolean add(E e) {
return offerLast(e);
}
@@ -1345,20 +1221,12 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
return offerLastx(e);
}
- /** {@inheritDoc} */
- @Override public E poll() {
- return pollFirst();
- }
-
- /** {@inheritDoc} */
- @Override public E remove() {
- return removeFirst();
- }
-
- /** {@inheritDoc} */
- @Override public E peek() {
- return peekFirst();
- }
+ public E poll() { return pollFirst(); }
+ public E remove() { return removeFirst(); }
+ public E peek() { return peekFirst(); }
+ public E element() { return getFirst(); }
+ public void push(E e) { addFirst(e); }
+ public E pop() { return removeFirst(); }
/**
* Retrieves, but does not remove, the header node of the queue represented by
@@ -1374,21 +1242,6 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
return peekFirstx();
}
- /** {@inheritDoc} */
- @Override public E element() {
- return getFirst();
- }
-
- /** {@inheritDoc} */
- @Override public void push(E e) {
- addFirst(e);
- }
-
- /** {@inheritDoc} */
- @Override public E pop() {
- return removeFirst();
- }
-
/**
* Removes the first element {@code e} such that
* {@code o.equals(e)}, if such an element exists in this deque.
@@ -1398,19 +1251,15 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
* @return {@code true} if the deque contained the specified element
* @throws NullPointerException if the specified element is null
*/
- @Override public boolean removeFirstOccurrence(Object o) {
+ public boolean removeFirstOccurrence(Object o) {
checkNotNull(o);
-
- for (Node<E> p = first(); p != null; p = successor(p)) {
+ for (Node<E> p = first(); p != null; p = succ(p)) {
E item = p.item;
-
if (item != null && o.equals(item) && p.casItem(item, null)) {
unlink(p);
-
return true;
}
}
-
return false;
}
@@ -1423,19 +1272,15 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
* @return {@code true} if the deque contained the specified element
* @throws NullPointerException if the specified element is null
*/
- @Override public boolean removeLastOccurrence(Object o) {
+ public boolean removeLastOccurrence(Object o) {
checkNotNull(o);
-
- for (Node<E> p = last(); p != null; p = predecessor(p)) {
+ for (Node<E> p = last(); p != null; p = pred(p)) {
E item = p.item;
-
if (item != null && o.equals(item) && p.casItem(item, null)) {
unlink(p);
-
return true;
}
}
-
return false;
}
@@ -1446,17 +1291,13 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
* @param o element whose presence in this deque is to be tested
* @return {@code true} if this deque contains the specified element
*/
- @Override public boolean contains(Object o) {
- if (o == null)
- return false;
-
- for (Node<E> p = first(); p != null; p = successor(p)) {
+ public boolean contains(Object o) {
+ if (o == null) return false;
+ for (Node<E> p = first(); p != null; p = succ(p)) {
E item = p.item;
-
if (item != null && o.equals(item))
return true;
}
-
return false;
}
@@ -1465,7 +1306,7 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
*
* @return {@code true} if this collection contains no elements
*/
- @Override public boolean isEmpty() {
+ public boolean isEmpty() {
return peekFirst() == null;
}
@@ -1497,16 +1338,14 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
*
* @return the number of elements in this deque
*/
- @Override public int size() {
- int cnt = 0;
-
- for (Node<E> p = first(); p != null; p = successor(p))
+ public int size() {
+ int count = 0;
+ for (Node<E> p = first(); p != null; p = succ(p))
if (p.item != null)
// Collection.size() spec says to max out
- if (++cnt == Integer.MAX_VALUE)
+ if (++count == Integer.MAX_VALUE)
break;
-
- return cnt;
+ return count;
}
/**
@@ -1525,7 +1364,7 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
* @return {@code true} if the deque contained the specified element
* @throws NullPointerException if the specified element is null
*/
- @Override public boolean remove(Object o) {
+ public boolean remove(Object o) {
return removeFirstOccurrence(o);
}
@@ -1541,8 +1380,7 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
* of its elements are null
* @throws IllegalArgumentException if the collection is this deque
*/
- @SuppressWarnings( {"TooBroadScope"})
- @Override public boolean addAll(Collection<? extends E> c) {
+ public boolean addAll(Collection<? extends E> c) {
if (c == this)
// As historically specified in AbstractQueue#addAll
throw new IllegalArgumentException();
@@ -1554,19 +1392,14 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
for (E e : c) {
checkNotNull(e);
-
Node<E> newNode = new Node<E>(e);
-
if (beginningOfTheEnd == null) {
beginningOfTheEnd = last = newNode;
-
s++;
}
else {
last.lazySetNext(newNode);
-
newNode.lazySetPrev(last);
-
last = newNode;
s++;
@@ -1580,9 +1413,10 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
// Atomically append the chain at the tail of this collection
restartFromTail:
- for (;;) {
+ for (;;)
for (Node<E> t = tail, p = t, q;;) {
- if ((q = p.next) != null && (q = (p = q).next) != null)
+ if ((q = p.next) != null &&
+ (q = (p = q).next) != null)
// Check for tail updates every other hop.
// If p == q, we are sure to follow tail instead.
p = (t != (t = tail)) ? t : q;
@@ -1591,7 +1425,6 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
else {
// p is last node
beginningOfTheEnd.lazySetPrev(p); // CAS piggyback
-
if (p.casNext(null, beginningOfTheEnd)) {
// Successful CAS is the linearization point
// for all elements to be added to this deque.
@@ -1599,26 +1432,22 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
// Try a little harder to update tail,
// since we may be adding many elements.
t = tail;
-
if (last.next == null)
casTail(t, last);
}
-
return true;
}
// Lost CAS race to another thread; re-read next
}
}
- }
}
/**
* Removes all of the elements from this deque.
*/
- @Override public void clear() {
- while (pollFirst() != null) {
- // No-op.
- }
+ public void clear() {
+ while (pollFirst() != null)
+ ;
}
/**
@@ -1634,7 +1463,7 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
*
* @return an array containing all of the elements in this deque
*/
- @Override public Object[] toArray() {
+ public Object[] toArray() {
return toArrayList().toArray();
}
@@ -1661,8 +1490,7 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
* The following code can be used to dump the deque into a newly
* allocated array of {@code String}:
*
- * <pre>
- * String[] y = x.toArray(new String[0]);</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
@@ -1676,8 +1504,7 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
* this deque
* @throws NullPointerException if the specified array is null
*/
- @SuppressWarnings( {"SuspiciousToArrayCall"})
- @Override public <T> T[] toArray(T[] a) {
+ public <T> T[] toArray(T[] a) {
return toArrayList().toArray(a);
}
@@ -1694,8 +1521,8 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
*
* @return an iterator over the elements in this deque in proper sequence
*/
- @Override public Iterator<E> iterator() {
- return new Iter();
+ public Iterator<E> iterator() {
+ return new Itr();
}
/**
@@ -1712,26 +1539,11 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
*
* @return an iterator over the elements in this deque in reverse order
*/
- @Override public Iterator<E> descendingIterator() {
- return new DescendingIter();
- }
-
- /**
- * Extended iterator interface.
- */
- public interface IteratorEx<E> extends Iterator<E> {
- /**
- * Same semantics as iterator's remove, but will return {@code false} if remove did not happen.
- *
- * @return {@code True} if element was removed by this call, {@code false} otherwise.
- */
- public boolean removex();
+ public Iterator<E> descendingIterator() {
+ return new DescendingItr();
}
- /**
- * Abstract iterator.
- */
- private abstract class AbstractIter implements IteratorEx<E> {
+ private abstract class AbstractItr implements Iterator<E> {
/**
* Next node to return item for.
*/
@@ -1751,21 +1563,10 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
*/
private Node<E> lastRet;
- /**
- * @return Starting node.
- */
abstract Node<E> startNode();
-
- /**
- * @param p Node.
- * @return Next node.
- */
abstract Node<E> nextNode(Node<E> p);
- /**
- * Advances to first element.
- */
- AbstractIter() {
+ AbstractItr() {
advance();
}
@@ -1777,150 +1578,127 @@ public class ConcurrentLinkedDeque8<E> extends AbstractCollection<E> implements
lastRet = nextNode;
Node<E> p = (nextNode == null) ? startNode() : nextNode(nextNode);
-
for (;; p = nextNode(p)) {
if (p == null) {
// p might be active end or TERMINATOR node; both are OK
nextNode = null;
nextItem = null;
-
break;
}
-
E item = p.item;
-
if (item != null) {
nextNode = p;
nextItem = item;
-
break;
}
}
}
- /** {@inheritDoc} */
- @Override public boolean hasNext() {
+ public boolean hasNext() {
return nextItem != null;
}
- /** {@inheritDoc} */
- @Override public E next() {
+ public E next() {
E item = nextItem;
-
- if (item == null)
- throw new NoSuchElementException();
-
+ if (item == null) throw new NoSuchElementException();
advance();
-
return item;
}
- /** {@inheritDoc} */
- @Override public void remove() {
+ public void remove() {
Node<E> l = lastRet;
-
- if (l == null)
- throw new IllegalStateException();
-
- unlinkx(l);
-
+ if (l == null) throw new IllegalStateException();
+ l.item = null;
+ unlink(l);
lastRet = null;
}
+ }
- /** {@inheritDoc} */
- @Override public boolean removex() {
- Node<E> l = lastRet;
-
- if (l == null)
- throw new IllegalStateException();
-
- boolean res = unlinkx(l);
-
- lastRet = null;
+ /** Forward iterator */
+ private class Itr extends AbstractItr {
+ Node<E> startNode() { return first(); }
+ Node<E> nextNode(Node<E> p) { return succ(p); }
+ }
- return res;
- }
+ /** Descending iterator */
+ private class DescendingItr extends AbstractItr {
+ Node<E> startNode() { return last(); }
+ Node<E> nextNode(Node<E> p) { return pred(p); }
}
/**
- * Forward iterator
+ * Saves this deque to a stream (that is, serializes it).
+ *
+ * @serialData All of the elements (each an {@code E}) in
+ * the proper order, followed by a null
*/
- private class Iter extends AbstractIter {
- /** {@inheritDoc} */
- @Override Node<E> startNode() {
- return first();
- }
+ private void writeObject(java.io.ObjectOutputStream s)
+ throws java.io.IOException {
+
+ // Write out any hidden stuff
+ s.defaultWriteObject();
- /** {@inheritDoc} */
- @Override Node<E> nextNode(Node<E> p) {
- return successor(p);
+ // Write out all elements in the proper order.
+ for (Node<E> p = first(); p != null; p = succ(p)) {
+ E item = p.item;
+ if (item != null)
+ s.writeObject(item);
}
+
+ // Use trailing null as sentinel
+ s.writeObject(null);
}
/**
- * Descending iterator.
+ * Reconstitutes this deque from a stream (that is, deserializes it).
*/
- private class DescendingIter extends AbstractIter {
- /** {@inheritDoc} */
- @Override Node<E> startNode() {
- return last();
- }
+ private void readObject(java.io.ObjectInputStream s)
+ throws java.io.IOException, ClassNotFoundException {
+ s.defaultReadObject();
- /** {@inheritDoc} */
- @Override Node<E> nextNode(Node<E> p) {
- return predecessor(p);
+ // Read in elements until trailing null sentinel found
+ Node<E> h = null, t = null;
+ Object item;
+ while ((item = s.readObject()) != null) {
+ @SuppressWarnings("unchecked")
+ Node<E> newNode = new Node<E>((E) item);
+ if (h == null)
+ h = t = newNode;
+ else {
+ t.lazySetNext(newNode);
+ newNode.lazySetPrev(t);
+ t = newNode;
+ }
}
+ initHeadTail(h, t);
}
- /**
- * CAS for head.
- *
- * @param cmp Compare value.
- * @param val New value.
- * @return {@code True} if set.
- */
private boolean casHead(Node<E> cmp, Node<E> val) {
return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val);
}
- /**
- * CAS for tail.
- *
- * @param cmp Compare value.
- * @param val New value.
- * @return {@code True} if set.
- */
private boolean casTail(Node<E> cmp, Node<E> val) {
return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val);
}
- /** Unsafe. */
- private static final Unsafe UNSAFE;
+ // Unsafe mechanics
- /** Head offset. */
+ private static final sun.misc.Unsafe UNSAFE;
private static final long headOffset;
-
- /** Tail offset. */
private static final long tailOffset;
-
- /**
- * Initialize terminators using unsafe semantics.
- */
static {
PREV_TERMINATOR = new Node<Object>();
PREV_TERMINATOR.next = PREV_TERMINATOR;
NEXT_TERMINATOR = new Node<Object>();
NEXT_TERMINATOR.prev = NEXT_TERMINATOR;
-
try {
UNSAFE = unsafe();
-
- Class cls = ConcurrentLinkedDeque8.class;
-
- headOffset = UNSAFE.objectFieldOffset(cls.getDeclaredField("head"));
- tailOffset = UNSAFE.objectFieldOffset(cls.getDeclaredField("tail"));
- }
- catch (Exception e) {
+ Class<?> k = ConcurrentLinkedDeque8.class;
+ headOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("head"));
+ tailOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("tail"));
+ } catch (Exception e) {
throw new Error(e);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/455b96fc/modules/core/src/main/java/org/jsr166/LongAdder8.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/jsr166/LongAdder8.java b/modules/core/src/main/java/org/jsr166/LongAdder8.java
index 2480a5d..79ea32e 100644
--- a/modules/core/src/main/java/org/jsr166/LongAdder8.java
+++ b/modules/core/src/main/java/org/jsr166/LongAdder8.java
@@ -5,8 +5,11 @@
*/
/*
- * The initial version of this file was copied from JSR-166:
- * http://gee.cs.oswego.edu/dl/concurrency-interest/
+ * The latest version of the file corresponds to the following CVS commit:
+ * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/main/java/util/concurrent/atomic/LongAdder.java?pathrev=1.3
+ *
+ * The later versions are based on updated Striped64 that uses java.util.function package which is unavailable in JDK 7.
+ * Thus they can't be imported.
*/
package org.jsr166;
@@ -22,7 +25,7 @@ import java.util.concurrent.atomic.*;
* #longValue}) returns the current total combined across the
* variables maintaining the sum.
*
- * <p> This class is usually preferable to {@link AtomicLong} when
+ * <p>This class is usually preferable to {@link AtomicLong} when
* multiple threads update a common sum that is used for purposes such
* as collecting statistics, not for fine-grained synchronization
* control. Under low update contention, the two classes have similar
@@ -36,7 +39,7 @@ import java.util.concurrent.atomic.*;
* collection keys.
*
* <p><em>jsr166e note: This class is targeted to be placed in
- * java.util.concurrent.atomic<em>
+ * java.util.concurrent.atomic.</em>
*
* @since 1.8
* @author Doug Lea
@@ -67,8 +70,8 @@ public class LongAdder8 extends Striped64_8 implements Serializable {
boolean uncontended = true;
int h = (hc = threadHashCode.get()).code;
if (as == null || (n = as.length) < 1 ||
- (a = as[(n - 1) & h]) == null ||
- !(uncontended = a.cas(v = a.value, v + x)))
+ (a = as[(n - 1) & h]) == null ||
+ !(uncontended = a.cas(v = a.value, v + x)))
retryUpdate(x, hc, uncontended);
}
}
@@ -149,6 +152,14 @@ public class LongAdder8 extends Striped64_8 implements Serializable {
}
/**
+ * Returns the String representation of the {@link #sum}.
+ * @return the String representation of the {@link #sum}
+ */
+ public String toString() {
+ return Long.toString(sum());
+ }
+
+ /**
* Equivalent to {@link #sum}.
*
* @return the sum
@@ -182,25 +193,17 @@ public class LongAdder8 extends Striped64_8 implements Serializable {
}
private void writeObject(java.io.ObjectOutputStream s)
- throws java.io.IOException {
+ throws java.io.IOException {
s.defaultWriteObject();
s.writeLong(sum());
}
private void readObject(ObjectInputStream s)
- throws IOException, ClassNotFoundException {
+ throws IOException, ClassNotFoundException {
s.defaultReadObject();
busy = 0;
cells = null;
base = s.readLong();
}
- /**
- * Returns the String representation of the {@link #sum}.
- *
- * @return String representation of the {@link #sum}
- */
- public String toString() {
- return Long.toString(sum());
- }
}
http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/455b96fc/modules/core/src/main/java/org/jsr166/README.txt
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/jsr166/README.txt b/modules/core/src/main/java/org/jsr166/README.txt
new file mode 100644
index 0000000..491f2b4
--- /dev/null
+++ b/modules/core/src/main/java/org/jsr166/README.txt
@@ -0,0 +1,11 @@
+Package contains classes that from JSR166.
+
+The files were imported from the following repositories:
+- http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/main/
+- http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jdk7/
+
+To keep the imported files up-to-date each of them (except ConcurrentLinkedHashMap) contains a reference to
+a corresponding CVS commit.
+
+For more information please refer to the community page:
+http://gee.cs.oswego.edu/dl/concurrency-interest/
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/455b96fc/modules/core/src/main/java/org/jsr166/Striped64_8.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/jsr166/Striped64_8.java b/modules/core/src/main/java/org/jsr166/Striped64_8.java
index 9a4b1db..7281334 100644
--- a/modules/core/src/main/java/org/jsr166/Striped64_8.java
+++ b/modules/core/src/main/java/org/jsr166/Striped64_8.java
@@ -5,8 +5,11 @@
*/
/*
- * The initial version of this file was copied from JSR-166:
- * http://gee.cs.oswego.edu/dl/concurrency-interest/
+ * The latest version of the file corresponds to the following CVS commit:
+ * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/main/java/util/concurrent/atomic/Striped64.java?pathrev=1.1
+ *
+ * The later versions use classes from java.util.function package that are unavailable in JDK 7.
+ * Thus they can't be imported.
*/
package org.jsr166;
@@ -329,18 +332,19 @@ abstract class Striped64_8 extends Number {
} catch (SecurityException se) {
try {
return java.security.AccessController.doPrivileged
- (new java.security
- .PrivilegedExceptionAction<sun.misc.Unsafe>() {
+ (new java.security.PrivilegedExceptionAction<sun.misc.Unsafe>() {
public sun.misc.Unsafe run() throws Exception {
- java.lang.reflect.Field f = sun.misc
- .Unsafe.class.getDeclaredField("theUnsafe");
+ java.lang.reflect.Field f = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
- return (sun.misc.Unsafe) f.get(null);
- }});
+
+ return (sun.misc.Unsafe)f.get(null);
+ }
+ });
} catch (java.security.PrivilegedActionException e) {
throw new RuntimeException("Could not initialize intrinsics",
- e.getCause());
+ e.getCause());
}
}
}
+
}
http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/455b96fc/modules/core/src/main/java/org/jsr166/ThreadLocalRandom8.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/jsr166/ThreadLocalRandom8.java b/modules/core/src/main/java/org/jsr166/ThreadLocalRandom8.java
index d7d5736..192db47 100644
--- a/modules/core/src/main/java/org/jsr166/ThreadLocalRandom8.java
+++ b/modules/core/src/main/java/org/jsr166/ThreadLocalRandom8.java
@@ -5,8 +5,12 @@
*/
/*
- * The initial version of this file was copied from JSR-166:
- * http://gee.cs.oswego.edu/dl/concurrency-interest/
+ * The latest version of the file corresponds to the following CVS commit:
+ * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jdk7/java/util/concurrent/
+ * ThreadLocalRandom.java.java?pathrev=1.3
+ *
+ * Note, that the repository above is JDK 7 based that is kept up-to-date too.
+ * The main repository (JDK 8 based) uses JDK 8 features significantly that unavailable in JDK 7.
*/
package org.jsr166;
@@ -22,7 +26,8 @@ import java.util.*;
* than shared {@code Random} objects in concurrent programs will
* typically encounter much less overhead and contention. Use of
* {@code ThreadLocalRandom} is particularly appropriate when multiple
- * tasks use random numbers in parallel in thread pools.
+ * tasks (for example, each a {@link ForkJoinTask}) use random numbers
+ * in parallel in thread pools.
*
* <p>Usages of this class should typically be of the form:
* {@code ThreadLocalRandom.current().nextX(...)} (where
@@ -38,7 +43,7 @@ import java.util.*;
*/
@SuppressWarnings("ALL")
public class ThreadLocalRandom8 extends Random {
- // same constants as Random, but must be re-declared because private
+ // same constants as Random, but must be redeclared because private
private static final long multiplier = 0x5DEECE66DL;
private static final long addend = 0xBL;
private static final long mask = (1L << 48) - 1;
@@ -112,9 +117,9 @@ public class ThreadLocalRandom8 extends Random {
*
* @param least the least value returned
* @param bound the upper bound (exclusive)
+ * @return the next value
* @throws IllegalArgumentException if least greater than or equal
* to bound
- * @return the next value
*/
public int nextInt(int least, int bound) {
if (least >= bound)
@@ -177,7 +182,7 @@ public class ThreadLocalRandom8 extends Random {
* @throws IllegalArgumentException if n is not positive
*/
public double nextDouble(double n) {
- if (n <= 0)
+ if (!(n > 0))
throw new IllegalArgumentException("n must be positive");
return nextDouble() * n;
}
@@ -199,4 +204,4 @@ public class ThreadLocalRandom8 extends Random {
}
private static final long serialVersionUID = -5851777807851030925L;
-}
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/455b96fc/modules/core/src/main/java/org/jsr166/package-info.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/jsr166/package-info.java b/modules/core/src/main/java/org/jsr166/package-info.java
index 135297c..6e839bb 100644
--- a/modules/core/src/main/java/org/jsr166/package-info.java
+++ b/modules/core/src/main/java/org/jsr166/package-info.java
@@ -5,6 +5,16 @@
*/
/**
- * Classes that were originally introduced in JSR166.
+ * Package contains classes that from JSR166.
+ *
+ * The files were imported from the following repositories:
+ * - http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/main/
+ * - http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jdk7/
+ *
+ * To keep the imported files up-to-date each of them (except ConcurrentLinkedHashMap) contains a reference to
+ * a corresponding CVS commit.
+ *
+ * For more information please refer to the community page:
+ * http://gee.cs.oswego.edu/dl/concurrency-interest/
*/
package org.jsr166;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/455b96fc/modules/hadoop/src/test/java/org/apache/ignite/igfs/IgniteHadoopFileSystemAbstractSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/hadoop/src/test/java/org/apache/ignite/igfs/IgniteHadoopFileSystemAbstractSelfTest.java b/modules/hadoop/src/test/java/org/apache/ignite/igfs/IgniteHadoopFileSystemAbstractSelfTest.java
index b828aad..f215efb 100644
--- a/modules/hadoop/src/test/java/org/apache/ignite/igfs/IgniteHadoopFileSystemAbstractSelfTest.java
+++ b/modules/hadoop/src/test/java/org/apache/ignite/igfs/IgniteHadoopFileSystemAbstractSelfTest.java
@@ -90,7 +90,7 @@ public abstract class IgniteHadoopFileSystemAbstractSelfTest extends IgfsCommonA
private static final int THREAD_CNT = 8;
/** IP finder. */
- private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+ private final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
/** Barrier for multithreaded tests. */
private static CyclicBarrier barrier;
[4/7] incubator-ignite git commit: ignite-sprint-6: revert changes in
concurrent map
Posted by sb...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8203caf7/modules/core/src/main/java/org/jsr166/ConcurrentHashMap8.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/jsr166/ConcurrentHashMap8.java b/modules/core/src/main/java/org/jsr166/ConcurrentHashMap8.java
index 727db4c..041130b 100644
--- a/modules/core/src/main/java/org/jsr166/ConcurrentHashMap8.java
+++ b/modules/core/src/main/java/org/jsr166/ConcurrentHashMap8.java
@@ -5,22 +5,20 @@
*/
/*
- * The latest version of the file corresponds to the following CVS commit:
- * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jdk7/java/util/concurrent/ConcurrentHashMap.java?pathrev=1.43
+ * The latest version of the file was copied from the following CVS repository:
+ * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/
*
- * Note, that the repository above is JDK 7 based that is kept up-to-date too.
- * The main repository (JDK 8 based) uses JDK 8 features significantly that unavailable in JDK 7.
+ * Corresponding commit version in CVS repository is unknown (lost in our side).
+ * On the other hand we can't simply synch the latest version from CVS here, because Ignite uses functionality that
+ * is no longer supported.
*/
-
package org.jsr166;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
-import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.*;
-import java.lang.reflect.*;
/**
* A hash table supporting full concurrency of retrievals and
@@ -74,15 +72,21 @@ import java.lang.reflect.*;
* expected {@code concurrencyLevel} as an additional hint for
* internal sizing. Note that using many keys with exactly the same
* {@code hashCode()} is a sure way to slow down performance of any
- * hash table. To ameliorate impact, when keys are {@link Comparable},
- * this class may use comparison order among keys to help break ties.
+ * hash table.
*
- * <p>A {@link Set} projection of a ConcurrentHashMap may be created
+ * <p>A {@link Set} projection of a ConcurrentHashMapV8 may be created
* (using {@link #newKeySet()} or {@link #newKeySet(int)}), or viewed
* (using {@link #keySet(Object)} when only keys are of interest, and the
* mapped values are (perhaps transiently) not used or all take the
* same mapping value.
*
+ * <p>A ConcurrentHashMapV8 can be used as scalable frequency map (a
+ * form of histogram or multiset) by using {@link LongAdder8} values
+ * and initializing via {@link #computeIfAbsent}. For example, to add
+ * a count to a {@code ConcurrentHashMapV8<String,LongAdder8> freqs}, you
+ * can use {@code freqs.computeIfAbsent(k -> new
+ * LongAdder8()).increment();}
+ *
* <p>This class and its views and iterators implement all of the
* <em>optional</em> methods of the {@link Map} and {@link Iterator}
* interfaces.
@@ -90,9 +94,90 @@ import java.lang.reflect.*;
* <p>Like {@link Hashtable} but unlike {@link HashMap}, this class
* does <em>not</em> allow {@code null} to be used as a key or value.
*
- * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
- * Java Collections Framework</a>.
+ * <ul>
+ * <li> forEach: Perform a given action on each element.
+ * A variant form applies a given transformation on each element
+ * before performing the action.</li>
+ *
+ * <li> search: Return the first available non-null result of
+ * applying a given function on each element; skipping further
+ * search when a result is found.</li>
+ *
+ * <li> reduce: Accumulate each element. The supplied reduction
+ * function cannot rely on ordering (more formally, it should be
+ * both associative and commutative). There are five variants:
+ *
+ * <ul>
+ *
+ * <li> Plain reductions. (There is not a form of this method for
+ * (key, value) function arguments since there is no corresponding
+ * return type.)</li>
+ *
+ * <li> Mapped reductions that accumulate the results of a given
+ * function applied to each element.</li>
+ *
+ * <li> Reductions to scalar doubles, longs, and ints, using a
+ * given basis value.</li>
+ *
+ * </li>
+ * </ul>
+ * </ul>
+ *
+ * <p>The concurrency properties of bulk operations follow
+ * from those of ConcurrentHashMapV8: Any non-null result returned
+ * from {@code get(key)} and related access methods bears a
+ * happens-before relation with the associated insertion or
+ * update. The result of any bulk operation reflects the
+ * composition of these per-element relations (but is not
+ * necessarily atomic with respect to the map as a whole unless it
+ * is somehow known to be quiescent). Conversely, because keys
+ * and values in the map are never null, null serves as a reliable
+ * atomic indicator of the current lack of any result. To
+ * maintain this property, null serves as an implicit basis for
+ * all non-scalar reduction operations. For the double, long, and
+ * int versions, the basis should be one that, when combined with
+ * any other value, returns that other value (more formally, it
+ * should be the identity element for the reduction). Most common
+ * reductions have these properties; for example, computing a sum
+ * with basis 0 or a minimum with basis MAX_VALUE.
+ *
+ * <p>Search and transformation functions provided as arguments
+ * should similarly return null to indicate the lack of any result
+ * (in which case it is not used). In the case of mapped
+ * reductions, this also enables transformations to serve as
+ * filters, returning null (or, in the case of primitive
+ * specializations, the identity basis) if the element should not
+ * be combined. You can create compound transformations and
+ * filterings by composing them yourself under this "null means
+ * there is nothing there now" rule before using them in search or
+ * reduce operations.
+ *
+ * <p>Methods accepting and/or returning Entry arguments maintain
+ * key-value associations. They may be useful for example when
+ * finding the key for the greatest value. Note that "plain" Entry
+ * arguments can be supplied using {@code new
+ * AbstractMap.SimpleEntry(k,v)}.
+ *
+ * <p>Bulk operations may complete abruptly, throwing an
+ * exception encountered in the application of a supplied
+ * function. Bear in mind when handling such exceptions that other
+ * concurrently executing functions could also have thrown
+ * exceptions, or would have done so if the first exception had
+ * not occurred.
+ *
+ * <p>Parallel speedups for bulk operations compared to sequential
+ * processing are common but not guaranteed. Operations involving
+ * brief functions on small maps may execute more slowly than
+ * sequential loops if the underlying work to parallelize the
+ * computation is more expensive than the computation itself.
+ * Similarly, parallelization may not lead to much actual parallelism
+ * if all processors are busy performing unrelated tasks.
+ *
+ * <p>All arguments to all task methods must be non-null.
+ *
+ * <p><em>jsr166e note: During transition, this class
+ * uses nested functional interfaces with different names but the
+ * same forms as those expected for JDK8.</em>
*
* @since 1.5
* @author Doug Lea
@@ -100,9 +185,80 @@ import java.lang.reflect.*;
* @param <V> the type of mapped values
*/
@SuppressWarnings("ALL")
-public class ConcurrentHashMap8<K,V> implements ConcurrentMap<K,V>, Serializable {
+public class ConcurrentHashMap8<K, V>
+ implements ConcurrentMap<K, V>, Serializable {
private static final long serialVersionUID = 7249069246763182397L;
+ /**
+ * A partitionable iterator. A Spliterator can be traversed
+ * directly, but can also be partitioned (before traversal) by
+ * creating another Spliterator that covers a non-overlapping
+ * portion of the elements, and so may be amenable to parallel
+ * execution.
+ *
+ * <p>This interface exports a subset of expected JDK8
+ * functionality.
+ *
+ * <p>Sample usage: Here is one (of the several) ways to compute
+ * the sum of the values held in a map using the ForkJoin
+ * framework. As illustrated here, Spliterators are well suited to
+ * designs in which a task repeatedly splits off half its work
+ * into forked subtasks until small enough to process directly,
+ * and then joins these subtasks. Variants of this style can also
+ * be used in completion-based designs.
+ *
+ * <pre>
+ * {@code ConcurrentHashMapV8<String, Long> m = ...
+ * // split as if have 8 * parallelism, for load balance
+ * int n = m.size();
+ * int p = aForkJoinPool.getParallelism() * 8;
+ * int split = (n < p)? n : p;
+ * long sum = aForkJoinPool.invoke(new SumValues(m.valueSpliterator(), split, null));
+ * // ...
+ * static class SumValues extends RecursiveTask<Long> {
+ * final Spliterator<Long> s;
+ * final int split; // split while > 1
+ * final SumValues nextJoin; // records forked subtasks to join
+ * SumValues(Spliterator<Long> s, int depth, SumValues nextJoin) {
+ * this.s = s; this.depth = depth; this.nextJoin = nextJoin;
+ * }
+ * public Long compute() {
+ * long sum = 0;
+ * SumValues subtasks = null; // fork subtasks
+ * for (int s = split >>> 1; s > 0; s >>>= 1)
+ * (subtasks = new SumValues(s.split(), s, subtasks)).fork();
+ * while (s.hasNext()) // directly process remaining elements
+ * sum += s.next();
+ * for (SumValues t = subtasks; t != null; t = t.nextJoin)
+ * sum += t.join(); // collect subtask results
+ * return sum;
+ * }
+ * }
+ * }</pre>
+ */
+ public static interface Spliterator<T> extends Iterator<T> {
+ /**
+ * Returns a Spliterator covering approximately half of the
+ * elements, guaranteed not to overlap with those subsequently
+ * returned by this Spliterator. After invoking this method,
+ * the current Spliterator will <em>not</em> produce any of
+ * the elements of the returned Spliterator, but the two
+ * Spliterators together will produce all of the elements that
+ * would have been produced by this Spliterator had this
+ * method not been called. The exact number of elements
+ * produced by the returned Spliterator is not guaranteed, and
+ * may be zero (i.e., with {@code hasNext()} reporting {@code
+ * false}) if this Spliterator cannot be further split.
+ *
+ * @return a Spliterator covering approximately half of the
+ * elements
+ * @throws IllegalStateException if this Spliterator has
+ * already commenced traversing elements
+ */
+ Spliterator<T> split();
+ }
+
+
/*
* Overview:
*
@@ -113,21 +269,18 @@ public class ConcurrentHashMap8<K,V> implements ConcurrentMap<K,V>, Serializable
* the same or better than java.util.HashMap, and to support high
* initial insertion rates on an empty table by many threads.
*
- * This map usually acts as a binned (bucketed) hash table. Each
- * key-value mapping is held in a Node. Most nodes are instances
- * of the basic Node class with hash, key, value, and next
- * fields. However, various subclasses exist: TreeNodes are
- * arranged in balanced trees, not lists. TreeBins hold the roots
- * of sets of TreeNodes. ForwardingNodes are placed at the heads
- * of bins during resizing. ReservationNodes are used as
- * placeholders while establishing values in computeIfAbsent and
- * related methods. The types TreeBin, ForwardingNode, and
- * ReservationNode do not hold normal user keys, values, or
- * hashes, and are readily distinguishable during search etc
- * because they have negative hash fields and null key and value
- * fields. (These special nodes are either uncommon or transient,
- * so the impact of carrying around some unused fields is
- * insignificant.)
+ * Each key-value mapping is held in a Node. Because Node fields
+ * can contain special values, they are defined using plain Object
+ * types. Similarly in turn, all internal methods that use them
+ * work off Object types. And similarly, so do the internal
+ * methods of auxiliary iterator and view classes. All public
+ * generic typed methods relay in/out of these internal methods,
+ * supplying null-checks and casts as needed. This also allows
+ * many of the public methods to be factored into a smaller number
+ * of internal methods (although sadly not so for the five
+ * variants of put-related operations). The validation-based
+ * approach explained below leads to a lot of code sprawl because
+ * retry-control precludes factoring into smaller methods.
*
* The table is lazily initialized to a power-of-two size upon the
* first insertion. Each bin in the table normally contains a
@@ -135,12 +288,24 @@ public class ConcurrentHashMap8<K,V> implements ConcurrentMap<K,V>, Serializable
* Table accesses require volatile/atomic reads, writes, and
* CASes. Because there is no other way to arrange this without
* adding further indirections, we use intrinsics
- * (sun.misc.Unsafe) operations.
+ * (sun.misc.Unsafe) operations. The lists of nodes within bins
+ * are always accurately traversable under volatile reads, so long
+ * as lookups check hash code and non-nullness of value before
+ * checking key equality.
*
- * We use the top (sign) bit of Node hash fields for control
- * purposes -- it is available anyway because of addressing
- * constraints. Nodes with negative hash fields are specially
- * handled or ignored in map methods.
+ * We use the top two bits of Node hash fields for control
+ * purposes -- they are available anyway because of addressing
+ * constraints. As explained further below, these top bits are
+ * used as follows:
+ * 00 - Normal
+ * 01 - Locked
+ * 11 - Locked and may have a thread waiting for lock
+ * 10 - Node is a forwarding node
+ *
+ * The lower 30 bits of each Node's hash field contain a
+ * transformation of the key's hash code, except for forwarding
+ * nodes, for which the lower bits are zero (and so always have
+ * hash field == MOVED).
*
* Insertion (via put or its variants) of the first node in an
* empty bin is performed by just CASing it to the bin. This is
@@ -149,15 +314,22 @@ public class ConcurrentHashMap8<K,V> implements ConcurrentMap<K,V>, Serializable
* delete, and replace) require locks. We do not want to waste
* the space required to associate a distinct lock object with
* each bin, so instead use the first node of a bin list itself as
- * a lock. Locking support for these locks relies on builtin
- * "synchronized" monitors.
+ * a lock. Blocking support for these locks relies on the builtin
+ * "synchronized" monitors. However, we also need a tryLock
+ * construction, so we overlay these by using bits of the Node
+ * hash field for lock control (see above), and so normally use
+ * builtin monitors only for blocking and signalling using
+ * wait/notifyAll constructions. See Node.tryAwaitLock.
*
* Using the first node of a list as a lock does not by itself
* suffice though: When a node is locked, any update must first
* validate that it is still the first node after locking it, and
* retry if not. Because new nodes are always appended to lists,
* once a node is first in a bin, it remains first until deleted
- * or the bin becomes invalidated (upon resizing).
+ * or the bin becomes invalidated (upon resizing). However,
+ * operations that only conditionally update may inspect nodes
+ * until the point of update. This is a converse of sorts to the
+ * lazy locking technique described by Herlihy & Shavit.
*
* The main disadvantage of per-bin locks is that other update
* operations on other nodes in a bin list protected by the same
@@ -190,12 +362,15 @@ public class ConcurrentHashMap8<K,V> implements ConcurrentMap<K,V>, Serializable
* sometimes deviate significantly from uniform randomness. This
* includes the case when N > (1<<30), so some keys MUST collide.
* Similarly for dumb or hostile usages in which multiple keys are
- * designed to have identical hash codes or ones that differs only
- * in masked-out high bits. So we use a secondary strategy that
- * applies when the number of nodes in a bin exceeds a
- * threshold. These TreeBins use a balanced tree to hold nodes (a
- * specialized form of red-black trees), bounding search time to
- * O(log N). Each search step in a TreeBin is at least twice as
+ * designed to have identical hash codes. Also, although we guard
+ * against the worst effects of this (see method spread), sets of
+ * hashes may differ only in bits that do not impact their bin
+ * index for a given power-of-two mask. So we use a secondary
+ * strategy that applies when the number of nodes in a bin exceeds
+ * a threshold, and at least one of the keys implements
+ * Comparable. These TreeBins use a balanced tree to hold nodes
+ * (a specialized form of red-black trees), bounding search time
+ * to O(log N). Each search step in a TreeBin is around twice as
* slow as in a regular list, but given that N cannot exceed
* (1<<64) (before running out of addresses) this bounds search
* steps, lock hold times, etc, to reasonable constants (roughly
@@ -206,50 +381,43 @@ public class ConcurrentHashMap8<K,V> implements ConcurrentMap<K,V>, Serializable
* iterators in the same way.
*
* The table is resized when occupancy exceeds a percentage
- * threshold (nominally, 0.75, but see below). Any thread
- * noticing an overfull bin may assist in resizing after the
- * initiating thread allocates and sets up the replacement array.
- * However, rather than stalling, these other threads may proceed
- * with insertions etc. The use of TreeBins shields us from the
- * worst case effects of overfilling while resizes are in
- * progress. Resizing proceeds by transferring bins, one by one,
- * from the table to the next table. However, threads claim small
- * blocks of indices to transfer (via field transferIndex) before
- * doing so, reducing contention. A generation stamp in field
- * sizeCtl ensures that resizings do not overlap. 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. On average, 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. Upon transfer, the old table bin contains
- * only a special forwarding node (with hash field "MOVED") that
- * contains the next table as its key. On encountering a
- * forwarding node, access and update operations restart, using
- * the new table.
- *
- * Each bin transfer requires its bin lock, which can stall
- * waiting for locks while resizing. However, because other
- * threads can join in and help resize rather than contend for
- * locks, average aggregate waits become shorter as resizing
- * progresses. The transfer operation must also ensure that all
- * accessible bins in both the old and new table are usable by any
- * traversal. This is arranged in part by proceeding from the
- * last bin (table.length - 1) up towards the first. Upon seeing
- * a forwarding node, traversals (see class Traverser) arrange to
- * move to the new table without revisiting nodes. To ensure that
- * no intervening nodes are skipped even when moved out of order,
- * a stack (see class TableStack) is created on first encounter of
- * a forwarding node during a traversal, to maintain its place if
- * later processing the current table. The need for these
- * save/restore mechanics is relatively rare, but when one
- * forwarding node is encountered, typically many more will be.
- * So Traversers use a simple caching scheme to avoid creating so
- * many new TableStack nodes. (Thanks to Peter Levart for
- * suggesting use of a stack here.)
+ * threshold (nominally, 0.75, but see below). Only a single
+ * thread performs the resize (using field "sizeCtl", to arrange
+ * exclusion), but the table otherwise remains usable for reads
+ * and updates. Resizing proceeds by transferring bins, one by
+ * one, from the table to the next 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. On
+ * average, 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. Upon
+ * transfer, the old table bin contains only a special forwarding
+ * node (with hash field "MOVED") that contains the next table as
+ * its key. On encountering a forwarding node, access and update
+ * operations restart, using the new table.
+ *
+ * Each bin transfer requires its bin lock. However, unlike other
+ * cases, a transfer can skip a bin if it fails to acquire its
+ * lock, and revisit it later (unless it is a TreeBin). Method
+ * rebuild maintains a buffer of TRANSFER_BUFFER_SIZE bins that
+ * have been skipped because of failure to acquire a lock, and
+ * blocks only if none are available (i.e., only very rarely).
+ * The transfer operation must also ensure that all accessible
+ * bins in both the old and new table are usable by any traversal.
+ * When there are no lock acquisition failures, this is arranged
+ * simply by proceeding from the last bin (table.length - 1) up
+ * towards the first. Upon seeing a forwarding node, traversals
+ * (see class Iter) arrange to move to the new table
+ * without revisiting nodes. However, when any node is skipped
+ * during a transfer, all earlier table bins may have become
+ * visible, so are initialized with a reverse-forwarding node back
+ * to the old table until the new ones are established. (This
+ * sometimes requires transiently locking a forwarding node, which
+ * is possible under the above encoding.) These more expensive
+ * mechanics trigger only when necessary.
*
* The traversal scheme also applies to partial traversals of
* ranges of bins (via an alternate Traverser constructor)
@@ -264,54 +432,20 @@ public class ConcurrentHashMap8<K,V> implements ConcurrentMap<K,V>, Serializable
* These cases attempt to override the initial capacity settings,
* but harmlessly fail to take effect in cases of races.
*
- * The element count is maintained using a specialization of
- * LongAdder. We need to incorporate a specialization rather than
- * just use a LongAdder in order to access implicit
- * contention-sensing that leads to creation of multiple
- * CounterCells. The counter mechanics avoid contention on
- * updates but can encounter cache thrashing if read too
- * frequently during concurrent access. To avoid reading so often,
- * resizing under contention is attempted only upon adding to a
- * bin already holding two or more nodes. Under uniform hash
- * distributions, the probability of this occurring at threshold
- * is around 13%, meaning that only about 1 in 8 puts check
- * threshold (and after resizing, many fewer do so).
- *
- * TreeBins use a special form of comparison for search and
- * related operations (which is the main reason we cannot use
- * existing collections such as TreeMaps). TreeBins contain
- * Comparable elements, but may contain others, as well as
- * elements that are Comparable but not necessarily Comparable for
- * the same T, so we cannot invoke compareTo among them. To handle
- * this, the tree is ordered primarily by hash value, then by
- * Comparable.compareTo order if applicable. On lookup at a node,
- * if elements are not comparable or compare as 0 then both left
- * and right children may need to be searched in the case of tied
- * hash values. (This corresponds to the full list search that
- * would be necessary if all elements were non-Comparable and had
- * tied hashes.) On insertion, to keep a total ordering (or as
- * close as is required here) across rebalancings, we compare
- * classes and identityHashCodes as tie-breakers. The red-black
- * balancing code is updated from pre-jdk-collections
- * (http://gee.cs.oswego.edu/dl/classes/collections/RBCell.java)
- * based in turn on Cormen, Leiserson, and Rivest "Introduction to
- * Algorithms" (CLR).
- *
- * TreeBins also require an additional locking mechanism. While
- * list traversal is always possible by readers even during
- * updates, tree traversal is not, mainly because of tree-rotations
- * that may change the root node and/or its linkages. TreeBins
- * include a simple read-write lock mechanism parasitic on the
- * main bin-synchronization strategy: Structural adjustments
- * associated with an insertion or removal are already bin-locked
- * (and so cannot conflict with other writers) but must wait for
- * ongoing readers to finish. Since there can be only one such
- * waiter, we use a simple scheme using a single "waiter" field to
- * block writers. However, readers need never block. If the root
- * lock is held, they proceed along the slow traversal path (via
- * next-pointers) until the lock becomes available or the list is
- * exhausted, whichever comes first. These cases are not fast, but
- * maximize aggregate expected throughput.
+ * The element count is maintained using a LongAdder8, which avoids
+ * contention on updates but can encounter cache thrashing if read
+ * too frequently during concurrent access. To avoid reading so
+ * often, resizing is attempted either when a bin lock is
+ * contended, or upon adding to a bin already holding two or more
+ * nodes (checked before adding in the xIfAbsent methods, after
+ * adding in others). Under uniform hash distributions, the
+ * probability of this occurring at threshold is around 13%,
+ * meaning that only about 1 in 8 puts check threshold (and after
+ * resizing, many fewer do so). But this approximation has high
+ * variance for small table sizes, so we check on any collision
+ * for sizes <= 64. The bulk putAll operation further reduces
+ * contention by only committing count updates upon these size
+ * checks.
*
* Maintaining API and serialization compatibility with previous
* versions of this class introduces several oddities. Mainly: We
@@ -321,20 +455,8 @@ public class ConcurrentHashMap8<K,V> implements ConcurrentMap<K,V>, Serializable
* time that we can guarantee to honor it.) We also declare an
* unused "Segment" class that is instantiated in minimal form
* only when serializing.
- *
- * Also, solely for compatibility with previous versions of this
- * class, it extends AbstractMap, even though all of its methods
- * are overridden, so it is just useless baggage.
- *
- * This file is organized to make things a little easier to follow
- * while reading than they might otherwise: First the main static
- * declarations and utilities, then fields, then main public
- * methods (with a few factorings of multiple public methods into
- * internal ones), then sizing methods, trees, traversers, and
- * bulk operations.
*/
-
/* ---------------- Constants -------------- */
/**
@@ -374,2362 +496,2737 @@ public class ConcurrentHashMap8<K,V> implements ConcurrentMap<K,V>, Serializable
private static final float LOAD_FACTOR = 0.75f;
/**
- * The bin count threshold for using a tree rather than list for a
- * bin. Bins are converted to trees when adding an element to a
- * bin with at least this many nodes. The value must be greater
- * than 2, and should be at least 8 to mesh with assumptions in
- * tree removal about conversion back to plain bins upon
- * shrinkage.
+ * The buffer size for skipped bins during transfers. The
+ * value is arbitrary but should be large enough to avoid
+ * most locking stalls during resizes.
*/
- static final int TREEIFY_THRESHOLD = 8;
+ private static final int TRANSFER_BUFFER_SIZE = 32;
/**
- * The bin count threshold for untreeifying a (split) bin during a
- * resize operation. Should be less than TREEIFY_THRESHOLD, and at
- * most 6 to mesh with shrinkage detection under removal.
+ * The bin count threshold for using a tree rather than list for a
+ * bin. The value reflects the approximate break-even point for
+ * using tree-based operations.
*/
- static final int UNTREEIFY_THRESHOLD = 6;
+ private static final int TREE_THRESHOLD = 8;
- /**
- * The smallest table capacity for which bins may be treeified.
- * (Otherwise the table is resized if too many nodes in a bin.)
- * The value should be at least 4 * TREEIFY_THRESHOLD to avoid
- * conflicts between resizing and treeification thresholds.
+ /*
+ * Encodings for special uses of Node hash fields. See above for
+ * explanation.
*/
- static final int MIN_TREEIFY_CAPACITY = 64;
+ static final int MOVED = 0x80000000; // hash field for forwarding nodes
+ static final int LOCKED = 0x40000000; // set/tested only as a bit
+ static final int WAITING = 0xc0000000; // both bits set/tested together
+ static final int HASH_BITS = 0x3fffffff; // usable bits of normal node hash
- /**
- * Minimum number of rebinnings per transfer step. Ranges are
- * subdivided to allow multiple resizer threads. This value
- * serves as a lower bound to avoid resizers encountering
- * excessive memory contention. The value should be at least
- * DEFAULT_CAPACITY.
- */
- private static final int MIN_TRANSFER_STRIDE = 16;
+ /* ---------------- Fields -------------- */
/**
- * The number of bits used for generation stamp in sizeCtl.
- * Must be at least 6 for 32bit arrays.
+ * The array of bins. Lazily initialized upon first insertion.
+ * Size is always a power of two. Accessed directly by iterators.
*/
- private static int RESIZE_STAMP_BITS = 16;
+ transient volatile Node[] table;
/**
- * The maximum number of threads that can help resize.
- * Must fit in 32 - RESIZE_STAMP_BITS bits.
+ * The counter maintaining number of elements.
*/
- private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
+ private transient final LongAdder8 counter;
/**
- * The bit shift for recording size stamp in sizeCtl.
+ * Table initialization and resizing control. When negative, the
+ * table is being initialized or resized. Otherwise, when table is
+ * null, holds the initial table size to use upon creation, or 0
+ * for default. After initialization, holds the next element count
+ * value upon which to resize the table.
*/
- private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
+ private transient volatile int sizeCtl;
+
+ // views
+ private transient KeySetView<K,V> keySet;
+ private transient ValuesView<K,V> values;
+ private transient EntrySetView<K,V> entrySet;
+
+ /** For serialization compatibility. Null unless serialized; see below */
+ private Segment<K,V>[] segments;
+
+ /* ---------------- Table element access -------------- */
/*
- * Encodings for Node hash fields. See above for explanation.
+ * Volatile access methods are used for table elements as well as
+ * elements of in-progress next table while resizing. Uses are
+ * null checked by callers, and implicitly bounds-checked, relying
+ * on the invariants that tab arrays have non-zero size, and all
+ * indices are masked with (tab.length - 1) which is never
+ * negative and always less than length. Note that, to be correct
+ * wrt arbitrary concurrency errors by users, bounds checks must
+ * operate on local variables, which accounts for some odd-looking
+ * inline assignments below.
*/
- static final int MOVED = 0x8fffffff; // (-1) hash for forwarding nodes
- static final int TREEBIN = 0x80000000; // hash for roots of trees
- static final int RESERVED = 0x80000001; // hash for transient reservations
- static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash
- /** Number of CPUS, to place bounds on some sizings */
- static final int NCPU = Runtime.getRuntime().availableProcessors();
+ static final Node tabAt(Node[] tab, int i) { // used by Iter
+ return (Node)UNSAFE.getObjectVolatile(tab, ((long)i<<ASHIFT)+ABASE);
+ }
+
+ private static final boolean casTabAt(Node[] tab, int i, Node c, Node v) {
+ return UNSAFE.compareAndSwapObject(tab, ((long)i<<ASHIFT)+ABASE, c, v);
+ }
- /** For serialization compatibility. */
- private static final ObjectStreamField[] serialPersistentFields = {
- new ObjectStreamField("segments", Segment[].class),
- new ObjectStreamField("segmentMask", Integer.TYPE),
- new ObjectStreamField("segmentShift", Integer.TYPE)
- };
+ private static final void setTabAt(Node[] tab, int i, Node v) {
+ UNSAFE.putObjectVolatile(tab, ((long)i<<ASHIFT)+ABASE, v);
+ }
/* ---------------- Nodes -------------- */
/**
- * Key-value entry. This class is never exported out as a
- * user-mutable Map.Entry (i.e., one supporting setValue; see
- * MapEntry below), but can be used for read-only traversals used
- * in bulk tasks. Subclasses of Node with a negative hash field
- * are special, and contain null keys and values (but are never
- * exported). Otherwise, keys and vals are never null.
+ * Key-value entry. Note that this is never exported out as a
+ * user-visible Map.Entry (see MapEntry below). Nodes with a hash
+ * field of MOVED are special, and do not contain user keys or
+ * values. Otherwise, keys are never null, and null val fields
+ * indicate that a node is in the process of being deleted or
+ * created. For purposes of read-only access, a key may be read
+ * before a val, but can only be used after checking val to be
+ * non-null.
*/
- static class Node<K,V> implements Map.Entry<K,V> {
- final int hash;
- final K key;
- volatile V val;
- Node<K,V> next;
+ static class Node {
+ volatile int hash;
+ final Object key;
+ volatile Object val;
+ volatile Node next;
- Node(int hash, K key, V val, Node<K,V> next) {
+ Node(int hash, Object key, Object val, Node next) {
this.hash = hash;
this.key = key;
this.val = val;
this.next = next;
}
- public final K getKey() { return key; }
- public final V getValue() { return val; }
- public final int hashCode() { return key.hashCode() ^ val.hashCode(); }
- public final String toString(){ return key + "=" + val; }
- public final V setValue(V value) {
- throw new UnsupportedOperationException();
+ /** CompareAndSet the hash field */
+ final boolean casHash(int cmp, int val) {
+ return UNSAFE.compareAndSwapInt(this, hashOffset, cmp, val);
}
- public final boolean equals(Object o) {
- Object k, v, u; Map.Entry<?,?> e;
- return ((o instanceof Map.Entry) &&
- (k = (e = (Map.Entry<?,?>)o).getKey()) != null &&
- (v = e.getValue()) != null &&
- (k == key || k.equals(key)) &&
- (v == (u = val) || v.equals(u)));
- }
+ /** The number of spins before blocking for a lock */
+ static final int MAX_SPINS =
+ Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1;
/**
- * Virtualized support for map.get(); overridden in subclasses.
+ * Spins a while if LOCKED bit set and this node is the first
+ * of its bin, and then sets WAITING bits on hash field and
+ * blocks (once) if they are still set. It is OK for this
+ * method to return even if lock is not available upon exit,
+ * which enables these simple single-wait mechanics.
+ *
+ * The corresponding signalling operation is performed within
+ * callers: Upon detecting that WAITING has been set when
+ * unlocking lock (via a failed CAS from non-waiting LOCKED
+ * state), unlockers acquire the sync lock and perform a
+ * notifyAll.
+ *
+ * The initial sanity check on tab and bounds is not currently
+ * necessary in the only usages of this method, but enables
+ * use in other future contexts.
*/
- Node<K,V> find(int h, Object k) {
- Node<K,V> e = this;
- if (k != null) {
- do {
- K ek;
- if (e.hash == h &&
- ((ek = e.key) == k || (ek != null && k.equals(ek))))
- return e;
- } while ((e = e.next) != null);
+ final void tryAwaitLock(Node[] tab, int i) {
+ if (tab != null && i >= 0 && i < tab.length) { // sanity check
+ int r = ThreadLocalRandom8.current().nextInt(); // randomize spins
+ int spins = MAX_SPINS, h;
+ while (tabAt(tab, i) == this && ((h = hash) & LOCKED) != 0) {
+ if (spins >= 0) {
+ r ^= r << 1; r ^= r >>> 3; r ^= r << 10; // xorshift
+ if (r >= 0 && --spins == 0)
+ Thread.yield(); // yield before block
+ }
+ else if (casHash(h, h | WAITING)) {
+ synchronized (this) {
+ if (tabAt(tab, i) == this &&
+ (hash & WAITING) == WAITING) {
+ try {
+ wait();
+ } catch (InterruptedException ie) {
+ try {
+ Thread.currentThread().interrupt();
+ } catch (SecurityException ignore) {
+ }
+ }
+ }
+ else
+ notifyAll(); // possibly won race vs signaller
+ }
+ break;
+ }
+ }
}
- return null;
}
- }
- /* ---------------- Static utilities -------------- */
-
- /**
- * Spreads (XORs) higher bits of hash to lower and also forces top
- * bit to 0. Because the table uses power-of-two masking, sets of
- * hashes that vary only in bits above the current mask will
- * always collide. (Among known examples are sets of Float keys
- * holding consecutive whole numbers in small tables.) So we
- * apply a transform that spreads the impact of higher bits
- * downward. There is a tradeoff between speed, utility, and
- * quality of bit-spreading. Because many common sets of hashes
- * are already reasonably distributed (so don't benefit from
- * spreading), and because we use trees to handle large sets of
- * collisions in bins, we just XOR some shifted bits in the
- * cheapest possible way to reduce systematic lossage, as well as
- * to incorporate impact of the highest bits that would otherwise
- * never be used in index calculations because of table bounds.
- */
- static final int spread(int h) {
- return (h ^ (h >>> 16)) & HASH_BITS;
- }
+ // Unsafe mechanics for casHash
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long hashOffset;
- /**
- * Returns a power of two table size for the given desired capacity.
- * See Hackers Delight, sec 3.2
- */
- private static final int tableSizeFor(int c) {
- int n = c - 1;
- n |= n >>> 1;
- n |= n >>> 2;
- n |= n >>> 4;
- n |= n >>> 8;
- n |= n >>> 16;
- return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
- }
-
- /**
- * Returns x's Class if it is of the form "class C implements
- * Comparable<C>", else null.
- */
- static Class<?> comparableClassFor(Object x) {
- if (x instanceof Comparable) {
- Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
- if ((c = x.getClass()) == String.class) // bypass checks
- return c;
- if ((ts = c.getGenericInterfaces()) != null) {
- for (int i = 0; i < ts.length; ++i) {
- if (((t = ts[i]) instanceof ParameterizedType) &&
- ((p = (ParameterizedType)t).getRawType() ==
- Comparable.class) &&
- (as = p.getActualTypeArguments()) != null &&
- as.length == 1 && as[0] == c) // type arg is c
- return c;
- }
+ static {
+ try {
+ UNSAFE = getUnsafe();
+ Class<?> k = Node.class;
+ hashOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("hash"));
+ } catch (Exception e) {
+ throw new Error(e);
}
}
- return null;
- }
-
- /**
- * Returns k.compareTo(x) if x matches kc (k's screened comparable
- * class), else 0.
- */
- @SuppressWarnings({"rawtypes","unchecked"}) // for cast to Comparable
- static int compareComparables(Class<?> kc, Object k, Object x) {
- return (x == null || x.getClass() != kc ? 0 :
- ((Comparable)k).compareTo(x));
- }
-
- /* ---------------- Table element access -------------- */
-
- /*
- * Volatile access methods are used for table elements as well as
- * elements of in-progress next table while resizing. All uses of
- * the tab arguments must be null checked by callers. All callers
- * also paranoically precheck that tab's length is not zero (or an
- * equivalent check), thus ensuring that any index argument taking
- * the form of a hash value anded with (length - 1) is a valid
- * index. Note that, to be correct wrt arbitrary concurrency
- * errors by users, these checks must operate on local variables,
- * which accounts for some odd-looking inline assignments below.
- * Note that calls to setTabAt always occur within locked regions,
- * and so do not need full volatile semantics, but still require
- * ordering to maintain concurrent readability.
- */
-
- @SuppressWarnings("unchecked")
- static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
- return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
- }
-
- static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
- Node<K,V> c, Node<K,V> v) {
- return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}
- static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
- U.putOrderedObject(tab, ((long)i << ASHIFT) + ABASE, v);
- }
-
- /* ---------------- Fields -------------- */
-
- /**
- * The array of bins. Lazily initialized upon first insertion.
- * Size is always a power of two. Accessed directly by iterators.
- */
- transient volatile Node<K,V>[] table;
+ /* ---------------- TreeBins -------------- */
/**
- * The next table to use; non-null only while resizing.
+ * Nodes for use in TreeBins
*/
- private transient volatile Node<K,V>[] nextTable;
+ static final class TreeNode extends Node {
+ TreeNode parent; // red-black tree links
+ TreeNode left;
+ TreeNode right;
+ TreeNode prev; // needed to unlink next upon deletion
+ boolean red;
- /**
- * Base counter value, used mainly when there is no contention,
- * but also as a fallback during table initialization
- * races. Updated via CAS.
- */
- private transient volatile long baseCount;
+ TreeNode(int hash, Object key, Object val, Node next, TreeNode parent) {
+ super(hash, key, val, next);
+ this.parent = parent;
+ }
+ }
/**
- * Table initialization and resizing control. When negative, the
- * table is being initialized or resized: -1 for initialization,
- * else -(1 + the number of active resizing threads). Otherwise,
- * when table is null, holds the initial table size to use upon
- * creation, or 0 for default. After initialization, holds the
- * next element count value upon which to resize the table.
- */
- private transient volatile int sizeCtl;
+ * A specialized form of red-black tree for use in bins
+ * whose size exceeds a threshold.
+ *
+ * TreeBins use a special form of comparison for search and
+ * related operations (which is the main reason we cannot use
+ * existing collections such as TreeMaps). TreeBins contain
+ * Comparable elements, but may contain others, as well as
+ * elements that are Comparable but not necessarily Comparable<T>
+ * for the same T, so we cannot invoke compareTo among them. To
+ * handle this, the tree is ordered primarily by hash value, then
+ * by getClass().getName() order, and then by Comparator order
+ * among elements of the same class. On lookup at a node, if
+ * elements are not comparable or compare as 0, both left and
+ * right children may need to be searched in the case of tied hash
+ * values. (This corresponds to the full list search that would be
+ * necessary if all elements were non-Comparable and had tied
+ * hashes.) The red-black balancing code is updated from
+ * pre-jdk-collections
+ * (http://gee.cs.oswego.edu/dl/classes/collections/RBCell.java)
+ * based in turn on Cormen, Leiserson, and Rivest "Introduction to
+ * Algorithms" (CLR).
+ *
+ * TreeBins also maintain a separate locking discipline than
+ * regular bins. Because they are forwarded via special MOVED
+ * nodes at bin heads (which can never change once established),
+ * we cannot use those nodes as locks. Instead, TreeBin
+ * extends AbstractQueuedSynchronizer to support a simple form of
+ * read-write lock. For update operations and table validation,
+ * the exclusive form of lock behaves in the same way as bin-head
+ * locks. However, lookups use shared read-lock mechanics to allow
+ * multiple readers in the absence of writers. Additionally,
+ * these lookups do not ever block: While the lock is not
+ * available, they proceed along the slow traversal path (via
+ * next-pointers) until the lock becomes available or the list is
+ * exhausted, whichever comes first. (These cases are not fast,
+ * but maximize aggregate expected throughput.) The AQS mechanics
+ * for doing this are straightforward. The lock state is held as
+ * AQS getState(). Read counts are negative; the write count (1)
+ * is positive. There are no signalling preferences among readers
+ * and writers. Since we don't need to export full Lock API, we
+ * just override the minimal AQS methods and use them directly.
+ */
+ static final class TreeBin extends AbstractQueuedSynchronizer {
+ private static final long serialVersionUID = 2249069246763182397L;
+ transient TreeNode root; // root of tree
+ transient TreeNode first; // head of next-pointer list
+
+ /* AQS overrides */
+ public final boolean isHeldExclusively() { return getState() > 0; }
+ public final boolean tryAcquire(int ignore) {
+ if (compareAndSetState(0, 1)) {
+ setExclusiveOwnerThread(Thread.currentThread());
+ return true;
+ }
+ return false;
+ }
+ public final boolean tryRelease(int ignore) {
+ setExclusiveOwnerThread(null);
+ setState(0);
+ return true;
+ }
+ public final int tryAcquireShared(int ignore) {
+ for (int c;;) {
+ if ((c = getState()) > 0)
+ return -1;
+ if (compareAndSetState(c, c -1))
+ return 1;
+ }
+ }
+ public final boolean tryReleaseShared(int ignore) {
+ int c;
+ do {} while (!compareAndSetState(c = getState(), c + 1));
+ return c == -1;
+ }
- /**
- * The next table index (plus one) to split while resizing.
- */
- private transient volatile int transferIndex;
+ /** From CLR */
+ private void rotateLeft(TreeNode p) {
+ if (p != null) {
+ TreeNode r = p.right, pp, rl;
+ if ((rl = p.right = r.left) != null)
+ rl.parent = p;
+ if ((pp = r.parent = p.parent) == null)
+ root = r;
+ else if (pp.left == p)
+ pp.left = r;
+ else
+ pp.right = r;
+ r.left = p;
+ p.parent = r;
+ }
+ }
- /**
- * Spinlock (locked via CAS) used when resizing and/or creating CounterCells.
- */
- private transient volatile int cellsBusy;
+ /** From CLR */
+ private void rotateRight(TreeNode p) {
+ if (p != null) {
+ TreeNode l = p.left, pp, lr;
+ if ((lr = p.left = l.right) != null)
+ lr.parent = p;
+ if ((pp = l.parent = p.parent) == null)
+ root = l;
+ else if (pp.right == p)
+ pp.right = l;
+ else
+ pp.left = l;
+ l.right = p;
+ p.parent = l;
+ }
+ }
- /**
- * Table of counter cells. When non-null, size is a power of 2.
- */
- private transient volatile CounterCell[] counterCells;
+ /**
+ * Returns the TreeNode (or null if not found) for the given key
+ * starting at given root.
+ */
+ @SuppressWarnings("unchecked") final TreeNode getTreeNode
+ (int h, Object k, TreeNode p) {
+ Class<?> c = k.getClass();
+ while (p != null) {
+ int dir, ph; Object pk; Class<?> pc;
+ if ((ph = p.hash) == h) {
+ if ((pk = p.key) == k || k.equals(pk))
+ return p;
+ if (c != (pc = pk.getClass()) ||
+ !(k instanceof Comparable) ||
+ (dir = ((Comparable)k).compareTo((Comparable)pk)) == 0) {
+ dir = (c == pc) ? 0 : c.getName().compareTo(pc.getName());
+ TreeNode r = null, s = null, pl, pr;
+ if (dir >= 0) {
+ if ((pl = p.left) != null && h <= pl.hash)
+ s = pl;
+ }
+ else if ((pr = p.right) != null && h >= pr.hash)
+ s = pr;
+ if (s != null && (r = getTreeNode(h, k, s)) != null)
+ return r;
+ }
+ }
+ else
+ dir = (h < ph) ? -1 : 1;
+ p = (dir > 0) ? p.right : p.left;
+ }
+ return null;
+ }
- // views
- private transient KeySetView<K,V> keySet;
- private transient ValuesView<K,V> values;
- private transient EntrySetView<K,V> entrySet;
+ /**
+ * Wrapper for getTreeNode used by CHM.get. Tries to obtain
+ * read-lock to call getTreeNode, but during failure to get
+ * lock, searches along next links.
+ */
+ final Object getValue(int h, Object k) {
+ Node r = null;
+ int c = getState(); // Must read lock state first
+ for (Node e = first; e != null; e = e.next) {
+ if (c <= 0 && compareAndSetState(c, c - 1)) {
+ try {
+ r = getTreeNode(h, k, root);
+ } finally {
+ releaseShared(0);
+ }
+ break;
+ }
+ else if ((e.hash & HASH_BITS) == h && k.equals(e.key)) {
+ r = e;
+ break;
+ }
+ else
+ c = getState();
+ }
+ return r == null ? null : r.val;
+ }
+ /**
+ * Finds or adds a node.
+ * @return null if added
+ */
+ @SuppressWarnings("unchecked") final TreeNode putTreeNode
+ (int h, Object k, Object v) {
+ Class<?> c = k.getClass();
+ TreeNode pp = root, p = null;
+ int dir = 0;
+ while (pp != null) { // find existing node or leaf to insert at
+ int ph; Object pk; Class<?> pc;
+ p = pp;
+ if ((ph = p.hash) == h) {
+ if ((pk = p.key) == k || k.equals(pk))
+ return p;
+ if (c != (pc = pk.getClass()) ||
+ !(k instanceof Comparable) ||
+ (dir = ((Comparable)k).compareTo((Comparable)pk)) == 0) {
+ dir = (c == pc) ? 0 : c.getName().compareTo(pc.getName());
+ TreeNode r = null, s = null, pl, pr;
+ if (dir >= 0) {
+ if ((pl = p.left) != null && h <= pl.hash)
+ s = pl;
+ }
+ else if ((pr = p.right) != null && h >= pr.hash)
+ s = pr;
+ if (s != null && (r = getTreeNode(h, k, s)) != null)
+ return r;
+ }
+ }
+ else
+ dir = (h < ph) ? -1 : 1;
+ pp = (dir > 0) ? p.right : p.left;
+ }
- /* ---------------- Public operations -------------- */
+ TreeNode f = first;
+ TreeNode x = first = new TreeNode(h, k, v, f, p);
+ if (p == null)
+ root = x;
+ else { // attach and rebalance; adapted from CLR
+ TreeNode xp, xpp;
+ if (f != null)
+ f.prev = x;
+ if (dir <= 0)
+ p.left = x;
+ else
+ p.right = x;
+ x.red = true;
+ while (x != null && (xp = x.parent) != null && xp.red &&
+ (xpp = xp.parent) != null) {
+ TreeNode xppl = xpp.left;
+ if (xp == xppl) {
+ TreeNode y = xpp.right;
+ if (y != null && y.red) {
+ y.red = false;
+ xp.red = false;
+ xpp.red = true;
+ x = xpp;
+ }
+ else {
+ if (x == xp.right) {
+ rotateLeft(x = xp);
+ xpp = (xp = x.parent) == null ? null : xp.parent;
+ }
+ if (xp != null) {
+ xp.red = false;
+ if (xpp != null) {
+ xpp.red = true;
+ rotateRight(xpp);
+ }
+ }
+ }
+ }
+ else {
+ TreeNode y = xppl;
+ if (y != null && y.red) {
+ y.red = false;
+ xp.red = false;
+ xpp.red = true;
+ x = xpp;
+ }
+ else {
+ if (x == xp.left) {
+ rotateRight(x = xp);
+ xpp = (xp = x.parent) == null ? null : xp.parent;
+ }
+ if (xp != null) {
+ xp.red = false;
+ if (xpp != null) {
+ xpp.red = true;
+ rotateLeft(xpp);
+ }
+ }
+ }
+ }
+ }
+ TreeNode r = root;
+ if (r != null && r.red)
+ r.red = false;
+ }
+ return null;
+ }
- /**
- * Creates a new, empty map with the default initial table size (16).
- */
- public ConcurrentHashMap8() {
- }
-
- /**
- * Creates a new, empty map with an initial table size
- * accommodating the specified number of elements without the need
- * to dynamically resize.
- *
- * @param initialCapacity The implementation performs internal
- * sizing to accommodate this many elements.
- * @throws IllegalArgumentException if the initial capacity of
- * elements is negative
- */
- public ConcurrentHashMap8(int initialCapacity) {
- if (initialCapacity < 0)
- throw new IllegalArgumentException();
- int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
- MAXIMUM_CAPACITY :
- tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
- this.sizeCtl = cap;
- }
-
- /**
- * Creates a new map with the same mappings as the given map.
- *
- * @param m the map
- */
- public ConcurrentHashMap8(Map<? extends K, ? extends V> m) {
- this.sizeCtl = DEFAULT_CAPACITY;
- putAll(m);
+ /**
+ * Removes the given node, that must be present before this
+ * call. This is messier than typical red-black deletion code
+ * because we cannot swap the contents of an interior node
+ * with a leaf successor that is pinned by "next" pointers
+ * that are accessible independently of lock. So instead we
+ * swap the tree linkages.
+ */
+ final void deleteTreeNode(TreeNode p) {
+ TreeNode next = (TreeNode)p.next; // unlink traversal pointers
+ TreeNode pred = p.prev;
+ if (pred == null)
+ first = next;
+ else
+ pred.next = next;
+ if (next != null)
+ next.prev = pred;
+ TreeNode replacement;
+ TreeNode pl = p.left;
+ TreeNode pr = p.right;
+ if (pl != null && pr != null) {
+ TreeNode s = pr, sl;
+ while ((sl = s.left) != null) // find successor
+ s = sl;
+ boolean c = s.red; s.red = p.red; p.red = c; // swap colors
+ TreeNode sr = s.right;
+ TreeNode pp = p.parent;
+ if (s == pr) { // p was s's direct parent
+ p.parent = s;
+ s.right = p;
+ }
+ else {
+ TreeNode sp = s.parent;
+ if ((p.parent = sp) != null) {
+ if (s == sp.left)
+ sp.left = p;
+ else
+ sp.right = p;
+ }
+ if ((s.right = pr) != null)
+ pr.parent = s;
+ }
+ p.left = null;
+ if ((p.right = sr) != null)
+ sr.parent = p;
+ if ((s.left = pl) != null)
+ pl.parent = s;
+ if ((s.parent = pp) == null)
+ root = s;
+ else if (p == pp.left)
+ pp.left = s;
+ else
+ pp.right = s;
+ replacement = sr;
+ }
+ else
+ replacement = (pl != null) ? pl : pr;
+ TreeNode pp = p.parent;
+ if (replacement == null) {
+ if (pp == null) {
+ root = null;
+ return;
+ }
+ replacement = p;
+ }
+ else {
+ replacement.parent = pp;
+ if (pp == null)
+ root = replacement;
+ else if (p == pp.left)
+ pp.left = replacement;
+ else
+ pp.right = replacement;
+ p.left = p.right = p.parent = null;
+ }
+ if (!p.red) { // rebalance, from CLR
+ TreeNode x = replacement;
+ while (x != null) {
+ TreeNode xp, xpl;
+ if (x.red || (xp = x.parent) == null) {
+ x.red = false;
+ break;
+ }
+ if (x == (xpl = xp.left)) {
+ TreeNode sib = xp.right;
+ if (sib != null && sib.red) {
+ sib.red = false;
+ xp.red = true;
+ rotateLeft(xp);
+ sib = (xp = x.parent) == null ? null : xp.right;
+ }
+ if (sib == null)
+ x = xp;
+ else {
+ TreeNode sl = sib.left, sr = sib.right;
+ if ((sr == null || !sr.red) &&
+ (sl == null || !sl.red)) {
+ sib.red = true;
+ x = xp;
+ }
+ else {
+ if (sr == null || !sr.red) {
+ if (sl != null)
+ sl.red = false;
+ sib.red = true;
+ rotateRight(sib);
+ sib = (xp = x.parent) == null ? null : xp.right;
+ }
+ if (sib != null) {
+ sib.red = (xp == null) ? false : xp.red;
+ if ((sr = sib.right) != null)
+ sr.red = false;
+ }
+ if (xp != null) {
+ xp.red = false;
+ rotateLeft(xp);
+ }
+ x = root;
+ }
+ }
+ }
+ else { // symmetric
+ TreeNode sib = xpl;
+ if (sib != null && sib.red) {
+ sib.red = false;
+ xp.red = true;
+ rotateRight(xp);
+ sib = (xp = x.parent) == null ? null : xp.left;
+ }
+ if (sib == null)
+ x = xp;
+ else {
+ TreeNode sl = sib.left, sr = sib.right;
+ if ((sl == null || !sl.red) &&
+ (sr == null || !sr.red)) {
+ sib.red = true;
+ x = xp;
+ }
+ else {
+ if (sl == null || !sl.red) {
+ if (sr != null)
+ sr.red = false;
+ sib.red = true;
+ rotateLeft(sib);
+ sib = (xp = x.parent) == null ? null : xp.left;
+ }
+ if (sib != null) {
+ sib.red = (xp == null) ? false : xp.red;
+ if ((sl = sib.left) != null)
+ sl.red = false;
+ }
+ if (xp != null) {
+ xp.red = false;
+ rotateRight(xp);
+ }
+ x = root;
+ }
+ }
+ }
+ }
+ }
+ if (p == replacement && (pp = p.parent) != null) {
+ if (p == pp.left) // detach pointers
+ pp.left = null;
+ else if (p == pp.right)
+ pp.right = null;
+ p.parent = null;
+ }
+ }
}
- /**
- * Creates a new, empty map with an initial table size based on
- * the given number of elements ({@code initialCapacity}) and
- * initial table density ({@code loadFactor}).
- *
- * @param initialCapacity the initial capacity. The implementation
- * performs internal sizing to accommodate this many elements,
- * given the specified load factor.
- * @param loadFactor the load factor (table density) for
- * establishing the initial table size
- * @throws IllegalArgumentException if the initial capacity of
- * elements is negative or the load factor is nonpositive
- *
- * @since 1.6
- */
- public ConcurrentHashMap8(int initialCapacity, float loadFactor) {
- this(initialCapacity, loadFactor, 1);
- }
+ /* ---------------- Collision reduction methods -------------- */
/**
- * Creates a new, empty map with an initial table size based on
- * the given number of elements ({@code initialCapacity}), table
- * density ({@code loadFactor}), and number of concurrently
- * updating threads ({@code concurrencyLevel}).
- *
- * @param initialCapacity the initial capacity. The implementation
- * performs internal sizing to accommodate this many elements,
- * given the specified load factor.
- * @param loadFactor the load factor (table density) for
- * establishing the initial table size
- * @param concurrencyLevel the estimated number of concurrently
- * updating threads. The implementation may use this value as
- * a sizing hint.
- * @throws IllegalArgumentException if the initial capacity is
- * negative or the load factor or concurrencyLevel are
- * nonpositive
+ * Spreads higher bits to lower, and also forces top 2 bits to 0.
+ * Because the table uses power-of-two masking, sets of hashes
+ * that vary only in bits above the current mask will always
+ * collide. (Among known examples are sets of Float keys holding
+ * consecutive whole numbers in small tables.) To counter this,
+ * we apply a transform that spreads the impact of higher bits
+ * downward. There is a tradeoff between speed, utility, and
+ * quality of bit-spreading. Because many common sets of hashes
+ * are already reasonably distributed across bits (so don't benefit
+ * from spreading), and because we use trees to handle large sets
+ * of collisions in bins, we don't need excessively high quality.
*/
- public ConcurrentHashMap8(int initialCapacity,
- float loadFactor, int concurrencyLevel) {
- if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)
- throw new IllegalArgumentException();
- if (initialCapacity < concurrencyLevel) // Use at least as many bins
- initialCapacity = concurrencyLevel; // as estimated threads
- long size = (long)(1.0 + (long)initialCapacity / loadFactor);
- int cap = (size >= (long)MAXIMUM_CAPACITY) ?
- MAXIMUM_CAPACITY : tableSizeFor((int)size);
- this.sizeCtl = cap;
+ private static final int spread(int h) {
+ h ^= (h >>> 18) ^ (h >>> 12);
+ return (h ^ (h >>> 10)) & HASH_BITS;
}
- // Original (since JDK1.2) Map methods
-
/**
- * {@inheritDoc}
+ * Replaces a list bin with a tree bin. Call only when locked.
+ * Fails to replace if the given key is non-comparable or table
+ * is, or needs, resizing.
*/
- public int size() {
- long n = sumCount();
- return ((n < 0L) ? 0 :
- (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE :
- (int)n);
+ private final void replaceWithTreeBin(Node[] tab, int index, Object key) {
+ if ((key instanceof Comparable) &&
+ (tab.length >= MAXIMUM_CAPACITY || counter.sum() < (long)sizeCtl)) {
+ TreeBin t = new TreeBin();
+ for (Node e = tabAt(tab, index); e != null; e = e.next)
+ t.putTreeNode(e.hash & HASH_BITS, e.key, e.val);
+ setTabAt(tab, index, new Node(MOVED, t, null, null));
+ }
}
- /**
- * {@inheritDoc}
- */
- public boolean isEmpty() {
- return sumCount() <= 0L; // ignore transient negative values
- }
+ /* ---------------- Internal access and update methods -------------- */
- /**
- * Returns the value to which the specified key is mapped,
- * or {@code null} if this map contains no mapping for the key.
- *
- * <p>More formally, if this map contains a mapping from a key
- * {@code k} to a value {@code v} such that {@code key.equals(k)},
- * then this method returns {@code v}; otherwise it returns
- * {@code null}. (There can be at most one such mapping.)
- *
- * @throws NullPointerException if the specified key is null
- */
- public V get(Object key) {
- Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
- int h = spread(key.hashCode());
- if ((tab = table) != null && (n = tab.length) > 0 &&
- (e = tabAt(tab, (n - 1) & h)) != null) {
- if ((eh = e.hash) == h) {
- if ((ek = e.key) == key || (ek != null && key.equals(ek)))
- return e.val;
- }
- else if (eh < 0)
- return (p = e.find(h, key)) != null ? p.val : null;
- while ((e = e.next) != null) {
- if (e.hash == h &&
- ((ek = e.key) == key || (ek != null && key.equals(ek))))
- return e.val;
+ /** Implementation for get and containsKey */
+ private final Object internalGet(Object k) {
+ int h = spread(k.hashCode());
+ retry: for (Node[] tab = table; tab != null;) {
+ Node e, p; Object ek, ev; int eh; // locals to read fields once
+ for (e = tabAt(tab, (tab.length - 1) & h); e != null; e = e.next) {
+ if ((eh = e.hash) == MOVED) {
+ if ((ek = e.key) instanceof TreeBin) // search TreeBin
+ return ((TreeBin)ek).getValue(h, k);
+ else { // restart with new table
+ tab = (Node[])ek;
+ continue retry;
+ }
+ }
+ else if ((eh & HASH_BITS) == h && (ev = e.val) != null &&
+ ((ek = e.key) == k || k.equals(ek)))
+ return ev;
}
+ break;
}
return null;
}
/**
- * Tests if the specified object is a key in this table.
- *
- * @param key possible key
- * @return {@code true} if and only if the specified object
- * is a key in this table, as determined by the
- * {@code equals} method; {@code false} otherwise
- * @throws NullPointerException if the specified key is null
- */
- public boolean containsKey(Object key) {
- return get(key) != null;
- }
-
- /**
- * Returns {@code true} if this map maps one or more keys to the
- * specified value. Note: This method may require a full traversal
- * of the map, and is much slower than method {@code containsKey}.
- *
- * @param value value whose presence in this map is to be tested
- * @return {@code true} if this map maps one or more keys to the
- * specified value
- * @throws NullPointerException if the specified value is null
+ * Implementation for the four public remove/replace methods:
+ * Replaces node value with v, conditional upon match of cv if
+ * non-null. If resulting value is null, delete.
*/
- public boolean containsValue(Object value) {
- if (value == null)
- throw new NullPointerException();
- Node<K,V>[] t;
- if ((t = table) != null) {
- Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
- for (Node<K,V> p; (p = it.advance()) != null; ) {
- V v;
- if ((v = p.val) == value || (v != null && value.equals(v)))
- return true;
+ private final Object internalReplace(Object k, Object v, Object cv) {
+ int h = spread(k.hashCode());
+ Object oldVal = null;
+ for (Node[] tab = table;;) {
+ Node f; int i, fh; Object fk;
+ if (tab == null ||
+ (f = tabAt(tab, i = (tab.length - 1) & h)) == null)
+ break;
+ else if ((fh = f.hash) == MOVED) {
+ if ((fk = f.key) instanceof TreeBin) {
+ TreeBin t = (TreeBin)fk;
+ boolean validated = false;
+ boolean deleted = false;
+ t.acquire(0);
+ try {
+ if (tabAt(tab, i) == f) {
+ validated = true;
+ TreeNode p = t.getTreeNode(h, k, t.root);
+ if (p != null) {
+ Object pv = p.val;
+ if (cv == null || cv == pv || cv.equals(pv)) {
+ oldVal = pv;
+ if ((p.val = v) == null) {
+ deleted = true;
+ t.deleteTreeNode(p);
+ }
+ }
+ }
+ }
+ } finally {
+ t.release(0);
+ }
+ if (validated) {
+ if (deleted)
+ counter.add(-1L);
+ break;
+ }
+ }
+ else
+ tab = (Node[])fk;
+ }
+ else if ((fh & HASH_BITS) != h && f.next == null) // precheck
+ break; // rules out possible existence
+ else if ((fh & LOCKED) != 0) {
+ checkForResize(); // try resizing if can't get lock
+ f.tryAwaitLock(tab, i);
+ }
+ else if (f.casHash(fh, fh | LOCKED)) {
+ boolean validated = false;
+ boolean deleted = false;
+ try {
+ if (tabAt(tab, i) == f) {
+ validated = true;
+ for (Node e = f, pred = null;;) {
+ Object ek, ev;
+ if ((e.hash & HASH_BITS) == h &&
+ ((ev = e.val) != null) &&
+ ((ek = e.key) == k || k.equals(ek))) {
+ if (cv == null || cv == ev || cv.equals(ev)) {
+ oldVal = ev;
+ if ((e.val = v) == null) {
+ deleted = true;
+ Node en = e.next;
+ if (pred != null)
+ pred.next = en;
+ else
+ setTabAt(tab, i, en);
+ }
+ }
+ break;
+ }
+ pred = e;
+ if ((e = e.next) == null)
+ break;
+ }
+ }
+ } finally {
+ if (!f.casHash(fh | LOCKED, fh)) {
+ f.hash = fh;
+ synchronized (f) { f.notifyAll(); };
+ }
+ }
+ if (validated) {
+ if (deleted)
+ counter.add(-1L);
+ break;
+ }
}
}
- return false;
+ return oldVal;
}
- /**
- * Maps the specified key to the specified value in this table.
- * Neither the key nor the value can be null.
+ /*
+ * Internal versions of the six insertion methods, each a
+ * little more complicated than the last. All have
+ * the same basic structure as the first (internalPut):
+ * 1. If table uninitialized, create
+ * 2. If bin empty, try to CAS new node
+ * 3. If bin stale, use new table
+ * 4. if bin converted to TreeBin, validate and relay to TreeBin methods
+ * 5. Lock and validate; if valid, scan and add or update
*
- * <p>The value can be retrieved by calling the {@code get} method
- * with a key that is equal to the original key.
+ * The others interweave other checks and/or alternative actions:
+ * * Plain put checks for and performs resize after insertion.
+ * * putIfAbsent prescans for mapping without lock (and fails to add
+ * if present), which also makes pre-emptive resize checks worthwhile.
+ * * computeIfAbsent extends form used in putIfAbsent with additional
+ * mechanics to deal with, calls, potential exceptions and null
+ * returns from function call.
+ * * compute uses the same function-call mechanics, but without
+ * the prescans
+ * * merge acts as putIfAbsent in the absent case, but invokes the
+ * update function if present
+ * * putAll attempts to pre-allocate enough table space
+ * and more lazily performs count updates and checks.
*
- * @param key key with which the specified value is to be associated
- * @param value value to be associated with the specified key
- * @return the previous value associated with {@code key}, or
- * {@code null} if there was no mapping for {@code key}
- * @throws NullPointerException if the specified key or value is null
+ * Someday when details settle down a bit more, it might be worth
+ * some factoring to reduce sprawl.
*/
- public V put(K key, V value) {
- return putVal(key, value, false);
- }
- /** Implementation for put and putIfAbsent */
- final V putVal(K key, V value, boolean onlyIfAbsent) {
- if (key == null || value == null) throw new NullPointerException();
- int hash = spread(key.hashCode());
- int binCount = 0;
- for (Node<K,V>[] tab = table;;) {
- Node<K,V> f; int n, i, fh;
- if (tab == null || (n = tab.length) == 0)
+ /** Implementation for put */
+ private final Object internalPut(Object k, Object v) {
+ int h = spread(k.hashCode());
+ int count = 0;
+ for (Node[] tab = table;;) {
+ int i; Node f; int fh; Object fk;
+ if (tab == null)
tab = initTable();
- else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
- if (casTabAt(tab, i, null,
- new Node<K,V>(hash, key, value, null)))
+ else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) {
+ if (casTabAt(tab, i, null, new Node(h, k, v, null)))
break; // no lock when adding to empty bin
}
- else if ((fh = f.hash) == MOVED)
- tab = helpTransfer(tab, f);
- else {
- V oldVal = null;
- synchronized (f) {
- if (tabAt(tab, i) == f) {
- if (fh >= 0) {
- binCount = 1;
- for (Node<K,V> e = f;; ++binCount) {
- K ek;
- if (e.hash == hash &&
- ((ek = e.key) == key ||
- (ek != null && key.equals(ek)))) {
- oldVal = e.val;
- if (!onlyIfAbsent)
- e.val = value;
- break;
- }
- Node<K,V> pred = e;
- if ((e = e.next) == null) {
- pred.next = new Node<K,V>(hash, key,
- value, null);
- break;
- }
+ else if ((fh = f.hash) == MOVED) {
+ if ((fk = f.key) instanceof TreeBin) {
+ TreeBin t = (TreeBin)fk;
+ Object oldVal = null;
+ t.acquire(0);
+ try {
+ if (tabAt(tab, i) == f) {
+ count = 2;
+ TreeNode p = t.putTreeNode(h, k, v);
+ if (p != null) {
+ oldVal = p.val;
+ p.val = v;
}
}
- else if (f instanceof TreeBin) {
- Node<K,V> p;
- binCount = 2;
- if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
- value)) != null) {
- oldVal = p.val;
- if (!onlyIfAbsent)
- p.val = value;
+ } finally {
+ t.release(0);
+ }
+ if (count != 0) {
+ if (oldVal != null)
+ return oldVal;
+ break;
+ }
+ }
+ else
+ tab = (Node[])fk;
+ }
+ else if ((fh & LOCKED) != 0) {
+ checkForResize();
+ f.tryAwaitLock(tab, i);
+ }
+ else if (f.casHash(fh, fh | LOCKED)) {
+ Object oldVal = null;
+ try { // needed in case equals() throws
+ if (tabAt(tab, i) == f) {
+ count = 1;
+ for (Node e = f;; ++count) {
+ Object ek, ev;
+ if ((e.hash & HASH_BITS) == h &&
+ (ev = e.val) != null &&
+ ((ek = e.key) == k || k.equals(ek))) {
+ oldVal = ev;
+ e.val = v;
+ break;
+ }
+ Node last = e;
+ if ((e = e.next) == null) {
+ last.next = new Node(h, k, v, null);
+ if (count >= TREE_THRESHOLD)
+ replaceWithTreeBin(tab, i, k);
+ break;
}
}
}
+ } finally { // unlock and signal if needed
+ if (!f.casHash(fh | LOCKED, fh)) {
+ f.hash = fh;
+ synchronized (f) { f.notifyAll(); };
+ }
}
- if (binCount != 0) {
- if (binCount >= TREEIFY_THRESHOLD)
- treeifyBin(tab, i);
+ if (count != 0) {
if (oldVal != null)
return oldVal;
+ if (tab.length <= 64)
+ count = 2;
break;
}
}
}
- addCount(1L, binCount);
+ counter.add(1L);
+ if (count > 1)
+ checkForResize();
return null;
}
- /**
- * Copies all of the mappings from the specified map to this one.
- * These mappings replace any mappings that this map had for any of the
- * keys currently in the specified map.
- *
- * @param m mappings to be stored in this map
- */
- public void putAll(Map<? extends K, ? extends V> m) {
- tryPresize(m.size());
- for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
- putVal(e.getKey(), e.getValue(), false);
- }
-
- /**
- * Removes the key (and its corresponding value) from this map.
- * This method does nothing if the key is not in the map.
- *
- * @param key the key that needs to be removed
- * @return the previous value associated with {@code key}, or
- * {@code null} if there was no mapping for {@code key}
- * @throws NullPointerException if the specified key is null
- */
- public V remove(Object key) {
- return replaceNode(key, null, null);
+ /** Implementation for putIfAbsent */
+ private final Object internalPutIfAbsent(Object k, Object v) {
+ int h = spread(k.hashCode());
+ int count = 0;
+ for (Node[] tab = table;;) {
+ int i; Node f; int fh; Object fk, fv;
+ if (tab == null)
+ tab = initTable();
+ else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) {
+ if (casTabAt(tab, i, null, new Node(h, k, v, null)))
+ break;
+ }
+ else if ((fh = f.hash) == MOVED) {
+ if ((fk = f.key) instanceof TreeBin) {
+ TreeBin t = (TreeBin)fk;
+ Object oldVal = null;
+ t.acquire(0);
+ try {
+ if (tabAt(tab, i) == f) {
+ count = 2;
+ TreeNode p = t.putTreeNode(h, k, v);
+ if (p != null)
+ oldVal = p.val;
+ }
+ } finally {
+ t.release(0);
+ }
+ if (count != 0)
<TRUNCATED>
[3/7] incubator-ignite git commit: ignite-sprint-6: merge from
ignite-545
Posted by sb...@apache.org.
ignite-sprint-6: merge from ignite-545
Project: http://git-wip-us.apache.org/repos/asf/incubator-ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ignite/commit/455b96fc
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ignite/tree/455b96fc
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ignite/diff/455b96fc
Branch: refs/heads/ignite-sprint-6
Commit: 455b96fc1791c9ec48372890d782513c58c1acd6
Parents: 593d862
Author: Denis Magda <dm...@gridgain.com>
Authored: Wed Jun 10 17:23:56 2015 +0300
Committer: Denis Magda <dm...@gridgain.com>
Committed: Wed Jun 10 17:23:56 2015 +0300
----------------------------------------------------------------------
.../processors/cache/KeyCacheObjectImpl.java | 11 +-
.../datastreamer/DataStreamerCacheUpdaters.java | 2 +-
.../java/org/jsr166/ConcurrentHashMap8.java | 5431 ++++++++----------
.../java/org/jsr166/ConcurrentLinkedDeque8.java | 586 +-
.../src/main/java/org/jsr166/LongAdder8.java | 35 +-
.../core/src/main/java/org/jsr166/README.txt | 11 +
.../src/main/java/org/jsr166/Striped64_8.java | 22 +-
.../java/org/jsr166/ThreadLocalRandom8.java | 19 +-
.../src/main/java/org/jsr166/package-info.java | 12 +-
.../IgniteHadoopFileSystemAbstractSelfTest.java | 2 +-
10 files changed, 2742 insertions(+), 3389 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/455b96fc/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/KeyCacheObjectImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/KeyCacheObjectImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/KeyCacheObjectImpl.java
index 61ca882..e5fa891 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/KeyCacheObjectImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/KeyCacheObjectImpl.java
@@ -23,7 +23,7 @@ import org.jetbrains.annotations.*;
/**
*
*/
-public class KeyCacheObjectImpl extends CacheObjectAdapter implements KeyCacheObject, Comparable<KeyCacheObjectImpl> {
+public class KeyCacheObjectImpl extends CacheObjectAdapter implements KeyCacheObject {
/** */
private static final long serialVersionUID = 0L;
@@ -46,15 +46,6 @@ public class KeyCacheObjectImpl extends CacheObjectAdapter implements KeyCacheOb
}
/** {@inheritDoc} */
- @SuppressWarnings("unchecked")
- @Override public int compareTo(KeyCacheObjectImpl other) {
- assert val instanceof Comparable : val;
- assert other.val instanceof Comparable : val;
-
- return ((Comparable)val).compareTo(other.val);
- }
-
- /** {@inheritDoc} */
@Override public byte[] valueBytes(CacheObjectContext ctx) throws IgniteCheckedException {
if (valBytes == null)
valBytes = ctx.processor().marshal(ctx, val);
http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/455b96fc/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamerCacheUpdaters.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamerCacheUpdaters.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamerCacheUpdaters.java
index 50e9ab9..dc9d025 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamerCacheUpdaters.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/datastreamer/DataStreamerCacheUpdaters.java
@@ -160,7 +160,7 @@ public class DataStreamerCacheUpdaters {
/**
* Batched updater. Updates cache using batch operations thus is dead lock prone.
*/
- private static class BatchedSorted<K, V> implements StreamReceiver<K, V>, InternalUpdater {
+ private static class BatchedSorted<K, V> implements StreamReceiver<K, V> {
/** */
private static final long serialVersionUID = 0L;
[5/7] incubator-ignite git commit: ignite-sprint-6: revert changes in
concurrent map
Posted by sb...@apache.org.
ignite-sprint-6: revert changes in concurrent map
Project: http://git-wip-us.apache.org/repos/asf/incubator-ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ignite/commit/8203caf7
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ignite/tree/8203caf7
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ignite/diff/8203caf7
Branch: refs/heads/ignite-sprint-6
Commit: 8203caf7ac5f79e08b8f398e8608619c00256aac
Parents: 455b96f
Author: Denis Magda <dm...@gridgain.com>
Authored: Wed Jun 10 18:01:16 2015 +0300
Committer: Denis Magda <dm...@gridgain.com>
Committed: Wed Jun 10 18:01:16 2015 +0300
----------------------------------------------------------------------
.../java/org/jsr166/ConcurrentHashMap8.java | 5433 ++++++++++--------
1 file changed, 2943 insertions(+), 2490 deletions(-)
----------------------------------------------------------------------
[2/7] incubator-ignite git commit: ignite-sprint-6: merge from
ignite-545
Posted by sb...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/455b96fc/modules/core/src/main/java/org/jsr166/ConcurrentHashMap8.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/jsr166/ConcurrentHashMap8.java b/modules/core/src/main/java/org/jsr166/ConcurrentHashMap8.java
index 79b42b2..727db4c 100644
--- a/modules/core/src/main/java/org/jsr166/ConcurrentHashMap8.java
+++ b/modules/core/src/main/java/org/jsr166/ConcurrentHashMap8.java
@@ -5,16 +5,22 @@
*/
/*
- * The initial version of this file was copied from JSR-166:
- * http://gee.cs.oswego.edu/dl/concurrency-interest/
+ * The latest version of the file corresponds to the following CVS commit:
+ * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jdk7/java/util/concurrent/ConcurrentHashMap.java?pathrev=1.43
+ *
+ * Note, that the repository above is JDK 7 based that is kept up-to-date too.
+ * The main repository (JDK 8 based) uses JDK 8 features significantly that unavailable in JDK 7.
*/
+
package org.jsr166;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
+import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.*;
+import java.lang.reflect.*;
/**
* A hash table supporting full concurrency of retrievals and
@@ -68,21 +74,15 @@ import java.util.concurrent.locks.*;
* expected {@code concurrencyLevel} as an additional hint for
* internal sizing. Note that using many keys with exactly the same
* {@code hashCode()} is a sure way to slow down performance of any
- * hash table.
+ * hash table. To ameliorate impact, when keys are {@link Comparable},
+ * this class may use comparison order among keys to help break ties.
*
- * <p>A {@link Set} projection of a ConcurrentHashMapV8 may be created
+ * <p>A {@link Set} projection of a ConcurrentHashMap may be created
* (using {@link #newKeySet()} or {@link #newKeySet(int)}), or viewed
* (using {@link #keySet(Object)} when only keys are of interest, and the
* mapped values are (perhaps transiently) not used or all take the
* same mapping value.
*
- * <p>A ConcurrentHashMapV8 can be used as scalable frequency map (a
- * form of histogram or multiset) by using {@link LongAdder8} values
- * and initializing via {@link #computeIfAbsent}. For example, to add
- * a count to a {@code ConcurrentHashMapV8<String,LongAdder8> freqs}, you
- * can use {@code freqs.computeIfAbsent(k -> new
- * LongAdder8()).increment();}
- *
* <p>This class and its views and iterators implement all of the
* <em>optional</em> methods of the {@link Map} and {@link Iterator}
* interfaces.
@@ -90,90 +90,9 @@ import java.util.concurrent.locks.*;
* <p>Like {@link Hashtable} but unlike {@link HashMap}, this class
* does <em>not</em> allow {@code null} to be used as a key or value.
*
- * <ul>
- * <li> forEach: Perform a given action on each element.
- * A variant form applies a given transformation on each element
- * before performing the action.</li>
- *
- * <li> search: Return the first available non-null result of
- * applying a given function on each element; skipping further
- * search when a result is found.</li>
- *
- * <li> reduce: Accumulate each element. The supplied reduction
- * function cannot rely on ordering (more formally, it should be
- * both associative and commutative). There are five variants:
- *
- * <ul>
- *
- * <li> Plain reductions. (There is not a form of this method for
- * (key, value) function arguments since there is no corresponding
- * return type.)</li>
- *
- * <li> Mapped reductions that accumulate the results of a given
- * function applied to each element.</li>
- *
- * <li> Reductions to scalar doubles, longs, and ints, using a
- * given basis value.</li>
- *
- * </li>
- * </ul>
- * </ul>
- *
- * <p>The concurrency properties of bulk operations follow
- * from those of ConcurrentHashMapV8: Any non-null result returned
- * from {@code get(key)} and related access methods bears a
- * happens-before relation with the associated insertion or
- * update. The result of any bulk operation reflects the
- * composition of these per-element relations (but is not
- * necessarily atomic with respect to the map as a whole unless it
- * is somehow known to be quiescent). Conversely, because keys
- * and values in the map are never null, null serves as a reliable
- * atomic indicator of the current lack of any result. To
- * maintain this property, null serves as an implicit basis for
- * all non-scalar reduction operations. For the double, long, and
- * int versions, the basis should be one that, when combined with
- * any other value, returns that other value (more formally, it
- * should be the identity element for the reduction). Most common
- * reductions have these properties; for example, computing a sum
- * with basis 0 or a minimum with basis MAX_VALUE.
- *
- * <p>Search and transformation functions provided as arguments
- * should similarly return null to indicate the lack of any result
- * (in which case it is not used). In the case of mapped
- * reductions, this also enables transformations to serve as
- * filters, returning null (or, in the case of primitive
- * specializations, the identity basis) if the element should not
- * be combined. You can create compound transformations and
- * filterings by composing them yourself under this "null means
- * there is nothing there now" rule before using them in search or
- * reduce operations.
- *
- * <p>Methods accepting and/or returning Entry arguments maintain
- * key-value associations. They may be useful for example when
- * finding the key for the greatest value. Note that "plain" Entry
- * arguments can be supplied using {@code new
- * AbstractMap.SimpleEntry(k,v)}.
- *
- * <p>Bulk operations may complete abruptly, throwing an
- * exception encountered in the application of a supplied
- * function. Bear in mind when handling such exceptions that other
- * concurrently executing functions could also have thrown
- * exceptions, or would have done so if the first exception had
- * not occurred.
- *
- * <p>Parallel speedups for bulk operations compared to sequential
- * processing are common but not guaranteed. Operations involving
- * brief functions on small maps may execute more slowly than
- * sequential loops if the underlying work to parallelize the
- * computation is more expensive than the computation itself.
- * Similarly, parallelization may not lead to much actual parallelism
- * if all processors are busy performing unrelated tasks.
- *
- * <p>All arguments to all task methods must be non-null.
- *
- * <p><em>jsr166e note: During transition, this class
- * uses nested functional interfaces with different names but the
- * same forms as those expected for JDK8.</em>
+ * <p>This class is a member of the
+ * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * Java Collections Framework</a>.
*
* @since 1.5
* @author Doug Lea
@@ -181,80 +100,9 @@ import java.util.concurrent.locks.*;
* @param <V> the type of mapped values
*/
@SuppressWarnings("ALL")
-public class ConcurrentHashMap8<K, V>
- implements ConcurrentMap<K, V>, Serializable {
+public class ConcurrentHashMap8<K,V> implements ConcurrentMap<K,V>, Serializable {
private static final long serialVersionUID = 7249069246763182397L;
- /**
- * A partitionable iterator. A Spliterator can be traversed
- * directly, but can also be partitioned (before traversal) by
- * creating another Spliterator that covers a non-overlapping
- * portion of the elements, and so may be amenable to parallel
- * execution.
- *
- * <p>This interface exports a subset of expected JDK8
- * functionality.
- *
- * <p>Sample usage: Here is one (of the several) ways to compute
- * the sum of the values held in a map using the ForkJoin
- * framework. As illustrated here, Spliterators are well suited to
- * designs in which a task repeatedly splits off half its work
- * into forked subtasks until small enough to process directly,
- * and then joins these subtasks. Variants of this style can also
- * be used in completion-based designs.
- *
- * <pre>
- * {@code ConcurrentHashMapV8<String, Long> m = ...
- * // split as if have 8 * parallelism, for load balance
- * int n = m.size();
- * int p = aForkJoinPool.getParallelism() * 8;
- * int split = (n < p)? n : p;
- * long sum = aForkJoinPool.invoke(new SumValues(m.valueSpliterator(), split, null));
- * // ...
- * static class SumValues extends RecursiveTask<Long> {
- * final Spliterator<Long> s;
- * final int split; // split while > 1
- * final SumValues nextJoin; // records forked subtasks to join
- * SumValues(Spliterator<Long> s, int depth, SumValues nextJoin) {
- * this.s = s; this.depth = depth; this.nextJoin = nextJoin;
- * }
- * public Long compute() {
- * long sum = 0;
- * SumValues subtasks = null; // fork subtasks
- * for (int s = split >>> 1; s > 0; s >>>= 1)
- * (subtasks = new SumValues(s.split(), s, subtasks)).fork();
- * while (s.hasNext()) // directly process remaining elements
- * sum += s.next();
- * for (SumValues t = subtasks; t != null; t = t.nextJoin)
- * sum += t.join(); // collect subtask results
- * return sum;
- * }
- * }
- * }</pre>
- */
- public static interface Spliterator<T> extends Iterator<T> {
- /**
- * Returns a Spliterator covering approximately half of the
- * elements, guaranteed not to overlap with those subsequently
- * returned by this Spliterator. After invoking this method,
- * the current Spliterator will <em>not</em> produce any of
- * the elements of the returned Spliterator, but the two
- * Spliterators together will produce all of the elements that
- * would have been produced by this Spliterator had this
- * method not been called. The exact number of elements
- * produced by the returned Spliterator is not guaranteed, and
- * may be zero (i.e., with {@code hasNext()} reporting {@code
- * false}) if this Spliterator cannot be further split.
- *
- * @return a Spliterator covering approximately half of the
- * elements
- * @throws IllegalStateException if this Spliterator has
- * already commenced traversing elements
- */
- Spliterator<T> split();
- }
-
-
/*
* Overview:
*
@@ -265,18 +113,21 @@ public class ConcurrentHashMap8<K, V>
* the same or better than java.util.HashMap, and to support high
* initial insertion rates on an empty table by many threads.
*
- * Each key-value mapping is held in a Node. Because Node fields
- * can contain special values, they are defined using plain Object
- * types. Similarly in turn, all internal methods that use them
- * work off Object types. And similarly, so do the internal
- * methods of auxiliary iterator and view classes. All public
- * generic typed methods relay in/out of these internal methods,
- * supplying null-checks and casts as needed. This also allows
- * many of the public methods to be factored into a smaller number
- * of internal methods (although sadly not so for the five
- * variants of put-related operations). The validation-based
- * approach explained below leads to a lot of code sprawl because
- * retry-control precludes factoring into smaller methods.
+ * This map usually acts as a binned (bucketed) hash table. Each
+ * key-value mapping is held in a Node. Most nodes are instances
+ * of the basic Node class with hash, key, value, and next
+ * fields. However, various subclasses exist: TreeNodes are
+ * arranged in balanced trees, not lists. TreeBins hold the roots
+ * of sets of TreeNodes. ForwardingNodes are placed at the heads
+ * of bins during resizing. ReservationNodes are used as
+ * placeholders while establishing values in computeIfAbsent and
+ * related methods. The types TreeBin, ForwardingNode, and
+ * ReservationNode do not hold normal user keys, values, or
+ * hashes, and are readily distinguishable during search etc
+ * because they have negative hash fields and null key and value
+ * fields. (These special nodes are either uncommon or transient,
+ * so the impact of carrying around some unused fields is
+ * insignificant.)
*
* The table is lazily initialized to a power-of-two size upon the
* first insertion. Each bin in the table normally contains a
@@ -284,24 +135,12 @@ public class ConcurrentHashMap8<K, V>
* Table accesses require volatile/atomic reads, writes, and
* CASes. Because there is no other way to arrange this without
* adding further indirections, we use intrinsics
- * (sun.misc.Unsafe) operations. The lists of nodes within bins
- * are always accurately traversable under volatile reads, so long
- * as lookups check hash code and non-nullness of value before
- * checking key equality.
- *
- * We use the top two bits of Node hash fields for control
- * purposes -- they are available anyway because of addressing
- * constraints. As explained further below, these top bits are
- * used as follows:
- * 00 - Normal
- * 01 - Locked
- * 11 - Locked and may have a thread waiting for lock
- * 10 - Node is a forwarding node
+ * (sun.misc.Unsafe) operations.
*
- * The lower 30 bits of each Node's hash field contain a
- * transformation of the key's hash code, except for forwarding
- * nodes, for which the lower bits are zero (and so always have
- * hash field == MOVED).
+ * We use the top (sign) bit of Node hash fields for control
+ * purposes -- it is available anyway because of addressing
+ * constraints. Nodes with negative hash fields are specially
+ * handled or ignored in map methods.
*
* Insertion (via put or its variants) of the first node in an
* empty bin is performed by just CASing it to the bin. This is
@@ -310,22 +149,15 @@ public class ConcurrentHashMap8<K, V>
* delete, and replace) require locks. We do not want to waste
* the space required to associate a distinct lock object with
* each bin, so instead use the first node of a bin list itself as
- * a lock. Blocking support for these locks relies on the builtin
- * "synchronized" monitors. However, we also need a tryLock
- * construction, so we overlay these by using bits of the Node
- * hash field for lock control (see above), and so normally use
- * builtin monitors only for blocking and signalling using
- * wait/notifyAll constructions. See Node.tryAwaitLock.
+ * a lock. Locking support for these locks relies on builtin
+ * "synchronized" monitors.
*
* Using the first node of a list as a lock does not by itself
* suffice though: When a node is locked, any update must first
* validate that it is still the first node after locking it, and
* retry if not. Because new nodes are always appended to lists,
* once a node is first in a bin, it remains first until deleted
- * or the bin becomes invalidated (upon resizing). However,
- * operations that only conditionally update may inspect nodes
- * until the point of update. This is a converse of sorts to the
- * lazy locking technique described by Herlihy & Shavit.
+ * or the bin becomes invalidated (upon resizing).
*
* The main disadvantage of per-bin locks is that other update
* operations on other nodes in a bin list protected by the same
@@ -358,15 +190,12 @@ public class ConcurrentHashMap8<K, V>
* sometimes deviate significantly from uniform randomness. This
* includes the case when N > (1<<30), so some keys MUST collide.
* Similarly for dumb or hostile usages in which multiple keys are
- * designed to have identical hash codes. Also, although we guard
- * against the worst effects of this (see method spread), sets of
- * hashes may differ only in bits that do not impact their bin
- * index for a given power-of-two mask. So we use a secondary
- * strategy that applies when the number of nodes in a bin exceeds
- * a threshold, and at least one of the keys implements
- * Comparable. These TreeBins use a balanced tree to hold nodes
- * (a specialized form of red-black trees), bounding search time
- * to O(log N). Each search step in a TreeBin is around twice as
+ * designed to have identical hash codes or ones that differs only
+ * in masked-out high bits. So we use a secondary strategy that
+ * applies when the number of nodes in a bin exceeds a
+ * threshold. These TreeBins use a balanced tree to hold nodes (a
+ * specialized form of red-black trees), bounding search time to
+ * O(log N). Each search step in a TreeBin is at least twice as
* slow as in a regular list, but given that N cannot exceed
* (1<<64) (before running out of addresses) this bounds search
* steps, lock hold times, etc, to reasonable constants (roughly
@@ -377,43 +206,50 @@ public class ConcurrentHashMap8<K, V>
* iterators in the same way.
*
* The table is resized when occupancy exceeds a percentage
- * threshold (nominally, 0.75, but see below). Only a single
- * thread performs the resize (using field "sizeCtl", to arrange
- * exclusion), but the table otherwise remains usable for reads
- * and updates. Resizing proceeds by transferring bins, one by
- * one, from the table to the next 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. On
- * average, 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. Upon
- * transfer, the old table bin contains only a special forwarding
- * node (with hash field "MOVED") that contains the next table as
- * its key. On encountering a forwarding node, access and update
- * operations restart, using the new table.
- *
- * Each bin transfer requires its bin lock. However, unlike other
- * cases, a transfer can skip a bin if it fails to acquire its
- * lock, and revisit it later (unless it is a TreeBin). Method
- * rebuild maintains a buffer of TRANSFER_BUFFER_SIZE bins that
- * have been skipped because of failure to acquire a lock, and
- * blocks only if none are available (i.e., only very rarely).
- * The transfer operation must also ensure that all accessible
- * bins in both the old and new table are usable by any traversal.
- * When there are no lock acquisition failures, this is arranged
- * simply by proceeding from the last bin (table.length - 1) up
- * towards the first. Upon seeing a forwarding node, traversals
- * (see class Iter) arrange to move to the new table
- * without revisiting nodes. However, when any node is skipped
- * during a transfer, all earlier table bins may have become
- * visible, so are initialized with a reverse-forwarding node back
- * to the old table until the new ones are established. (This
- * sometimes requires transiently locking a forwarding node, which
- * is possible under the above encoding.) These more expensive
- * mechanics trigger only when necessary.
+ * threshold (nominally, 0.75, but see below). Any thread
+ * noticing an overfull bin may assist in resizing after the
+ * initiating thread allocates and sets up the replacement array.
+ * However, rather than stalling, these other threads may proceed
+ * with insertions etc. The use of TreeBins shields us from the
+ * worst case effects of overfilling while resizes are in
+ * progress. Resizing proceeds by transferring bins, one by one,
+ * from the table to the next table. However, threads claim small
+ * blocks of indices to transfer (via field transferIndex) before
+ * doing so, reducing contention. A generation stamp in field
+ * sizeCtl ensures that resizings do not overlap. 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. On average, 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. Upon transfer, the old table bin contains
+ * only a special forwarding node (with hash field "MOVED") that
+ * contains the next table as its key. On encountering a
+ * forwarding node, access and update operations restart, using
+ * the new table.
+ *
+ * Each bin transfer requires its bin lock, which can stall
+ * waiting for locks while resizing. However, because other
+ * threads can join in and help resize rather than contend for
+ * locks, average aggregate waits become shorter as resizing
+ * progresses. The transfer operation must also ensure that all
+ * accessible bins in both the old and new table are usable by any
+ * traversal. This is arranged in part by proceeding from the
+ * last bin (table.length - 1) up towards the first. Upon seeing
+ * a forwarding node, traversals (see class Traverser) arrange to
+ * move to the new table without revisiting nodes. To ensure that
+ * no intervening nodes are skipped even when moved out of order,
+ * a stack (see class TableStack) is created on first encounter of
+ * a forwarding node during a traversal, to maintain its place if
+ * later processing the current table. The need for these
+ * save/restore mechanics is relatively rare, but when one
+ * forwarding node is encountered, typically many more will be.
+ * So Traversers use a simple caching scheme to avoid creating so
+ * many new TableStack nodes. (Thanks to Peter Levart for
+ * suggesting use of a stack here.)
*
* The traversal scheme also applies to partial traversals of
* ranges of bins (via an alternate Traverser constructor)
@@ -428,20 +264,54 @@ public class ConcurrentHashMap8<K, V>
* These cases attempt to override the initial capacity settings,
* but harmlessly fail to take effect in cases of races.
*
- * The element count is maintained using a LongAdder8, which avoids
- * contention on updates but can encounter cache thrashing if read
- * too frequently during concurrent access. To avoid reading so
- * often, resizing is attempted either when a bin lock is
- * contended, or upon adding to a bin already holding two or more
- * nodes (checked before adding in the xIfAbsent methods, after
- * adding in others). Under uniform hash distributions, the
- * probability of this occurring at threshold is around 13%,
- * meaning that only about 1 in 8 puts check threshold (and after
- * resizing, many fewer do so). But this approximation has high
- * variance for small table sizes, so we check on any collision
- * for sizes <= 64. The bulk putAll operation further reduces
- * contention by only committing count updates upon these size
- * checks.
+ * The element count is maintained using a specialization of
+ * LongAdder. We need to incorporate a specialization rather than
+ * just use a LongAdder in order to access implicit
+ * contention-sensing that leads to creation of multiple
+ * CounterCells. The counter mechanics avoid contention on
+ * updates but can encounter cache thrashing if read too
+ * frequently during concurrent access. To avoid reading so often,
+ * resizing under contention is attempted only upon adding to a
+ * bin already holding two or more nodes. Under uniform hash
+ * distributions, the probability of this occurring at threshold
+ * is around 13%, meaning that only about 1 in 8 puts check
+ * threshold (and after resizing, many fewer do so).
+ *
+ * TreeBins use a special form of comparison for search and
+ * related operations (which is the main reason we cannot use
+ * existing collections such as TreeMaps). TreeBins contain
+ * Comparable elements, but may contain others, as well as
+ * elements that are Comparable but not necessarily Comparable for
+ * the same T, so we cannot invoke compareTo among them. To handle
+ * this, the tree is ordered primarily by hash value, then by
+ * Comparable.compareTo order if applicable. On lookup at a node,
+ * if elements are not comparable or compare as 0 then both left
+ * and right children may need to be searched in the case of tied
+ * hash values. (This corresponds to the full list search that
+ * would be necessary if all elements were non-Comparable and had
+ * tied hashes.) On insertion, to keep a total ordering (or as
+ * close as is required here) across rebalancings, we compare
+ * classes and identityHashCodes as tie-breakers. The red-black
+ * balancing code is updated from pre-jdk-collections
+ * (http://gee.cs.oswego.edu/dl/classes/collections/RBCell.java)
+ * based in turn on Cormen, Leiserson, and Rivest "Introduction to
+ * Algorithms" (CLR).
+ *
+ * TreeBins also require an additional locking mechanism. While
+ * list traversal is always possible by readers even during
+ * updates, tree traversal is not, mainly because of tree-rotations
+ * that may change the root node and/or its linkages. TreeBins
+ * include a simple read-write lock mechanism parasitic on the
+ * main bin-synchronization strategy: Structural adjustments
+ * associated with an insertion or removal are already bin-locked
+ * (and so cannot conflict with other writers) but must wait for
+ * ongoing readers to finish. Since there can be only one such
+ * waiter, we use a simple scheme using a single "waiter" field to
+ * block writers. However, readers need never block. If the root
+ * lock is held, they proceed along the slow traversal path (via
+ * next-pointers) until the lock becomes available or the list is
+ * exhausted, whichever comes first. These cases are not fast, but
+ * maximize aggregate expected throughput.
*
* Maintaining API and serialization compatibility with previous
* versions of this class introduces several oddities. Mainly: We
@@ -451,8 +321,20 @@ public class ConcurrentHashMap8<K, V>
* time that we can guarantee to honor it.) We also declare an
* unused "Segment" class that is instantiated in minimal form
* only when serializing.
+ *
+ * Also, solely for compatibility with previous versions of this
+ * class, it extends AbstractMap, even though all of its methods
+ * are overridden, so it is just useless baggage.
+ *
+ * This file is organized to make things a little easier to follow
+ * while reading than they might otherwise: First the main static
+ * declarations and utilities, then fields, then main public
+ * methods (with a few factorings of multiple public methods into
+ * internal ones), then sizing methods, trees, traversers, and
+ * bulk operations.
*/
+
/* ---------------- Constants -------------- */
/**
@@ -492,2737 +374,2362 @@ public class ConcurrentHashMap8<K, V>
private static final float LOAD_FACTOR = 0.75f;
/**
- * The buffer size for skipped bins during transfers. The
- * value is arbitrary but should be large enough to avoid
- * most locking stalls during resizes.
+ * The bin count threshold for using a tree rather than list for a
+ * bin. Bins are converted to trees when adding an element to a
+ * bin with at least this many nodes. The value must be greater
+ * than 2, and should be at least 8 to mesh with assumptions in
+ * tree removal about conversion back to plain bins upon
+ * shrinkage.
*/
- private static final int TRANSFER_BUFFER_SIZE = 32;
+ static final int TREEIFY_THRESHOLD = 8;
/**
- * The bin count threshold for using a tree rather than list for a
- * bin. The value reflects the approximate break-even point for
- * using tree-based operations.
+ * The bin count threshold for untreeifying a (split) bin during a
+ * resize operation. Should be less than TREEIFY_THRESHOLD, and at
+ * most 6 to mesh with shrinkage detection under removal.
*/
- private static final int TREE_THRESHOLD = 8;
+ static final int UNTREEIFY_THRESHOLD = 6;
- /*
- * Encodings for special uses of Node hash fields. See above for
- * explanation.
+ /**
+ * The smallest table capacity for which bins may be treeified.
+ * (Otherwise the table is resized if too many nodes in a bin.)
+ * The value should be at least 4 * TREEIFY_THRESHOLD to avoid
+ * conflicts between resizing and treeification thresholds.
*/
- static final int MOVED = 0x80000000; // hash field for forwarding nodes
- static final int LOCKED = 0x40000000; // set/tested only as a bit
- static final int WAITING = 0xc0000000; // both bits set/tested together
- static final int HASH_BITS = 0x3fffffff; // usable bits of normal node hash
-
- /* ---------------- Fields -------------- */
+ static final int MIN_TREEIFY_CAPACITY = 64;
/**
- * The array of bins. Lazily initialized upon first insertion.
- * Size is always a power of two. Accessed directly by iterators.
+ * Minimum number of rebinnings per transfer step. Ranges are
+ * subdivided to allow multiple resizer threads. This value
+ * serves as a lower bound to avoid resizers encountering
+ * excessive memory contention. The value should be at least
+ * DEFAULT_CAPACITY.
*/
- transient volatile Node[] table;
+ private static final int MIN_TRANSFER_STRIDE = 16;
/**
- * The counter maintaining number of elements.
+ * The number of bits used for generation stamp in sizeCtl.
+ * Must be at least 6 for 32bit arrays.
*/
- private transient final LongAdder8 counter;
+ private static int RESIZE_STAMP_BITS = 16;
/**
- * Table initialization and resizing control. When negative, the
- * table is being initialized or resized. Otherwise, when table is
- * null, holds the initial table size to use upon creation, or 0
- * for default. After initialization, holds the next element count
- * value upon which to resize the table.
+ * The maximum number of threads that can help resize.
+ * Must fit in 32 - RESIZE_STAMP_BITS bits.
*/
- private transient volatile int sizeCtl;
-
- // views
- private transient KeySetView<K,V> keySet;
- private transient ValuesView<K,V> values;
- private transient EntrySetView<K,V> entrySet;
-
- /** For serialization compatibility. Null unless serialized; see below */
- private Segment<K,V>[] segments;
+ private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
- /* ---------------- Table element access -------------- */
+ /**
+ * The bit shift for recording size stamp in sizeCtl.
+ */
+ private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
/*
- * Volatile access methods are used for table elements as well as
- * elements of in-progress next table while resizing. Uses are
- * null checked by callers, and implicitly bounds-checked, relying
- * on the invariants that tab arrays have non-zero size, and all
- * indices are masked with (tab.length - 1) which is never
- * negative and always less than length. Note that, to be correct
- * wrt arbitrary concurrency errors by users, bounds checks must
- * operate on local variables, which accounts for some odd-looking
- * inline assignments below.
+ * Encodings for Node hash fields. See above for explanation.
*/
+ static final int MOVED = 0x8fffffff; // (-1) hash for forwarding nodes
+ static final int TREEBIN = 0x80000000; // hash for roots of trees
+ static final int RESERVED = 0x80000001; // hash for transient reservations
+ static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash
- static final Node tabAt(Node[] tab, int i) { // used by Iter
- return (Node)UNSAFE.getObjectVolatile(tab, ((long)i<<ASHIFT)+ABASE);
- }
-
- private static final boolean casTabAt(Node[] tab, int i, Node c, Node v) {
- return UNSAFE.compareAndSwapObject(tab, ((long)i<<ASHIFT)+ABASE, c, v);
- }
+ /** Number of CPUS, to place bounds on some sizings */
+ static final int NCPU = Runtime.getRuntime().availableProcessors();
- private static final void setTabAt(Node[] tab, int i, Node v) {
- UNSAFE.putObjectVolatile(tab, ((long)i<<ASHIFT)+ABASE, v);
- }
+ /** For serialization compatibility. */
+ private static final ObjectStreamField[] serialPersistentFields = {
+ new ObjectStreamField("segments", Segment[].class),
+ new ObjectStreamField("segmentMask", Integer.TYPE),
+ new ObjectStreamField("segmentShift", Integer.TYPE)
+ };
/* ---------------- Nodes -------------- */
/**
- * Key-value entry. Note that this is never exported out as a
- * user-visible Map.Entry (see MapEntry below). Nodes with a hash
- * field of MOVED are special, and do not contain user keys or
- * values. Otherwise, keys are never null, and null val fields
- * indicate that a node is in the process of being deleted or
- * created. For purposes of read-only access, a key may be read
- * before a val, but can only be used after checking val to be
- * non-null.
+ * Key-value entry. This class is never exported out as a
+ * user-mutable Map.Entry (i.e., one supporting setValue; see
+ * MapEntry below), but can be used for read-only traversals used
+ * in bulk tasks. Subclasses of Node with a negative hash field
+ * are special, and contain null keys and values (but are never
+ * exported). Otherwise, keys and vals are never null.
*/
- static class Node {
- volatile int hash;
- final Object key;
- volatile Object val;
- volatile Node next;
+ static class Node<K,V> implements Map.Entry<K,V> {
+ final int hash;
+ final K key;
+ volatile V val;
+ Node<K,V> next;
- Node(int hash, Object key, Object val, Node next) {
+ Node(int hash, K key, V val, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.val = val;
this.next = next;
}
- /** CompareAndSet the hash field */
- final boolean casHash(int cmp, int val) {
- return UNSAFE.compareAndSwapInt(this, hashOffset, cmp, val);
+ public final K getKey() { return key; }
+ public final V getValue() { return val; }
+ public final int hashCode() { return key.hashCode() ^ val.hashCode(); }
+ public final String toString(){ return key + "=" + val; }
+ public final V setValue(V value) {
+ throw new UnsupportedOperationException();
}
- /** The number of spins before blocking for a lock */
- static final int MAX_SPINS =
- Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1;
+ public final boolean equals(Object o) {
+ Object k, v, u; Map.Entry<?,?> e;
+ return ((o instanceof Map.Entry) &&
+ (k = (e = (Map.Entry<?,?>)o).getKey()) != null &&
+ (v = e.getValue()) != null &&
+ (k == key || k.equals(key)) &&
+ (v == (u = val) || v.equals(u)));
+ }
/**
- * Spins a while if LOCKED bit set and this node is the first
- * of its bin, and then sets WAITING bits on hash field and
- * blocks (once) if they are still set. It is OK for this
- * method to return even if lock is not available upon exit,
- * which enables these simple single-wait mechanics.
- *
- * The corresponding signalling operation is performed within
- * callers: Upon detecting that WAITING has been set when
- * unlocking lock (via a failed CAS from non-waiting LOCKED
- * state), unlockers acquire the sync lock and perform a
- * notifyAll.
- *
- * The initial sanity check on tab and bounds is not currently
- * necessary in the only usages of this method, but enables
- * use in other future contexts.
+ * Virtualized support for map.get(); overridden in subclasses.
*/
- final void tryAwaitLock(Node[] tab, int i) {
- if (tab != null && i >= 0 && i < tab.length) { // sanity check
- int r = ThreadLocalRandom8.current().nextInt(); // randomize spins
- int spins = MAX_SPINS, h;
- while (tabAt(tab, i) == this && ((h = hash) & LOCKED) != 0) {
- if (spins >= 0) {
- r ^= r << 1; r ^= r >>> 3; r ^= r << 10; // xorshift
- if (r >= 0 && --spins == 0)
- Thread.yield(); // yield before block
- }
- else if (casHash(h, h | WAITING)) {
- synchronized (this) {
- if (tabAt(tab, i) == this &&
- (hash & WAITING) == WAITING) {
- try {
- wait();
- } catch (InterruptedException ie) {
- try {
- Thread.currentThread().interrupt();
- } catch (SecurityException ignore) {
- }
- }
- }
- else
- notifyAll(); // possibly won race vs signaller
- }
- break;
- }
- }
+ Node<K,V> find(int h, Object k) {
+ Node<K,V> e = this;
+ if (k != null) {
+ do {
+ K ek;
+ if (e.hash == h &&
+ ((ek = e.key) == k || (ek != null && k.equals(ek))))
+ return e;
+ } while ((e = e.next) != null);
}
+ return null;
}
+ }
- // Unsafe mechanics for casHash
- private static final sun.misc.Unsafe UNSAFE;
- private static final long hashOffset;
+ /* ---------------- Static utilities -------------- */
- static {
- try {
- UNSAFE = getUnsafe();
- Class<?> k = Node.class;
- hashOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("hash"));
- } catch (Exception e) {
- throw new Error(e);
+ /**
+ * Spreads (XORs) higher bits of hash to lower and also forces top
+ * bit to 0. Because the table uses power-of-two masking, sets of
+ * hashes that vary only in bits above the current mask will
+ * always collide. (Among known examples are sets of Float keys
+ * holding consecutive whole numbers in small tables.) So we
+ * apply a transform that spreads the impact of higher bits
+ * downward. There is a tradeoff between speed, utility, and
+ * quality of bit-spreading. Because many common sets of hashes
+ * are already reasonably distributed (so don't benefit from
+ * spreading), and because we use trees to handle large sets of
+ * collisions in bins, we just XOR some shifted bits in the
+ * cheapest possible way to reduce systematic lossage, as well as
+ * to incorporate impact of the highest bits that would otherwise
+ * never be used in index calculations because of table bounds.
+ */
+ static final int spread(int h) {
+ return (h ^ (h >>> 16)) & HASH_BITS;
+ }
+
+ /**
+ * Returns a power of two table size for the given desired capacity.
+ * See Hackers Delight, sec 3.2
+ */
+ private static final int tableSizeFor(int c) {
+ int n = c - 1;
+ n |= n >>> 1;
+ n |= n >>> 2;
+ n |= n >>> 4;
+ n |= n >>> 8;
+ n |= n >>> 16;
+ return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
+ }
+
+ /**
+ * Returns x's Class if it is of the form "class C implements
+ * Comparable<C>", else null.
+ */
+ static Class<?> comparableClassFor(Object x) {
+ if (x instanceof Comparable) {
+ Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
+ if ((c = x.getClass()) == String.class) // bypass checks
+ return c;
+ if ((ts = c.getGenericInterfaces()) != null) {
+ for (int i = 0; i < ts.length; ++i) {
+ if (((t = ts[i]) instanceof ParameterizedType) &&
+ ((p = (ParameterizedType)t).getRawType() ==
+ Comparable.class) &&
+ (as = p.getActualTypeArguments()) != null &&
+ as.length == 1 && as[0] == c) // type arg is c
+ return c;
+ }
}
}
+ return null;
}
- /* ---------------- TreeBins -------------- */
-
/**
- * Nodes for use in TreeBins
+ * Returns k.compareTo(x) if x matches kc (k's screened comparable
+ * class), else 0.
*/
- static final class TreeNode extends Node {
- TreeNode parent; // red-black tree links
- TreeNode left;
- TreeNode right;
- TreeNode prev; // needed to unlink next upon deletion
- boolean red;
+ @SuppressWarnings({"rawtypes","unchecked"}) // for cast to Comparable
+ static int compareComparables(Class<?> kc, Object k, Object x) {
+ return (x == null || x.getClass() != kc ? 0 :
+ ((Comparable)k).compareTo(x));
+ }
- TreeNode(int hash, Object key, Object val, Node next, TreeNode parent) {
- super(hash, key, val, next);
- this.parent = parent;
- }
+ /* ---------------- Table element access -------------- */
+
+ /*
+ * Volatile access methods are used for table elements as well as
+ * elements of in-progress next table while resizing. All uses of
+ * the tab arguments must be null checked by callers. All callers
+ * also paranoically precheck that tab's length is not zero (or an
+ * equivalent check), thus ensuring that any index argument taking
+ * the form of a hash value anded with (length - 1) is a valid
+ * index. Note that, to be correct wrt arbitrary concurrency
+ * errors by users, these checks must operate on local variables,
+ * which accounts for some odd-looking inline assignments below.
+ * Note that calls to setTabAt always occur within locked regions,
+ * and so do not need full volatile semantics, but still require
+ * ordering to maintain concurrent readability.
+ */
+
+ @SuppressWarnings("unchecked")
+ static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
+ return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
+ }
+
+ static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
+ Node<K,V> c, Node<K,V> v) {
+ return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
+ }
+
+ static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
+ U.putOrderedObject(tab, ((long)i << ASHIFT) + ABASE, v);
}
+ /* ---------------- Fields -------------- */
+
/**
- * A specialized form of red-black tree for use in bins
- * whose size exceeds a threshold.
- *
- * TreeBins use a special form of comparison for search and
- * related operations (which is the main reason we cannot use
- * existing collections such as TreeMaps). TreeBins contain
- * Comparable elements, but may contain others, as well as
- * elements that are Comparable but not necessarily Comparable<T>
- * for the same T, so we cannot invoke compareTo among them. To
- * handle this, the tree is ordered primarily by hash value, then
- * by getClass().getName() order, and then by Comparator order
- * among elements of the same class. On lookup at a node, if
- * elements are not comparable or compare as 0, both left and
- * right children may need to be searched in the case of tied hash
- * values. (This corresponds to the full list search that would be
- * necessary if all elements were non-Comparable and had tied
- * hashes.) The red-black balancing code is updated from
- * pre-jdk-collections
- * (http://gee.cs.oswego.edu/dl/classes/collections/RBCell.java)
- * based in turn on Cormen, Leiserson, and Rivest "Introduction to
- * Algorithms" (CLR).
- *
- * TreeBins also maintain a separate locking discipline than
- * regular bins. Because they are forwarded via special MOVED
- * nodes at bin heads (which can never change once established),
- * we cannot use those nodes as locks. Instead, TreeBin
- * extends AbstractQueuedSynchronizer to support a simple form of
- * read-write lock. For update operations and table validation,
- * the exclusive form of lock behaves in the same way as bin-head
- * locks. However, lookups use shared read-lock mechanics to allow
- * multiple readers in the absence of writers. Additionally,
- * these lookups do not ever block: While the lock is not
- * available, they proceed along the slow traversal path (via
- * next-pointers) until the lock becomes available or the list is
- * exhausted, whichever comes first. (These cases are not fast,
- * but maximize aggregate expected throughput.) The AQS mechanics
- * for doing this are straightforward. The lock state is held as
- * AQS getState(). Read counts are negative; the write count (1)
- * is positive. There are no signalling preferences among readers
- * and writers. Since we don't need to export full Lock API, we
- * just override the minimal AQS methods and use them directly.
- */
- static final class TreeBin extends AbstractQueuedSynchronizer {
- private static final long serialVersionUID = 2249069246763182397L;
- transient TreeNode root; // root of tree
- transient TreeNode first; // head of next-pointer list
-
- /* AQS overrides */
- public final boolean isHeldExclusively() { return getState() > 0; }
- public final boolean tryAcquire(int ignore) {
- if (compareAndSetState(0, 1)) {
- setExclusiveOwnerThread(Thread.currentThread());
- return true;
- }
- return false;
- }
- public final boolean tryRelease(int ignore) {
- setExclusiveOwnerThread(null);
- setState(0);
- return true;
- }
- public final int tryAcquireShared(int ignore) {
- for (int c;;) {
- if ((c = getState()) > 0)
- return -1;
- if (compareAndSetState(c, c -1))
- return 1;
- }
- }
- public final boolean tryReleaseShared(int ignore) {
- int c;
- do {} while (!compareAndSetState(c = getState(), c + 1));
- return c == -1;
- }
+ * The array of bins. Lazily initialized upon first insertion.
+ * Size is always a power of two. Accessed directly by iterators.
+ */
+ transient volatile Node<K,V>[] table;
- /** From CLR */
- private void rotateLeft(TreeNode p) {
- if (p != null) {
- TreeNode r = p.right, pp, rl;
- if ((rl = p.right = r.left) != null)
- rl.parent = p;
- if ((pp = r.parent = p.parent) == null)
- root = r;
- else if (pp.left == p)
- pp.left = r;
- else
- pp.right = r;
- r.left = p;
- p.parent = r;
- }
- }
+ /**
+ * The next table to use; non-null only while resizing.
+ */
+ private transient volatile Node<K,V>[] nextTable;
- /** From CLR */
- private void rotateRight(TreeNode p) {
- if (p != null) {
- TreeNode l = p.left, pp, lr;
- if ((lr = p.left = l.right) != null)
- lr.parent = p;
- if ((pp = l.parent = p.parent) == null)
- root = l;
- else if (pp.right == p)
- pp.right = l;
- else
- pp.left = l;
- l.right = p;
- p.parent = l;
- }
- }
+ /**
+ * Base counter value, used mainly when there is no contention,
+ * but also as a fallback during table initialization
+ * races. Updated via CAS.
+ */
+ private transient volatile long baseCount;
- /**
- * Returns the TreeNode (or null if not found) for the given key
- * starting at given root.
- */
- @SuppressWarnings("unchecked") final TreeNode getTreeNode
- (int h, Object k, TreeNode p) {
- Class<?> c = k.getClass();
- while (p != null) {
- int dir, ph; Object pk; Class<?> pc;
- if ((ph = p.hash) == h) {
- if ((pk = p.key) == k || k.equals(pk))
- return p;
- if (c != (pc = pk.getClass()) ||
- !(k instanceof Comparable) ||
- (dir = ((Comparable)k).compareTo((Comparable)pk)) == 0) {
- dir = (c == pc) ? 0 : c.getName().compareTo(pc.getName());
- TreeNode r = null, s = null, pl, pr;
- if (dir >= 0) {
- if ((pl = p.left) != null && h <= pl.hash)
- s = pl;
- }
- else if ((pr = p.right) != null && h >= pr.hash)
- s = pr;
- if (s != null && (r = getTreeNode(h, k, s)) != null)
- return r;
- }
- }
- else
- dir = (h < ph) ? -1 : 1;
- p = (dir > 0) ? p.right : p.left;
- }
- return null;
- }
+ /**
+ * Table initialization and resizing control. When negative, the
+ * table is being initialized or resized: -1 for initialization,
+ * else -(1 + the number of active resizing threads). Otherwise,
+ * when table is null, holds the initial table size to use upon
+ * creation, or 0 for default. After initialization, holds the
+ * next element count value upon which to resize the table.
+ */
+ private transient volatile int sizeCtl;
- /**
- * Wrapper for getTreeNode used by CHM.get. Tries to obtain
- * read-lock to call getTreeNode, but during failure to get
- * lock, searches along next links.
- */
- final Object getValue(int h, Object k) {
- Node r = null;
- int c = getState(); // Must read lock state first
- for (Node e = first; e != null; e = e.next) {
- if (c <= 0 && compareAndSetState(c, c - 1)) {
- try {
- r = getTreeNode(h, k, root);
- } finally {
- releaseShared(0);
- }
- break;
- }
- else if ((e.hash & HASH_BITS) == h && k.equals(e.key)) {
- r = e;
- break;
- }
- else
- c = getState();
- }
- return r == null ? null : r.val;
- }
+ /**
+ * The next table index (plus one) to split while resizing.
+ */
+ private transient volatile int transferIndex;
- /**
- * Finds or adds a node.
- * @return null if added
- */
- @SuppressWarnings("unchecked") final TreeNode putTreeNode
- (int h, Object k, Object v) {
- Class<?> c = k.getClass();
- TreeNode pp = root, p = null;
- int dir = 0;
- while (pp != null) { // find existing node or leaf to insert at
- int ph; Object pk; Class<?> pc;
- p = pp;
- if ((ph = p.hash) == h) {
- if ((pk = p.key) == k || k.equals(pk))
- return p;
- if (c != (pc = pk.getClass()) ||
- !(k instanceof Comparable) ||
- (dir = ((Comparable)k).compareTo((Comparable)pk)) == 0) {
- dir = (c == pc) ? 0 : c.getName().compareTo(pc.getName());
- TreeNode r = null, s = null, pl, pr;
- if (dir >= 0) {
- if ((pl = p.left) != null && h <= pl.hash)
- s = pl;
- }
- else if ((pr = p.right) != null && h >= pr.hash)
- s = pr;
- if (s != null && (r = getTreeNode(h, k, s)) != null)
- return r;
- }
- }
- else
- dir = (h < ph) ? -1 : 1;
- pp = (dir > 0) ? p.right : p.left;
- }
+ /**
+ * Spinlock (locked via CAS) used when resizing and/or creating CounterCells.
+ */
+ private transient volatile int cellsBusy;
- TreeNode f = first;
- TreeNode x = first = new TreeNode(h, k, v, f, p);
- if (p == null)
- root = x;
- else { // attach and rebalance; adapted from CLR
- TreeNode xp, xpp;
- if (f != null)
- f.prev = x;
- if (dir <= 0)
- p.left = x;
- else
- p.right = x;
- x.red = true;
- while (x != null && (xp = x.parent) != null && xp.red &&
- (xpp = xp.parent) != null) {
- TreeNode xppl = xpp.left;
- if (xp == xppl) {
- TreeNode y = xpp.right;
- if (y != null && y.red) {
- y.red = false;
- xp.red = false;
- xpp.red = true;
- x = xpp;
- }
- else {
- if (x == xp.right) {
- rotateLeft(x = xp);
- xpp = (xp = x.parent) == null ? null : xp.parent;
- }
- if (xp != null) {
- xp.red = false;
- if (xpp != null) {
- xpp.red = true;
- rotateRight(xpp);
- }
- }
- }
- }
- else {
- TreeNode y = xppl;
- if (y != null && y.red) {
- y.red = false;
- xp.red = false;
- xpp.red = true;
- x = xpp;
- }
- else {
- if (x == xp.left) {
- rotateRight(x = xp);
- xpp = (xp = x.parent) == null ? null : xp.parent;
- }
- if (xp != null) {
- xp.red = false;
- if (xpp != null) {
- xpp.red = true;
- rotateLeft(xpp);
- }
- }
- }
- }
- }
- TreeNode r = root;
- if (r != null && r.red)
- r.red = false;
- }
- return null;
- }
+ /**
+ * Table of counter cells. When non-null, size is a power of 2.
+ */
+ private transient volatile CounterCell[] counterCells;
- /**
- * Removes the given node, that must be present before this
- * call. This is messier than typical red-black deletion code
- * because we cannot swap the contents of an interior node
- * with a leaf successor that is pinned by "next" pointers
- * that are accessible independently of lock. So instead we
- * swap the tree linkages.
- */
- final void deleteTreeNode(TreeNode p) {
- TreeNode next = (TreeNode)p.next; // unlink traversal pointers
- TreeNode pred = p.prev;
- if (pred == null)
- first = next;
- else
- pred.next = next;
- if (next != null)
- next.prev = pred;
- TreeNode replacement;
- TreeNode pl = p.left;
- TreeNode pr = p.right;
- if (pl != null && pr != null) {
- TreeNode s = pr, sl;
- while ((sl = s.left) != null) // find successor
- s = sl;
- boolean c = s.red; s.red = p.red; p.red = c; // swap colors
- TreeNode sr = s.right;
- TreeNode pp = p.parent;
- if (s == pr) { // p was s's direct parent
- p.parent = s;
- s.right = p;
- }
- else {
- TreeNode sp = s.parent;
- if ((p.parent = sp) != null) {
- if (s == sp.left)
- sp.left = p;
- else
- sp.right = p;
- }
- if ((s.right = pr) != null)
- pr.parent = s;
- }
- p.left = null;
- if ((p.right = sr) != null)
- sr.parent = p;
- if ((s.left = pl) != null)
- pl.parent = s;
- if ((s.parent = pp) == null)
- root = s;
- else if (p == pp.left)
- pp.left = s;
- else
- pp.right = s;
- replacement = sr;
- }
- else
- replacement = (pl != null) ? pl : pr;
- TreeNode pp = p.parent;
- if (replacement == null) {
- if (pp == null) {
- root = null;
- return;
- }
- replacement = p;
- }
- else {
- replacement.parent = pp;
- if (pp == null)
- root = replacement;
- else if (p == pp.left)
- pp.left = replacement;
- else
- pp.right = replacement;
- p.left = p.right = p.parent = null;
- }
- if (!p.red) { // rebalance, from CLR
- TreeNode x = replacement;
- while (x != null) {
- TreeNode xp, xpl;
- if (x.red || (xp = x.parent) == null) {
- x.red = false;
- break;
- }
- if (x == (xpl = xp.left)) {
- TreeNode sib = xp.right;
- if (sib != null && sib.red) {
- sib.red = false;
- xp.red = true;
- rotateLeft(xp);
- sib = (xp = x.parent) == null ? null : xp.right;
- }
- if (sib == null)
- x = xp;
- else {
- TreeNode sl = sib.left, sr = sib.right;
- if ((sr == null || !sr.red) &&
- (sl == null || !sl.red)) {
- sib.red = true;
- x = xp;
- }
- else {
- if (sr == null || !sr.red) {
- if (sl != null)
- sl.red = false;
- sib.red = true;
- rotateRight(sib);
- sib = (xp = x.parent) == null ? null : xp.right;
- }
- if (sib != null) {
- sib.red = (xp == null) ? false : xp.red;
- if ((sr = sib.right) != null)
- sr.red = false;
- }
- if (xp != null) {
- xp.red = false;
- rotateLeft(xp);
- }
- x = root;
- }
- }
- }
- else { // symmetric
- TreeNode sib = xpl;
- if (sib != null && sib.red) {
- sib.red = false;
- xp.red = true;
- rotateRight(xp);
- sib = (xp = x.parent) == null ? null : xp.left;
- }
- if (sib == null)
- x = xp;
- else {
- TreeNode sl = sib.left, sr = sib.right;
- if ((sl == null || !sl.red) &&
- (sr == null || !sr.red)) {
- sib.red = true;
- x = xp;
- }
- else {
- if (sl == null || !sl.red) {
- if (sr != null)
- sr.red = false;
- sib.red = true;
- rotateLeft(sib);
- sib = (xp = x.parent) == null ? null : xp.left;
- }
- if (sib != null) {
- sib.red = (xp == null) ? false : xp.red;
- if ((sl = sib.left) != null)
- sl.red = false;
- }
- if (xp != null) {
- xp.red = false;
- rotateRight(xp);
- }
- x = root;
- }
- }
- }
- }
- }
- if (p == replacement && (pp = p.parent) != null) {
- if (p == pp.left) // detach pointers
- pp.left = null;
- else if (p == pp.right)
- pp.right = null;
- p.parent = null;
- }
- }
+ // views
+ private transient KeySetView<K,V> keySet;
+ private transient ValuesView<K,V> values;
+ private transient EntrySetView<K,V> entrySet;
+
+
+ /* ---------------- Public operations -------------- */
+
+ /**
+ * Creates a new, empty map with the default initial table size (16).
+ */
+ public ConcurrentHashMap8() {
}
- /* ---------------- Collision reduction methods -------------- */
+ /**
+ * Creates a new, empty map with an initial table size
+ * accommodating the specified number of elements without the need
+ * to dynamically resize.
+ *
+ * @param initialCapacity The implementation performs internal
+ * sizing to accommodate this many elements.
+ * @throws IllegalArgumentException if the initial capacity of
+ * elements is negative
+ */
+ public ConcurrentHashMap8(int initialCapacity) {
+ if (initialCapacity < 0)
+ throw new IllegalArgumentException();
+ int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
+ MAXIMUM_CAPACITY :
+ tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
+ this.sizeCtl = cap;
+ }
/**
- * Spreads higher bits to lower, and also forces top 2 bits to 0.
- * Because the table uses power-of-two masking, sets of hashes
- * that vary only in bits above the current mask will always
- * collide. (Among known examples are sets of Float keys holding
- * consecutive whole numbers in small tables.) To counter this,
- * we apply a transform that spreads the impact of higher bits
- * downward. There is a tradeoff between speed, utility, and
- * quality of bit-spreading. Because many common sets of hashes
- * are already reasonably distributed across bits (so don't benefit
- * from spreading), and because we use trees to handle large sets
- * of collisions in bins, we don't need excessively high quality.
+ * Creates a new map with the same mappings as the given map.
+ *
+ * @param m the map
*/
- private static final int spread(int h) {
- h ^= (h >>> 18) ^ (h >>> 12);
- return (h ^ (h >>> 10)) & HASH_BITS;
+ public ConcurrentHashMap8(Map<? extends K, ? extends V> m) {
+ this.sizeCtl = DEFAULT_CAPACITY;
+ putAll(m);
}
/**
- * Replaces a list bin with a tree bin. Call only when locked.
- * Fails to replace if the given key is non-comparable or table
- * is, or needs, resizing.
+ * Creates a new, empty map with an initial table size based on
+ * the given number of elements ({@code initialCapacity}) and
+ * initial table density ({@code loadFactor}).
+ *
+ * @param initialCapacity the initial capacity. The implementation
+ * performs internal sizing to accommodate this many elements,
+ * given the specified load factor.
+ * @param loadFactor the load factor (table density) for
+ * establishing the initial table size
+ * @throws IllegalArgumentException if the initial capacity of
+ * elements is negative or the load factor is nonpositive
+ *
+ * @since 1.6
*/
- private final void replaceWithTreeBin(Node[] tab, int index, Object key) {
- if ((key instanceof Comparable) &&
- (tab.length >= MAXIMUM_CAPACITY || counter.sum() < (long)sizeCtl)) {
- TreeBin t = new TreeBin();
- for (Node e = tabAt(tab, index); e != null; e = e.next)
- t.putTreeNode(e.hash & HASH_BITS, e.key, e.val);
- setTabAt(tab, index, new Node(MOVED, t, null, null));
- }
+ public ConcurrentHashMap8(int initialCapacity, float loadFactor) {
+ this(initialCapacity, loadFactor, 1);
}
- /* ---------------- Internal access and update methods -------------- */
+ /**
+ * Creates a new, empty map with an initial table size based on
+ * the given number of elements ({@code initialCapacity}), table
+ * density ({@code loadFactor}), and number of concurrently
+ * updating threads ({@code concurrencyLevel}).
+ *
+ * @param initialCapacity the initial capacity. The implementation
+ * performs internal sizing to accommodate this many elements,
+ * given the specified load factor.
+ * @param loadFactor the load factor (table density) for
+ * establishing the initial table size
+ * @param concurrencyLevel the estimated number of concurrently
+ * updating threads. The implementation may use this value as
+ * a sizing hint.
+ * @throws IllegalArgumentException if the initial capacity is
+ * negative or the load factor or concurrencyLevel are
+ * nonpositive
+ */
+ public ConcurrentHashMap8(int initialCapacity,
+ float loadFactor, int concurrencyLevel) {
+ if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)
+ throw new IllegalArgumentException();
+ if (initialCapacity < concurrencyLevel) // Use at least as many bins
+ initialCapacity = concurrencyLevel; // as estimated threads
+ long size = (long)(1.0 + (long)initialCapacity / loadFactor);
+ int cap = (size >= (long)MAXIMUM_CAPACITY) ?
+ MAXIMUM_CAPACITY : tableSizeFor((int)size);
+ this.sizeCtl = cap;
+ }
- /** Implementation for get and containsKey */
- private final Object internalGet(Object k) {
- int h = spread(k.hashCode());
- retry: for (Node[] tab = table; tab != null;) {
- Node e, p; Object ek, ev; int eh; // locals to read fields once
- for (e = tabAt(tab, (tab.length - 1) & h); e != null; e = e.next) {
- if ((eh = e.hash) == MOVED) {
- if ((ek = e.key) instanceof TreeBin) // search TreeBin
- return ((TreeBin)ek).getValue(h, k);
- else { // restart with new table
- tab = (Node[])ek;
- continue retry;
- }
- }
- else if ((eh & HASH_BITS) == h && (ev = e.val) != null &&
- ((ek = e.key) == k || k.equals(ek)))
- return ev;
+ // Original (since JDK1.2) Map methods
+
+ /**
+ * {@inheritDoc}
+ */
+ public int size() {
+ long n = sumCount();
+ return ((n < 0L) ? 0 :
+ (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE :
+ (int)n);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isEmpty() {
+ return sumCount() <= 0L; // ignore transient negative values
+ }
+
+ /**
+ * Returns the value to which the specified key is mapped,
+ * or {@code null} if this map contains no mapping for the key.
+ *
+ * <p>More formally, if this map contains a mapping from a key
+ * {@code k} to a value {@code v} such that {@code key.equals(k)},
+ * then this method returns {@code v}; otherwise it returns
+ * {@code null}. (There can be at most one such mapping.)
+ *
+ * @throws NullPointerException if the specified key is null
+ */
+ public V get(Object key) {
+ Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
+ int h = spread(key.hashCode());
+ if ((tab = table) != null && (n = tab.length) > 0 &&
+ (e = tabAt(tab, (n - 1) & h)) != null) {
+ if ((eh = e.hash) == h) {
+ if ((ek = e.key) == key || (ek != null && key.equals(ek)))
+ return e.val;
+ }
+ else if (eh < 0)
+ return (p = e.find(h, key)) != null ? p.val : null;
+ while ((e = e.next) != null) {
+ if (e.hash == h &&
+ ((ek = e.key) == key || (ek != null && key.equals(ek))))
+ return e.val;
}
- break;
}
return null;
}
/**
- * Implementation for the four public remove/replace methods:
- * Replaces node value with v, conditional upon match of cv if
- * non-null. If resulting value is null, delete.
+ * Tests if the specified object is a key in this table.
+ *
+ * @param key possible key
+ * @return {@code true} if and only if the specified object
+ * is a key in this table, as determined by the
+ * {@code equals} method; {@code false} otherwise
+ * @throws NullPointerException if the specified key is null
*/
- private final Object internalReplace(Object k, Object v, Object cv) {
- int h = spread(k.hashCode());
- Object oldVal = null;
- for (Node[] tab = table;;) {
- Node f; int i, fh; Object fk;
- if (tab == null ||
- (f = tabAt(tab, i = (tab.length - 1) & h)) == null)
- break;
- else if ((fh = f.hash) == MOVED) {
- if ((fk = f.key) instanceof TreeBin) {
- TreeBin t = (TreeBin)fk;
- boolean validated = false;
- boolean deleted = false;
- t.acquire(0);
- try {
- if (tabAt(tab, i) == f) {
- validated = true;
- TreeNode p = t.getTreeNode(h, k, t.root);
- if (p != null) {
- Object pv = p.val;
- if (cv == null || cv == pv || cv.equals(pv)) {
- oldVal = pv;
- if ((p.val = v) == null) {
- deleted = true;
- t.deleteTreeNode(p);
- }
- }
- }
- }
- } finally {
- t.release(0);
- }
- if (validated) {
- if (deleted)
- counter.add(-1L);
- break;
- }
- }
- else
- tab = (Node[])fk;
- }
- else if ((fh & HASH_BITS) != h && f.next == null) // precheck
- break; // rules out possible existence
- else if ((fh & LOCKED) != 0) {
- checkForResize(); // try resizing if can't get lock
- f.tryAwaitLock(tab, i);
- }
- else if (f.casHash(fh, fh | LOCKED)) {
- boolean validated = false;
- boolean deleted = false;
- try {
- if (tabAt(tab, i) == f) {
- validated = true;
- for (Node e = f, pred = null;;) {
- Object ek, ev;
- if ((e.hash & HASH_BITS) == h &&
- ((ev = e.val) != null) &&
- ((ek = e.key) == k || k.equals(ek))) {
- if (cv == null || cv == ev || cv.equals(ev)) {
- oldVal = ev;
- if ((e.val = v) == null) {
- deleted = true;
- Node en = e.next;
- if (pred != null)
- pred.next = en;
- else
- setTabAt(tab, i, en);
- }
- }
- break;
- }
- pred = e;
- if ((e = e.next) == null)
- break;
- }
- }
- } finally {
- if (!f.casHash(fh | LOCKED, fh)) {
- f.hash = fh;
- synchronized (f) { f.notifyAll(); };
- }
- }
- if (validated) {
- if (deleted)
- counter.add(-1L);
- break;
- }
+ public boolean containsKey(Object key) {
+ return get(key) != null;
+ }
+
+ /**
+ * Returns {@code true} if this map maps one or more keys to the
+ * specified value. Note: This method may require a full traversal
+ * of the map, and is much slower than method {@code containsKey}.
+ *
+ * @param value value whose presence in this map is to be tested
+ * @return {@code true} if this map maps one or more keys to the
+ * specified value
+ * @throws NullPointerException if the specified value is null
+ */
+ public boolean containsValue(Object value) {
+ if (value == null)
+ throw new NullPointerException();
+ Node<K,V>[] t;
+ if ((t = table) != null) {
+ Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
+ for (Node<K,V> p; (p = it.advance()) != null; ) {
+ V v;
+ if ((v = p.val) == value || (v != null && value.equals(v)))
+ return true;
}
}
- return oldVal;
+ return false;
}
- /*
- * Internal versions of the six insertion methods, each a
- * little more complicated than the last. All have
- * the same basic structure as the first (internalPut):
- * 1. If table uninitialized, create
- * 2. If bin empty, try to CAS new node
- * 3. If bin stale, use new table
- * 4. if bin converted to TreeBin, validate and relay to TreeBin methods
- * 5. Lock and validate; if valid, scan and add or update
+ /**
+ * Maps the specified key to the specified value in this table.
+ * Neither the key nor the value can be null.
*
- * The others interweave other checks and/or alternative actions:
- * * Plain put checks for and performs resize after insertion.
- * * putIfAbsent prescans for mapping without lock (and fails to add
- * if present), which also makes pre-emptive resize checks worthwhile.
- * * computeIfAbsent extends form used in putIfAbsent with additional
- * mechanics to deal with, calls, potential exceptions and null
- * returns from function call.
- * * compute uses the same function-call mechanics, but without
- * the prescans
- * * merge acts as putIfAbsent in the absent case, but invokes the
- * update function if present
- * * putAll attempts to pre-allocate enough table space
- * and more lazily performs count updates and checks.
+ * <p>The value can be retrieved by calling the {@code get} method
+ * with a key that is equal to the original key.
*
- * Someday when details settle down a bit more, it might be worth
- * some factoring to reduce sprawl.
+ * @param key key with which the specified value is to be associated
+ * @param value value to be associated with the specified key
+ * @return the previous value associated with {@code key}, or
+ * {@code null} if there was no mapping for {@code key}
+ * @throws NullPointerException if the specified key or value is null
*/
+ public V put(K key, V value) {
+ return putVal(key, value, false);
+ }
- /** Implementation for put */
- private final Object internalPut(Object k, Object v) {
- int h = spread(k.hashCode());
- int count = 0;
- for (Node[] tab = table;;) {
- int i; Node f; int fh; Object fk;
- if (tab == null)
+ /** Implementation for put and putIfAbsent */
+ final V putVal(K key, V value, boolean onlyIfAbsent) {
+ if (key == null || value == null) throw new NullPointerException();
+ int hash = spread(key.hashCode());
+ int binCount = 0;
+ for (Node<K,V>[] tab = table;;) {
+ Node<K,V> f; int n, i, fh;
+ if (tab == null || (n = tab.length) == 0)
tab = initTable();
- else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) {
- if (casTabAt(tab, i, null, new Node(h, k, v, null)))
+ else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
+ if (casTabAt(tab, i, null,
+ new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
- else if ((fh = f.hash) == MOVED) {
- if ((fk = f.key) instanceof TreeBin) {
- TreeBin t = (TreeBin)fk;
- Object oldVal = null;
- t.acquire(0);
- try {
- if (tabAt(tab, i) == f) {
- count = 2;
- TreeNode p = t.putTreeNode(h, k, v);
- if (p != null) {
- oldVal = p.val;
- p.val = v;
- }
- }
- } finally {
- t.release(0);
- }
- if (count != 0) {
- if (oldVal != null)
- return oldVal;
- break;
- }
- }
- else
- tab = (Node[])fk;
- }
- else if ((fh & LOCKED) != 0) {
- checkForResize();
- f.tryAwaitLock(tab, i);
- }
- else if (f.casHash(fh, fh | LOCKED)) {
- Object oldVal = null;
- try { // needed in case equals() throws
+ else if ((fh = f.hash) == MOVED)
+ tab = helpTransfer(tab, f);
+ else {
+ V oldVal = null;
+ synchronized (f) {
if (tabAt(tab, i) == f) {
- count = 1;
- for (Node e = f;; ++count) {
- Object ek, ev;
- if ((e.hash & HASH_BITS) == h &&
- (ev = e.val) != null &&
- ((ek = e.key) == k || k.equals(ek))) {
- oldVal = ev;
- e.val = v;
- break;
+ if (fh >= 0) {
+ binCount = 1;
+ for (Node<K,V> e = f;; ++binCount) {
+ K ek;
+ if (e.hash == hash &&
+ ((ek = e.key) == key ||
+ (ek != null && key.equals(ek)))) {
+ oldVal = e.val;
+ if (!onlyIfAbsent)
+ e.val = value;
+ break;
+ }
+ Node<K,V> pred = e;
+ if ((e = e.next) == null) {
+ pred.next = new Node<K,V>(hash, key,
+ value, null);
+ break;
+ }
}
- Node last = e;
- if ((e = e.next) == null) {
- last.next = new Node(h, k, v, null);
- if (count >= TREE_THRESHOLD)
- replaceWithTreeBin(tab, i, k);
- break;
+ }
+ else if (f instanceof TreeBin) {
+ Node<K,V> p;
+ binCount = 2;
+ if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
+ value)) != null) {
+ oldVal = p.val;
+ if (!onlyIfAbsent)
+ p.val = value;
}
}
}
- } finally { // unlock and signal if needed
- if (!f.casHash(fh | LOCKED, fh)) {
- f.hash = fh;
- synchronized (f) { f.notifyAll(); };
- }
}
- if (count != 0) {
+ if (binCount != 0) {
+ if (binCount >= TREEIFY_THRESHOLD)
+ treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
- if (tab.length <= 64)
- count = 2;
break;
}
}
}
- counter.add(1L);
- if (count > 1)
- checkForResize();
+ addCount(1L, binCount);
return null;
}
- /** Implementation for putIfAbsent */
- private final Object internalPutIfAbsent(Object k, Object v) {
- int h = spread(k.hashCode());
- int count = 0;
- for (Node[] tab = table;;) {
- int i; Node f; int fh; Object fk, fv;
- if (tab == null)
- tab = initTable();
- else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) {
- if (casTabAt(tab, i, null, new Node(h, k, v, null)))
- break;
- }
- else if ((fh = f.hash) == MOVED) {
- if ((fk = f.key) instanceof TreeBin) {
- TreeBin t = (TreeBin)fk;
- Object oldVal = null;
- t.acquire(0);
- try {
- if (tabAt(tab, i) == f) {
- count = 2;
- TreeNode p = t.putTreeNode(h, k, v);
- if (p != null)
- oldVal = p.val;
- }
- } finally {
- t.release(0);
- }
- if (count != 0) {
- if (oldVal != null)
- return oldVal;
- break;
- }
- }
- else
- tab = (Node[])fk;
- }
- else if ((fh & HASH_BITS) == h && (fv = f.val) != null &&
- ((fk = f.key) == k || k.equals(fk)))
- return fv;
- else {
- Node g = f.next;
- if (g != null) { // at least 2 nodes -- search and maybe resize
- for (Node e = g;;) {
- Object ek, ev;
- if ((e.hash & HASH_BITS) == h && (ev = e.val) != null &&
- ((ek = e.key) == k || k.equals(ek)))
- return ev;
- if ((e = e.next) == null) {
- checkForResize();
- break;
- }
- }
- }
- if (((fh = f.hash) & LOCKED) != 0) {
- checkForResize();
- f.tryAwaitLock(tab, i);
- }
- else if (tabAt(tab, i) == f && f.casHash(fh, fh | LOCKED)) {
- Object oldVal = null;
- try {
- if (tabAt(tab, i) == f) {
- count = 1;
- for (Node e = f;; ++count) {
- Object ek, ev;
- if ((e.hash & HASH_BITS) == h &&
-
<TRUNCATED>
[6/7] incubator-ignite git commit: ignite-sprint-6: revert changes in
concurrent map
Posted by sb...@apache.org.
ignite-sprint-6: revert changes in concurrent map
Project: http://git-wip-us.apache.org/repos/asf/incubator-ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ignite/commit/8a1c85d6
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ignite/tree/8a1c85d6
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ignite/diff/8a1c85d6
Branch: refs/heads/ignite-sprint-6
Commit: 8a1c85d6070ec19c90abb7e8ae8a6f04720c72b6
Parents: 8203caf
Author: Denis Magda <dm...@gridgain.com>
Authored: Wed Jun 10 18:03:05 2015 +0300
Committer: Denis Magda <dm...@gridgain.com>
Committed: Wed Jun 10 18:03:05 2015 +0300
----------------------------------------------------------------------
modules/core/src/main/java/org/jsr166/ConcurrentHashMap8.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8a1c85d6/modules/core/src/main/java/org/jsr166/ConcurrentHashMap8.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/jsr166/ConcurrentHashMap8.java b/modules/core/src/main/java/org/jsr166/ConcurrentHashMap8.java
index 041130b..0b60ff7 100644
--- a/modules/core/src/main/java/org/jsr166/ConcurrentHashMap8.java
+++ b/modules/core/src/main/java/org/jsr166/ConcurrentHashMap8.java
@@ -8,7 +8,7 @@
* The latest version of the file was copied from the following CVS repository:
* http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/
*
- * Corresponding commit version in CVS repository is unknown (lost in our side).
+ * Corresponding commit version in CVS repository is unknown (lost on our side).
* On the other hand we can't simply synch the latest version from CVS here, because Ignite uses functionality that
* is no longer supported.
*/
[7/7] incubator-ignite git commit: Merge branch 'ignite-sprint-6' of
https://git-wip-us.apache.org/repos/asf/incubator-ignite into ignite-sprint-6
Posted by sb...@apache.org.
Merge branch 'ignite-sprint-6' of https://git-wip-us.apache.org/repos/asf/incubator-ignite into ignite-sprint-6
Project: http://git-wip-us.apache.org/repos/asf/incubator-ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ignite/commit/95b7147f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ignite/tree/95b7147f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ignite/diff/95b7147f
Branch: refs/heads/ignite-sprint-6
Commit: 95b7147fe18ef75ac67741647c9f2ab61a59dcb3
Parents: 8a1c85d a12302e
Author: Denis Magda <dm...@gridgain.com>
Authored: Wed Jun 10 18:04:08 2015 +0300
Committer: Denis Magda <dm...@gridgain.com>
Committed: Wed Jun 10 18:04:08 2015 +0300
----------------------------------------------------------------------
.../client/memcache/MemcacheRestExample.java | 32 ++++++++++----------
.../client/router/TcpSslRouterSelfTest.java | 5 +++
.../client/suite/IgniteClientTestSuite.java | 3 +-
.../java/org/apache/ignite/IgniteCache.java | 25 ++++++++++++++-
.../affinity/GridAffinityAssignmentCache.java | 5 ++-
.../processors/cache/IgniteInternalCache.java | 27 +++++------------
.../internal/visor/query/VisorQueryJob.java | 2 +-
.../internal/visor/util/VisorTaskUtils.java | 16 +++-------
.../ignite/spi/discovery/tcp/ServerImpl.java | 6 ++--
.../spi/discovery/tcp/TcpDiscoverySpi.java | 2 +-
.../RoundRobinGlobalLoadBalancer.java | 2 +-
.../ignite/GridSuppressedExceptionSelfTest.java | 4 ++-
...inodeUpdateNearEnabledNoBackupsSelfTest.java | 2 +-
...CacheMultinodeUpdateNearEnabledSelfTest.java | 2 +-
.../processors/cache/GridCacheOffHeapTest.java | 28 ++++++++++++-----
.../cache/GridCachePutAllFailoverSelfTest.java | 5 +++
.../processors/cache/GridCacheStopSelfTest.java | 5 +++
.../cache/GridCacheVersionMultinodeTest.java | 2 +-
.../IgniteCacheInterceptorSelfTestSuite.java | 2 +-
.../cache/IgniteCacheInvokeReadThroughTest.java | 5 +++
...gniteCacheTransactionalStopBusySelfTest.java | 5 +++
.../IgniteTxMultiThreadedAbstractTest.java | 4 +--
...dCacheQueueMultiNodeConsistencySelfTest.java | 5 +++
...omicOffheapQueueCreateMultiNodeSelfTest.java | 5 +++
...ionedAtomicQueueCreateMultiNodeSelfTest.java | 5 +++
...rtitionedDataStructuresFailoverSelfTest.java | 5 +++
...edOffheapDataStructuresFailoverSelfTest.java | 5 +++
...PartitionedQueueCreateMultiNodeSelfTest.java | 5 +++
...dCachePartitionedQueueEntryMoveSelfTest.java | 5 +++
...nedQueueFailoverDataConsistencySelfTest.java | 5 +++
...eplicatedDataStructuresFailoverSelfTest.java | 5 +++
...CacheLoadingConcurrentGridStartSelfTest.java | 5 +++
.../dht/GridCacheColocatedFailoverSelfTest.java | 5 +++
.../GridCacheColocatedTxExceptionSelfTest.java | 5 +++
...ePartitionedNearDisabledMetricsSelfTest.java | 4 ++-
...dCachePartitionedTopologyChangeSelfTest.java | 5 +++
.../near/GridCacheNearTxExceptionSelfTest.java | 5 +++
.../GridCachePartitionedFailoverSelfTest.java | 5 +++
...PartitionedFullApiMultithreadedSelfTest.java | 5 +++
.../GridCachePartitionedNodeRestartTest.java | 5 +++
...ePartitionedOptimisticTxNodeRestartTest.java | 5 +++
...CachePartitionedTxMultiThreadedSelfTest.java | 5 +++
.../GridCacheReplicatedFailoverSelfTest.java | 5 +++
...eReplicatedFullApiMultithreadedSelfTest.java | 5 +++
.../GridCacheReplicatedInvalidateSelfTest.java | 4 +--
...ridCacheReplicatedMultiNodeLockSelfTest.java | 5 +++
.../GridCacheReplicatedMultiNodeSelfTest.java | 5 +++
.../GridCacheReplicatedNodeRestartSelfTest.java | 5 +++
.../GridCacheReplicatedTxExceptionSelfTest.java | 5 +++
.../replicated/GridReplicatedTxPreloadTest.java | 2 ++
...acheAtomicReplicatedNodeRestartSelfTest.java | 5 +++
.../GridCacheEvictionFilterSelfTest.java | 4 ++-
...cheSynchronousEvictionsFailoverSelfTest.java | 5 +++
.../IgniteCacheExpiryPolicyAbstractTest.java | 10 +++---
...eCacheExpiryPolicyWithStoreAbstractTest.java | 4 ++-
...dCacheLocalFullApiMultithreadedSelfTest.java | 5 +++
.../GridCacheLocalTxExceptionSelfTest.java | 5 +++
.../processors/igfs/IgfsModesSelfTest.java | 4 ++-
.../unsafe/GridUnsafeMemorySelfTest.java | 4 ++-
.../ignite/testframework/GridTestUtils.java | 2 +-
.../IgniteCacheDataStructuresSelfTestSuite.java | 24 ++++++---------
.../IgniteCacheEvictionSelfTestSuite.java | 3 +-
.../IgniteCacheFailoverTestSuite.java | 8 ++---
.../IgniteCacheFullApiSelfTestSuite.java | 8 ++---
.../testsuites/IgniteCacheRestartTestSuite.java | 10 +++---
.../ignite/testsuites/IgniteCacheTestSuite.java | 16 +++++-----
.../testsuites/IgniteCacheTestSuite2.java | 4 +--
.../testsuites/IgniteCacheTestSuite3.java | 14 +++------
.../testsuites/IgniteCacheTestSuite4.java | 10 +++---
.../apache/ignite/util/GridRandomSelfTest.java | 4 ++-
.../HadoopIgfs20FileSystemAbstractSelfTest.java | 4 ++-
.../collections/HadoopHashMapSelfTest.java | 4 ++-
.../HadoopExternalTaskExecutionSelfTest.java | 2 ++
.../HadoopExternalCommunicationSelfTest.java | 5 +++
.../testsuites/IgniteHadoopTestSuite.java | 7 +++--
.../hibernate/HibernateL2CacheSelfTest.java | 5 +++
.../HibernateL2CacheTransactionalSelfTest.java | 5 +++
.../testsuites/IgniteHibernateTestSuite.java | 4 +--
.../cache/GridCacheCrossCacheQuerySelfTest.java | 8 +++--
.../IgniteCacheQueryNodeRestartSelfTest.java | 5 +++
.../h2/GridIndexingSpiAbstractSelfTest.java | 4 ++-
.../query/h2/sql/BaseH2CompareQueryTest.java | 4 ++-
.../IgniteCacheQuerySelfTestSuite.java | 2 +-
.../IgniteWebSessionSelfTestSuite.java | 2 +-
84 files changed, 384 insertions(+), 150 deletions(-)
----------------------------------------------------------------------