8010293: java/util/concurrent/ConcurrentHashMap/toArray.java fails intermittently
Co-authored-by: Doug Lea <dl@cs.oswego.edu> Co-authored-by: Peter Levart <peter.levart@gmail.com> Reviewed-by: forax, chegar, alanb
This commit is contained in:
parent
e763c78a79
commit
bef51c4a08
@ -374,27 +374,26 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
* The table is resized when occupancy exceeds a percentage
|
* The table is resized when occupancy exceeds a percentage
|
||||||
* threshold (nominally, 0.75, but see below). Any thread
|
* threshold (nominally, 0.75, but see below). Any thread
|
||||||
* noticing an overfull bin may assist in resizing after the
|
* noticing an overfull bin may assist in resizing after the
|
||||||
* initiating thread allocates and sets up the replacement
|
* initiating thread allocates and sets up the replacement array.
|
||||||
* array. However, rather than stalling, these other threads may
|
* However, rather than stalling, these other threads may proceed
|
||||||
* proceed with insertions etc. The use of TreeBins shields us
|
* with insertions etc. The use of TreeBins shields us from the
|
||||||
* from the worst case effects of overfilling while resizes are in
|
* worst case effects of overfilling while resizes are in
|
||||||
* progress. Resizing proceeds by transferring bins, one by one,
|
* progress. Resizing proceeds by transferring bins, one by one,
|
||||||
* from the table to the next table. To enable concurrency, the
|
* from the table to the next table. However, threads claim small
|
||||||
* next table must be (incrementally) prefilled with place-holders
|
* blocks of indices to transfer (via field transferIndex) before
|
||||||
* serving as reverse forwarders to the old table. Because we are
|
* doing so, reducing contention. Because we are using
|
||||||
* using power-of-two expansion, the elements from each bin must
|
* power-of-two expansion, the elements from each bin must either
|
||||||
* either stay at same index, or move with a power of two
|
* stay at same index, or move with a power of two offset. We
|
||||||
* offset. We eliminate unnecessary node creation by catching
|
* eliminate unnecessary node creation by catching cases where old
|
||||||
* cases where old nodes can be reused because their next fields
|
* nodes can be reused because their next fields won't change. On
|
||||||
* won't change. On average, only about one-sixth of them need
|
* average, only about one-sixth of them need cloning when a table
|
||||||
* cloning when a table doubles. The nodes they replace will be
|
* doubles. The nodes they replace will be garbage collectable as
|
||||||
* garbage collectable as soon as they are no longer referenced by
|
* soon as they are no longer referenced by any reader thread that
|
||||||
* any reader thread that may be in the midst of concurrently
|
* may be in the midst of concurrently traversing table. Upon
|
||||||
* traversing table. Upon transfer, the old table bin contains
|
* transfer, the old table bin contains only a special forwarding
|
||||||
* only a special forwarding node (with hash field "MOVED") that
|
* node (with hash field "MOVED") that contains the next table as
|
||||||
* contains the next table as its key. On encountering a
|
* its key. On encountering a forwarding node, access and update
|
||||||
* forwarding node, access and update operations restart, using
|
* operations restart, using the new table.
|
||||||
* the new table.
|
|
||||||
*
|
*
|
||||||
* Each bin transfer requires its bin lock, which can stall
|
* Each bin transfer requires its bin lock, which can stall
|
||||||
* waiting for locks while resizing. However, because other
|
* waiting for locks while resizing. However, because other
|
||||||
@ -402,13 +401,19 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
* locks, average aggregate waits become shorter as resizing
|
* locks, average aggregate waits become shorter as resizing
|
||||||
* progresses. The transfer operation must also ensure that all
|
* progresses. The transfer operation must also ensure that all
|
||||||
* accessible bins in both the old and new table are usable by any
|
* accessible bins in both the old and new table are usable by any
|
||||||
* traversal. This is arranged by proceeding from the last bin
|
* traversal. This is arranged in part by proceeding from the
|
||||||
* (table.length - 1) up towards the first. Upon seeing a
|
* last bin (table.length - 1) up towards the first. Upon seeing
|
||||||
* forwarding node, traversals (see class Traverser) arrange to
|
* a forwarding node, traversals (see class Traverser) arrange to
|
||||||
* move to the new table without revisiting nodes. However, to
|
* move to the new table without revisiting nodes. To ensure that
|
||||||
* ensure that no intervening nodes are skipped, bin splitting can
|
* no intervening nodes are skipped even when moved out of order,
|
||||||
* only begin after the associated reverse-forwarders are in
|
* a stack (see class TableStack) is created on first encounter of
|
||||||
* place.
|
* 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
|
* The traversal scheme also applies to partial traversals of
|
||||||
* ranges of bins (via an alternate Traverser constructor)
|
* ranges of bins (via an alternate Traverser constructor)
|
||||||
@ -776,11 +781,6 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
*/
|
*/
|
||||||
private transient volatile int transferIndex;
|
private transient volatile int transferIndex;
|
||||||
|
|
||||||
/**
|
|
||||||
* The least available table index to split while resizing.
|
|
||||||
*/
|
|
||||||
private transient volatile int transferOrigin;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spinlock (locked via CAS) used when resizing and/or creating CounterCells.
|
* Spinlock (locked via CAS) used when resizing and/or creating CounterCells.
|
||||||
*/
|
*/
|
||||||
@ -1377,7 +1377,8 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
}
|
}
|
||||||
int segmentShift = 32 - sshift;
|
int segmentShift = 32 - sshift;
|
||||||
int segmentMask = ssize - 1;
|
int segmentMask = ssize - 1;
|
||||||
@SuppressWarnings("unchecked") Segment<K,V>[] segments = (Segment<K,V>[])
|
@SuppressWarnings("unchecked")
|
||||||
|
Segment<K,V>[] segments = (Segment<K,V>[])
|
||||||
new Segment<?,?>[DEFAULT_CONCURRENCY_LEVEL];
|
new Segment<?,?>[DEFAULT_CONCURRENCY_LEVEL];
|
||||||
for (int i = 0; i < segments.length; ++i)
|
for (int i = 0; i < segments.length; ++i)
|
||||||
segments[i] = new Segment<K,V>(LOAD_FACTOR);
|
segments[i] = new Segment<K,V>(LOAD_FACTOR);
|
||||||
@ -1420,8 +1421,10 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
long size = 0L;
|
long size = 0L;
|
||||||
Node<K,V> p = null;
|
Node<K,V> p = null;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@SuppressWarnings("unchecked") K k = (K) s.readObject();
|
@SuppressWarnings("unchecked")
|
||||||
@SuppressWarnings("unchecked") V v = (V) s.readObject();
|
K k = (K) s.readObject();
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
V v = (V) s.readObject();
|
||||||
if (k != null && v != null) {
|
if (k != null && v != null) {
|
||||||
p = new Node<K,V>(spread(k.hashCode()), k, v, p);
|
p = new Node<K,V>(spread(k.hashCode()), k, v, p);
|
||||||
++size;
|
++size;
|
||||||
@ -1439,8 +1442,8 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
int sz = (int)size;
|
int sz = (int)size;
|
||||||
n = tableSizeFor(sz + (sz >>> 1) + 1);
|
n = tableSizeFor(sz + (sz >>> 1) + 1);
|
||||||
}
|
}
|
||||||
@SuppressWarnings({"rawtypes","unchecked"})
|
@SuppressWarnings("unchecked")
|
||||||
Node<K,V>[] tab = (Node<K,V>[])new Node[n];
|
Node<K,V>[] tab = (Node<K,V>[])new Node<?,?>[n];
|
||||||
int mask = n - 1;
|
int mask = n - 1;
|
||||||
long added = 0L;
|
long added = 0L;
|
||||||
while (p != null) {
|
while (p != null) {
|
||||||
@ -2200,8 +2203,8 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
try {
|
try {
|
||||||
if ((tab = table) == null || tab.length == 0) {
|
if ((tab = table) == null || tab.length == 0) {
|
||||||
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
|
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
|
||||||
@SuppressWarnings({"rawtypes","unchecked"})
|
@SuppressWarnings("unchecked")
|
||||||
Node<K,V>[] nt = (Node<K,V>[])new Node[n];
|
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
|
||||||
table = tab = nt;
|
table = tab = nt;
|
||||||
sc = n - (n >>> 2);
|
sc = n - (n >>> 2);
|
||||||
}
|
}
|
||||||
@ -2246,7 +2249,7 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
|
while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
|
||||||
tab.length < MAXIMUM_CAPACITY) {
|
tab.length < MAXIMUM_CAPACITY) {
|
||||||
if (sc < 0) {
|
if (sc < 0) {
|
||||||
if (sc == -1 || transferIndex <= transferOrigin ||
|
if (sc == -1 || transferIndex <= 0 ||
|
||||||
(nt = nextTable) == null)
|
(nt = nextTable) == null)
|
||||||
break;
|
break;
|
||||||
if (U.compareAndSwapInt(this, SIZECTL, sc, sc - 1))
|
if (U.compareAndSwapInt(this, SIZECTL, sc, sc - 1))
|
||||||
@ -2266,10 +2269,13 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
Node<K,V>[] nextTab; int sc;
|
Node<K,V>[] nextTab; int sc;
|
||||||
if ((f instanceof ForwardingNode) &&
|
if ((f instanceof ForwardingNode) &&
|
||||||
(nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
|
(nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
|
||||||
if (nextTab == nextTable && tab == table &&
|
while (transferIndex > 0 && nextTab == nextTable &&
|
||||||
transferIndex > transferOrigin && (sc = sizeCtl) < -1 &&
|
(sc = sizeCtl) < -1) {
|
||||||
U.compareAndSwapInt(this, SIZECTL, sc, sc - 1))
|
if (U.compareAndSwapInt(this, SIZECTL, sc, sc - 1)) {
|
||||||
transfer(tab, nextTab);
|
transfer(tab, nextTab);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
return nextTab;
|
return nextTab;
|
||||||
}
|
}
|
||||||
return table;
|
return table;
|
||||||
@ -2291,8 +2297,8 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
|
if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
|
||||||
try {
|
try {
|
||||||
if (table == tab) {
|
if (table == tab) {
|
||||||
@SuppressWarnings({"rawtypes","unchecked"})
|
@SuppressWarnings("unchecked")
|
||||||
Node<K,V>[] nt = (Node<K,V>[])new Node[n];
|
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
|
||||||
table = nt;
|
table = nt;
|
||||||
sc = n - (n >>> 2);
|
sc = n - (n >>> 2);
|
||||||
}
|
}
|
||||||
@ -2319,36 +2325,27 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
stride = MIN_TRANSFER_STRIDE; // subdivide range
|
stride = MIN_TRANSFER_STRIDE; // subdivide range
|
||||||
if (nextTab == null) { // initiating
|
if (nextTab == null) { // initiating
|
||||||
try {
|
try {
|
||||||
@SuppressWarnings({"rawtypes","unchecked"})
|
@SuppressWarnings("unchecked")
|
||||||
Node<K,V>[] nt = (Node<K,V>[])new Node[n << 1];
|
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];
|
||||||
nextTab = nt;
|
nextTab = nt;
|
||||||
} catch (Throwable ex) { // try to cope with OOME
|
} catch (Throwable ex) { // try to cope with OOME
|
||||||
sizeCtl = Integer.MAX_VALUE;
|
sizeCtl = Integer.MAX_VALUE;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
nextTable = nextTab;
|
nextTable = nextTab;
|
||||||
transferOrigin = n;
|
|
||||||
transferIndex = n;
|
transferIndex = n;
|
||||||
ForwardingNode<K,V> rev = new ForwardingNode<K,V>(tab);
|
|
||||||
for (int k = n; k > 0;) { // progressively reveal ready slots
|
|
||||||
int nextk = (k > stride) ? k - stride : 0;
|
|
||||||
for (int m = nextk; m < k; ++m)
|
|
||||||
nextTab[m] = rev;
|
|
||||||
for (int m = n + nextk; m < n + k; ++m)
|
|
||||||
nextTab[m] = rev;
|
|
||||||
U.putOrderedInt(this, TRANSFERORIGIN, k = nextk);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
int nextn = nextTab.length;
|
int nextn = nextTab.length;
|
||||||
ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
|
ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
|
||||||
boolean advance = true;
|
boolean advance = true;
|
||||||
boolean finishing = false; // to ensure sweep before committing nextTab
|
boolean finishing = false; // to ensure sweep before committing nextTab
|
||||||
for (int i = 0, bound = 0;;) {
|
for (int i = 0, bound = 0;;) {
|
||||||
int nextIndex, nextBound, fh; Node<K,V> f;
|
Node<K,V> f; int fh;
|
||||||
while (advance) {
|
while (advance) {
|
||||||
|
int nextIndex, nextBound;
|
||||||
if (--i >= bound || finishing)
|
if (--i >= bound || finishing)
|
||||||
advance = false;
|
advance = false;
|
||||||
else if ((nextIndex = transferIndex) <= transferOrigin) {
|
else if ((nextIndex = transferIndex) <= 0) {
|
||||||
i = -1;
|
i = -1;
|
||||||
advance = false;
|
advance = false;
|
||||||
}
|
}
|
||||||
@ -2362,29 +2359,22 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i < 0 || i >= n || i + n >= nextn) {
|
if (i < 0 || i >= n || i + n >= nextn) {
|
||||||
|
int sc;
|
||||||
if (finishing) {
|
if (finishing) {
|
||||||
nextTable = null;
|
nextTable = null;
|
||||||
table = nextTab;
|
table = nextTab;
|
||||||
sizeCtl = (n << 1) - (n >>> 1);
|
sizeCtl = (n << 1) - (n >>> 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (int sc;;) {
|
if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, ++sc)) {
|
||||||
if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, ++sc)) {
|
if (sc != -1)
|
||||||
if (sc != -1)
|
return;
|
||||||
return;
|
finishing = advance = true;
|
||||||
finishing = advance = true;
|
i = n; // recheck before commit
|
||||||
i = n; // recheck before commit
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((f = tabAt(tab, i)) == null) {
|
|
||||||
if (casTabAt(tab, i, null, fwd)) {
|
|
||||||
setTabAt(nextTab, i, null);
|
|
||||||
setTabAt(nextTab, i + n, null);
|
|
||||||
advance = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if ((f = tabAt(tab, i)) == null)
|
||||||
|
advance = casTabAt(tab, i, null, fwd);
|
||||||
else if ((fh = f.hash) == MOVED)
|
else if ((fh = f.hash) == MOVED)
|
||||||
advance = true; // already processed
|
advance = true; // already processed
|
||||||
else {
|
else {
|
||||||
@ -3223,6 +3213,18 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
|
|
||||||
/* ----------------Table Traversal -------------- */
|
/* ----------------Table Traversal -------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Records the table, its length, and current traversal index for a
|
||||||
|
* traverser that must process a region of a forwarded table before
|
||||||
|
* proceeding with current table.
|
||||||
|
*/
|
||||||
|
static final class TableStack<K,V> {
|
||||||
|
int length;
|
||||||
|
int index;
|
||||||
|
Node<K,V>[] tab;
|
||||||
|
TableStack<K,V> next;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encapsulates traversal for methods such as containsValue; also
|
* Encapsulates traversal for methods such as containsValue; also
|
||||||
* serves as a base class for other iterators and spliterators.
|
* serves as a base class for other iterators and spliterators.
|
||||||
@ -3247,6 +3249,7 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
static class Traverser<K,V> {
|
static class Traverser<K,V> {
|
||||||
Node<K,V>[] tab; // current table; updated if resized
|
Node<K,V>[] tab; // current table; updated if resized
|
||||||
Node<K,V> next; // the next entry to use
|
Node<K,V> next; // the next entry to use
|
||||||
|
TableStack<K,V> stack, spare; // to save/restore on ForwardingNodes
|
||||||
int index; // index of bin to use next
|
int index; // index of bin to use next
|
||||||
int baseIndex; // current index of initial table
|
int baseIndex; // current index of initial table
|
||||||
int baseLimit; // index bound for initial table
|
int baseLimit; // index bound for initial table
|
||||||
@ -3268,16 +3271,17 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
if ((e = next) != null)
|
if ((e = next) != null)
|
||||||
e = e.next;
|
e = e.next;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
Node<K,V>[] t; int i, n; K ek; // must use locals in checks
|
Node<K,V>[] t; int i, n; // must use locals in checks
|
||||||
if (e != null)
|
if (e != null)
|
||||||
return next = e;
|
return next = e;
|
||||||
if (baseIndex >= baseLimit || (t = tab) == null ||
|
if (baseIndex >= baseLimit || (t = tab) == null ||
|
||||||
(n = t.length) <= (i = index) || i < 0)
|
(n = t.length) <= (i = index) || i < 0)
|
||||||
return next = null;
|
return next = null;
|
||||||
if ((e = tabAt(t, index)) != null && e.hash < 0) {
|
if ((e = tabAt(t, i)) != null && e.hash < 0) {
|
||||||
if (e instanceof ForwardingNode) {
|
if (e instanceof ForwardingNode) {
|
||||||
tab = ((ForwardingNode<K,V>)e).nextTable;
|
tab = ((ForwardingNode<K,V>)e).nextTable;
|
||||||
e = null;
|
e = null;
|
||||||
|
pushState(t, i, n);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if (e instanceof TreeBin)
|
else if (e instanceof TreeBin)
|
||||||
@ -3285,10 +3289,49 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
else
|
else
|
||||||
e = null;
|
e = null;
|
||||||
}
|
}
|
||||||
if ((index += baseSize) >= n)
|
if (stack != null)
|
||||||
index = ++baseIndex; // visit upper slots if present
|
recoverState(n);
|
||||||
|
else if ((index = i + baseSize) >= n)
|
||||||
|
index = ++baseIndex; // visit upper slots if present
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves traversal state upon encountering a forwarding node.
|
||||||
|
*/
|
||||||
|
private void pushState(Node<K,V>[] t, int i, int n) {
|
||||||
|
TableStack<K,V> s = spare; // reuse if possible
|
||||||
|
if (s != null)
|
||||||
|
spare = s.next;
|
||||||
|
else
|
||||||
|
s = new TableStack<K,V>();
|
||||||
|
s.tab = t;
|
||||||
|
s.length = n;
|
||||||
|
s.index = i;
|
||||||
|
s.next = stack;
|
||||||
|
stack = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possibly pops traversal state.
|
||||||
|
*
|
||||||
|
* @param n length of current table
|
||||||
|
*/
|
||||||
|
private void recoverState(int n) {
|
||||||
|
TableStack<K,V> s; int len;
|
||||||
|
while ((s = stack) != null && (index += (len = s.length)) >= n) {
|
||||||
|
n = len;
|
||||||
|
index = s.index;
|
||||||
|
tab = s.tab;
|
||||||
|
s.tab = null;
|
||||||
|
TableStack<K,V> next = s.next;
|
||||||
|
s.next = spare; // save for reuse
|
||||||
|
stack = next;
|
||||||
|
spare = s;
|
||||||
|
}
|
||||||
|
if (s == null && (index += baseSize) >= n)
|
||||||
|
index = ++baseIndex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -4722,6 +4765,7 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
abstract static class BulkTask<K,V,R> extends CountedCompleter<R> {
|
abstract static class BulkTask<K,V,R> extends CountedCompleter<R> {
|
||||||
Node<K,V>[] tab; // same as Traverser
|
Node<K,V>[] tab; // same as Traverser
|
||||||
Node<K,V> next;
|
Node<K,V> next;
|
||||||
|
TableStack<K,V> stack, spare;
|
||||||
int index;
|
int index;
|
||||||
int baseIndex;
|
int baseIndex;
|
||||||
int baseLimit;
|
int baseLimit;
|
||||||
@ -4750,16 +4794,17 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
if ((e = next) != null)
|
if ((e = next) != null)
|
||||||
e = e.next;
|
e = e.next;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
Node<K,V>[] t; int i, n; K ek; // must use locals in checks
|
Node<K,V>[] t; int i, n;
|
||||||
if (e != null)
|
if (e != null)
|
||||||
return next = e;
|
return next = e;
|
||||||
if (baseIndex >= baseLimit || (t = tab) == null ||
|
if (baseIndex >= baseLimit || (t = tab) == null ||
|
||||||
(n = t.length) <= (i = index) || i < 0)
|
(n = t.length) <= (i = index) || i < 0)
|
||||||
return next = null;
|
return next = null;
|
||||||
if ((e = tabAt(t, index)) != null && e.hash < 0) {
|
if ((e = tabAt(t, i)) != null && e.hash < 0) {
|
||||||
if (e instanceof ForwardingNode) {
|
if (e instanceof ForwardingNode) {
|
||||||
tab = ((ForwardingNode<K,V>)e).nextTable;
|
tab = ((ForwardingNode<K,V>)e).nextTable;
|
||||||
e = null;
|
e = null;
|
||||||
|
pushState(t, i, n);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if (e instanceof TreeBin)
|
else if (e instanceof TreeBin)
|
||||||
@ -4767,10 +4812,41 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
else
|
else
|
||||||
e = null;
|
e = null;
|
||||||
}
|
}
|
||||||
if ((index += baseSize) >= n)
|
if (stack != null)
|
||||||
index = ++baseIndex; // visit upper slots if present
|
recoverState(n);
|
||||||
|
else if ((index = i + baseSize) >= n)
|
||||||
|
index = ++baseIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void pushState(Node<K,V>[] t, int i, int n) {
|
||||||
|
TableStack<K,V> s = spare;
|
||||||
|
if (s != null)
|
||||||
|
spare = s.next;
|
||||||
|
else
|
||||||
|
s = new TableStack<K,V>();
|
||||||
|
s.tab = t;
|
||||||
|
s.length = n;
|
||||||
|
s.index = i;
|
||||||
|
s.next = stack;
|
||||||
|
stack = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void recoverState(int n) {
|
||||||
|
TableStack<K,V> s; int len;
|
||||||
|
while ((s = stack) != null && (index += (len = s.length)) >= n) {
|
||||||
|
n = len;
|
||||||
|
index = s.index;
|
||||||
|
tab = s.tab;
|
||||||
|
s.tab = null;
|
||||||
|
TableStack<K,V> next = s.next;
|
||||||
|
s.next = spare; // save for reuse
|
||||||
|
stack = next;
|
||||||
|
spare = s;
|
||||||
|
}
|
||||||
|
if (s == null && (index += baseSize) >= n)
|
||||||
|
index = ++baseIndex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -5229,7 +5305,8 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
result = r;
|
result = r;
|
||||||
CountedCompleter<?> c;
|
CountedCompleter<?> c;
|
||||||
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
||||||
@SuppressWarnings("unchecked") ReduceKeysTask<K,V>
|
@SuppressWarnings("unchecked")
|
||||||
|
ReduceKeysTask<K,V>
|
||||||
t = (ReduceKeysTask<K,V>)c,
|
t = (ReduceKeysTask<K,V>)c,
|
||||||
s = t.rights;
|
s = t.rights;
|
||||||
while (s != null) {
|
while (s != null) {
|
||||||
@ -5276,7 +5353,8 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
result = r;
|
result = r;
|
||||||
CountedCompleter<?> c;
|
CountedCompleter<?> c;
|
||||||
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
||||||
@SuppressWarnings("unchecked") ReduceValuesTask<K,V>
|
@SuppressWarnings("unchecked")
|
||||||
|
ReduceValuesTask<K,V>
|
||||||
t = (ReduceValuesTask<K,V>)c,
|
t = (ReduceValuesTask<K,V>)c,
|
||||||
s = t.rights;
|
s = t.rights;
|
||||||
while (s != null) {
|
while (s != null) {
|
||||||
@ -5321,7 +5399,8 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
result = r;
|
result = r;
|
||||||
CountedCompleter<?> c;
|
CountedCompleter<?> c;
|
||||||
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
||||||
@SuppressWarnings("unchecked") ReduceEntriesTask<K,V>
|
@SuppressWarnings("unchecked")
|
||||||
|
ReduceEntriesTask<K,V>
|
||||||
t = (ReduceEntriesTask<K,V>)c,
|
t = (ReduceEntriesTask<K,V>)c,
|
||||||
s = t.rights;
|
s = t.rights;
|
||||||
while (s != null) {
|
while (s != null) {
|
||||||
@ -5374,7 +5453,8 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
result = r;
|
result = r;
|
||||||
CountedCompleter<?> c;
|
CountedCompleter<?> c;
|
||||||
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
||||||
@SuppressWarnings("unchecked") MapReduceKeysTask<K,V,U>
|
@SuppressWarnings("unchecked")
|
||||||
|
MapReduceKeysTask<K,V,U>
|
||||||
t = (MapReduceKeysTask<K,V,U>)c,
|
t = (MapReduceKeysTask<K,V,U>)c,
|
||||||
s = t.rights;
|
s = t.rights;
|
||||||
while (s != null) {
|
while (s != null) {
|
||||||
@ -5427,7 +5507,8 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
result = r;
|
result = r;
|
||||||
CountedCompleter<?> c;
|
CountedCompleter<?> c;
|
||||||
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
||||||
@SuppressWarnings("unchecked") MapReduceValuesTask<K,V,U>
|
@SuppressWarnings("unchecked")
|
||||||
|
MapReduceValuesTask<K,V,U>
|
||||||
t = (MapReduceValuesTask<K,V,U>)c,
|
t = (MapReduceValuesTask<K,V,U>)c,
|
||||||
s = t.rights;
|
s = t.rights;
|
||||||
while (s != null) {
|
while (s != null) {
|
||||||
@ -5480,7 +5561,8 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
result = r;
|
result = r;
|
||||||
CountedCompleter<?> c;
|
CountedCompleter<?> c;
|
||||||
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
||||||
@SuppressWarnings("unchecked") MapReduceEntriesTask<K,V,U>
|
@SuppressWarnings("unchecked")
|
||||||
|
MapReduceEntriesTask<K,V,U>
|
||||||
t = (MapReduceEntriesTask<K,V,U>)c,
|
t = (MapReduceEntriesTask<K,V,U>)c,
|
||||||
s = t.rights;
|
s = t.rights;
|
||||||
while (s != null) {
|
while (s != null) {
|
||||||
@ -5533,7 +5615,8 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
result = r;
|
result = r;
|
||||||
CountedCompleter<?> c;
|
CountedCompleter<?> c;
|
||||||
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
||||||
@SuppressWarnings("unchecked") MapReduceMappingsTask<K,V,U>
|
@SuppressWarnings("unchecked")
|
||||||
|
MapReduceMappingsTask<K,V,U>
|
||||||
t = (MapReduceMappingsTask<K,V,U>)c,
|
t = (MapReduceMappingsTask<K,V,U>)c,
|
||||||
s = t.rights;
|
s = t.rights;
|
||||||
while (s != null) {
|
while (s != null) {
|
||||||
@ -5585,7 +5668,8 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
result = r;
|
result = r;
|
||||||
CountedCompleter<?> c;
|
CountedCompleter<?> c;
|
||||||
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
||||||
@SuppressWarnings("unchecked") MapReduceKeysToDoubleTask<K,V>
|
@SuppressWarnings("unchecked")
|
||||||
|
MapReduceKeysToDoubleTask<K,V>
|
||||||
t = (MapReduceKeysToDoubleTask<K,V>)c,
|
t = (MapReduceKeysToDoubleTask<K,V>)c,
|
||||||
s = t.rights;
|
s = t.rights;
|
||||||
while (s != null) {
|
while (s != null) {
|
||||||
@ -5634,7 +5718,8 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
result = r;
|
result = r;
|
||||||
CountedCompleter<?> c;
|
CountedCompleter<?> c;
|
||||||
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
||||||
@SuppressWarnings("unchecked") MapReduceValuesToDoubleTask<K,V>
|
@SuppressWarnings("unchecked")
|
||||||
|
MapReduceValuesToDoubleTask<K,V>
|
||||||
t = (MapReduceValuesToDoubleTask<K,V>)c,
|
t = (MapReduceValuesToDoubleTask<K,V>)c,
|
||||||
s = t.rights;
|
s = t.rights;
|
||||||
while (s != null) {
|
while (s != null) {
|
||||||
@ -5683,7 +5768,8 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
result = r;
|
result = r;
|
||||||
CountedCompleter<?> c;
|
CountedCompleter<?> c;
|
||||||
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
||||||
@SuppressWarnings("unchecked") MapReduceEntriesToDoubleTask<K,V>
|
@SuppressWarnings("unchecked")
|
||||||
|
MapReduceEntriesToDoubleTask<K,V>
|
||||||
t = (MapReduceEntriesToDoubleTask<K,V>)c,
|
t = (MapReduceEntriesToDoubleTask<K,V>)c,
|
||||||
s = t.rights;
|
s = t.rights;
|
||||||
while (s != null) {
|
while (s != null) {
|
||||||
@ -5732,7 +5818,8 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
result = r;
|
result = r;
|
||||||
CountedCompleter<?> c;
|
CountedCompleter<?> c;
|
||||||
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
||||||
@SuppressWarnings("unchecked") MapReduceMappingsToDoubleTask<K,V>
|
@SuppressWarnings("unchecked")
|
||||||
|
MapReduceMappingsToDoubleTask<K,V>
|
||||||
t = (MapReduceMappingsToDoubleTask<K,V>)c,
|
t = (MapReduceMappingsToDoubleTask<K,V>)c,
|
||||||
s = t.rights;
|
s = t.rights;
|
||||||
while (s != null) {
|
while (s != null) {
|
||||||
@ -5781,7 +5868,8 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
result = r;
|
result = r;
|
||||||
CountedCompleter<?> c;
|
CountedCompleter<?> c;
|
||||||
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
||||||
@SuppressWarnings("unchecked") MapReduceKeysToLongTask<K,V>
|
@SuppressWarnings("unchecked")
|
||||||
|
MapReduceKeysToLongTask<K,V>
|
||||||
t = (MapReduceKeysToLongTask<K,V>)c,
|
t = (MapReduceKeysToLongTask<K,V>)c,
|
||||||
s = t.rights;
|
s = t.rights;
|
||||||
while (s != null) {
|
while (s != null) {
|
||||||
@ -5830,7 +5918,8 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
result = r;
|
result = r;
|
||||||
CountedCompleter<?> c;
|
CountedCompleter<?> c;
|
||||||
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
||||||
@SuppressWarnings("unchecked") MapReduceValuesToLongTask<K,V>
|
@SuppressWarnings("unchecked")
|
||||||
|
MapReduceValuesToLongTask<K,V>
|
||||||
t = (MapReduceValuesToLongTask<K,V>)c,
|
t = (MapReduceValuesToLongTask<K,V>)c,
|
||||||
s = t.rights;
|
s = t.rights;
|
||||||
while (s != null) {
|
while (s != null) {
|
||||||
@ -5879,7 +5968,8 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
result = r;
|
result = r;
|
||||||
CountedCompleter<?> c;
|
CountedCompleter<?> c;
|
||||||
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
||||||
@SuppressWarnings("unchecked") MapReduceEntriesToLongTask<K,V>
|
@SuppressWarnings("unchecked")
|
||||||
|
MapReduceEntriesToLongTask<K,V>
|
||||||
t = (MapReduceEntriesToLongTask<K,V>)c,
|
t = (MapReduceEntriesToLongTask<K,V>)c,
|
||||||
s = t.rights;
|
s = t.rights;
|
||||||
while (s != null) {
|
while (s != null) {
|
||||||
@ -5928,7 +6018,8 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
result = r;
|
result = r;
|
||||||
CountedCompleter<?> c;
|
CountedCompleter<?> c;
|
||||||
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
||||||
@SuppressWarnings("unchecked") MapReduceMappingsToLongTask<K,V>
|
@SuppressWarnings("unchecked")
|
||||||
|
MapReduceMappingsToLongTask<K,V>
|
||||||
t = (MapReduceMappingsToLongTask<K,V>)c,
|
t = (MapReduceMappingsToLongTask<K,V>)c,
|
||||||
s = t.rights;
|
s = t.rights;
|
||||||
while (s != null) {
|
while (s != null) {
|
||||||
@ -5977,7 +6068,8 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
result = r;
|
result = r;
|
||||||
CountedCompleter<?> c;
|
CountedCompleter<?> c;
|
||||||
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
||||||
@SuppressWarnings("unchecked") MapReduceKeysToIntTask<K,V>
|
@SuppressWarnings("unchecked")
|
||||||
|
MapReduceKeysToIntTask<K,V>
|
||||||
t = (MapReduceKeysToIntTask<K,V>)c,
|
t = (MapReduceKeysToIntTask<K,V>)c,
|
||||||
s = t.rights;
|
s = t.rights;
|
||||||
while (s != null) {
|
while (s != null) {
|
||||||
@ -6026,7 +6118,8 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
result = r;
|
result = r;
|
||||||
CountedCompleter<?> c;
|
CountedCompleter<?> c;
|
||||||
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
||||||
@SuppressWarnings("unchecked") MapReduceValuesToIntTask<K,V>
|
@SuppressWarnings("unchecked")
|
||||||
|
MapReduceValuesToIntTask<K,V>
|
||||||
t = (MapReduceValuesToIntTask<K,V>)c,
|
t = (MapReduceValuesToIntTask<K,V>)c,
|
||||||
s = t.rights;
|
s = t.rights;
|
||||||
while (s != null) {
|
while (s != null) {
|
||||||
@ -6075,7 +6168,8 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
result = r;
|
result = r;
|
||||||
CountedCompleter<?> c;
|
CountedCompleter<?> c;
|
||||||
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
||||||
@SuppressWarnings("unchecked") MapReduceEntriesToIntTask<K,V>
|
@SuppressWarnings("unchecked")
|
||||||
|
MapReduceEntriesToIntTask<K,V>
|
||||||
t = (MapReduceEntriesToIntTask<K,V>)c,
|
t = (MapReduceEntriesToIntTask<K,V>)c,
|
||||||
s = t.rights;
|
s = t.rights;
|
||||||
while (s != null) {
|
while (s != null) {
|
||||||
@ -6124,7 +6218,8 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
result = r;
|
result = r;
|
||||||
CountedCompleter<?> c;
|
CountedCompleter<?> c;
|
||||||
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
for (c = firstComplete(); c != null; c = c.nextComplete()) {
|
||||||
@SuppressWarnings("unchecked") MapReduceMappingsToIntTask<K,V>
|
@SuppressWarnings("unchecked")
|
||||||
|
MapReduceMappingsToIntTask<K,V>
|
||||||
t = (MapReduceMappingsToIntTask<K,V>)c,
|
t = (MapReduceMappingsToIntTask<K,V>)c,
|
||||||
s = t.rights;
|
s = t.rights;
|
||||||
while (s != null) {
|
while (s != null) {
|
||||||
@ -6140,7 +6235,6 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
private static final sun.misc.Unsafe U;
|
private static final sun.misc.Unsafe U;
|
||||||
private static final long SIZECTL;
|
private static final long SIZECTL;
|
||||||
private static final long TRANSFERINDEX;
|
private static final long TRANSFERINDEX;
|
||||||
private static final long TRANSFERORIGIN;
|
|
||||||
private static final long BASECOUNT;
|
private static final long BASECOUNT;
|
||||||
private static final long CELLSBUSY;
|
private static final long CELLSBUSY;
|
||||||
private static final long CELLVALUE;
|
private static final long CELLVALUE;
|
||||||
@ -6155,8 +6249,6 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
|
|||||||
(k.getDeclaredField("sizeCtl"));
|
(k.getDeclaredField("sizeCtl"));
|
||||||
TRANSFERINDEX = U.objectFieldOffset
|
TRANSFERINDEX = U.objectFieldOffset
|
||||||
(k.getDeclaredField("transferIndex"));
|
(k.getDeclaredField("transferIndex"));
|
||||||
TRANSFERORIGIN = U.objectFieldOffset
|
|
||||||
(k.getDeclaredField("transferOrigin"));
|
|
||||||
BASECOUNT = U.objectFieldOffset
|
BASECOUNT = U.objectFieldOffset
|
||||||
(k.getDeclaredField("baseCount"));
|
(k.getDeclaredField("baseCount"));
|
||||||
CELLSBUSY = U.objectFieldOffset
|
CELLSBUSY = U.objectFieldOffset
|
||||||
|
@ -23,39 +23,53 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @bug 4486658
|
* @bug 4486658 8010293
|
||||||
* @summary thread safety of toArray methods of subCollections
|
* @summary thread safety of toArray methods of subCollections
|
||||||
* @author Martin Buchholz
|
* @author Martin Buchholz
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
public class toArray {
|
public class ToArray {
|
||||||
|
|
||||||
public static void main(String[] args) throws Throwable {
|
public static void main(String[] args) throws Throwable {
|
||||||
|
// Execute a number of times to increase the probability of
|
||||||
|
// failure if there is an issue
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
executeTest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void executeTest() throws Throwable {
|
||||||
final Throwable throwable[] = new Throwable[1];
|
final Throwable throwable[] = new Throwable[1];
|
||||||
final int maxSize = 1000;
|
final ConcurrentHashMap<Integer, Integer> m = new ConcurrentHashMap<>();
|
||||||
final ConcurrentHashMap<Integer, Integer> m
|
|
||||||
= new ConcurrentHashMap<Integer, Integer>();
|
|
||||||
|
|
||||||
final Thread t1 = new Thread() { public void run() {
|
// Number of workers equal to the number of processors
|
||||||
for (int i = 0; i < maxSize; i++)
|
// Each worker will put globally unique keys into the map
|
||||||
m.put(i,i);}};
|
final int nWorkers = Runtime.getRuntime().availableProcessors();
|
||||||
|
final int sizePerWorker = 1024;
|
||||||
|
final int maxSize = nWorkers * sizePerWorker;
|
||||||
|
|
||||||
final Thread t2 = new Thread() {
|
// The foreman keeps checking that the size of the arrays
|
||||||
public Throwable exception = null;
|
// obtained from the key and value sets is never less than the
|
||||||
|
// previously observed size and is never greater than the maximum size
|
||||||
|
// NOTE: these size constraints are not specific to toArray and are
|
||||||
|
// applicable to any form of traversal of the collection views
|
||||||
|
CompletableFuture<?> foreman = CompletableFuture.runAsync(new Runnable() {
|
||||||
private int prevSize = 0;
|
private int prevSize = 0;
|
||||||
|
|
||||||
private boolean checkProgress(Object[] a) {
|
private boolean checkProgress(Object[] a) {
|
||||||
int size = a.length;
|
int size = a.length;
|
||||||
if (size < prevSize) throw new RuntimeException("WRONG WAY");
|
if (size < prevSize) throw new RuntimeException("WRONG WAY");
|
||||||
if (size > maxSize) throw new RuntimeException("OVERSHOOT");
|
if (size > maxSize) throw new RuntimeException("OVERSHOOT");
|
||||||
if (size == maxSize) return true;
|
if (size == maxSize) return true;
|
||||||
prevSize = size;
|
prevSize = size;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
Integer[] empty = new Integer[0];
|
Integer[] empty = new Integer[0];
|
||||||
@ -65,15 +79,25 @@ public class toArray {
|
|||||||
if (checkProgress(m.values().toArray(empty))) return;
|
if (checkProgress(m.values().toArray(empty))) return;
|
||||||
if (checkProgress(m.keySet().toArray(empty))) return;
|
if (checkProgress(m.keySet().toArray(empty))) return;
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
}
|
||||||
throwable[0] = t;
|
catch (Throwable t) {
|
||||||
}}};
|
throwable[0] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
t2.start();
|
// Create workers
|
||||||
t1.start();
|
// Each worker will put globally unique keys into the map
|
||||||
|
CompletableFuture<?>[] workers = IntStream.range(0, nWorkers).
|
||||||
|
mapToObj(w -> CompletableFuture.runAsync(() -> {
|
||||||
|
for (int i = 0, o = w * sizePerWorker; i < sizePerWorker; i++)
|
||||||
|
m.put(o + i, i);
|
||||||
|
})).
|
||||||
|
toArray(CompletableFuture<?>[]::new);
|
||||||
|
|
||||||
t1.join();
|
// Wait for workers and then foreman to complete
|
||||||
t2.join();
|
CompletableFuture.allOf(workers).join();
|
||||||
|
foreman.join();
|
||||||
|
|
||||||
if (throwable[0] != null)
|
if (throwable[0] != null)
|
||||||
throw throwable[0];
|
throw throwable[0];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user