undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
766
test/jdk/java/util/concurrent/ArrayBlockingQueue/WhiteBox.java
Normal file
766
test/jdk/java/util/concurrent/ArrayBlockingQueue/WhiteBox.java
Normal file
|
|
@ -0,0 +1,766 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @modules java.base/java.util.concurrent:open
|
||||
* @run testng WhiteBox
|
||||
* @summary White box tests of implementation details
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
@Test
|
||||
public class WhiteBox {
|
||||
final ThreadLocalRandom rnd = ThreadLocalRandom.current();
|
||||
final MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
final VarHandle ITRS, ITEMS, TAKEINDEX, PUTINDEX, COUNT, HEAD, NEXT, PREVTAKEINDEX;
|
||||
|
||||
WhiteBox() throws ReflectiveOperationException {
|
||||
Class<?> qClass = ArrayBlockingQueue.class;
|
||||
Class<?> itrClass = Class.forName(qClass.getName() + "$Itr");
|
||||
Class<?> itrsClass = Class.forName(qClass.getName() + "$Itrs");
|
||||
Class<?> nodeClass = Class.forName(itrsClass.getName() + "$Node");
|
||||
ITRS = findVarHandle(qClass, "itrs", itrsClass);
|
||||
ITEMS = findVarHandle(qClass, "items", Object[].class);
|
||||
TAKEINDEX = findVarHandle(qClass, "takeIndex", int.class);
|
||||
PUTINDEX = findVarHandle(qClass, "putIndex", int.class);
|
||||
COUNT = findVarHandle(qClass, "count", int.class);
|
||||
HEAD = findVarHandle(itrsClass, "head", nodeClass);
|
||||
NEXT = findVarHandle(nodeClass, "next", nodeClass);
|
||||
PREVTAKEINDEX = findVarHandle(itrClass, "prevTakeIndex", int.class);
|
||||
}
|
||||
|
||||
VarHandle findVarHandle(Class<?> recv, String name, Class<?> type)
|
||||
throws ReflectiveOperationException {
|
||||
return MethodHandles.privateLookupIn(recv, lookup)
|
||||
.findVarHandle(recv, name, type);
|
||||
}
|
||||
|
||||
Object itrs(ArrayBlockingQueue q) { return ITRS.get(q); }
|
||||
Object[] items(ArrayBlockingQueue q) { return (Object[]) ITEMS.get(q); }
|
||||
int takeIndex(ArrayBlockingQueue q) { return (int) TAKEINDEX.get(q); }
|
||||
int putIndex(ArrayBlockingQueue q) { return (int) PUTINDEX.get(q); }
|
||||
int count(ArrayBlockingQueue q) { return (int) COUNT.get(q); }
|
||||
Object head(Object itrs) { return HEAD.get(itrs); }
|
||||
Object next(Object node) { return NEXT.get(node); }
|
||||
int prevTakeIndex(Iterator itr) { return (int) PREVTAKEINDEX.get(itr); }
|
||||
|
||||
boolean isDetached(Iterator it) {
|
||||
return prevTakeIndex(it) < 0;
|
||||
}
|
||||
|
||||
void assertIteratorExhausted(Iterator it) {
|
||||
if (rnd.nextBoolean()) {
|
||||
assertTrue(!it.hasNext());
|
||||
assertTrue(isDetached(it));
|
||||
}
|
||||
if (rnd.nextBoolean()) {
|
||||
it.forEachRemaining(e -> { throw new AssertionError(); });
|
||||
assertTrue(isDetached(it));
|
||||
}
|
||||
if (rnd.nextBoolean())
|
||||
try { it.next(); fail("should throw"); }
|
||||
catch (NoSuchElementException success) {}
|
||||
}
|
||||
|
||||
List<Iterator> trackedIterators(ArrayBlockingQueue q) {
|
||||
List<Iterator> its = new ArrayList<>();
|
||||
Object itrs = itrs(q);
|
||||
if (itrs != null) {
|
||||
for (Object p = head(itrs); p != null; p = next(p))
|
||||
its.add(((WeakReference<Iterator>)(p)).get());
|
||||
Collections.reverse(its);
|
||||
}
|
||||
return its;
|
||||
}
|
||||
|
||||
List<Iterator> attachedIterators(ArrayBlockingQueue q) {
|
||||
List<Iterator> its = new ArrayList<>();
|
||||
Object itrs = itrs(q);
|
||||
if (itrs != null) {
|
||||
for (Object p = head(itrs); p != null; p = next(p)) {
|
||||
Iterator it = ((WeakReference<Iterator>)(p)).get();
|
||||
if (it != null && !isDetached(it))
|
||||
its.add(it);
|
||||
}
|
||||
Collections.reverse(its);
|
||||
}
|
||||
return its;
|
||||
}
|
||||
|
||||
void assertRemoveThrowsISE(Iterator it) {
|
||||
if (rnd.nextBoolean())
|
||||
try { it.remove(); fail("should throw"); }
|
||||
catch (IllegalStateException success) {}
|
||||
}
|
||||
|
||||
void assertRemoveHasNoEffect(Iterator it, Collection c) {
|
||||
if (rnd.nextBoolean()) {
|
||||
int size = c.size();
|
||||
it.remove(); // no effect
|
||||
assertEquals(c.size(), size);
|
||||
assertRemoveThrowsISE(it);
|
||||
}
|
||||
}
|
||||
|
||||
void checkIterationSanity(Queue q) {
|
||||
if (rnd.nextBoolean())
|
||||
return;
|
||||
int size = q.size();
|
||||
Object[] a = q.toArray();
|
||||
Object[] b = new Object[size+2];
|
||||
Arrays.fill(b, Boolean.TRUE);
|
||||
Object[] c = q.toArray(b);
|
||||
assertEquals(a.length, size);
|
||||
assertSame(b, c);
|
||||
assertNull(b[size]);
|
||||
assertSame(b[size+1], Boolean.TRUE);
|
||||
assertEquals(q.toString(), Arrays.toString(a));
|
||||
Integer[] xx = null, yy = null;
|
||||
if (size > 0) {
|
||||
xx = new Integer[size - 1];
|
||||
Arrays.fill(xx, 42);
|
||||
yy = ((Queue<Integer>)q).toArray(xx);
|
||||
for (Integer zz : xx)
|
||||
assertEquals(42, (int) zz);
|
||||
}
|
||||
Iterator it = q.iterator();
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (rnd.nextBoolean()) assertTrue(it.hasNext());
|
||||
Object x = it.next();
|
||||
assertSame(x, a[i]);
|
||||
assertSame(x, b[i]);
|
||||
assertSame(x, yy[i]);
|
||||
}
|
||||
if (rnd.nextBoolean()) assertTrue(!it.hasNext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Instead of having putIndex (and takeIndex) at the initial
|
||||
* default of 0, move them to a random location.
|
||||
*/
|
||||
void randomizePutIndex(ArrayBlockingQueue q) {
|
||||
assertTrue(q.isEmpty());
|
||||
int capacity = q.remainingCapacity();
|
||||
int n = rnd.nextInt(capacity + 1);
|
||||
int putIndex = putIndex(q);
|
||||
for (int i = n; i-->0; ) q.add(Boolean.TRUE);
|
||||
for (int i = n; i-->0; ) q.remove();
|
||||
assertEquals(putIndex(q), (putIndex + n) % items(q).length);
|
||||
}
|
||||
|
||||
/** No guarantees, but effective in practice. */
|
||||
static void forceFullGc() {
|
||||
long timeoutMillis = 1000L;
|
||||
CountDownLatch finalized = new CountDownLatch(1);
|
||||
ReferenceQueue<Object> queue = new ReferenceQueue<>();
|
||||
WeakReference<Object> ref = new WeakReference<>(
|
||||
new Object() { protected void finalize() { finalized.countDown(); }},
|
||||
queue);
|
||||
try {
|
||||
for (int tries = 3; tries--> 0; ) {
|
||||
System.gc();
|
||||
if (finalized.await(timeoutMillis, MILLISECONDS)
|
||||
&& queue.remove(timeoutMillis) != null
|
||||
&& ref.get() == null) {
|
||||
System.runFinalization(); // try to pick up stragglers
|
||||
return;
|
||||
}
|
||||
timeoutMillis *= 4;
|
||||
}
|
||||
} catch (InterruptedException unexpected) {
|
||||
throw new AssertionError("unexpected InterruptedException");
|
||||
}
|
||||
throw new AssertionError("failed to do a \"full\" gc");
|
||||
}
|
||||
|
||||
static void gcAwait(BooleanSupplier s) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (s.getAsBoolean())
|
||||
return;
|
||||
forceFullGc();
|
||||
}
|
||||
throw new AssertionError("failed to satisfy condition");
|
||||
}
|
||||
|
||||
public void clear_willClearItrs() {
|
||||
boolean fair = rnd.nextBoolean();
|
||||
int capacity = rnd.nextInt(2, 10);
|
||||
ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
|
||||
randomizePutIndex(q);
|
||||
List<Iterator> its = new ArrayList<>();
|
||||
for (int i = 0; i < capacity; i++)
|
||||
assertTrue(q.add(i));
|
||||
assertNull(itrs(q));
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
its.add(q.iterator());
|
||||
assertEquals(trackedIterators(q), its);
|
||||
q.poll();
|
||||
q.add(capacity + i);
|
||||
}
|
||||
q.clear();
|
||||
assertNull(itrs(q));
|
||||
int j = 0;
|
||||
for (Iterator it : its) {
|
||||
assertTrue(isDetached(it));
|
||||
if (rnd.nextBoolean()) assertTrue(it.hasNext());
|
||||
if (rnd.nextBoolean()) {
|
||||
assertEquals(it.next(), j);
|
||||
assertIteratorExhausted(it);
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
public void queueEmptyingWillClearItrs() {
|
||||
boolean fair = rnd.nextBoolean();
|
||||
int capacity = rnd.nextInt(2, 10);
|
||||
ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
|
||||
randomizePutIndex(q);
|
||||
List<Iterator> its = new ArrayList<>();
|
||||
for (int i = 0; i < capacity; i++)
|
||||
q.add(i);
|
||||
assertNull(itrs(q));
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
its.add(q.iterator());
|
||||
assertEquals(trackedIterators(q), its);
|
||||
q.poll();
|
||||
q.add(capacity+i);
|
||||
}
|
||||
for (int i = 0; i < capacity; i++)
|
||||
q.poll();
|
||||
assertNull(itrs(q));
|
||||
int j = 0;
|
||||
for (Iterator it : its) {
|
||||
assertTrue(isDetached(it));
|
||||
if (rnd.nextBoolean()) assertTrue(it.hasNext());
|
||||
if (rnd.nextBoolean()) {
|
||||
assertEquals(it.next(), j);
|
||||
assertIteratorExhausted(it);
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
public void advancing2CyclesWillRemoveIterators() {
|
||||
boolean fair = rnd.nextBoolean();
|
||||
int capacity = rnd.nextInt(2, 10);
|
||||
ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
|
||||
List<Iterator> its = new ArrayList<>();
|
||||
for (int i = 0; i < capacity; i++)
|
||||
q.add(i);
|
||||
assertNull(itrs(q));
|
||||
for (int i = capacity; i < 3 * capacity; i++) {
|
||||
its.add(q.iterator());
|
||||
assertEquals(trackedIterators(q), its);
|
||||
q.poll();
|
||||
q.add(i);
|
||||
}
|
||||
for (int i = 3 * capacity; i < 4 * capacity; i++) {
|
||||
assertEquals(trackedIterators(q), its.subList(capacity,2*capacity));
|
||||
q.poll();
|
||||
q.add(i);
|
||||
}
|
||||
assertNull(itrs(q));
|
||||
int j = 0;
|
||||
for (Iterator it : its) {
|
||||
assertTrue(isDetached(it));
|
||||
if (rnd.nextBoolean()) assertTrue(it.hasNext());
|
||||
if (rnd.nextBoolean()) {
|
||||
assertEquals(it.next(), j);
|
||||
assertIteratorExhausted(it);
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interior removal of elements used by an iterator will cause it
|
||||
* to be untracked.
|
||||
*/
|
||||
public void interiorRemovalOfElementsUsedByIterator() {
|
||||
boolean fair = rnd.nextBoolean();
|
||||
int capacity = rnd.nextInt(10, 20);
|
||||
ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
|
||||
randomizePutIndex(q);
|
||||
q.add(0);
|
||||
for (int i = 1; i < 2 * capacity; i++) {
|
||||
q.add(i);
|
||||
Integer[] elts = { -1, -2, -3 };
|
||||
for (Integer elt : elts) q.add(elt);
|
||||
assertEquals(q.remove(), i - 1);
|
||||
Iterator it = q.iterator();
|
||||
assertEquals(it.next(), i);
|
||||
assertEquals(it.next(), elts[0]);
|
||||
Collections.shuffle(Arrays.asList(elts));
|
||||
assertTrue(q.remove(elts[0]));
|
||||
assertTrue(q.remove(elts[1]));
|
||||
assertEquals(trackedIterators(q), Collections.singletonList(it));
|
||||
assertTrue(q.remove(elts[2]));
|
||||
assertNull(itrs(q));
|
||||
assertEquals(it.next(), -2);
|
||||
assertIteratorExhausted(it);
|
||||
assertTrue(isDetached(it));
|
||||
}
|
||||
}
|
||||
|
||||
public void iteratorsOnEmptyQueue() {
|
||||
boolean fair = rnd.nextBoolean();
|
||||
int capacity = rnd.nextInt(1, 10);
|
||||
ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
|
||||
randomizePutIndex(q);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Iterator it = q.iterator();
|
||||
assertNull(itrs(q));
|
||||
assertIteratorExhausted(it);
|
||||
assertTrue(isDetached(it));
|
||||
assertRemoveThrowsISE(it);
|
||||
}
|
||||
}
|
||||
|
||||
public void interiorRemovalOfIteratorsLastElement() {
|
||||
boolean fair = rnd.nextBoolean();
|
||||
int capacity = rnd.nextInt(3, 10);
|
||||
ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
|
||||
randomizePutIndex(q);
|
||||
List<Iterator> its = new ArrayList<>();
|
||||
for (int i = 0; i < capacity; i++)
|
||||
q.add(i);
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
Iterator it = q.iterator();
|
||||
its.add(it);
|
||||
for (int j = 0; j < i; j++)
|
||||
assertEquals(j, it.next());
|
||||
assertEquals(attachedIterators(q), its);
|
||||
}
|
||||
q.remove(capacity - 1);
|
||||
assertEquals(attachedIterators(q), its);
|
||||
for (int i = 1; i < capacity - 1; i++) {
|
||||
q.remove(capacity - i - 1);
|
||||
Iterator it = its.get(capacity - i);
|
||||
assertTrue(isDetached(it));
|
||||
assertEquals(attachedIterators(q), its.subList(0, capacity - i));
|
||||
if (rnd.nextBoolean()) assertTrue(it.hasNext());
|
||||
assertEquals(it.next(), capacity - i);
|
||||
assertIteratorExhausted(it);
|
||||
}
|
||||
assertEquals(attachedIterators(q), its.subList(0, 2));
|
||||
q.remove(0);
|
||||
assertTrue(q.isEmpty());
|
||||
assertNull(itrs(q));
|
||||
Iterator it = its.get(0);
|
||||
assertEquals(it.next(), 0);
|
||||
assertRemoveHasNoEffect(it, q);
|
||||
assertIteratorExhausted(it);
|
||||
assertTrue(isDetached(it));
|
||||
assertRemoveHasNoEffect(its.get(1), q);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks "interior" removal of alternating elements, straddling 2 cycles
|
||||
*/
|
||||
public void interiorRemovalOfAlternatingElements() {
|
||||
boolean fair = rnd.nextBoolean();
|
||||
int capacity = 2 * rnd.nextInt(2, 10);
|
||||
ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
|
||||
List<Iterator> its = new ArrayList<>();
|
||||
// Move takeIndex to middle
|
||||
for (int i = 0; i < capacity/2; i++) {
|
||||
assertTrue(q.add(i));
|
||||
assertEquals(q.poll(), i);
|
||||
}
|
||||
assertEquals(takeIndex(q), capacity/2);
|
||||
for (int i = 0; i < capacity; i++)
|
||||
q.add(i);
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
Iterator it = q.iterator();
|
||||
its.add(it);
|
||||
for (int j = 0; j < i; j++)
|
||||
assertEquals(j, it.next());
|
||||
assertEquals(attachedIterators(q), its);
|
||||
}
|
||||
|
||||
// Remove all even elements, in either direction, using
|
||||
// q.remove(), or iterator.remove()
|
||||
switch (rnd.nextInt(3)) {
|
||||
case 0:
|
||||
for (int i = 0; i < capacity; i+=2) assertTrue(q.remove(i));
|
||||
break;
|
||||
case 1:
|
||||
for (int i = capacity - 2; i >= 0; i-=2) assertTrue(q.remove(i));
|
||||
break;
|
||||
case 2:
|
||||
Iterator it = q.iterator();
|
||||
while (it.hasNext()) {
|
||||
int i = (Integer) it.next();
|
||||
if ((i & 1) == 0)
|
||||
it.remove();
|
||||
}
|
||||
break;
|
||||
default: throw new AssertionError();
|
||||
}
|
||||
assertEquals(attachedIterators(q), its);
|
||||
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
Iterator it = its.get(i);
|
||||
boolean even = ((i & 1) == 0);
|
||||
if (even) {
|
||||
if (rnd.nextBoolean()) assertTrue(it.hasNext());
|
||||
assertEquals(i, it.next());
|
||||
for (int j = i+1; j < capacity; j += 2)
|
||||
assertEquals(j, it.next());
|
||||
assertTrue(!isDetached(it));
|
||||
assertTrue(!it.hasNext());
|
||||
assertTrue(isDetached(it));
|
||||
} else { /* odd */
|
||||
if (rnd.nextBoolean()) assertTrue(it.hasNext());
|
||||
assertRemoveHasNoEffect(it, q);
|
||||
assertEquals(i, it.next());
|
||||
for (int j = i+2; j < capacity; j += 2)
|
||||
assertEquals(j, it.next());
|
||||
assertTrue(!isDetached(it));
|
||||
assertTrue(!it.hasNext());
|
||||
assertTrue(isDetached(it));
|
||||
}
|
||||
}
|
||||
assertEquals(trackedIterators(q), Collections.emptyList());
|
||||
assertNull(itrs(q));
|
||||
}
|
||||
|
||||
public void garbageCollectionOfUnreachableIterators() {
|
||||
boolean fair = rnd.nextBoolean();
|
||||
int capacity = rnd.nextInt(1, 10);
|
||||
ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
|
||||
randomizePutIndex(q);
|
||||
List<Iterator> its = new ArrayList<>();
|
||||
for (int i = 0; i < capacity; i++) q.add(i);
|
||||
for (int i = 0; i < capacity; i++) its.add(q.iterator());
|
||||
assertEquals(attachedIterators(q), its);
|
||||
its = null;
|
||||
gcAwait(() -> {
|
||||
List<Iterator> trackedIterators = trackedIterators(q);
|
||||
assertEquals(trackedIterators.size(), capacity);
|
||||
for (Iterator x : trackedIterators)
|
||||
if (x != null) return false;
|
||||
return true;
|
||||
});
|
||||
Iterator it = q.iterator(); //
|
||||
assertEquals(trackedIterators(q), Collections.singletonList(it));
|
||||
}
|
||||
|
||||
public void garbageCollectionOfUnreachableIteratorsWithRandomlyRetainedSubset() {
|
||||
boolean fair = rnd.nextBoolean();
|
||||
int capacity = rnd.nextInt(10, 20);
|
||||
ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
|
||||
randomizePutIndex(q);
|
||||
List<Iterator> its = new ArrayList<>();
|
||||
List<Iterator> retained = new ArrayList<>();
|
||||
final int size = 1 + rnd.nextInt(capacity);
|
||||
for (int i = 0; i < size; i++) q.add(i);
|
||||
for (int i = 0; i < size; i++) its.add(q.iterator());
|
||||
assertEquals(attachedIterators(q), its);
|
||||
// Leave sufficient gaps in retained
|
||||
for (int i = 0; i < size; i+= 2+rnd.nextInt(3))
|
||||
retained.add(its.get(i));
|
||||
its = null;
|
||||
gcAwait(() -> {
|
||||
List<Iterator> trackedIterators = trackedIterators(q);
|
||||
assertEquals(trackedIterators.size(), size);
|
||||
for (Iterator it : trackedIterators)
|
||||
if ((it != null) ^ retained.contains(it)) return false;
|
||||
return true;
|
||||
});
|
||||
Iterator it = q.iterator(); // trigger another sweep
|
||||
retained.add(it);
|
||||
assertEquals(trackedIterators(q), retained);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks incremental sweeping of unreachable iterators.
|
||||
* Excessively white box?!
|
||||
*/
|
||||
public void incrementalSweepingOfUnreachableIterators() {
|
||||
boolean fair = rnd.nextBoolean();
|
||||
int capacity = rnd.nextInt(10, 20);
|
||||
ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
|
||||
randomizePutIndex(q);
|
||||
final int SHORT_SWEEP_PROBES = 4;
|
||||
final int LONG_SWEEP_PROBES = 16;
|
||||
final int PROBE_HOP = LONG_SWEEP_PROBES + 6 * SHORT_SWEEP_PROBES;
|
||||
final int PROBE_HOP_COUNT = 10;
|
||||
// Expect around 8 sweeps per PROBE_HOP
|
||||
final int SWEEPS_PER_PROBE_HOP = 8;
|
||||
List<Iterator> its = new ArrayList<>();
|
||||
for (int i = 0; i < capacity; i++)
|
||||
q.add(i);
|
||||
for (int i = 0; i < PROBE_HOP_COUNT * PROBE_HOP; i++)
|
||||
its.add(q.iterator());
|
||||
assertEquals(attachedIterators(q), its);
|
||||
// make some garbage, separated by PROBE_HOP
|
||||
for (int i = 0; i < its.size(); i += PROBE_HOP)
|
||||
its.set(i, null);
|
||||
its.removeIf(it -> it == null);
|
||||
forceFullGc();
|
||||
int retries;
|
||||
for (retries = 0;
|
||||
trackedIterators(q).contains(null) && retries < 1000;
|
||||
retries++)
|
||||
// one round of sweeping
|
||||
its.add(q.iterator());
|
||||
assertTrue(retries >= PROBE_HOP_COUNT * (SWEEPS_PER_PROBE_HOP - 2));
|
||||
assertTrue(retries <= PROBE_HOP_COUNT * (SWEEPS_PER_PROBE_HOP + 2));
|
||||
assertEquals(trackedIterators(q), its);
|
||||
}
|
||||
|
||||
public void Iterator_remove_safetyWhileInDetachedMode() {
|
||||
boolean fair = rnd.nextBoolean();
|
||||
int capacity = rnd.nextInt(10, 20);
|
||||
ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
|
||||
List<Iterator> its = new ArrayList<>();
|
||||
for (int i = 0; i < capacity/2; i++) {
|
||||
q.add(i);
|
||||
q.remove();
|
||||
}
|
||||
assertEquals(takeIndex(q), capacity/2);
|
||||
for (int i = 0; i < capacity; i++)
|
||||
q.add(i);
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
Iterator it = q.iterator();
|
||||
its.add(it);
|
||||
for (int j = 0; j < i; j++)
|
||||
assertEquals(j, it.next());
|
||||
}
|
||||
assertEquals(attachedIterators(q), its);
|
||||
for (int i = capacity - 1; i >= 0; i--) {
|
||||
Iterator it = its.get(i);
|
||||
assertEquals(i, it.next()); // last element
|
||||
assertTrue(!isDetached(it));
|
||||
assertTrue(!it.hasNext()); // first hasNext failure
|
||||
assertTrue(isDetached(it));
|
||||
int size = q.size();
|
||||
assertTrue(q.contains(i));
|
||||
switch (rnd.nextInt(3)) {
|
||||
case 0:
|
||||
it.remove();
|
||||
assertTrue(!q.contains(i));
|
||||
assertEquals(q.size(), size - 1);
|
||||
break;
|
||||
case 1:
|
||||
// replace i with impostor
|
||||
if (q.remainingCapacity() == 0) {
|
||||
assertTrue(q.remove(i));
|
||||
assertTrue(q.add(-1));
|
||||
} else {
|
||||
assertTrue(q.add(-1));
|
||||
assertTrue(q.remove(i));
|
||||
}
|
||||
it.remove(); // should have no effect
|
||||
assertEquals(size, q.size());
|
||||
assertTrue(q.contains(-1));
|
||||
assertTrue(q.remove(-1));
|
||||
break;
|
||||
case 2:
|
||||
// replace i with true impostor
|
||||
if (i != 0) {
|
||||
assertTrue(q.remove(i));
|
||||
assertTrue(q.add(i));
|
||||
}
|
||||
it.remove();
|
||||
assertTrue(!q.contains(i));
|
||||
assertEquals(q.size(), size - 1);
|
||||
break;
|
||||
default: throw new AssertionError();
|
||||
}
|
||||
assertRemoveThrowsISE(it);
|
||||
assertTrue(isDetached(it));
|
||||
assertTrue(!trackedIterators(q).contains(it));
|
||||
}
|
||||
assertTrue(q.isEmpty());
|
||||
assertNull(itrs(q));
|
||||
for (Iterator it : its)
|
||||
assertIteratorExhausted(it);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks dequeues bypassing iterators' current positions.
|
||||
*/
|
||||
public void dequeuesBypassingIteratorCurrentPositions() {
|
||||
boolean fair = rnd.nextBoolean();
|
||||
int capacity = rnd.nextInt(10, 20);
|
||||
ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
|
||||
Queue<Iterator> its0 = new ArrayDeque<>();
|
||||
Queue<Iterator> itsMid = new ArrayDeque<>();
|
||||
List<Iterator> its = new ArrayList<>();
|
||||
for (int i = 0; i < capacity; i++)
|
||||
q.add(i);
|
||||
for (int i = 0; i < 2 * capacity + 1; i++) {
|
||||
Iterator it = q.iterator();
|
||||
its.add(it);
|
||||
its0.add(it);
|
||||
}
|
||||
for (int i = 0; i < 2 * capacity + 1; i++) {
|
||||
Iterator it = q.iterator();
|
||||
for (int j = 0; j < capacity/2; j++)
|
||||
assertEquals(j, it.next());
|
||||
its.add(it);
|
||||
itsMid.add(it);
|
||||
}
|
||||
for (int i = capacity; i < 3 * capacity; i++) {
|
||||
Iterator it;
|
||||
|
||||
it = its0.remove();
|
||||
assertRemoveThrowsISE(it);
|
||||
if (rnd.nextBoolean()) assertTrue(it.hasNext());
|
||||
assertEquals(0, it.next());
|
||||
int victim = i - capacity;
|
||||
for (int j = victim + (victim == 0 ? 1 : 0); j < i; j++) {
|
||||
if (rnd.nextBoolean()) assertTrue(it.hasNext());
|
||||
assertEquals(j, it.next());
|
||||
}
|
||||
assertIteratorExhausted(it);
|
||||
|
||||
it = itsMid.remove();
|
||||
if (victim >= capacity/2)
|
||||
assertRemoveHasNoEffect(it, q);
|
||||
assertEquals(capacity/2, it.next());
|
||||
if (victim > capacity/2)
|
||||
assertRemoveHasNoEffect(it, q);
|
||||
for (int j = Math.max(victim, capacity/2 + 1); j < i; j++) {
|
||||
if (rnd.nextBoolean()) assertTrue(it.hasNext());
|
||||
assertEquals(j, it.next());
|
||||
}
|
||||
assertIteratorExhausted(it);
|
||||
|
||||
if (rnd.nextBoolean()) {
|
||||
assertEquals(victim, q.remove());
|
||||
} else {
|
||||
ArrayList list = new ArrayList(1);
|
||||
q.drainTo(list, 1);
|
||||
assertEquals(list.size(), 1);
|
||||
assertEquals(victim, list.get(0));
|
||||
}
|
||||
assertTrue(q.add(i));
|
||||
}
|
||||
// takeIndex has wrapped twice.
|
||||
Iterator it0 = its0.remove();
|
||||
Iterator itMid = itsMid.remove();
|
||||
assertTrue(isDetached(it0));
|
||||
assertTrue(isDetached(itMid));
|
||||
if (rnd.nextBoolean()) assertTrue(it0.hasNext());
|
||||
if (rnd.nextBoolean()) assertTrue(itMid.hasNext());
|
||||
assertRemoveThrowsISE(it0);
|
||||
assertRemoveHasNoEffect(itMid, q);
|
||||
if (rnd.nextBoolean()) assertEquals(0, it0.next());
|
||||
if (rnd.nextBoolean()) assertEquals(capacity/2, itMid.next());
|
||||
assertTrue(isDetached(it0));
|
||||
assertTrue(isDetached(itMid));
|
||||
assertEquals(capacity, q.size());
|
||||
assertEquals(0, q.remainingCapacity());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks collective sanity of iteration, toArray() and toString().
|
||||
*/
|
||||
public void collectiveSanity() {
|
||||
boolean fair = rnd.nextBoolean();
|
||||
int capacity = rnd.nextInt(10, 20);
|
||||
ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
|
||||
randomizePutIndex(q);
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
checkIterationSanity(q);
|
||||
assertEquals(capacity, q.size() + q.remainingCapacity());
|
||||
q.add(i);
|
||||
}
|
||||
for (int i = 0; i < (capacity + (capacity >> 1)); i++) {
|
||||
checkIterationSanity(q);
|
||||
assertEquals(capacity, q.size() + q.remainingCapacity());
|
||||
assertEquals(i, q.peek());
|
||||
assertEquals(i, q.poll());
|
||||
checkIterationSanity(q);
|
||||
assertEquals(capacity, q.size() + q.remainingCapacity());
|
||||
q.add(capacity + i);
|
||||
}
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
checkIterationSanity(q);
|
||||
assertEquals(capacity, q.size() + q.remainingCapacity());
|
||||
int expected = i + capacity + (capacity >> 1);
|
||||
assertEquals(expected, q.peek());
|
||||
assertEquals(expected, q.poll());
|
||||
}
|
||||
checkIterationSanity(q);
|
||||
}
|
||||
|
||||
public void iteratorsDetachedWhenExhaustedAndLastRetRemoved() {
|
||||
boolean fair = rnd.nextBoolean();
|
||||
int capacity = rnd.nextInt(2, 10);
|
||||
ArrayBlockingQueue q = new ArrayBlockingQueue(capacity, fair);
|
||||
randomizePutIndex(q);
|
||||
int size = rnd.nextInt(1, capacity + 1);
|
||||
for (int i = 0; i < size; i++) q.add(i);
|
||||
Iterator it = q.iterator();
|
||||
for (int i = 0; i < size - 1; i++) assertEquals(i, it.next());
|
||||
assertEquals(trackedIterators(q), Collections.singletonList(it));
|
||||
assertFalse(isDetached(it));
|
||||
switch (rnd.nextInt(2)) {
|
||||
case 0: assertTrue(q.remove(size - 1)); break;
|
||||
case 1: assertTrue(q.removeIf(e -> e.equals(size - 1))); break;
|
||||
default: throw new AssertionError();
|
||||
}
|
||||
assertEquals(size - 1, it.next()); // should trigger detach
|
||||
assertNull(itrs(q));
|
||||
assertTrue(isDetached(it));
|
||||
assertRemoveHasNoEffect(it, q);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 4486658
|
||||
* @summary Checks for responsiveness of blocking queues to cancellation.
|
||||
* @library /test/lib
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
public class CancelledProducerConsumerLoops {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
static ExecutorService pool;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
final int maxPairs = (args.length > 0) ? Integer.parseInt(args[0]) : 5;
|
||||
|
||||
pool = Executors.newCachedThreadPool();
|
||||
for (int i = 1; i <= maxPairs; i += (i+1) >>> 1) {
|
||||
final List<BlockingQueue<Integer>> queues = new ArrayList<>();
|
||||
queues.add(new ArrayBlockingQueue<Integer>(100));
|
||||
queues.add(new LinkedBlockingQueue<Integer>(100));
|
||||
queues.add(new LinkedBlockingDeque<Integer>(100));
|
||||
queues.add(new SynchronousQueue<Integer>());
|
||||
// unbounded queue implementations are prone to OOME:
|
||||
// PriorityBlockingQueue, LinkedTransferQueue
|
||||
for (BlockingQueue<Integer> queue : queues)
|
||||
new CancelledProducerConsumerLoops(i, queue).run();
|
||||
}
|
||||
pool.shutdown();
|
||||
if (! pool.awaitTermination(LONG_DELAY_MS, MILLISECONDS))
|
||||
throw new AssertionError("timed out");
|
||||
pool = null;
|
||||
}
|
||||
|
||||
final int npairs;
|
||||
final BlockingQueue<Integer> queue;
|
||||
final CountDownLatch producersInterrupted;
|
||||
final CountDownLatch consumersInterrupted;
|
||||
final LoopHelpers.BarrierTimer timer = new LoopHelpers.BarrierTimer();
|
||||
final CyclicBarrier barrier;
|
||||
volatile boolean done = false;
|
||||
|
||||
CancelledProducerConsumerLoops(int npairs, BlockingQueue<Integer> queue) {
|
||||
this.npairs = npairs;
|
||||
this.queue = queue;
|
||||
this.producersInterrupted = new CountDownLatch(npairs - 1);
|
||||
this.consumersInterrupted = new CountDownLatch(npairs - 1);
|
||||
this.barrier = new CyclicBarrier(npairs * 2 + 1, timer);
|
||||
}
|
||||
|
||||
void run() throws Exception {
|
||||
Future<?>[] prods = new Future<?>[npairs];
|
||||
Future<?>[] cons = new Future<?>[npairs];
|
||||
|
||||
for (int i = 0; i < npairs; i++) {
|
||||
prods[i] = pool.submit(new Producer());
|
||||
cons[i] = pool.submit(new Consumer());
|
||||
}
|
||||
barrier.await();
|
||||
Thread.sleep(ThreadLocalRandom.current().nextInt(5));
|
||||
|
||||
for (int i = 1; i < npairs; i++) {
|
||||
if (!prods[i].cancel(true) ||
|
||||
!cons[i].cancel(true))
|
||||
throw new AssertionError("completed before done");
|
||||
}
|
||||
|
||||
for (int i = 1; i < npairs; i++) {
|
||||
assertCancelled(prods[i]);
|
||||
assertCancelled(cons[i]);
|
||||
}
|
||||
|
||||
if (!producersInterrupted.await(LONG_DELAY_MS, MILLISECONDS))
|
||||
throw new AssertionError("timed out");
|
||||
if (!consumersInterrupted.await(LONG_DELAY_MS, MILLISECONDS))
|
||||
throw new AssertionError("timed out");
|
||||
if (prods[0].isDone() || prods[0].isCancelled())
|
||||
throw new AssertionError("completed too early");
|
||||
|
||||
done = true;
|
||||
|
||||
if (! (prods[0].get(LONG_DELAY_MS, MILLISECONDS) instanceof Integer))
|
||||
throw new AssertionError("expected Integer");
|
||||
if (! (cons[0].get(LONG_DELAY_MS, MILLISECONDS) instanceof Integer))
|
||||
throw new AssertionError("expected Integer");
|
||||
}
|
||||
|
||||
void assertCancelled(Future<?> future) throws Exception {
|
||||
if (!future.isDone())
|
||||
throw new AssertionError("not done");
|
||||
if (!future.isCancelled())
|
||||
throw new AssertionError("not cancelled");
|
||||
try {
|
||||
future.get(LONG_DELAY_MS, MILLISECONDS);
|
||||
throw new AssertionError("should throw CancellationException");
|
||||
} catch (CancellationException success) {}
|
||||
}
|
||||
|
||||
class Producer implements Callable<Integer> {
|
||||
public Integer call() throws Exception {
|
||||
barrier.await();
|
||||
int sum = 0;
|
||||
try {
|
||||
int x = 4321;
|
||||
while (!done) {
|
||||
if (Thread.interrupted()) throw new InterruptedException();
|
||||
x = LoopHelpers.compute1(x);
|
||||
sum += LoopHelpers.compute2(x);
|
||||
queue.offer(new Integer(x), 1, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
} catch (InterruptedException cancelled) {
|
||||
producersInterrupted.countDown();
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
|
||||
class Consumer implements Callable<Integer> {
|
||||
public Integer call() throws Exception {
|
||||
barrier.await();
|
||||
int sum = 0;
|
||||
try {
|
||||
while (!done) {
|
||||
Integer x = queue.poll(1, TimeUnit.MILLISECONDS);
|
||||
if (x != null)
|
||||
sum += LoopHelpers.compute1(x.intValue());
|
||||
}
|
||||
} catch (InterruptedException cancelled) {
|
||||
consumersInterrupted.countDown();
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
218
test/jdk/java/util/concurrent/BlockingQueue/DrainToFails.java
Normal file
218
test/jdk/java/util/concurrent/BlockingQueue/DrainToFails.java
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Test drainTo failing due to c.add throwing
|
||||
* @library /test/lib
|
||||
*/
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.DelayQueue;
|
||||
import java.util.concurrent.Delayed;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
import java.util.concurrent.RunnableScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public class DrainToFails {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
final int CAPACITY = 10;
|
||||
final int SMALL = 2;
|
||||
|
||||
void test(String[] args) throws Throwable {
|
||||
testDelayQueue(new DelayQueue());
|
||||
testDelayQueue(new ScheduledThreadPoolExecutor(1).getQueue());
|
||||
|
||||
testUnbounded(new LinkedBlockingQueue());
|
||||
testUnbounded(new LinkedBlockingDeque());
|
||||
testUnbounded(new PriorityBlockingQueue());
|
||||
|
||||
testBounded(new LinkedBlockingQueue(CAPACITY));
|
||||
testBounded(new LinkedBlockingDeque(CAPACITY));
|
||||
testBounded(new ArrayBlockingQueue(CAPACITY));
|
||||
}
|
||||
|
||||
static class PDelay
|
||||
extends FutureTask<Void>
|
||||
implements Delayed, RunnableScheduledFuture<Void> {
|
||||
int pseudodelay;
|
||||
PDelay(int i) {
|
||||
super(new Runnable() { public void run() {}}, null);
|
||||
pseudodelay = i;
|
||||
}
|
||||
public int compareTo(PDelay other) {
|
||||
return Integer.compare(this.pseudodelay, other.pseudodelay);
|
||||
}
|
||||
public int compareTo(Delayed y) {
|
||||
return compareTo((PDelay)y);
|
||||
}
|
||||
public boolean equals(Object other) {
|
||||
return (other instanceof PDelay) &&
|
||||
this.pseudodelay == ((PDelay)other).pseudodelay;
|
||||
}
|
||||
public long getDelay(TimeUnit ignore) {
|
||||
return Integer.MIN_VALUE + pseudodelay;
|
||||
}
|
||||
public String toString() {
|
||||
return String.valueOf(pseudodelay);
|
||||
}
|
||||
public boolean isPeriodic() { return false; }
|
||||
}
|
||||
|
||||
void testDelayQueue(final BlockingQueue q) throws Throwable {
|
||||
System.err.println(q.getClass().getSimpleName());
|
||||
for (int i = 0; i < CAPACITY; i++)
|
||||
q.add(new PDelay(i));
|
||||
ArrayBlockingQueue q2 = new ArrayBlockingQueue(SMALL);
|
||||
try {
|
||||
q.drainTo(q2, SMALL + 3);
|
||||
fail("should throw");
|
||||
} catch (IllegalStateException success) {
|
||||
equal(SMALL, q2.size());
|
||||
equal(new PDelay(0), q2.poll());
|
||||
equal(new PDelay(1), q2.poll());
|
||||
check(q2.isEmpty());
|
||||
for (int i = SMALL; i < CAPACITY; i++)
|
||||
equal(new PDelay(i), q.poll());
|
||||
equal(0, q.size());
|
||||
}
|
||||
}
|
||||
|
||||
void testUnbounded(final BlockingQueue q) throws Throwable {
|
||||
System.err.println(q.getClass().getSimpleName());
|
||||
for (int i = 0; i < CAPACITY; i++)
|
||||
q.add(i);
|
||||
ArrayBlockingQueue q2 = new ArrayBlockingQueue(SMALL);
|
||||
try {
|
||||
q.drainTo(q2, 7);
|
||||
fail("should throw");
|
||||
} catch (IllegalStateException success) {
|
||||
assertContentsInOrder(q2, 0, 1);
|
||||
q2.clear();
|
||||
equal(q.size(), CAPACITY - SMALL);
|
||||
equal(SMALL, q.peek());
|
||||
}
|
||||
|
||||
try {
|
||||
q.drainTo(q2);
|
||||
fail("should throw");
|
||||
} catch (IllegalStateException success) {
|
||||
assertContentsInOrder(q2, 2, 3);
|
||||
equal(q.size(), CAPACITY - 2 * SMALL);
|
||||
for (int i = 2 * SMALL; i < CAPACITY; i++)
|
||||
equal(i, q.poll());
|
||||
equal(0, q.size());
|
||||
}
|
||||
}
|
||||
|
||||
void testBounded(final BlockingQueue q) throws Throwable {
|
||||
System.err.println(q.getClass().getSimpleName());
|
||||
for (int i = 0; i < CAPACITY; i++)
|
||||
q.add(i);
|
||||
List<Thread> putters = new ArrayList<>();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Thread putter = new Thread(putter(q, 42 + i));
|
||||
putters.add(putter);
|
||||
putter.setDaemon(true);
|
||||
putter.start();
|
||||
}
|
||||
ArrayBlockingQueue q2 = new ArrayBlockingQueue(SMALL);
|
||||
try {
|
||||
q.drainTo(q2, 7);
|
||||
fail("should throw");
|
||||
} catch (IllegalStateException success) {
|
||||
while (q.size() < CAPACITY)
|
||||
Thread.yield();
|
||||
assertContentsInOrder(q2, 0, 1);
|
||||
q2.clear();
|
||||
}
|
||||
|
||||
try {
|
||||
q.drainTo(q2);
|
||||
fail("should throw");
|
||||
} catch (IllegalStateException success) {
|
||||
for (Thread putter : putters) {
|
||||
putter.join(LONG_DELAY_MS);
|
||||
check(! putter.isAlive());
|
||||
}
|
||||
assertContentsInOrder(q2, 2, 3);
|
||||
for (int i = 2 * SMALL; i < CAPACITY; i++)
|
||||
equal(i, q.poll());
|
||||
equal(4, q.size());
|
||||
check(q.contains(42));
|
||||
check(q.contains(43));
|
||||
check(q.contains(44));
|
||||
check(q.contains(45));
|
||||
}
|
||||
}
|
||||
|
||||
Runnable putter(BlockingQueue q, int elt) {
|
||||
return () -> {
|
||||
try { q.put(elt); }
|
||||
catch (Throwable t) { unexpected(t); }};
|
||||
}
|
||||
|
||||
void assertContentsInOrder(Iterable it, Object... contents) {
|
||||
int i = 0;
|
||||
for (Object e : it)
|
||||
equal(contents[i++], e);
|
||||
equal(contents.length, i);
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new DrainToFails().instanceMain(args);}
|
||||
public void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
143
test/jdk/java/util/concurrent/BlockingQueue/Interrupt.java
Normal file
143
test/jdk/java/util/concurrent/BlockingQueue/Interrupt.java
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6384064
|
||||
* @summary Check proper handling of interrupts
|
||||
* @author Martin Buchholz
|
||||
* @library /test/lib
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingDeque;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
public class Interrupt {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
|
||||
static void checkInterrupted0(Iterable<Fun> fs, Executor ex) {
|
||||
for (Fun f : fs) {
|
||||
try {
|
||||
ex.execute(new Runnable() {
|
||||
final Thread thisThread = Thread.currentThread();
|
||||
public void run() { thisThread.interrupt(); }});
|
||||
f.f();
|
||||
fail("Expected InterruptedException not thrown");
|
||||
} catch (InterruptedException e) {
|
||||
check(! Thread.interrupted());
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
}
|
||||
}
|
||||
|
||||
static void checkInterrupted(Iterable<Fun> fs)
|
||||
throws InterruptedException {
|
||||
final Executor immediateExecutor = new Executor() {
|
||||
public void execute(Runnable r) {
|
||||
r.run(); }};
|
||||
final ScheduledThreadPoolExecutor stpe
|
||||
= new ScheduledThreadPoolExecutor(1);
|
||||
final Executor delayedExecutor = new Executor() {
|
||||
public void execute(Runnable r) {
|
||||
stpe.schedule(r, 20, MILLISECONDS); }};
|
||||
checkInterrupted0(fs, immediateExecutor);
|
||||
checkInterrupted0(fs, delayedExecutor);
|
||||
stpe.shutdown();
|
||||
check(stpe.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
|
||||
}
|
||||
|
||||
static void testQueue(final BlockingQueue<Object> q) {
|
||||
try {
|
||||
final BlockingDeque<Object> deq =
|
||||
(q instanceof BlockingDeque<?>) ?
|
||||
(BlockingDeque<Object>) q : null;
|
||||
q.clear();
|
||||
List<Fun> fs = new ArrayList<>();
|
||||
fs.add(() -> q.take());
|
||||
fs.add(() -> q.poll(LONG_DELAY_MS, MILLISECONDS));
|
||||
if (deq != null) {
|
||||
fs.add(() -> deq.takeFirst());
|
||||
fs.add(() -> deq.takeLast());
|
||||
fs.add(() -> deq.pollFirst(LONG_DELAY_MS, MILLISECONDS));
|
||||
fs.add(() -> deq.pollLast(LONG_DELAY_MS, MILLISECONDS));
|
||||
}
|
||||
|
||||
checkInterrupted(fs);
|
||||
|
||||
// fill q to capacity, to ensure insertions will block
|
||||
while (q.remainingCapacity() > 0)
|
||||
try { q.put(1); }
|
||||
catch (Throwable t) { unexpected(t); }
|
||||
|
||||
fs.clear();
|
||||
fs.add(() -> q.put(1));
|
||||
fs.add(() -> q.offer(1, LONG_DELAY_MS, MILLISECONDS));
|
||||
if (deq != null) {
|
||||
fs.add(() -> deq.putFirst(1));
|
||||
fs.add(() -> deq.putLast(1));
|
||||
fs.add(() -> deq.offerFirst(1, LONG_DELAY_MS, MILLISECONDS));
|
||||
fs.add(() -> deq.offerLast(1, LONG_DELAY_MS, MILLISECONDS));
|
||||
}
|
||||
checkInterrupted(fs);
|
||||
} catch (Throwable t) {
|
||||
System.out.printf("Failed: %s%n", q.getClass().getSimpleName());
|
||||
unexpected(t);
|
||||
} finally {
|
||||
Thread.interrupted(); // clear interrupts, just in case
|
||||
}
|
||||
}
|
||||
|
||||
private static void realMain(final String[] args) throws Throwable {
|
||||
testQueue(new SynchronousQueue<Object>());
|
||||
testQueue(new ArrayBlockingQueue<Object>(1,false));
|
||||
testQueue(new ArrayBlockingQueue<Object>(1,true));
|
||||
testQueue(new LinkedBlockingQueue<Object>(1));
|
||||
testQueue(new LinkedBlockingDeque<Object>(1));
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
static volatile int passed = 0, failed = 0;
|
||||
static void pass() {passed++;}
|
||||
static void fail() {failed++; Thread.dumpStack();}
|
||||
static void fail(String msg) {System.out.println(msg); fail();}
|
||||
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
static void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
static void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
try {realMain(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
interface Fun {void f() throws Throwable;}
|
||||
}
|
||||
121
test/jdk/java/util/concurrent/BlockingQueue/LastElement.java
Normal file
121
test/jdk/java/util/concurrent/BlockingQueue/LastElement.java
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6215625 7161229
|
||||
* @modules java.base/java.util.concurrent:open
|
||||
* @summary Check correct behavior when last element is removed.
|
||||
* @author Martin Buchholz
|
||||
*/
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
|
||||
public class LastElement {
|
||||
void test(String[] args) throws Throwable {
|
||||
testQueue(new LinkedBlockingQueue<Integer>());
|
||||
testQueue(new LinkedBlockingDeque<Integer>());
|
||||
testQueue(new ArrayBlockingQueue<Integer>(10, true));
|
||||
testQueue(new ArrayBlockingQueue<Integer>(10, false));
|
||||
testQueue(new LinkedTransferQueue<Integer>());
|
||||
testQueue(new PriorityBlockingQueue<Integer>());
|
||||
}
|
||||
|
||||
void testQueue(BlockingQueue<Integer> q) throws Throwable {
|
||||
Integer one = 1;
|
||||
Integer two = 2;
|
||||
Integer three = 3;
|
||||
|
||||
// remove(Object)
|
||||
q.put(one);
|
||||
q.put(two);
|
||||
check(! q.isEmpty() && q.size() == 2);
|
||||
check(q.remove(one));
|
||||
check(q.remove(two));
|
||||
check(q.isEmpty() && q.size() == 0);
|
||||
q.put(three);
|
||||
try {check(q.take() == three);}
|
||||
catch (Throwable t) {unexpected(t);}
|
||||
check(q.isEmpty() && q.size() == 0);
|
||||
check(noRetention(q));
|
||||
|
||||
// iterator().remove()
|
||||
q.clear();
|
||||
q.put(one);
|
||||
check(q.offer(two));
|
||||
check(! q.isEmpty() && q.size() == 2);
|
||||
Iterator<Integer> i = q.iterator();
|
||||
check(i.next() == one);
|
||||
i.remove();
|
||||
check(i.next() == two);
|
||||
i.remove();
|
||||
check(q.isEmpty() && q.size() == 0);
|
||||
q.put(three);
|
||||
try {check(q.take() == three);}
|
||||
catch (Throwable t) {unexpected(t);}
|
||||
check(q.isEmpty() && q.size() == 0);
|
||||
}
|
||||
|
||||
boolean noRetention(BlockingQueue<?> q) {
|
||||
if (q instanceof PriorityBlockingQueue) {
|
||||
PriorityBlockingQueue<?> pbq = (PriorityBlockingQueue) q;
|
||||
try {
|
||||
java.lang.reflect.Field queue =
|
||||
PriorityBlockingQueue.class.getDeclaredField("queue");
|
||||
queue.setAccessible(true);
|
||||
Object[] a = (Object[]) queue.get(pbq);
|
||||
return a[0] == null;
|
||||
}
|
||||
catch (NoSuchFieldException e) {
|
||||
unexpected(e);
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new LastElement().instanceMain(args);}
|
||||
public void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
103
test/jdk/java/util/concurrent/BlockingQueue/LoopHelpers.java
Normal file
103
test/jdk/java/util/concurrent/BlockingQueue/LoopHelpers.java
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/**
|
||||
* Misc utilities in JSR166 performance tests
|
||||
*/
|
||||
class LoopHelpers {
|
||||
|
||||
// Some mindless computation to do between synchronizations...
|
||||
|
||||
/**
|
||||
* generates 32 bit pseudo-random numbers.
|
||||
* Adapted from http://www.snippets.org
|
||||
*/
|
||||
public static int compute1(int x) {
|
||||
int lo = 16807 * (x & 0xFFFF);
|
||||
int hi = 16807 * (x >>> 16);
|
||||
lo += (hi & 0x7FFF) << 16;
|
||||
if ((lo & 0x80000000) != 0) {
|
||||
lo &= 0x7fffffff;
|
||||
++lo;
|
||||
}
|
||||
lo += hi >>> 15;
|
||||
if (lo == 0 || (lo & 0x80000000) != 0) {
|
||||
lo &= 0x7fffffff;
|
||||
++lo;
|
||||
}
|
||||
return lo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a linear congruential random number a random number
|
||||
* of times.
|
||||
*/
|
||||
public static int compute2(int x) {
|
||||
int loops = (x >>> 4) & 7;
|
||||
while (loops-- > 0) {
|
||||
x = (x * 2147483647) % 16807;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
public static class BarrierTimer implements Runnable {
|
||||
public volatile long startTime;
|
||||
public volatile long endTime;
|
||||
public void run() {
|
||||
long t = System.nanoTime();
|
||||
if (startTime == 0)
|
||||
startTime = t;
|
||||
else
|
||||
endTime = t;
|
||||
}
|
||||
public void clear() {
|
||||
startTime = 0;
|
||||
endTime = 0;
|
||||
}
|
||||
public long getTime() {
|
||||
return endTime - startTime;
|
||||
}
|
||||
}
|
||||
|
||||
public static String rightJustify(long n) {
|
||||
// There's probably a better way to do this...
|
||||
String field = " ";
|
||||
String num = Long.toString(n);
|
||||
if (num.length() >= field.length())
|
||||
return num;
|
||||
StringBuilder b = new StringBuilder(field);
|
||||
b.replace(b.length()-num.length(), b.length(), num);
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
}
|
||||
78
test/jdk/java/util/concurrent/BlockingQueue/MissedPoll.java
Normal file
78
test/jdk/java/util/concurrent/BlockingQueue/MissedPoll.java
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8371740
|
||||
* @summary Checks for poll() (or peek) returning null when an element must exist.
|
||||
*/
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class MissedPoll {
|
||||
public static void main(String... args) throws Throwable {
|
||||
test(new LinkedTransferQueue<>());
|
||||
test(new LinkedBlockingQueue<>());
|
||||
test(new LinkedBlockingDeque<>());
|
||||
test(new ArrayBlockingQueue<>(10));
|
||||
}
|
||||
|
||||
private static void test(BlockingQueue<Integer> q)
|
||||
throws ExecutionException, InterruptedException {
|
||||
System.out.println(q.getClass());
|
||||
try (var pool = Executors.newCachedThreadPool()) {
|
||||
var futures = new ArrayList<Future<Integer>>();
|
||||
var phaser = new Phaser(4) {
|
||||
@Override
|
||||
protected boolean onAdvance(int phase, int registeredParties) {
|
||||
q.clear();
|
||||
return super.onAdvance(phase, registeredParties);
|
||||
}
|
||||
};
|
||||
for (var i = 0; i < 4; i++) {
|
||||
futures.add(pool.submit(() -> {
|
||||
int errors = 0;
|
||||
for (int k = 0; k < 10; k++) {
|
||||
for (int j = 0; j < 1000; j++) {
|
||||
q.offer(j);
|
||||
if (q.peek() == null)
|
||||
++errors;
|
||||
if (q.poll() == null)
|
||||
++errors;
|
||||
}
|
||||
phaser.arriveAndAwaitAdvance();
|
||||
}
|
||||
return errors;
|
||||
}));
|
||||
}
|
||||
for (var future : futures) {
|
||||
Integer res;
|
||||
if ((res = future.get()) != 0)
|
||||
throw new AssertionError("Expected 0 but got " + res);
|
||||
}
|
||||
}
|
||||
if (!q.isEmpty())
|
||||
throw new AssertionError("Queue is not empty: " + q);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 4486658
|
||||
* @summary multiple producers and single consumer using blocking queues
|
||||
* @library /test/lib
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
public class MultipleProducersSingleConsumerLoops {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
static ExecutorService pool;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
final int maxProducers = (args.length > 0)
|
||||
? Integer.parseInt(args[0])
|
||||
: 5;
|
||||
|
||||
pool = Executors.newCachedThreadPool();
|
||||
for (int i = 1; i <= maxProducers; i += (i+1) >>> 1) {
|
||||
// Adjust iterations to limit typical single runs to <= 10 ms;
|
||||
// Notably, fair queues get fewer iters.
|
||||
// Unbounded queues can legitimately OOME if iterations
|
||||
// high enough, but we have a sufficiently low limit here.
|
||||
run(new ArrayBlockingQueue<Integer>(100), i, 300);
|
||||
run(new LinkedBlockingQueue<Integer>(100), i, 700);
|
||||
run(new LinkedBlockingDeque<Integer>(100), i , 500);
|
||||
run(new LinkedTransferQueue<Integer>(), i, 1000);
|
||||
run(new PriorityBlockingQueue<Integer>(), i, 1000);
|
||||
run(new SynchronousQueue<Integer>(), i, 500);
|
||||
run(new SynchronousQueue<Integer>(true), i, 200);
|
||||
run(new ArrayBlockingQueue<Integer>(100, true), i, 100);
|
||||
}
|
||||
|
||||
pool.shutdown();
|
||||
if (! pool.awaitTermination(LONG_DELAY_MS, MILLISECONDS))
|
||||
throw new Error();
|
||||
pool = null;
|
||||
}
|
||||
|
||||
static void run(BlockingQueue<Integer> queue, int nproducers, int iters) throws Exception {
|
||||
new MultipleProducersSingleConsumerLoops(queue, nproducers, iters).run();
|
||||
}
|
||||
|
||||
final BlockingQueue<Integer> queue;
|
||||
final int nproducers;
|
||||
final int iters;
|
||||
final LoopHelpers.BarrierTimer timer = new LoopHelpers.BarrierTimer();
|
||||
final CyclicBarrier barrier;
|
||||
final AtomicInteger checksum = new AtomicInteger(0);
|
||||
Throwable fail;
|
||||
|
||||
MultipleProducersSingleConsumerLoops(BlockingQueue<Integer> queue, int nproducers, int iters) {
|
||||
this.queue = queue;
|
||||
this.nproducers = nproducers;
|
||||
this.iters = iters;
|
||||
this.barrier = new CyclicBarrier(nproducers + 2, timer);
|
||||
}
|
||||
|
||||
void run() throws Exception {
|
||||
for (int i = 0; i < nproducers; i++)
|
||||
pool.execute(new Producer());
|
||||
pool.execute(new Consumer());
|
||||
barrier.await();
|
||||
barrier.await();
|
||||
System.out.printf("%s, nproducers=%d: %d ms%n",
|
||||
queue.getClass().getSimpleName(), nproducers,
|
||||
NANOSECONDS.toMillis(timer.getTime()));
|
||||
if (checksum.get() != 0) throw new AssertionError("checksum mismatch");
|
||||
if (fail != null) throw new AssertionError(fail);
|
||||
}
|
||||
|
||||
abstract class CheckedRunnable implements Runnable {
|
||||
abstract void realRun() throws Throwable;
|
||||
public final void run() {
|
||||
try {
|
||||
realRun();
|
||||
} catch (Throwable t) {
|
||||
fail = t;
|
||||
t.printStackTrace();
|
||||
throw new AssertionError(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Producer extends CheckedRunnable {
|
||||
void realRun() throws Throwable {
|
||||
barrier.await();
|
||||
int s = 0;
|
||||
int l = hashCode();
|
||||
for (int i = 0; i < iters; i++) {
|
||||
l = LoopHelpers.compute1(l);
|
||||
l = LoopHelpers.compute2(l);
|
||||
queue.put(new Integer(l));
|
||||
s += l;
|
||||
}
|
||||
checksum.getAndAdd(s);
|
||||
barrier.await();
|
||||
}
|
||||
}
|
||||
|
||||
class Consumer extends CheckedRunnable {
|
||||
void realRun() throws Throwable {
|
||||
barrier.await();
|
||||
int s = 0;
|
||||
for (int i = 0; i < nproducers * iters; i++) {
|
||||
s += queue.take().intValue();
|
||||
}
|
||||
checksum.getAndAdd(-s);
|
||||
barrier.await();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6805775 6815766
|
||||
* @library /test/lib
|
||||
* @run main OfferDrainToLoops 100
|
||||
* @summary Test concurrent offer vs. drainTo
|
||||
*/
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.SplittableRandom;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes", "deprecation"})
|
||||
public class OfferDrainToLoops {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
final long testDurationMillisDefault = 10_000L;
|
||||
final long testDurationMillis;
|
||||
|
||||
OfferDrainToLoops(String[] args) {
|
||||
testDurationMillis = (args.length > 0) ?
|
||||
Long.valueOf(args[0]) : testDurationMillisDefault;
|
||||
}
|
||||
|
||||
void checkNotContainsNull(Iterable it) {
|
||||
for (Object x : it)
|
||||
check(x != null);
|
||||
}
|
||||
|
||||
void test(String[] args) throws Throwable {
|
||||
test(new LinkedBlockingQueue());
|
||||
test(new LinkedBlockingQueue(2000));
|
||||
test(new LinkedBlockingDeque());
|
||||
test(new LinkedBlockingDeque(2000));
|
||||
test(new ArrayBlockingQueue(2000));
|
||||
test(new LinkedTransferQueue());
|
||||
}
|
||||
|
||||
void test(final BlockingQueue q) throws Throwable {
|
||||
System.out.println(q.getClass().getSimpleName());
|
||||
final long testDurationNanos = testDurationMillis * 1000L * 1000L;
|
||||
final long quittingTimeNanos = System.nanoTime() + testDurationNanos;
|
||||
final SplittableRandom rnd = new SplittableRandom();
|
||||
|
||||
// Poor man's bounded buffer.
|
||||
final AtomicLong approximateCount = new AtomicLong(0L);
|
||||
|
||||
abstract class CheckedThread extends Thread {
|
||||
final SplittableRandom rnd;
|
||||
|
||||
CheckedThread(String name, SplittableRandom rnd) {
|
||||
super(name);
|
||||
this.rnd = rnd;
|
||||
setDaemon(true);
|
||||
start();
|
||||
}
|
||||
/** Polls for quitting time. */
|
||||
protected boolean quittingTime() {
|
||||
return System.nanoTime() - quittingTimeNanos > 0;
|
||||
}
|
||||
/** Polls occasionally for quitting time. */
|
||||
protected boolean quittingTime(long i) {
|
||||
return (i % 1024) == 0 && quittingTime();
|
||||
}
|
||||
protected abstract void realRun();
|
||||
public void run() {
|
||||
try { realRun(); } catch (Throwable t) { unexpected(t); }
|
||||
}
|
||||
}
|
||||
|
||||
Thread offerer = new CheckedThread("offerer", rnd.split()) {
|
||||
protected void realRun() {
|
||||
long c = 0;
|
||||
for (long i = 0; ! quittingTime(i); i++) {
|
||||
if (q.offer(c)) {
|
||||
if ((++c % 1024) == 0) {
|
||||
approximateCount.getAndAdd(1024);
|
||||
while (approximateCount.get() > 10000)
|
||||
Thread.yield();
|
||||
}
|
||||
} else {
|
||||
Thread.yield();
|
||||
}}}};
|
||||
|
||||
Thread drainer = new CheckedThread("drainer", rnd.split()) {
|
||||
protected void realRun() {
|
||||
while (! quittingTime()) {
|
||||
List list = new ArrayList();
|
||||
int n = rnd.nextBoolean() ?
|
||||
q.drainTo(list) :
|
||||
q.drainTo(list, 100);
|
||||
approximateCount.getAndAdd(-n);
|
||||
equal(list.size(), n);
|
||||
for (int j = 0; j < n - 1; j++)
|
||||
equal((Long) list.get(j) + 1L, list.get(j + 1));
|
||||
Thread.yield();
|
||||
}
|
||||
q.clear();
|
||||
approximateCount.set(0); // Releases waiting offerer thread
|
||||
}};
|
||||
|
||||
Thread scanner = new CheckedThread("scanner", rnd.split()) {
|
||||
protected void realRun() {
|
||||
while (! quittingTime()) {
|
||||
switch (rnd.nextInt(3)) {
|
||||
case 0: checkNotContainsNull(q); break;
|
||||
case 1: q.size(); break;
|
||||
case 2:
|
||||
Long[] a = (Long[]) q.toArray(new Long[0]);
|
||||
int n = a.length;
|
||||
for (int j = 0; j < n - 1; j++) {
|
||||
check(a[j] < a[j+1]);
|
||||
check(a[j] != null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
Thread.yield();
|
||||
}}};
|
||||
|
||||
for (Thread thread : new Thread[] { offerer, drainer, scanner }) {
|
||||
thread.join(LONG_DELAY_MS + testDurationMillis);
|
||||
if (thread.isAlive()) {
|
||||
System.err.printf("Hung thread: %s%n", thread.getName());
|
||||
failed++;
|
||||
for (StackTraceElement e : thread.getStackTrace())
|
||||
System.err.println(e);
|
||||
thread.join(LONG_DELAY_MS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new OfferDrainToLoops(args).instanceMain(args);}
|
||||
public void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
160
test/jdk/java/util/concurrent/BlockingQueue/PollMemoryLeak.java
Normal file
160
test/jdk/java/util/concurrent/BlockingQueue/PollMemoryLeak.java
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6236036 6264015
|
||||
* @modules java.base/java.util.concurrent:open
|
||||
* java.base/java.util.concurrent.locks:open
|
||||
* @summary Checks for a memory leak when a sequence of aborted timed
|
||||
* waits occur without a signal. Uses the strategy of detecting
|
||||
* changes in the size of the object graph retained by a root object.
|
||||
*/
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class PollMemoryLeak {
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new PollMemoryLeak().main();
|
||||
}
|
||||
|
||||
void main() throws Throwable {
|
||||
test(new LinkedBlockingDeque(10));
|
||||
test(new LinkedBlockingQueue(10));
|
||||
test(new LinkedTransferQueue());
|
||||
test(new ArrayBlockingQueue(10));
|
||||
test(new PriorityBlockingQueue());
|
||||
test(new SynchronousQueue());
|
||||
test(new SynchronousQueue(true));
|
||||
}
|
||||
|
||||
void test(BlockingQueue q) throws Throwable {
|
||||
assertNoLeak(q, () -> timedPoll(q));
|
||||
|
||||
// A demo that the leak detection infrastructure works
|
||||
// assertNoLeak(q, () -> q.add(1));
|
||||
// printRetainedObjects(q);
|
||||
}
|
||||
|
||||
static void timedPoll(BlockingQueue q) {
|
||||
try { q.poll(1, TimeUnit.NANOSECONDS); }
|
||||
catch (InterruptedException ex) { throw new AssertionError(ex); }
|
||||
}
|
||||
|
||||
// -------- leak detection infrastructure ---------------
|
||||
void assertNoLeak(Object root, Runnable r) {
|
||||
int prev = retainedObjects(root).size();
|
||||
for (int i = 0; i < 3; i++) {
|
||||
for (int j = 0; j < 3; j++) r.run();
|
||||
int next = retainedObjects(root).size();
|
||||
if (next <= prev)
|
||||
return;
|
||||
prev = next;
|
||||
}
|
||||
throw new AssertionError(
|
||||
String.format("probable memory leak in %s: %s",
|
||||
root.getClass().getSimpleName(), root));
|
||||
}
|
||||
|
||||
ConcurrentHashMap<Class<?>, Collection<Field>> classFields
|
||||
= new ConcurrentHashMap<>();
|
||||
|
||||
Collection<Field> referenceFieldsOf(Class<?> k) {
|
||||
Collection<Field> fields = classFields.get(k);
|
||||
if (fields == null) {
|
||||
fields = new ArrayDeque<Field>();
|
||||
ArrayDeque<Field> allFields = new ArrayDeque<>();
|
||||
for (Class<?> c = k; c != null; c = c.getSuperclass())
|
||||
for (Field field : c.getDeclaredFields())
|
||||
if (!Modifier.isStatic(field.getModifiers())
|
||||
&& !field.getType().isPrimitive())
|
||||
fields.add(field);
|
||||
AccessibleObject.setAccessible(
|
||||
fields.toArray(new AccessibleObject[0]), true);
|
||||
classFields.put(k, fields);
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
static Object get(Field field, Object x) {
|
||||
try { return field.get(x); }
|
||||
catch (IllegalAccessException ex) { throw new AssertionError(ex); }
|
||||
}
|
||||
|
||||
Set<Object> retainedObjects(Object x) {
|
||||
ArrayDeque<Object> todo = new ArrayDeque<>() {
|
||||
public void push(Object x) { if (x != null) super.push(x); }};
|
||||
Set<Object> uniqueObjects = Collections.newSetFromMap(
|
||||
new IdentityHashMap<Object, Boolean>());
|
||||
todo.push(x);
|
||||
while (!todo.isEmpty()) {
|
||||
Object y = todo.pop();
|
||||
if (uniqueObjects.contains(y))
|
||||
continue;
|
||||
uniqueObjects.add(y);
|
||||
Class<?> k = y.getClass();
|
||||
if (k.isArray() && !k.getComponentType().isPrimitive()) {
|
||||
for (int i = 0, len = Array.getLength(y); i < len; i++)
|
||||
todo.push(Array.get(y, i));
|
||||
} else {
|
||||
for (Field field : referenceFieldsOf(k))
|
||||
todo.push(get(field, y));
|
||||
}
|
||||
}
|
||||
return uniqueObjects;
|
||||
}
|
||||
|
||||
/** for debugging the retained object graph */
|
||||
void printRetainedObjects(Object x) {
|
||||
for (Object y : retainedObjects(x))
|
||||
System.out.printf("%s : %s%n", y.getClass().getSimpleName(), y);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 4486658
|
||||
* @summary multiple producers and consumers using blocking queues
|
||||
* @library /test/lib
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
public class ProducerConsumerLoops {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
static ExecutorService pool;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
final int maxPairs = (args.length > 0)
|
||||
? Integer.parseInt(args[0])
|
||||
: 5;
|
||||
int iters = 10000;
|
||||
|
||||
pool = Executors.newCachedThreadPool();
|
||||
for (int i = 1; i <= maxPairs; i += (i+1) >>> 1) {
|
||||
// Adjust iterations to limit typical single runs to <= 10 ms;
|
||||
// Notably, fair queues get fewer iters.
|
||||
// Unbounded queues can legitimately OOME if iterations
|
||||
// high enough, but we have a sufficiently low limit here.
|
||||
run(new ArrayBlockingQueue<Integer>(100), i, 500);
|
||||
run(new LinkedBlockingQueue<Integer>(100), i, 1000);
|
||||
run(new LinkedBlockingDeque<Integer>(100), i, 1000);
|
||||
run(new LinkedTransferQueue<Integer>(), i, 1000);
|
||||
run(new PriorityBlockingQueue<Integer>(), i, 1000);
|
||||
run(new SynchronousQueue<Integer>(), i, 400);
|
||||
run(new SynchronousQueue<Integer>(true), i, 300);
|
||||
run(new ArrayBlockingQueue<Integer>(100, true), i, 100);
|
||||
}
|
||||
pool.shutdown();
|
||||
if (! pool.awaitTermination(LONG_DELAY_MS, MILLISECONDS))
|
||||
throw new Error();
|
||||
pool = null;
|
||||
}
|
||||
|
||||
static void run(BlockingQueue<Integer> queue, int pairs, int iters) throws Exception {
|
||||
new ProducerConsumerLoops(queue, pairs, iters).run();
|
||||
}
|
||||
|
||||
final BlockingQueue<Integer> queue;
|
||||
final int pairs;
|
||||
final int iters;
|
||||
final LoopHelpers.BarrierTimer timer = new LoopHelpers.BarrierTimer();
|
||||
final CyclicBarrier barrier;
|
||||
final AtomicInteger checksum = new AtomicInteger(0);
|
||||
Throwable fail;
|
||||
|
||||
ProducerConsumerLoops(BlockingQueue<Integer> queue, int pairs, int iters) {
|
||||
this.queue = queue;
|
||||
this.pairs = pairs;
|
||||
this.iters = iters;
|
||||
this.barrier = new CyclicBarrier(2 * pairs + 1, timer);
|
||||
}
|
||||
|
||||
void run() throws Exception {
|
||||
for (int i = 0; i < pairs; i++) {
|
||||
pool.execute(new Producer());
|
||||
pool.execute(new Consumer());
|
||||
}
|
||||
barrier.await();
|
||||
barrier.await();
|
||||
System.out.printf("%s, pairs=%d: %d ms%n",
|
||||
queue.getClass().getSimpleName(), pairs,
|
||||
NANOSECONDS.toMillis(timer.getTime()));
|
||||
if (checksum.get() != 0) throw new AssertionError("checksum mismatch");
|
||||
if (fail != null) throw new AssertionError(fail);
|
||||
}
|
||||
|
||||
abstract class CheckedRunnable implements Runnable {
|
||||
abstract void realRun() throws Throwable;
|
||||
public final void run() {
|
||||
try {
|
||||
realRun();
|
||||
} catch (Throwable t) {
|
||||
fail = t;
|
||||
t.printStackTrace();
|
||||
throw new AssertionError(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Producer extends CheckedRunnable {
|
||||
void realRun() throws Throwable {
|
||||
barrier.await();
|
||||
int s = 0;
|
||||
int l = hashCode();
|
||||
for (int i = 0; i < iters; i++) {
|
||||
l = LoopHelpers.compute2(l);
|
||||
queue.put(new Integer(l));
|
||||
s += LoopHelpers.compute1(l);
|
||||
}
|
||||
checksum.getAndAdd(s);
|
||||
barrier.await();
|
||||
}
|
||||
}
|
||||
|
||||
class Consumer extends CheckedRunnable {
|
||||
void realRun() throws Throwable {
|
||||
barrier.await();
|
||||
int l = 0;
|
||||
int s = 0;
|
||||
for (int i = 0; i < iters; i++) {
|
||||
l = LoopHelpers.compute1(queue.take().intValue());
|
||||
s += l;
|
||||
}
|
||||
checksum.getAndAdd(-s);
|
||||
barrier.await();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 4486658
|
||||
* @summary check ordering for blocking queues with 1 producer and multiple consumers
|
||||
* @library /test/lib
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
public class SingleProducerMultipleConsumerLoops {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
static ExecutorService pool;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
final int maxConsumers = (args.length > 0)
|
||||
? Integer.parseInt(args[0])
|
||||
: 5;
|
||||
|
||||
pool = Executors.newCachedThreadPool();
|
||||
for (int i = 1; i <= maxConsumers; i += (i+1) >>> 1) {
|
||||
// Adjust iterations to limit typical single runs to <= 10 ms;
|
||||
// Notably, fair queues get fewer iters.
|
||||
// Unbounded queues can legitimately OOME if iterations
|
||||
// high enough, but we have a sufficiently low limit here.
|
||||
run(new ArrayBlockingQueue<Integer>(100), i, 1000);
|
||||
run(new LinkedBlockingQueue<Integer>(100), i, 1000);
|
||||
run(new LinkedBlockingDeque<Integer>(100), i, 1000);
|
||||
run(new LinkedTransferQueue<Integer>(), i, 700);
|
||||
run(new PriorityBlockingQueue<Integer>(), i, 1000);
|
||||
run(new SynchronousQueue<Integer>(), i, 300);
|
||||
run(new SynchronousQueue<Integer>(true), i, 200);
|
||||
run(new ArrayBlockingQueue<Integer>(100, true), i, 100);
|
||||
}
|
||||
pool.shutdown();
|
||||
if (! pool.awaitTermination(LONG_DELAY_MS, MILLISECONDS))
|
||||
throw new Error();
|
||||
pool = null;
|
||||
}
|
||||
|
||||
static void run(BlockingQueue<Integer> queue, int consumers, int iters) throws Exception {
|
||||
new SingleProducerMultipleConsumerLoops(queue, consumers, iters).run();
|
||||
}
|
||||
|
||||
final BlockingQueue<Integer> queue;
|
||||
final int consumers;
|
||||
final int iters;
|
||||
final LoopHelpers.BarrierTimer timer = new LoopHelpers.BarrierTimer();
|
||||
final CyclicBarrier barrier;
|
||||
Throwable fail;
|
||||
|
||||
SingleProducerMultipleConsumerLoops(BlockingQueue<Integer> queue, int consumers, int iters) {
|
||||
this.queue = queue;
|
||||
this.consumers = consumers;
|
||||
this.iters = iters;
|
||||
this.barrier = new CyclicBarrier(consumers + 2, timer);
|
||||
}
|
||||
|
||||
void run() throws Exception {
|
||||
pool.execute(new Producer());
|
||||
for (int i = 0; i < consumers; i++) {
|
||||
pool.execute(new Consumer());
|
||||
}
|
||||
barrier.await();
|
||||
barrier.await();
|
||||
System.out.printf("%s, consumers=%d: %d ms%n",
|
||||
queue.getClass().getSimpleName(), consumers,
|
||||
NANOSECONDS.toMillis(timer.getTime()));
|
||||
if (fail != null) throw new AssertionError(fail);
|
||||
}
|
||||
|
||||
abstract class CheckedRunnable implements Runnable {
|
||||
abstract void realRun() throws Throwable;
|
||||
public final void run() {
|
||||
try {
|
||||
realRun();
|
||||
} catch (Throwable t) {
|
||||
fail = t;
|
||||
t.printStackTrace();
|
||||
throw new AssertionError(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Producer extends CheckedRunnable {
|
||||
volatile int result;
|
||||
void realRun() throws Throwable {
|
||||
barrier.await();
|
||||
for (int i = 0; i < iters * consumers; i++) {
|
||||
queue.put(new Integer(i));
|
||||
}
|
||||
barrier.await();
|
||||
result = 432;
|
||||
}
|
||||
}
|
||||
|
||||
class Consumer extends CheckedRunnable {
|
||||
volatile int result;
|
||||
void realRun() throws Throwable {
|
||||
barrier.await();
|
||||
int l = 0;
|
||||
int s = 0;
|
||||
int last = -1;
|
||||
for (int i = 0; i < iters; i++) {
|
||||
Integer item = queue.take();
|
||||
int v = item.intValue();
|
||||
if (v < last)
|
||||
throw new Error("Out-of-Order transfer");
|
||||
last = v;
|
||||
l = LoopHelpers.compute1(v);
|
||||
s += l;
|
||||
}
|
||||
barrier.await();
|
||||
result = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
885
test/jdk/java/util/concurrent/CompletableFuture/Basic.java
Normal file
885
test/jdk/java/util/concurrent/CompletableFuture/Basic.java
Normal file
|
|
@ -0,0 +1,885 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8005696
|
||||
* @summary Basic tests for CompletableFuture
|
||||
* @library /test/lib
|
||||
* @run main Basic
|
||||
* @run main/othervm -Djava.util.concurrent.ForkJoinPool.common.parallelism=0 Basic
|
||||
* @author Chris Hegarty
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.CompletableFuture.runAsync;
|
||||
import static java.util.concurrent.CompletableFuture.supplyAsync;
|
||||
import static java.util.concurrent.ForkJoinPool.commonPool;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.concurrent.Phaser;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
public class Basic {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
|
||||
static void checkCompletedNormally(CompletableFuture<?> cf, Object value) {
|
||||
checkCompletedNormally(cf, value == null ? null : new Object[] { value });
|
||||
}
|
||||
|
||||
static void checkCompletedNormally(CompletableFuture<?> cf, Object[] values) {
|
||||
try { equalAnyOf(cf.join(), values); } catch (Throwable x) { unexpected(x); }
|
||||
try { equalAnyOf(cf.getNow(null), values); } catch (Throwable x) { unexpected(x); }
|
||||
try { equalAnyOf(cf.get(), values); } catch (Throwable x) { unexpected(x); }
|
||||
try { equalAnyOf(cf.get(0L, SECONDS), values); } catch (Throwable x) { unexpected(x); }
|
||||
check(cf.isDone(), "Expected isDone to be true, got:" + cf);
|
||||
check(!cf.isCompletedExceptionally(), "Expected isCompletedExceptionally to return false");
|
||||
check(!cf.isCancelled(), "Expected isCancelled to be false");
|
||||
check(!cf.cancel(true), "Expected cancel to return false");
|
||||
check(cf.toString().matches(".*\\[.*Completed normally.*\\]"));
|
||||
check(cf.complete(null) == false, "Expected complete() to fail");
|
||||
check(cf.completeExceptionally(new Throwable()) == false,
|
||||
"Expected completeExceptionally() to fail");
|
||||
}
|
||||
|
||||
static <T> void checkCompletedExceptionally(CompletableFuture<T> cf)
|
||||
throws Exception
|
||||
{
|
||||
checkCompletedExceptionally(cf, false);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T> void checkCompletedExceptionally(CompletableFuture<T> cf, boolean cancelled)
|
||||
throws Exception
|
||||
{
|
||||
try { cf.join(); fail("Excepted exception to be thrown"); }
|
||||
catch (CompletionException x) { if (cancelled) fail(); else pass(); }
|
||||
catch (CancellationException x) { if (cancelled) pass(); else fail(); }
|
||||
try { cf.getNow(null); fail("Excepted exception to be thrown"); }
|
||||
catch (CompletionException x) { if (cancelled) fail(); else pass(); }
|
||||
catch (CancellationException x) { if (cancelled) pass(); else fail(); }
|
||||
try { cf.get(); fail("Excepted exception to be thrown");}
|
||||
catch (CancellationException x) { if (cancelled) pass(); else fail(); }
|
||||
catch (ExecutionException x) { if (cancelled) check(x.getCause() instanceof CancellationException); else pass(); }
|
||||
try { cf.get(0L, SECONDS); fail("Excepted exception to be thrown");}
|
||||
catch (CancellationException x) { if (cancelled) pass(); else fail(); }
|
||||
catch (ExecutionException x) { if (cancelled) check(x.getCause() instanceof CancellationException); else pass(); }
|
||||
check(cf.isDone(), "Expected isDone to be true, got:" + cf);
|
||||
check(cf.isCompletedExceptionally(), "Expected isCompletedExceptionally");
|
||||
check(cf.isCancelled() == cancelled, "Expected isCancelled: " + cancelled + ", got:" + cf.isCancelled());
|
||||
check(cf.cancel(true) == cancelled, "Expected cancel: " + cancelled + ", got:" + cf.cancel(true));
|
||||
check(cf.toString().matches(".*\\[.*Completed exceptionally.*\\]")); // ## TODO: 'E'xceptionally
|
||||
check(cf.complete((T)new Object()) == false, "Expected complete() to fail");
|
||||
check(cf.completeExceptionally(new Throwable()) == false,
|
||||
"Expected completeExceptionally() to fail, already completed");
|
||||
}
|
||||
|
||||
private static void realMain(String[] args) throws Throwable {
|
||||
ExecutorService pool = Executors.newFixedThreadPool(2);
|
||||
try {
|
||||
test(pool);
|
||||
} finally {
|
||||
pool.shutdown();
|
||||
if (! pool.awaitTermination(LONG_DELAY_MS, MILLISECONDS))
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
static AtomicInteger atomicInt = new AtomicInteger(0);
|
||||
|
||||
private static void test(ExecutorService executor) throws Throwable {
|
||||
|
||||
Thread.currentThread().setName("mainThread");
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// supplyAsync tests
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
CompletableFuture<String> cf = supplyAsync(() -> "a test string");
|
||||
checkCompletedNormally(cf, cf.join());
|
||||
cf = supplyAsync(() -> "a test string", commonPool());
|
||||
checkCompletedNormally(cf, cf.join());
|
||||
cf = supplyAsync(() -> "a test string", executor);
|
||||
checkCompletedNormally(cf, cf.join());
|
||||
cf = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
checkCompletedExceptionally(cf);
|
||||
cf = supplyAsync(() -> { throw new RuntimeException(); }, commonPool());
|
||||
checkCompletedExceptionally(cf);
|
||||
cf = supplyAsync(() -> { throw new RuntimeException(); }, executor);
|
||||
checkCompletedExceptionally(cf);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// runAsync tests
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
CompletableFuture<Void> cf = runAsync(() -> {});
|
||||
checkCompletedNormally(cf, cf.join());
|
||||
cf = runAsync(() -> {}, commonPool());
|
||||
checkCompletedNormally(cf, cf.join());
|
||||
cf = runAsync(() -> {}, executor);
|
||||
checkCompletedNormally(cf, cf.join());
|
||||
cf = runAsync(() -> { throw new RuntimeException(); });
|
||||
checkCompletedExceptionally(cf);
|
||||
cf = runAsync(() -> { throw new RuntimeException(); }, commonPool());
|
||||
checkCompletedExceptionally(cf);
|
||||
cf = runAsync(() -> { throw new RuntimeException(); }, executor);
|
||||
checkCompletedExceptionally(cf);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// explicit completion
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
final Phaser phaser = new Phaser(1);
|
||||
final int phase = phaser.getPhase();
|
||||
CompletableFuture<Integer> cf;
|
||||
cf = supplyAsync(() -> { phaser.awaitAdvance(phase); return 1; });
|
||||
cf.complete(2);
|
||||
phaser.arrive();
|
||||
checkCompletedNormally(cf, 2);
|
||||
|
||||
cf = supplyAsync(() -> { phaser.awaitAdvance(phase+1); return 1; });
|
||||
cf.completeExceptionally(new Throwable());
|
||||
phaser.arrive();
|
||||
checkCompletedExceptionally(cf);
|
||||
|
||||
cf = supplyAsync(() -> { phaser.awaitAdvance(phase+2); return 1; });
|
||||
cf.cancel(true);
|
||||
phaser.arrive();
|
||||
checkCompletedExceptionally(cf, true);
|
||||
|
||||
cf = supplyAsync(() -> { phaser.awaitAdvance(phase+3); return 1; });
|
||||
check(cf.getNow(2) == 2);
|
||||
phaser.arrive();
|
||||
checkCompletedNormally(cf, 1);
|
||||
check(cf.getNow(2) == 1);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// thenApplyXXX tests
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
CompletableFuture<Integer> cf2;
|
||||
CompletableFuture<String> cf1 = supplyAsync(() -> "a test string");
|
||||
cf2 = cf1.thenApply(x -> x.equals("a test string") ? 1 : 0);
|
||||
checkCompletedNormally(cf1, "a test string");
|
||||
checkCompletedNormally(cf2, 1);
|
||||
|
||||
cf1 = supplyAsync(() -> "a test string");
|
||||
cf2 = cf1.thenApplyAsync(x -> x.equals("a test string") ? 1 : 0);
|
||||
checkCompletedNormally(cf1, "a test string");
|
||||
checkCompletedNormally(cf2, 1);
|
||||
|
||||
cf1 = supplyAsync(() -> "a test string");
|
||||
cf2 = cf1.thenApplyAsync(x -> x.equals("a test string") ? 1 : 0, executor);
|
||||
checkCompletedNormally(cf1, "a test string");
|
||||
checkCompletedNormally(cf2, 1);
|
||||
|
||||
cf1 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf2 = cf1.thenApply(x -> 0);
|
||||
checkCompletedExceptionally(cf1);
|
||||
checkCompletedExceptionally(cf2);
|
||||
|
||||
cf1 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf2 = cf1.thenApplyAsync(x -> 0);
|
||||
checkCompletedExceptionally(cf1);
|
||||
checkCompletedExceptionally(cf2);
|
||||
|
||||
cf1 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf2 = cf1.thenApplyAsync(x -> 0, executor);
|
||||
checkCompletedExceptionally(cf1);
|
||||
checkCompletedExceptionally(cf2);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// thenAcceptXXX tests
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
CompletableFuture<Void> cf2;
|
||||
int before = atomicInt.get();
|
||||
CompletableFuture<String> cf1 = supplyAsync(() -> "a test string");
|
||||
cf2 = cf1.thenAccept(x -> { if (x.equals("a test string")) { atomicInt.incrementAndGet(); return; } throw new RuntimeException(); });
|
||||
checkCompletedNormally(cf1, "a test string");
|
||||
checkCompletedNormally(cf2, null);
|
||||
check(atomicInt.get() == (before + 1));
|
||||
|
||||
before = atomicInt.get();
|
||||
cf1 = supplyAsync(() -> "a test string");
|
||||
cf2 = cf1.thenAcceptAsync(x -> { if (x.equals("a test string")) { atomicInt.incrementAndGet(); return; } throw new RuntimeException(); });
|
||||
checkCompletedNormally(cf1, "a test string");
|
||||
checkCompletedNormally(cf2, null);
|
||||
check(atomicInt.get() == (before + 1));
|
||||
|
||||
before = atomicInt.get();
|
||||
cf1 = supplyAsync(() -> "a test string");
|
||||
cf2 = cf1.thenAcceptAsync(x -> { if (x.equals("a test string")) { atomicInt.incrementAndGet(); return; } throw new RuntimeException(); }, executor);
|
||||
checkCompletedNormally(cf1, "a test string");
|
||||
checkCompletedNormally(cf2, null);
|
||||
check(atomicInt.get() == (before + 1));
|
||||
|
||||
before = atomicInt.get();
|
||||
cf1 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf2 = cf1.thenAccept(x -> atomicInt.incrementAndGet());
|
||||
checkCompletedExceptionally(cf1);
|
||||
checkCompletedExceptionally(cf2);
|
||||
check(atomicInt.get() == before);
|
||||
|
||||
cf1 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf2 = cf1.thenAcceptAsync(x -> atomicInt.incrementAndGet());
|
||||
checkCompletedExceptionally(cf1);
|
||||
checkCompletedExceptionally(cf2);
|
||||
check(atomicInt.get() == before);
|
||||
|
||||
cf1 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf2 = cf1.thenAcceptAsync(x -> atomicInt.incrementAndGet(), executor );
|
||||
checkCompletedExceptionally(cf1);
|
||||
checkCompletedExceptionally(cf2);
|
||||
check(atomicInt.get() == before);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// thenRunXXX tests
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
CompletableFuture<Void> cf2;
|
||||
int before = atomicInt.get();
|
||||
CompletableFuture<String> cf1 = supplyAsync(() -> "a test string");
|
||||
cf2 = cf1.thenRun(() -> atomicInt.incrementAndGet());
|
||||
checkCompletedNormally(cf1, "a test string");
|
||||
checkCompletedNormally(cf2, null);
|
||||
check(atomicInt.get() == (before + 1));
|
||||
|
||||
before = atomicInt.get();
|
||||
cf1 = supplyAsync(() -> "a test string");
|
||||
cf2 = cf1.thenRunAsync(() -> atomicInt.incrementAndGet());
|
||||
checkCompletedNormally(cf1, "a test string");
|
||||
checkCompletedNormally(cf2, null);
|
||||
check(atomicInt.get() == (before + 1));
|
||||
|
||||
before = atomicInt.get();
|
||||
cf1 = supplyAsync(() -> "a test string");
|
||||
cf2 = cf1.thenRunAsync(() -> atomicInt.incrementAndGet(), executor);
|
||||
checkCompletedNormally(cf1, "a test string");
|
||||
checkCompletedNormally(cf2, null);
|
||||
check(atomicInt.get() == (before + 1));
|
||||
|
||||
before = atomicInt.get();
|
||||
cf1 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf2 = cf1.thenRun(() -> atomicInt.incrementAndGet());
|
||||
checkCompletedExceptionally(cf1);
|
||||
checkCompletedExceptionally(cf2);
|
||||
check(atomicInt.get() == before);
|
||||
|
||||
cf1 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf2 = cf1.thenRunAsync(() -> atomicInt.incrementAndGet());
|
||||
checkCompletedExceptionally(cf1);
|
||||
checkCompletedExceptionally(cf2);
|
||||
check(atomicInt.get() == before);
|
||||
|
||||
cf1 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf2 = cf1.thenRunAsync(() -> atomicInt.incrementAndGet(), executor);
|
||||
checkCompletedExceptionally(cf1);
|
||||
checkCompletedExceptionally(cf2);
|
||||
check(atomicInt.get() == before);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// thenCombineXXX tests
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
CompletableFuture<Integer> cf3;
|
||||
CompletableFuture<Integer> cf1 = supplyAsync(() -> 1);
|
||||
CompletableFuture<Integer> cf2 = supplyAsync(() -> 1);
|
||||
cf3 = cf1.thenCombine(cf2, (x, y) -> x + y);
|
||||
checkCompletedNormally(cf1, 1);
|
||||
checkCompletedNormally(cf2, 1);
|
||||
checkCompletedNormally(cf3, 2);
|
||||
|
||||
cf1 = supplyAsync(() -> 1);
|
||||
cf2 = supplyAsync(() -> 1);
|
||||
cf3 = cf1.thenCombineAsync(cf2, (x, y) -> x + y);
|
||||
checkCompletedNormally(cf1, 1);
|
||||
checkCompletedNormally(cf2, 1);
|
||||
checkCompletedNormally(cf3, 2);
|
||||
|
||||
cf1 = supplyAsync(() -> 1);
|
||||
cf2 = supplyAsync(() -> 1);
|
||||
cf3 = cf1.thenCombineAsync(cf2, (x, y) -> x + y, executor);
|
||||
checkCompletedNormally(cf1, 1);
|
||||
checkCompletedNormally(cf2, 1);
|
||||
checkCompletedNormally(cf3, 2);
|
||||
|
||||
cf1 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf2 = supplyAsync(() -> 1);
|
||||
cf3 = cf1.thenCombine(cf2, (x, y) -> 0);
|
||||
checkCompletedExceptionally(cf1);
|
||||
checkCompletedNormally(cf2, 1);
|
||||
checkCompletedExceptionally(cf3);
|
||||
|
||||
cf1 = supplyAsync(() -> 1);
|
||||
cf2 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf3 = cf1.thenCombineAsync(cf2, (x, y) -> 0);
|
||||
checkCompletedNormally(cf1, 1);
|
||||
checkCompletedExceptionally(cf2);
|
||||
checkCompletedExceptionally(cf3);
|
||||
|
||||
cf1 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf2 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf3 = cf1.thenCombineAsync(cf2, (x, y) -> 0, executor);
|
||||
checkCompletedExceptionally(cf1);
|
||||
checkCompletedExceptionally(cf2);
|
||||
checkCompletedExceptionally(cf3);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// thenAcceptBothXXX tests
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
CompletableFuture<Void> cf3;
|
||||
int before = atomicInt.get();
|
||||
CompletableFuture<Integer> cf1 = supplyAsync(() -> 1);
|
||||
CompletableFuture<Integer> cf2 = supplyAsync(() -> 1);
|
||||
cf3 = cf1.thenAcceptBoth(cf2, (x, y) -> { check(x + y == 2); atomicInt.incrementAndGet(); });
|
||||
checkCompletedNormally(cf1, 1);
|
||||
checkCompletedNormally(cf2, 1);
|
||||
checkCompletedNormally(cf3, null);
|
||||
check(atomicInt.get() == (before + 1));
|
||||
|
||||
before = atomicInt.get();
|
||||
cf1 = supplyAsync(() -> 1);
|
||||
cf2 = supplyAsync(() -> 1);
|
||||
cf3 = cf1.thenAcceptBothAsync(cf2, (x, y) -> { check(x + y == 2); atomicInt.incrementAndGet(); });
|
||||
checkCompletedNormally(cf1, 1);
|
||||
checkCompletedNormally(cf2, 1);
|
||||
checkCompletedNormally(cf3, null);
|
||||
check(atomicInt.get() == (before + 1));
|
||||
|
||||
before = atomicInt.get();
|
||||
cf1 = supplyAsync(() -> 1);
|
||||
cf2 = supplyAsync(() -> 1);
|
||||
cf3 = cf1.thenAcceptBothAsync(cf2, (x, y) -> { check(x + y == 2); atomicInt.incrementAndGet(); }, executor);
|
||||
checkCompletedNormally(cf1, 1);
|
||||
checkCompletedNormally(cf2, 1);
|
||||
checkCompletedNormally(cf3, null);
|
||||
check(atomicInt.get() == (before + 1));
|
||||
|
||||
before = atomicInt.get();
|
||||
cf1 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf2 = supplyAsync(() -> 1);
|
||||
cf3 = cf1.thenAcceptBoth(cf2, (x, y) -> atomicInt.incrementAndGet());
|
||||
checkCompletedExceptionally(cf1);
|
||||
checkCompletedNormally(cf2, 1);
|
||||
checkCompletedExceptionally(cf3);
|
||||
check(atomicInt.get() == before);
|
||||
|
||||
cf1 = supplyAsync(() -> 1);
|
||||
cf2 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf3 = cf1.thenAcceptBothAsync(cf2, (x, y) -> atomicInt.incrementAndGet());
|
||||
checkCompletedNormally(cf1, 1);
|
||||
checkCompletedExceptionally(cf2);
|
||||
checkCompletedExceptionally(cf3);
|
||||
check(atomicInt.get() == before);
|
||||
|
||||
cf1 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf2 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf3 = cf1.thenAcceptBothAsync(cf2, (x, y) -> atomicInt.incrementAndGet(), executor);
|
||||
checkCompletedExceptionally(cf1);
|
||||
checkCompletedExceptionally(cf2);
|
||||
checkCompletedExceptionally(cf3);
|
||||
check(atomicInt.get() == before);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// runAfterBothXXX tests
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
CompletableFuture<Void> cf3;
|
||||
int before = atomicInt.get();
|
||||
CompletableFuture<Integer> cf1 = supplyAsync(() -> 1);
|
||||
CompletableFuture<Integer> cf2 = supplyAsync(() -> 1);
|
||||
cf3 = cf1.runAfterBoth(cf2, () -> { check(cf1.isDone()); check(cf2.isDone()); atomicInt.incrementAndGet(); });
|
||||
checkCompletedNormally(cf1, 1);
|
||||
checkCompletedNormally(cf2, 1);
|
||||
checkCompletedNormally(cf3, null);
|
||||
check(atomicInt.get() == (before + 1));
|
||||
|
||||
before = atomicInt.get();
|
||||
CompletableFuture<Integer> cfa = supplyAsync(() -> 1);
|
||||
CompletableFuture<Integer> cfb = supplyAsync(() -> 1);
|
||||
cf3 = cfa.runAfterBothAsync(cfb, () -> { check(cfa.isDone()); check(cfb.isDone()); atomicInt.incrementAndGet(); });
|
||||
checkCompletedNormally(cfa, 1);
|
||||
checkCompletedNormally(cfb, 1);
|
||||
checkCompletedNormally(cf3, null);
|
||||
check(atomicInt.get() == (before + 1));
|
||||
|
||||
before = atomicInt.get();
|
||||
CompletableFuture<Integer> cfx = supplyAsync(() -> 1);
|
||||
CompletableFuture<Integer> cfy = supplyAsync(() -> 1);
|
||||
cf3 = cfy.runAfterBothAsync(cfx, () -> { check(cfx.isDone()); check(cfy.isDone()); atomicInt.incrementAndGet(); }, executor);
|
||||
checkCompletedNormally(cfx, 1);
|
||||
checkCompletedNormally(cfy, 1);
|
||||
checkCompletedNormally(cf3, null);
|
||||
check(atomicInt.get() == (before + 1));
|
||||
|
||||
before = atomicInt.get();
|
||||
CompletableFuture<Integer> cf4 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
CompletableFuture<Integer> cf5 = supplyAsync(() -> 1);
|
||||
cf3 = cf5.runAfterBothAsync(cf4, () -> atomicInt.incrementAndGet(), executor);
|
||||
checkCompletedExceptionally(cf4);
|
||||
checkCompletedNormally(cf5, 1);
|
||||
checkCompletedExceptionally(cf3);
|
||||
check(atomicInt.get() == before);
|
||||
|
||||
before = atomicInt.get();
|
||||
cf4 = supplyAsync(() -> 1);
|
||||
cf5 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf3 = cf5.runAfterBothAsync(cf4, () -> atomicInt.incrementAndGet());
|
||||
checkCompletedNormally(cf4, 1);
|
||||
checkCompletedExceptionally(cf5);
|
||||
checkCompletedExceptionally(cf3);
|
||||
check(atomicInt.get() == before);
|
||||
|
||||
before = atomicInt.get();
|
||||
cf4 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf5 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf3 = cf5.runAfterBoth(cf4, () -> atomicInt.incrementAndGet());
|
||||
checkCompletedExceptionally(cf4);
|
||||
checkCompletedExceptionally(cf5);
|
||||
checkCompletedExceptionally(cf3);
|
||||
check(atomicInt.get() == before);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// applyToEitherXXX tests
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
CompletableFuture<Integer> cf3;
|
||||
CompletableFuture<Integer> cf1 = supplyAsync(() -> 1);
|
||||
CompletableFuture<Integer> cf2 = supplyAsync(() -> 2);
|
||||
cf3 = cf1.applyToEither(cf2, x -> { check(x == 1 || x == 2); return x; });
|
||||
checkCompletedNormally(cf3, new Object[] {1, 2});
|
||||
check(cf1.isDone() || cf2.isDone());
|
||||
|
||||
cf1 = supplyAsync(() -> 1);
|
||||
cf2 = supplyAsync(() -> 2);
|
||||
cf3 = cf1.applyToEitherAsync(cf2, x -> { check(x == 1 || x == 2); return x; });
|
||||
checkCompletedNormally(cf3, new Object[] {1, 2});
|
||||
check(cf1.isDone() || cf2.isDone());
|
||||
|
||||
cf1 = supplyAsync(() -> 1);
|
||||
cf2 = supplyAsync(() -> 2);
|
||||
cf3 = cf1.applyToEitherAsync(cf2, x -> { check(x == 1 || x == 2); return x; }, executor);
|
||||
checkCompletedNormally(cf3, new Object[] {1, 2});
|
||||
check(cf1.isDone() || cf2.isDone());
|
||||
|
||||
cf1 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf2 = supplyAsync(() -> 2);
|
||||
cf3 = cf1.applyToEither(cf2, x -> { check(x == 2); return x; });
|
||||
try { check(cf3.join() == 2); } catch (CompletionException x) { pass(); }
|
||||
check(cf3.isDone());
|
||||
check(cf1.isDone() || cf2.isDone());
|
||||
|
||||
cf1 = supplyAsync(() -> 1);
|
||||
cf2 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf3 = cf1.applyToEitherAsync(cf2, x -> { check(x == 1); return x; });
|
||||
try { check(cf3.join() == 1); } catch (CompletionException x) { pass(); }
|
||||
check(cf3.isDone());
|
||||
check(cf1.isDone() || cf2.isDone());
|
||||
|
||||
cf1 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf2 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf3 = cf1.applyToEitherAsync(cf2, x -> { fail(); return x; });
|
||||
checkCompletedExceptionally(cf3);
|
||||
check(cf1.isDone() || cf2.isDone());
|
||||
|
||||
final Phaser cf3Done = new Phaser(2);
|
||||
cf1 = supplyAsync(() -> { cf3Done.arriveAndAwaitAdvance(); return 1; });
|
||||
cf2 = supplyAsync(() -> 2);
|
||||
cf3 = cf1.applyToEither(cf2, x -> { check(x == 2); return x; });
|
||||
checkCompletedNormally(cf3, 2);
|
||||
checkCompletedNormally(cf2, 2);
|
||||
check(!cf1.isDone());
|
||||
cf3Done.arrive();
|
||||
checkCompletedNormally(cf1, 1);
|
||||
checkCompletedNormally(cf3, 2);
|
||||
|
||||
cf1 = supplyAsync(() -> 1);
|
||||
cf2 = supplyAsync(() -> { cf3Done.arriveAndAwaitAdvance(); return 2; });
|
||||
cf3 = cf1.applyToEitherAsync(cf2, x -> { check(x == 1); return x; });
|
||||
checkCompletedNormally(cf3, 1);
|
||||
checkCompletedNormally(cf1, 1);
|
||||
check(!cf2.isDone());
|
||||
cf3Done.arrive();
|
||||
checkCompletedNormally(cf2, 2);
|
||||
checkCompletedNormally(cf3, 1);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// acceptEitherXXX tests
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
CompletableFuture<Void> cf3;
|
||||
int before = atomicInt.get();
|
||||
CompletableFuture<Integer> cf1 = supplyAsync(() -> 1);
|
||||
CompletableFuture<Integer> cf2 = supplyAsync(() -> 2);
|
||||
cf3 = cf1.acceptEither(cf2, x -> { check(x == 1 || x == 2); atomicInt.incrementAndGet(); });
|
||||
checkCompletedNormally(cf3, null);
|
||||
check(cf1.isDone() || cf2.isDone());
|
||||
check(atomicInt.get() == (before + 1));
|
||||
|
||||
before = atomicInt.get();
|
||||
cf1 = supplyAsync(() -> 1);
|
||||
cf2 = supplyAsync(() -> 2);
|
||||
cf3 = cf1.acceptEitherAsync(cf2, x -> { check(x == 1 || x == 2); atomicInt.incrementAndGet(); });
|
||||
checkCompletedNormally(cf3, null);
|
||||
check(cf1.isDone() || cf2.isDone());
|
||||
check(atomicInt.get() == (before + 1));
|
||||
|
||||
before = atomicInt.get();
|
||||
cf1 = supplyAsync(() -> 1);
|
||||
cf2 = supplyAsync(() -> 2);
|
||||
cf3 = cf2.acceptEitherAsync(cf1, x -> { check(x == 1 || x == 2); atomicInt.incrementAndGet(); }, executor);
|
||||
checkCompletedNormally(cf3, null);
|
||||
check(cf1.isDone() || cf2.isDone());
|
||||
check(atomicInt.get() == (before + 1));
|
||||
|
||||
cf1 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf2 = supplyAsync(() -> 2);
|
||||
cf3 = cf2.acceptEitherAsync(cf1, x -> { check(x == 2); }, executor);
|
||||
try { check(cf3.join() == null); } catch (CompletionException x) { pass(); }
|
||||
check(cf3.isDone());
|
||||
check(cf1.isDone() || cf2.isDone());
|
||||
|
||||
cf1 = supplyAsync(() -> 1);
|
||||
cf2 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf3 = cf2.acceptEitherAsync(cf1, x -> { check(x == 1); });
|
||||
try { check(cf3.join() == null); } catch (CompletionException x) { pass(); }
|
||||
check(cf3.isDone());
|
||||
check(cf1.isDone() || cf2.isDone());
|
||||
|
||||
cf1 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf2 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf3 = cf2.acceptEitherAsync(cf1, x -> { fail(); });
|
||||
checkCompletedExceptionally(cf3);
|
||||
check(cf1.isDone() || cf2.isDone());
|
||||
|
||||
final Phaser cf3Done = new Phaser(2);
|
||||
cf1 = supplyAsync(() -> { cf3Done.arriveAndAwaitAdvance(); return 1; });
|
||||
cf2 = supplyAsync(() -> 2);
|
||||
cf3 = cf1.acceptEither(cf2, x -> { check(x == 2); });
|
||||
checkCompletedNormally(cf3, null);
|
||||
checkCompletedNormally(cf2, 2);
|
||||
check(!cf1.isDone());
|
||||
cf3Done.arrive();
|
||||
checkCompletedNormally(cf1, 1);
|
||||
checkCompletedNormally(cf3, null);
|
||||
|
||||
cf1 = supplyAsync(() -> 1);
|
||||
cf2 = supplyAsync(() -> { cf3Done.arriveAndAwaitAdvance(); return 2; });
|
||||
cf3 = cf1.acceptEitherAsync(cf2, x -> { check(x == 1); });
|
||||
checkCompletedNormally(cf3, null);
|
||||
checkCompletedNormally(cf1, 1);
|
||||
check(!cf2.isDone());
|
||||
cf3Done.arrive();
|
||||
checkCompletedNormally(cf2, 2);
|
||||
checkCompletedNormally(cf3, null);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// runAfterEitherXXX tests
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
CompletableFuture<Void> cf3;
|
||||
int before = atomicInt.get();
|
||||
CompletableFuture<Void> cf1 = runAsync(() -> {});
|
||||
CompletableFuture<Void> cf2 = runAsync(() -> {});
|
||||
cf3 = cf1.runAfterEither(cf2, () -> atomicInt.incrementAndGet());
|
||||
checkCompletedNormally(cf3, null);
|
||||
check(cf1.isDone() || cf2.isDone());
|
||||
check(atomicInt.get() == (before + 1));
|
||||
|
||||
before = atomicInt.get();
|
||||
cf1 = runAsync(() -> {});
|
||||
cf2 = runAsync(() -> {});
|
||||
cf3 = cf1.runAfterEitherAsync(cf2, () -> atomicInt.incrementAndGet());
|
||||
checkCompletedNormally(cf3, null);
|
||||
check(cf1.isDone() || cf2.isDone());
|
||||
check(atomicInt.get() == (before + 1));
|
||||
|
||||
before = atomicInt.get();
|
||||
cf1 = runAsync(() -> {});
|
||||
cf2 = runAsync(() -> {});
|
||||
cf3 = cf2.runAfterEitherAsync(cf1, () -> atomicInt.incrementAndGet(), executor);
|
||||
checkCompletedNormally(cf3, null);
|
||||
check(cf1.isDone() || cf2.isDone());
|
||||
check(atomicInt.get() == (before + 1));
|
||||
|
||||
before = atomicInt.get();
|
||||
cf1 = runAsync(() -> { throw new RuntimeException(); });
|
||||
cf2 = runAsync(() -> {});
|
||||
cf3 = cf2.runAfterEither(cf1, () -> atomicInt.incrementAndGet());
|
||||
try {
|
||||
check(cf3.join() == null);
|
||||
check(atomicInt.get() == (before + 1));
|
||||
} catch (CompletionException x) { pass(); }
|
||||
check(cf3.isDone());
|
||||
check(cf1.isDone() || cf2.isDone());
|
||||
|
||||
before = atomicInt.get();
|
||||
cf1 = runAsync(() -> {});
|
||||
cf2 = runAsync(() -> { throw new RuntimeException(); });
|
||||
cf3 = cf1.runAfterEitherAsync(cf2, () -> atomicInt.incrementAndGet());
|
||||
try {
|
||||
check(cf3.join() == null);
|
||||
check(atomicInt.get() == (before + 1));
|
||||
} catch (CompletionException x) { pass(); }
|
||||
check(cf3.isDone());
|
||||
check(cf1.isDone() || cf2.isDone());
|
||||
|
||||
before = atomicInt.get();
|
||||
cf1 = runAsync(() -> { throw new RuntimeException(); });
|
||||
cf2 = runAsync(() -> { throw new RuntimeException(); });
|
||||
cf3 = cf2.runAfterEitherAsync(cf1, () -> atomicInt.incrementAndGet(), executor);
|
||||
checkCompletedExceptionally(cf3);
|
||||
check(cf1.isDone() || cf2.isDone());
|
||||
check(atomicInt.get() == before);
|
||||
|
||||
final Phaser cf3Done = new Phaser(2);
|
||||
before = atomicInt.get();
|
||||
cf1 = runAsync(() -> cf3Done.arriveAndAwaitAdvance());
|
||||
cf2 = runAsync(() -> {});
|
||||
cf3 = cf1.runAfterEither(cf2, () -> atomicInt.incrementAndGet());
|
||||
checkCompletedNormally(cf3, null);
|
||||
checkCompletedNormally(cf2, null);
|
||||
check(!cf1.isDone());
|
||||
check(atomicInt.get() == (before + 1));
|
||||
cf3Done.arrive();
|
||||
checkCompletedNormally(cf1, null);
|
||||
checkCompletedNormally(cf3, null);
|
||||
|
||||
before = atomicInt.get();
|
||||
cf1 = runAsync(() -> {});
|
||||
cf2 = runAsync(() -> cf3Done.arriveAndAwaitAdvance());
|
||||
cf3 = cf1.runAfterEitherAsync(cf2, () -> atomicInt.incrementAndGet());
|
||||
checkCompletedNormally(cf3, null);
|
||||
checkCompletedNormally(cf1, null);
|
||||
check(!cf2.isDone());
|
||||
check(atomicInt.get() == (before + 1));
|
||||
cf3Done.arrive();
|
||||
checkCompletedNormally(cf2, null);
|
||||
checkCompletedNormally(cf3, null);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// thenComposeXXX tests
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
CompletableFuture<Integer> cf2;
|
||||
CompletableFuture<Integer> cf1 = supplyAsync(() -> 1);
|
||||
cf2 = cf1.thenCompose(x -> { check(x == 1); return CompletableFuture.completedFuture(2); });
|
||||
checkCompletedNormally(cf1, 1);
|
||||
checkCompletedNormally(cf2, 2);
|
||||
|
||||
cf1 = supplyAsync(() -> 1);
|
||||
cf2 = cf1.thenComposeAsync(x -> { check(x == 1); return CompletableFuture.completedFuture(2); });
|
||||
checkCompletedNormally(cf1, 1);
|
||||
checkCompletedNormally(cf2, 2);
|
||||
|
||||
cf1 = supplyAsync(() -> 1);
|
||||
cf2 = cf1.thenComposeAsync(x -> { check(x == 1); return CompletableFuture.completedFuture(2); }, executor);
|
||||
checkCompletedNormally(cf1, 1);
|
||||
checkCompletedNormally(cf2, 2);
|
||||
|
||||
int before = atomicInt.get();
|
||||
cf1 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf2 = cf1.thenCompose(x -> { atomicInt.incrementAndGet(); return CompletableFuture.completedFuture(2); });
|
||||
checkCompletedExceptionally(cf1);
|
||||
checkCompletedExceptionally(cf2);
|
||||
check(atomicInt.get() == before);
|
||||
|
||||
cf1 = supplyAsync(() -> { throw new RuntimeException(); });
|
||||
cf2 = cf1.thenComposeAsync(x -> { atomicInt.incrementAndGet(); return CompletableFuture.completedFuture(2); });
|
||||
checkCompletedExceptionally(cf1);
|
||||
checkCompletedExceptionally(cf2);
|
||||
check(atomicInt.get() == before);
|
||||
|
||||
cf1 = supplyAsync(() -> 1);
|
||||
cf2 = cf1.thenComposeAsync(x -> { throw new RuntimeException(); }, executor);
|
||||
checkCompletedNormally(cf1, 1);
|
||||
checkCompletedExceptionally(cf2);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// anyOf tests
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
CompletableFuture<Object> cf3;
|
||||
for (int k=0; k < 10; k++){
|
||||
CompletableFuture<Integer> cf1 = supplyAsync(() -> 1);
|
||||
CompletableFuture<Integer> cf2 = supplyAsync(() -> 2);
|
||||
cf3 = CompletableFuture.anyOf(cf1, cf2);
|
||||
checkCompletedNormally(cf3, new Object[] {1, 2});
|
||||
check(cf1.isDone() || cf2.isDone());
|
||||
}
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// allOf tests
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
CompletableFuture<?> cf3;
|
||||
for (int k=0; k < 10; k++){
|
||||
CompletableFuture<Integer>[] cfs = (CompletableFuture<Integer>[])
|
||||
Array.newInstance(CompletableFuture.class, 10);
|
||||
for (int j=0; j < 10; j++) {
|
||||
final int v = j;
|
||||
cfs[j] = supplyAsync(() -> v);
|
||||
}
|
||||
cf3 = CompletableFuture.allOf(cfs);
|
||||
for (int j=0; j < 10; j++)
|
||||
checkCompletedNormally(cfs[j], j);
|
||||
checkCompletedNormally(cf3, null);
|
||||
}
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// exceptionally tests
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
CompletableFuture<Integer> cf2;
|
||||
CompletableFuture<Integer> cf1 = supplyAsync(() -> 1);
|
||||
cf2 = cf1.exceptionally(t -> { fail("function should never be called"); return 2;});
|
||||
checkCompletedNormally(cf1, 1);
|
||||
checkCompletedNormally(cf2, 1);
|
||||
|
||||
final RuntimeException t = new RuntimeException();
|
||||
cf1 = supplyAsync(() -> { throw t; });
|
||||
cf2 = cf1.exceptionally(x -> { check(x.getCause() == t); return 2;});
|
||||
checkCompletedExceptionally(cf1);
|
||||
checkCompletedNormally(cf2, 2);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// handle tests
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
CompletableFuture<Integer> cf2;
|
||||
CompletableFuture<Integer> cf1 = supplyAsync(() -> 1);
|
||||
cf2 = cf1.handle((x,t) -> x+1);
|
||||
checkCompletedNormally(cf1, 1);
|
||||
checkCompletedNormally(cf2, 2);
|
||||
|
||||
final RuntimeException ex = new RuntimeException();
|
||||
cf1 = supplyAsync(() -> { throw ex; });
|
||||
cf2 = cf1.handle((x,t) -> { check(t.getCause() == ex); return 2;});
|
||||
checkCompletedExceptionally(cf1);
|
||||
checkCompletedNormally(cf2, 2);
|
||||
|
||||
cf1 = supplyAsync(() -> 1);
|
||||
cf2 = cf1.handleAsync((x,t) -> x+1);
|
||||
checkCompletedNormally(cf1, 1);
|
||||
checkCompletedNormally(cf2, 2);
|
||||
|
||||
cf1 = supplyAsync(() -> { throw ex; });
|
||||
cf2 = cf1.handleAsync((x,t) -> { check(t.getCause() == ex); return 2;});
|
||||
checkCompletedExceptionally(cf1);
|
||||
checkCompletedNormally(cf2, 2);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// whenComplete tests
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
AtomicInteger count = new AtomicInteger();
|
||||
CompletableFuture<Integer> cf2;
|
||||
CompletableFuture<Integer> cf1 = supplyAsync(() -> 1);
|
||||
cf2 = cf1.whenComplete((x,t) -> count.getAndIncrement());
|
||||
checkCompletedNormally(cf1, 1);
|
||||
checkCompletedNormally(cf2, 1);
|
||||
check(count.get() == 1, "action count should be incremented");
|
||||
|
||||
final RuntimeException ex = new RuntimeException();
|
||||
cf1 = supplyAsync(() -> { throw ex; });
|
||||
cf2 = cf1.whenComplete((x,t) -> count.getAndIncrement());
|
||||
checkCompletedExceptionally(cf1);
|
||||
checkCompletedExceptionally(cf2);
|
||||
check(count.get() == 2, "action count should be incremented");
|
||||
|
||||
cf1 = supplyAsync(() -> 1);
|
||||
cf2 = cf1.whenCompleteAsync((x,t) -> count.getAndIncrement());
|
||||
checkCompletedNormally(cf1, 1);
|
||||
checkCompletedNormally(cf2, 1);
|
||||
check(count.get() == 3, "action count should be incremented");
|
||||
|
||||
cf1 = supplyAsync(() -> { throw ex; });
|
||||
cf2 = cf1.whenCompleteAsync((x,t) -> count.getAndIncrement());
|
||||
checkCompletedExceptionally(cf1);
|
||||
checkCompletedExceptionally(cf2);
|
||||
check(count.get() == 4, "action count should be incremented");
|
||||
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
static volatile int passed = 0, failed = 0;
|
||||
static void pass() {passed++;}
|
||||
static void fail() {failed++; Thread.dumpStack();}
|
||||
static void fail(String msg) {System.out.println(msg); fail();}
|
||||
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
static void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
static void check(boolean cond, String msg) {if (cond) pass(); else fail(msg);}
|
||||
static void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
static void equalAnyOf(Object x, Object[] y) {
|
||||
if (x == null && y == null) { pass(); return; }
|
||||
for (Object z : y) { if (x.equals(z)) { pass(); return; } }
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Object o : y)
|
||||
sb.append(o).append(" ");
|
||||
fail(x + " not equal to one of [" + sb + "]");}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
try {realMain(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8303742
|
||||
* @summary CompletableFuture.orTimeout can leak memory if completed exceptionally
|
||||
* @modules java.base/java.util.concurrent:open
|
||||
* @run junit/othervm -Xmx128m CompletableFutureOrTimeoutExceptionallyTest
|
||||
*/
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class CompletableFutureOrTimeoutExceptionallyTest {
|
||||
// updated February 2025 to adapt to CompletableFuture DelayScheduler changes
|
||||
/**
|
||||
* Test that orTimeout task is cancelled if the CompletableFuture is completed Exceptionally
|
||||
*/
|
||||
@Test
|
||||
void testOrTimeoutWithCompleteExceptionallyDoesNotLeak() throws InterruptedException {
|
||||
ForkJoinPool delayer = ForkJoinPool.commonPool();
|
||||
assertEquals(delayer.getDelayedTaskCount(), 0);
|
||||
var future = new CompletableFuture<>().orTimeout(12, TimeUnit.HOURS);
|
||||
future.completeExceptionally(new RuntimeException("This is fine"));
|
||||
while (delayer.getDelayedTaskCount() > 0) {
|
||||
Thread.sleep(100);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the completeOnTimeout task is cancelled if the CompletableFuture is completed Exceptionally
|
||||
*/
|
||||
@Test
|
||||
void testCompleteOnTimeoutWithCompleteExceptionallyDoesNotLeak() throws InterruptedException {
|
||||
ForkJoinPool delayer = ForkJoinPool.commonPool();
|
||||
assertEquals(delayer.getDelayedTaskCount(), 0);
|
||||
var future = new CompletableFuture<>().completeOnTimeout(null, 12, TimeUnit.HOURS);
|
||||
future.completeExceptionally(new RuntimeException("This is fine"));
|
||||
while (delayer.getDelayedTaskCount() > 0) {
|
||||
Thread.sleep(100);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import static java.util.concurrent.TimeUnit.DAYS;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8254350
|
||||
* @run main LostInterrupt
|
||||
* @summary CompletableFuture.get may swallow interrupted status
|
||||
* @key randomness
|
||||
*/
|
||||
|
||||
// TODO: Rewrite as a CompletableFuture tck test ?
|
||||
|
||||
/**
|
||||
* Submits a task that completes immediately, then invokes CompletableFuture.get
|
||||
* with the interrupted status set. CompletableFuture.get should either complete
|
||||
* immediately with the interrupted status set, or else throw InterruptedException
|
||||
* with the interrupted status cleared.
|
||||
*/
|
||||
public class LostInterrupt {
|
||||
static final int ITERATIONS = 10_000;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ThreadLocalRandom rnd = ThreadLocalRandom.current();
|
||||
ForkJoinPool executor = new ForkJoinPool(1);
|
||||
try {
|
||||
for (int i = 0; i < ITERATIONS; i++) {
|
||||
CompletableFuture<String> future = new CompletableFuture<>();
|
||||
boolean timed = rnd.nextBoolean();
|
||||
executor.execute(() -> future.complete("foo"));
|
||||
|
||||
Thread.currentThread().interrupt();
|
||||
try {
|
||||
String result = timed ? future.get(1, DAYS) : future.get();
|
||||
|
||||
if (!Thread.interrupted())
|
||||
throw new AssertionError("lost interrupt, run=" + i);
|
||||
} catch (InterruptedException expected) {
|
||||
if (Thread.interrupted())
|
||||
throw new AssertionError(
|
||||
"interrupted status not cleared, run=" + i);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
executor.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import static java.util.concurrent.TimeUnit.DAYS;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8254350
|
||||
* @run main SwallowedInterruptedException
|
||||
* @key randomness
|
||||
*/
|
||||
|
||||
// TODO: incorporate into CompletableFuture tck tests
|
||||
|
||||
public class SwallowedInterruptedException {
|
||||
static final int ITERATIONS = 100;
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
ThreadLocalRandom rnd = ThreadLocalRandom.current();
|
||||
for (int i = 1; i <= ITERATIONS; i++) {
|
||||
boolean timed = rnd.nextBoolean();
|
||||
long sleepMillis = rnd.nextLong(10);
|
||||
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
CountDownLatch threadRunning = new CountDownLatch(1);
|
||||
AtomicReference<Throwable> fail = new AtomicReference<>();
|
||||
|
||||
Thread thread = new Thread(() -> {
|
||||
threadRunning.countDown();
|
||||
|
||||
try {
|
||||
Void result = (timed) ? future.get(1, DAYS) : future.get();
|
||||
|
||||
if (!Thread.currentThread().isInterrupted()) {
|
||||
fail.set(new AssertionError(
|
||||
"Future.get completed with interrupted status not set"));
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
fail.set(new AssertionError(
|
||||
"InterruptedException with interrupted status set"));
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
fail.set(ex);
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
threadRunning.await();
|
||||
|
||||
// interrupt thread, then set result after an optional (random) delay
|
||||
thread.interrupt();
|
||||
if (sleepMillis > 0)
|
||||
Thread.sleep(sleepMillis);
|
||||
future.complete(null);
|
||||
|
||||
thread.join();
|
||||
if (fail.get() != null) {
|
||||
throw new AssertionError(
|
||||
String.format("Test failed at iteration %d with [timed=%s sleepMillis=%d]",
|
||||
i, timed, sleepMillis),
|
||||
fail.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8029164
|
||||
* @run testng ThenComposeAsyncTest
|
||||
* @run testng/othervm -Djava.util.concurrent.ForkJoinPool.common.parallelism=0 ThenComposeAsyncTest
|
||||
* @summary Test that CompletableFuture.thenCompose works correctly if the
|
||||
* composing task is complete before composition
|
||||
*/
|
||||
@Test
|
||||
public class ThenComposeAsyncTest {
|
||||
|
||||
public void testThenComposeAsync() throws Exception {
|
||||
// Composing CompletableFuture is complete
|
||||
CompletableFuture<String> cf1 = CompletableFuture.completedFuture("one");
|
||||
|
||||
// Composing function returns a CompletableFuture executed asynchronously
|
||||
CountDownLatch cdl = new CountDownLatch(1);
|
||||
CompletableFuture<String> cf2 = cf1.thenCompose(str -> CompletableFuture.supplyAsync(() -> {
|
||||
while (true) {
|
||||
try {
|
||||
cdl.await();
|
||||
break;
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
return str + ", two";
|
||||
}));
|
||||
|
||||
// Ensure returned CompletableFuture completes after call to thenCompose
|
||||
// This guarantees that any premature internal completion will be
|
||||
// detected
|
||||
cdl.countDown();
|
||||
|
||||
String val = cf2.get();
|
||||
Assert.assertNotNull(val);
|
||||
Assert.assertEquals(val, "one, two");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8068432 8072030
|
||||
* @run testng ThenComposeExceptionTest
|
||||
* @summary Test that CompletableFuture.thenCompose works correctly if the
|
||||
* composing future completes exceptionally
|
||||
*/
|
||||
@Test
|
||||
public class ThenComposeExceptionTest {
|
||||
|
||||
static final BiFunction<CompletableFuture<String>, CompletableFuture<String>, CompletableFuture<String>>
|
||||
THEN_COMPOSE = (f, fe) -> f.thenCompose(s -> fe);
|
||||
|
||||
static final BiFunction<CompletableFuture<String>, CompletableFuture<String>, CompletableFuture<String>>
|
||||
THEN_COMPOSE_ASYNC = (f, fe) -> f.thenComposeAsync(s -> fe);
|
||||
|
||||
static final Consumer<CompletableFuture<String>>
|
||||
COMPLETE_EXCEPTIONALLY = f -> f.completeExceptionally(new RuntimeException());
|
||||
|
||||
static final Consumer<CompletableFuture<String>>
|
||||
NOP = f -> { };
|
||||
|
||||
static Object[][] actionsDataProvider;
|
||||
|
||||
@DataProvider(name = "actions")
|
||||
static Object[][] actionsDataProvider() {
|
||||
if (actionsDataProvider != null) {
|
||||
return actionsDataProvider;
|
||||
}
|
||||
|
||||
List<Object[]> data = new ArrayList<>();
|
||||
data.add(new Object[]{"thenCompose and completeExceptionally", NOP, THEN_COMPOSE, COMPLETE_EXCEPTIONALLY});
|
||||
data.add(new Object[]{"thenComposeAsync and completeExceptionally", NOP, THEN_COMPOSE_ASYNC, COMPLETE_EXCEPTIONALLY});
|
||||
data.add(new Object[]{"completeExceptionally and thenCompose", COMPLETE_EXCEPTIONALLY, THEN_COMPOSE, NOP});
|
||||
data.add(new Object[]{"completeExceptionally and thenComposeAsync", COMPLETE_EXCEPTIONALLY, THEN_COMPOSE_ASYNC, NOP});
|
||||
|
||||
return actionsDataProvider = data.toArray(new Object[0][]);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "actions")
|
||||
public void testThenCompose(
|
||||
String description,
|
||||
Consumer<CompletableFuture<String>> beforeAction,
|
||||
BiFunction<CompletableFuture<String>, CompletableFuture<String>, CompletableFuture<String>> composeFunction,
|
||||
Consumer<CompletableFuture<String>> afterAction) throws Exception {
|
||||
CompletableFuture<String> f = new CompletableFuture<>();
|
||||
CompletableFuture<String> fe = new CompletableFuture<>();
|
||||
|
||||
// Ensure pre-composed stage is completed to trigger
|
||||
// processing the composing future
|
||||
f.complete("");
|
||||
|
||||
beforeAction.accept(fe);
|
||||
|
||||
CompletableFuture<String> f_thenCompose = composeFunction.apply(f, fe);
|
||||
Assert.assertNotSame(f_thenCompose, fe, "Composed CompletableFuture returned directly");
|
||||
|
||||
AtomicReference<Throwable> eOnWhenComplete = new AtomicReference<>();
|
||||
CompletableFuture<String> f_whenComplete = f_thenCompose.
|
||||
whenComplete((r, e) -> eOnWhenComplete.set(e));
|
||||
|
||||
afterAction.accept(fe);
|
||||
|
||||
Throwable eOnJoined = null;
|
||||
try {
|
||||
f_thenCompose.join();
|
||||
}
|
||||
catch (Throwable t) {
|
||||
eOnJoined = t;
|
||||
}
|
||||
Assert.assertTrue(eOnJoined instanceof CompletionException,
|
||||
"Incorrect exception reported when joined on thenCompose: " + eOnJoined);
|
||||
|
||||
// Need to wait for f_whenComplete to complete to avoid
|
||||
// race condition when updating eOnWhenComplete
|
||||
eOnJoined = null;
|
||||
try {
|
||||
f_whenComplete.join();
|
||||
} catch (Throwable t) {
|
||||
eOnJoined = t;
|
||||
}
|
||||
Assert.assertTrue(eOnJoined instanceof CompletionException,
|
||||
"Incorrect exception reported when joined on whenComplete: " + eOnJoined);
|
||||
Assert.assertTrue(eOnWhenComplete.get() instanceof CompletionException,
|
||||
"Incorrect exception passed to whenComplete: " + eOnWhenComplete.get());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.ThreadInfo;
|
||||
import java.lang.management.ThreadMXBean;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8028564
|
||||
* @run testng/timeout=1200 ConcurrentAssociateTest
|
||||
* @summary Test that association operations, such as put and compute,
|
||||
* place entries in the map
|
||||
* @modules java.management
|
||||
*/
|
||||
@Test
|
||||
public class ConcurrentAssociateTest {
|
||||
|
||||
/** Maximum time (in seconds) to wait for a test method to complete. */
|
||||
private static final int TIMEOUT = Integer.getInteger("timeout", 200);
|
||||
|
||||
/** The number of entries for each thread to place in a map. */
|
||||
private static final int N = Integer.getInteger("n", 128);
|
||||
|
||||
/** The number of iterations of the test. */
|
||||
private static final int I = Integer.getInteger("i", 64);
|
||||
|
||||
/** Objects to be placed in the concurrent map. */
|
||||
static class X {
|
||||
// Limit the hash code to trigger collisions
|
||||
final int hc = ThreadLocalRandom.current().nextInt(1, 9);
|
||||
|
||||
public int hashCode() { return hc; }
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPut() throws Throwable {
|
||||
test("CHM.put", (m, o) -> m.put(o, o));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompute() throws Throwable {
|
||||
test("CHM.compute", (m, o) -> m.compute(o, (k, v) -> o));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComputeIfAbsent() throws Throwable {
|
||||
test("CHM.computeIfAbsent", (m, o) -> m.computeIfAbsent(o, (k) -> o));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMerge() throws Throwable {
|
||||
test("CHM.merge", (m, o) -> m.merge(o, o, (v1, v2) -> v1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutAll() throws Throwable {
|
||||
test("CHM.putAll", (m, o) -> {
|
||||
Map<Object, Object> hm = new HashMap<>();
|
||||
hm.put(o, o);
|
||||
m.putAll(hm);
|
||||
});
|
||||
}
|
||||
|
||||
private static void test(String desc, BiConsumer<ConcurrentMap<Object, Object>, Object> associator) throws Throwable {
|
||||
for (int i = 0; i < I; i++) {
|
||||
testOnce(desc, associator);
|
||||
}
|
||||
}
|
||||
|
||||
static class AssociationFailure extends RuntimeException {
|
||||
AssociationFailure(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
private static void testOnce(String desc, BiConsumer<ConcurrentMap<Object, Object>, Object> associator) throws Throwable {
|
||||
ConcurrentHashMap<Object, Object> m = new ConcurrentHashMap<>();
|
||||
CountDownLatch s = new CountDownLatch(1);
|
||||
|
||||
Supplier<Runnable> sr = () -> () -> {
|
||||
try {
|
||||
if (!s.await(TIMEOUT, TimeUnit.SECONDS)) {
|
||||
dumpTestThreads();
|
||||
throw new AssertionError("timed out");
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
}
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
Object o = new X();
|
||||
associator.accept(m, o);
|
||||
if (!m.containsKey(o)) {
|
||||
throw new AssociationFailure(desc + " failed: entry does not exist");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Bound concurrency to avoid degenerate performance
|
||||
int ps = Math.min(Runtime.getRuntime().availableProcessors(), 8);
|
||||
Stream<CompletableFuture> runners = IntStream.range(0, ps)
|
||||
.mapToObj(i -> sr.get())
|
||||
.map(CompletableFuture::runAsync);
|
||||
|
||||
CompletableFuture all = CompletableFuture.allOf(
|
||||
runners.toArray(CompletableFuture[]::new));
|
||||
|
||||
// Trigger the runners to start associating
|
||||
s.countDown();
|
||||
|
||||
try {
|
||||
all.get(TIMEOUT, TimeUnit.SECONDS);
|
||||
} catch (TimeoutException e) {
|
||||
dumpTestThreads();
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
dumpTestThreads();
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof AssociationFailure) {
|
||||
throw cause;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A debugging tool to print stack traces of most threads, as jstack does.
|
||||
* Uninteresting threads are filtered out.
|
||||
*/
|
||||
static void dumpTestThreads() {
|
||||
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
|
||||
System.err.println("------ stacktrace dump start ------");
|
||||
for (ThreadInfo info : threadMXBean.dumpAllThreads(true, true)) {
|
||||
final String name = info.getThreadName();
|
||||
String lockName;
|
||||
if ("Signal Dispatcher".equals(name))
|
||||
continue;
|
||||
if ("Reference Handler".equals(name))
|
||||
continue;
|
||||
if ("Finalizer".equals(name))
|
||||
continue;
|
||||
System.err.print(info);
|
||||
}
|
||||
System.err.println("------ stacktrace dump end ------");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8028564
|
||||
* @run testng ConcurrentContainsKeyTest
|
||||
* @summary Test that entries are always present in the map,
|
||||
* when entries are held within one bin that is a tree
|
||||
*/
|
||||
@Test
|
||||
public class ConcurrentContainsKeyTest {
|
||||
|
||||
// The number of entries for each thread to place in a map
|
||||
// Should be > ConcurrentHashMap.TREEIFY_THRESHOLD but small
|
||||
// enough to allow for enough iteration overlap by multiple threads
|
||||
private static final int N = Integer.getInteger("n", 16);
|
||||
// The number of rounds each thread performs per entry
|
||||
private static final int R = Integer.getInteger("r", 32);
|
||||
// The number of iterations of the test
|
||||
private static final int I = Integer.getInteger("i", 256);
|
||||
|
||||
// Object to be placed in the concurrent map
|
||||
static class X implements Comparable<X> {
|
||||
|
||||
private final int a;
|
||||
|
||||
X(int a) {
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public int compareTo(X o) {
|
||||
return this.a - o.a;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
// Return the same hash code to guarantee collisions
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContainsKey() {
|
||||
X[] content = IntStream.range(0, N).mapToObj(i -> new X(i)).toArray(X[]::new);
|
||||
// Create map with an initial size >= ConcurrentHashMap.TREEIFY_THRESHOLD
|
||||
// ensuring tree'ification will occur for a small number of entries
|
||||
// with the same hash code
|
||||
ConcurrentHashMap<Object, Object> m = new ConcurrentHashMap<>(64);
|
||||
Stream.of(content).forEach(x -> m.put(x, x));
|
||||
test(content, m);
|
||||
}
|
||||
|
||||
private static void test(X[] content, ConcurrentHashMap<Object, Object> m) {
|
||||
for (int i = 0; i < I; i++) {
|
||||
testOnce(content, m);
|
||||
}
|
||||
}
|
||||
|
||||
static class AssociationFailure extends RuntimeException {
|
||||
AssociationFailure(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
private static void testOnce(Object[] content, ConcurrentHashMap<Object, Object> m) {
|
||||
CountDownLatch s = new CountDownLatch(1);
|
||||
|
||||
Supplier<Runnable> sr = () -> () -> {
|
||||
try {
|
||||
s.await();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
}
|
||||
|
||||
for (int i = 0; i < R * N; i++) {
|
||||
Object o = content[i % content.length];
|
||||
if (!m.containsKey(o)) {
|
||||
throw new AssociationFailure("CHM.containsKey failed: entry does not exist");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int ps = Runtime.getRuntime().availableProcessors();
|
||||
Stream<CompletableFuture> runners = IntStream.range(0, ps)
|
||||
.mapToObj(i -> sr.get())
|
||||
.map(CompletableFuture::runAsync);
|
||||
|
||||
CompletableFuture all = CompletableFuture.allOf(
|
||||
runners.toArray(CompletableFuture[]::new));
|
||||
|
||||
// Trigger the runners to start checking key membership
|
||||
s.countDown();
|
||||
try {
|
||||
all.join();
|
||||
}
|
||||
catch (CompletionException e) {
|
||||
Throwable t = e.getCause();
|
||||
if (t instanceof AssociationFailure) {
|
||||
throw (AssociationFailure) t;
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Portions Copyright (c) 2011 IBM Corporation
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6312706
|
||||
* @summary Sets from Map.entrySet() return distinct objects for each Entry
|
||||
* @author Neil Richards <neil.richards@ngmr.net>, <neil_richards@uk.ibm.com>
|
||||
*/
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class DistinctEntrySetElements {
|
||||
public static void main(String[] args) throws Exception {
|
||||
final ConcurrentHashMap<String, String> concurrentHashMap =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
concurrentHashMap.put("One", "Un");
|
||||
concurrentHashMap.put("Two", "Deux");
|
||||
concurrentHashMap.put("Three", "Trois");
|
||||
|
||||
Set<Map.Entry<String, String>> entrySet = concurrentHashMap.entrySet();
|
||||
HashSet<Map.Entry<String, String>> hashSet = new HashSet<>(entrySet);
|
||||
|
||||
if (false == hashSet.equals(entrySet)) {
|
||||
throw new RuntimeException("Test FAILED: Sets are not equal.");
|
||||
}
|
||||
if (hashSet.hashCode() != entrySet.hashCode()) {
|
||||
throw new RuntimeException("Test FAILED: Set's hashcodes are not equal.");
|
||||
}
|
||||
}
|
||||
}
|
||||
103
test/jdk/java/util/concurrent/ConcurrentHashMap/LoopHelpers.java
Normal file
103
test/jdk/java/util/concurrent/ConcurrentHashMap/LoopHelpers.java
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/**
|
||||
* Misc utilities in JSR166 performance tests
|
||||
*/
|
||||
class LoopHelpers {
|
||||
|
||||
// Some mindless computation to do between synchronizations...
|
||||
|
||||
/**
|
||||
* generates 32 bit pseudo-random numbers.
|
||||
* Adapted from http://www.snippets.org
|
||||
*/
|
||||
public static int compute1(int x) {
|
||||
int lo = 16807 * (x & 0xFFFF);
|
||||
int hi = 16807 * (x >>> 16);
|
||||
lo += (hi & 0x7FFF) << 16;
|
||||
if ((lo & 0x80000000) != 0) {
|
||||
lo &= 0x7fffffff;
|
||||
++lo;
|
||||
}
|
||||
lo += hi >>> 15;
|
||||
if (lo == 0 || (lo & 0x80000000) != 0) {
|
||||
lo &= 0x7fffffff;
|
||||
++lo;
|
||||
}
|
||||
return lo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a linear congruential random number a random number
|
||||
* of times.
|
||||
*/
|
||||
public static int compute2(int x) {
|
||||
int loops = (x >>> 4) & 7;
|
||||
while (loops-- > 0) {
|
||||
x = (x * 2147483647) % 16807;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
public static class BarrierTimer implements Runnable {
|
||||
public volatile long startTime;
|
||||
public volatile long endTime;
|
||||
public void run() {
|
||||
long t = System.nanoTime();
|
||||
if (startTime == 0)
|
||||
startTime = t;
|
||||
else
|
||||
endTime = t;
|
||||
}
|
||||
public void clear() {
|
||||
startTime = 0;
|
||||
endTime = 0;
|
||||
}
|
||||
public long getTime() {
|
||||
return endTime - startTime;
|
||||
}
|
||||
}
|
||||
|
||||
public static String rightJustify(long n) {
|
||||
// There's probably a better way to do this...
|
||||
String field = " ";
|
||||
String num = Long.toString(n);
|
||||
if (num.length() >= field.length())
|
||||
return num;
|
||||
StringBuilder b = new StringBuilder(field);
|
||||
b.replace(b.length()-num.length(), b.length(), num);
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
}
|
||||
622
test/jdk/java/util/concurrent/ConcurrentHashMap/MapCheck.java
Normal file
622
test/jdk/java/util/concurrent/ConcurrentHashMap/MapCheck.java
Normal file
|
|
@ -0,0 +1,622 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 4486658
|
||||
* @run main/timeout=240 MapCheck
|
||||
* @summary Times and checks basic map operations
|
||||
*/
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public class MapCheck {
|
||||
|
||||
static final int absentSize = 1 << 17;
|
||||
static final int absentMask = absentSize - 1;
|
||||
static Object[] absent = new Object[absentSize];
|
||||
|
||||
static final Object MISSING = new Object();
|
||||
|
||||
static TestTimer timer = new TestTimer();
|
||||
|
||||
static void reallyAssert(boolean b) {
|
||||
if (!b) throw new Error("Failed Assertion");
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Class mapClass = java.util.concurrent.ConcurrentHashMap.class;
|
||||
int numTests = 8;
|
||||
int size = 50000;
|
||||
|
||||
if (args.length > 0) {
|
||||
try {
|
||||
mapClass = Class.forName(args[0]);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("Class " + args[0] + " not found.");
|
||||
}
|
||||
}
|
||||
|
||||
if (args.length > 1)
|
||||
numTests = Integer.parseInt(args[1]);
|
||||
|
||||
if (args.length > 2)
|
||||
size = Integer.parseInt(args[2]);
|
||||
|
||||
boolean doSerializeTest = args.length > 3;
|
||||
|
||||
System.out.println("Testing " + mapClass.getName() + " trials: " + numTests + " size: " + size);
|
||||
|
||||
for (int i = 0; i < absentSize; ++i) absent[i] = new Object();
|
||||
|
||||
Object[] key = new Object[size];
|
||||
for (int i = 0; i < size; ++i) key[i] = new Object();
|
||||
|
||||
forceMem(size * 8);
|
||||
|
||||
for (int rep = 0; rep < numTests; ++rep) {
|
||||
runTest(newMap(mapClass), key);
|
||||
}
|
||||
|
||||
TestTimer.printStats();
|
||||
|
||||
if (doSerializeTest)
|
||||
stest(newMap(mapClass), size);
|
||||
}
|
||||
|
||||
static Map newMap(Class cl) {
|
||||
try {
|
||||
return (Map)cl.getDeclaredConstructor().newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Can't instantiate " + cl + ": " + e);
|
||||
}
|
||||
}
|
||||
|
||||
static void runTest(Map s, Object[] key) {
|
||||
shuffle(key);
|
||||
int size = key.length;
|
||||
long startTime = System.currentTimeMillis();
|
||||
test(s, key);
|
||||
long time = System.currentTimeMillis() - startTime;
|
||||
}
|
||||
|
||||
static void forceMem(int n) {
|
||||
// force enough memory
|
||||
Long[] junk = new Long[n];
|
||||
for (int i = 0; i < junk.length; ++i) junk[i] = new Long(i);
|
||||
int sum = 0;
|
||||
for (int i = 0; i < junk.length; ++i)
|
||||
sum += (int)(junk[i].longValue() + i);
|
||||
if (sum == 0) System.out.println("Useless number = " + sum);
|
||||
junk = null;
|
||||
// System.gc();
|
||||
}
|
||||
|
||||
static void t1(String nm, int n, Map s, Object[] key, int expect) {
|
||||
int sum = 0;
|
||||
int iters = 4;
|
||||
timer.start(nm, n * iters);
|
||||
for (int j = 0; j < iters; ++j) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (s.get(key[i]) != null) ++sum;
|
||||
}
|
||||
}
|
||||
timer.finish();
|
||||
reallyAssert(sum == expect * iters);
|
||||
}
|
||||
|
||||
static void t2(String nm, int n, Map s, Object[] key, int expect) {
|
||||
int sum = 0;
|
||||
timer.start(nm, n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (s.remove(key[i]) != null) ++sum;
|
||||
}
|
||||
timer.finish();
|
||||
reallyAssert(sum == expect);
|
||||
}
|
||||
|
||||
static void t3(String nm, int n, Map s, Object[] key, int expect) {
|
||||
int sum = 0;
|
||||
timer.start(nm, n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (s.put(key[i], absent[i & absentMask]) == null) ++sum;
|
||||
}
|
||||
timer.finish();
|
||||
reallyAssert(sum == expect);
|
||||
}
|
||||
|
||||
static void t4(String nm, int n, Map s, Object[] key, int expect) {
|
||||
int sum = 0;
|
||||
timer.start(nm, n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (s.containsKey(key[i])) ++sum;
|
||||
}
|
||||
timer.finish();
|
||||
reallyAssert(sum == expect);
|
||||
}
|
||||
|
||||
static void t5(String nm, int n, Map s, Object[] key, int expect) {
|
||||
int sum = 0;
|
||||
timer.start(nm, n/2);
|
||||
for (int i = n-2; i >= 0; i-=2) {
|
||||
if (s.remove(key[i]) != null) ++sum;
|
||||
}
|
||||
timer.finish();
|
||||
reallyAssert(sum == expect);
|
||||
}
|
||||
|
||||
static void t6(String nm, int n, Map s, Object[] k1, Object[] k2) {
|
||||
int sum = 0;
|
||||
timer.start(nm, n * 2);
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (s.get(k1[i]) != null) ++sum;
|
||||
if (s.get(k2[i & absentMask]) != null) ++sum;
|
||||
}
|
||||
timer.finish();
|
||||
reallyAssert(sum == n);
|
||||
}
|
||||
|
||||
static void t7(String nm, int n, Map s, Object[] k1, Object[] k2) {
|
||||
int sum = 0;
|
||||
timer.start(nm, n * 2);
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (s.containsKey(k1[i])) ++sum;
|
||||
if (s.containsKey(k2[i & absentMask])) ++sum;
|
||||
}
|
||||
timer.finish();
|
||||
reallyAssert(sum == n);
|
||||
}
|
||||
|
||||
static void t8(String nm, int n, Map s, Object[] key, int expect) {
|
||||
int sum = 0;
|
||||
timer.start(nm, n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (s.get(key[i]) != null) ++sum;
|
||||
}
|
||||
timer.finish();
|
||||
reallyAssert(sum == expect);
|
||||
}
|
||||
|
||||
|
||||
static void t9(Map s) {
|
||||
int sum = 0;
|
||||
int iters = 20;
|
||||
timer.start("ContainsValue (/n) ", iters * s.size());
|
||||
int step = absentSize / iters;
|
||||
for (int i = 0; i < absentSize; i += step)
|
||||
if (s.containsValue(absent[i])) ++sum;
|
||||
timer.finish();
|
||||
reallyAssert(sum != 0);
|
||||
}
|
||||
|
||||
|
||||
static void ktest(Map s, int size, Object[] key) {
|
||||
timer.start("ContainsKey ", size);
|
||||
Set ks = s.keySet();
|
||||
int sum = 0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (ks.contains(key[i])) ++sum;
|
||||
}
|
||||
timer.finish();
|
||||
reallyAssert(sum == size);
|
||||
}
|
||||
|
||||
|
||||
static void ittest1(Map s, int size) {
|
||||
int sum = 0;
|
||||
timer.start("Iter Key ", size);
|
||||
for (Iterator it = s.keySet().iterator(); it.hasNext(); ) {
|
||||
if (it.next() != MISSING)
|
||||
++sum;
|
||||
}
|
||||
timer.finish();
|
||||
reallyAssert(sum == size);
|
||||
}
|
||||
|
||||
static void ittest2(Map s, int size) {
|
||||
int sum = 0;
|
||||
timer.start("Iter Value ", size);
|
||||
for (Iterator it = s.values().iterator(); it.hasNext(); ) {
|
||||
if (it.next() != MISSING)
|
||||
++sum;
|
||||
}
|
||||
timer.finish();
|
||||
reallyAssert(sum == size);
|
||||
}
|
||||
static void ittest3(Map s, int size) {
|
||||
int sum = 0;
|
||||
timer.start("Iter Entry ", size);
|
||||
for (Iterator it = s.entrySet().iterator(); it.hasNext(); ) {
|
||||
if (it.next() != MISSING)
|
||||
++sum;
|
||||
}
|
||||
timer.finish();
|
||||
reallyAssert(sum == size);
|
||||
}
|
||||
|
||||
static void ittest4(Map s, int size, int pos) {
|
||||
IdentityHashMap seen = new IdentityHashMap(size);
|
||||
reallyAssert(s.size() == size);
|
||||
int sum = 0;
|
||||
timer.start("Iter XEntry ", size);
|
||||
Iterator it = s.entrySet().iterator();
|
||||
Object k = null;
|
||||
Object v = null;
|
||||
for (int i = 0; i < size-pos; ++i) {
|
||||
Map.Entry x = (Map.Entry)(it.next());
|
||||
k = x.getKey();
|
||||
v = x.getValue();
|
||||
seen.put(k, k);
|
||||
if (x != MISSING)
|
||||
++sum;
|
||||
}
|
||||
reallyAssert(s.containsKey(k));
|
||||
it.remove();
|
||||
reallyAssert(!s.containsKey(k));
|
||||
while (it.hasNext()) {
|
||||
Map.Entry x = (Map.Entry)(it.next());
|
||||
Object k2 = x.getKey();
|
||||
seen.put(k2, k2);
|
||||
if (x != MISSING)
|
||||
++sum;
|
||||
}
|
||||
|
||||
reallyAssert(s.size() == size-1);
|
||||
s.put(k, v);
|
||||
reallyAssert(seen.size() == size);
|
||||
timer.finish();
|
||||
reallyAssert(sum == size);
|
||||
reallyAssert(s.size() == size);
|
||||
}
|
||||
|
||||
|
||||
static void ittest(Map s, int size) {
|
||||
ittest1(s, size);
|
||||
ittest2(s, size);
|
||||
ittest3(s, size);
|
||||
// for (int i = 0; i < size-1; ++i)
|
||||
// ittest4(s, size, i);
|
||||
}
|
||||
|
||||
static void entest1(Hashtable ht, int size) {
|
||||
int sum = 0;
|
||||
|
||||
timer.start("Iter Enumeration Key ", size);
|
||||
for (Enumeration en = ht.keys(); en.hasMoreElements(); ) {
|
||||
if (en.nextElement() != MISSING)
|
||||
++sum;
|
||||
}
|
||||
timer.finish();
|
||||
reallyAssert(sum == size);
|
||||
}
|
||||
|
||||
static void entest2(Hashtable ht, int size) {
|
||||
int sum = 0;
|
||||
timer.start("Iter Enumeration Value ", size);
|
||||
for (Enumeration en = ht.elements(); en.hasMoreElements(); ) {
|
||||
if (en.nextElement() != MISSING)
|
||||
++sum;
|
||||
}
|
||||
timer.finish();
|
||||
reallyAssert(sum == size);
|
||||
}
|
||||
|
||||
|
||||
static void entest3(Hashtable ht, int size) {
|
||||
int sum = 0;
|
||||
|
||||
timer.start("Iterf Enumeration Key ", size);
|
||||
Enumeration en = ht.keys();
|
||||
for (int i = 0; i < size; ++i) {
|
||||
if (en.nextElement() != MISSING)
|
||||
++sum;
|
||||
}
|
||||
timer.finish();
|
||||
reallyAssert(sum == size);
|
||||
}
|
||||
|
||||
static void entest4(Hashtable ht, int size) {
|
||||
int sum = 0;
|
||||
timer.start("Iterf Enumeration Value", size);
|
||||
Enumeration en = ht.elements();
|
||||
for (int i = 0; i < size; ++i) {
|
||||
if (en.nextElement() != MISSING)
|
||||
++sum;
|
||||
}
|
||||
timer.finish();
|
||||
reallyAssert(sum == size);
|
||||
}
|
||||
|
||||
static void entest(Map s, int size) {
|
||||
if (s instanceof Hashtable) {
|
||||
Hashtable ht = (Hashtable)s;
|
||||
// entest3(ht, size);
|
||||
// entest4(ht, size);
|
||||
entest1(ht, size);
|
||||
entest2(ht, size);
|
||||
entest1(ht, size);
|
||||
entest2(ht, size);
|
||||
entest1(ht, size);
|
||||
entest2(ht, size);
|
||||
}
|
||||
}
|
||||
|
||||
static void rtest(Map s, int size) {
|
||||
timer.start("Remove (iterator) ", size);
|
||||
for (Iterator it = s.keySet().iterator(); it.hasNext(); ) {
|
||||
it.next();
|
||||
it.remove();
|
||||
}
|
||||
timer.finish();
|
||||
}
|
||||
|
||||
static void rvtest(Map s, int size) {
|
||||
timer.start("Remove (iterator) ", size);
|
||||
for (Iterator it = s.values().iterator(); it.hasNext(); ) {
|
||||
it.next();
|
||||
it.remove();
|
||||
}
|
||||
timer.finish();
|
||||
}
|
||||
|
||||
|
||||
static void dtest(Map s, int size, Object[] key) {
|
||||
timer.start("Put (putAll) ", size * 2);
|
||||
Map s2 = null;
|
||||
try {
|
||||
s2 = (Map) s.getClass().getDeclaredConstructor().newInstance();
|
||||
s2.putAll(s);
|
||||
}
|
||||
catch (Exception e) { e.printStackTrace(); return; }
|
||||
timer.finish();
|
||||
|
||||
timer.start("Iter Equals ", size * 2);
|
||||
boolean eqt = s2.equals(s) && s.equals(s2);
|
||||
reallyAssert(eqt);
|
||||
timer.finish();
|
||||
|
||||
timer.start("Iter HashCode ", size * 2);
|
||||
int shc = s.hashCode();
|
||||
int s2hc = s2.hashCode();
|
||||
reallyAssert(shc == s2hc);
|
||||
timer.finish();
|
||||
|
||||
timer.start("Put (present) ", size);
|
||||
s2.putAll(s);
|
||||
timer.finish();
|
||||
|
||||
timer.start("Iter EntrySet contains ", size * 2);
|
||||
Set es2 = s2.entrySet();
|
||||
int sum = 0;
|
||||
for (Iterator i1 = s.entrySet().iterator(); i1.hasNext(); ) {
|
||||
Object entry = i1.next();
|
||||
if (es2.contains(entry)) ++sum;
|
||||
}
|
||||
timer.finish();
|
||||
reallyAssert(sum == size);
|
||||
|
||||
t6("Get ", size, s2, key, absent);
|
||||
|
||||
Object hold = s2.get(key[size-1]);
|
||||
s2.put(key[size-1], absent[0]);
|
||||
timer.start("Iter Equals ", size * 2);
|
||||
eqt = s2.equals(s) && s.equals(s2);
|
||||
reallyAssert(!eqt);
|
||||
timer.finish();
|
||||
|
||||
timer.start("Iter HashCode ", size * 2);
|
||||
int s1h = s.hashCode();
|
||||
int s2h = s2.hashCode();
|
||||
reallyAssert(s1h != s2h);
|
||||
timer.finish();
|
||||
|
||||
s2.put(key[size-1], hold);
|
||||
timer.start("Remove (iterator) ", size * 2);
|
||||
Iterator s2i = s2.entrySet().iterator();
|
||||
Set es = s.entrySet();
|
||||
while (s2i.hasNext())
|
||||
es.remove(s2i.next());
|
||||
timer.finish();
|
||||
|
||||
reallyAssert(s.isEmpty());
|
||||
|
||||
timer.start("Clear ", size);
|
||||
s2.clear();
|
||||
timer.finish();
|
||||
reallyAssert(s2.isEmpty() && s.isEmpty());
|
||||
}
|
||||
|
||||
static void stest(Map s, int size) throws Exception {
|
||||
if (!(s instanceof Serializable))
|
||||
return;
|
||||
System.out.print("Serialize : ");
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
s.put(new Integer(i), Boolean.TRUE);
|
||||
}
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
FileOutputStream fs = new FileOutputStream("MapCheck.dat");
|
||||
ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(fs));
|
||||
out.writeObject(s);
|
||||
out.close();
|
||||
|
||||
FileInputStream is = new FileInputStream("MapCheck.dat");
|
||||
ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(is));
|
||||
Map m = (Map)in.readObject();
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
long time = endTime - startTime;
|
||||
|
||||
System.out.print(time + "ms");
|
||||
|
||||
if (s instanceof IdentityHashMap) return;
|
||||
reallyAssert(s.equals(m));
|
||||
}
|
||||
|
||||
|
||||
static void test(Map s, Object[] key) {
|
||||
int size = key.length;
|
||||
|
||||
t3("Put (absent) ", size, s, key, size);
|
||||
t3("Put (present) ", size, s, key, 0);
|
||||
t7("ContainsKey ", size, s, key, absent);
|
||||
t4("ContainsKey ", size, s, key, size);
|
||||
ktest(s, size, key);
|
||||
t4("ContainsKey ", absentSize, s, absent, 0);
|
||||
t6("Get ", size, s, key, absent);
|
||||
t1("Get (present) ", size, s, key, size);
|
||||
t1("Get (absent) ", absentSize, s, absent, 0);
|
||||
t2("Remove (absent) ", absentSize, s, absent, 0);
|
||||
t5("Remove (present) ", size, s, key, size / 2);
|
||||
t3("Put (half present) ", size, s, key, size / 2);
|
||||
|
||||
ittest(s, size);
|
||||
entest(s, size);
|
||||
t9(s);
|
||||
rtest(s, size);
|
||||
|
||||
t4("ContainsKey ", size, s, key, 0);
|
||||
t2("Remove (absent) ", size, s, key, 0);
|
||||
t3("Put (presized) ", size, s, key, size);
|
||||
dtest(s, size, key);
|
||||
}
|
||||
|
||||
static class TestTimer {
|
||||
private String name;
|
||||
private long numOps;
|
||||
private long startTime;
|
||||
private String cname;
|
||||
|
||||
static final java.util.TreeMap accum = new java.util.TreeMap();
|
||||
|
||||
static void printStats() {
|
||||
for (Iterator it = accum.entrySet().iterator(); it.hasNext(); ) {
|
||||
Map.Entry e = (Map.Entry)(it.next());
|
||||
Stats stats = (Stats)(e.getValue());
|
||||
int n = stats.number;
|
||||
double t;
|
||||
if (n > 0)
|
||||
t = stats.sum / n;
|
||||
else
|
||||
t = stats.least;
|
||||
long nano = Math.round(1000000.0 * t);
|
||||
System.out.println(e.getKey() + ": " + nano);
|
||||
}
|
||||
}
|
||||
|
||||
void start(String name, long numOps) {
|
||||
this.name = name;
|
||||
this.cname = classify();
|
||||
this.numOps = numOps;
|
||||
startTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
|
||||
String classify() {
|
||||
if (name.startsWith("Get"))
|
||||
return "Get ";
|
||||
else if (name.startsWith("Put"))
|
||||
return "Put ";
|
||||
else if (name.startsWith("Remove"))
|
||||
return "Remove ";
|
||||
else if (name.startsWith("Iter"))
|
||||
return "Iter ";
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
void finish() {
|
||||
long endTime = System.currentTimeMillis();
|
||||
long time = endTime - startTime;
|
||||
double timePerOp = ((double)time)/numOps;
|
||||
|
||||
Object st = accum.get(name);
|
||||
if (st == null)
|
||||
accum.put(name, new Stats(timePerOp));
|
||||
else {
|
||||
Stats stats = (Stats) st;
|
||||
stats.sum += timePerOp;
|
||||
stats.number++;
|
||||
if (timePerOp < stats.least) stats.least = timePerOp;
|
||||
}
|
||||
|
||||
if (cname != null) {
|
||||
st = accum.get(cname);
|
||||
if (st == null)
|
||||
accum.put(cname, new Stats(timePerOp));
|
||||
else {
|
||||
Stats stats = (Stats) st;
|
||||
stats.sum += timePerOp;
|
||||
stats.number++;
|
||||
if (timePerOp < stats.least) stats.least = timePerOp;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class Stats {
|
||||
double sum = 0;
|
||||
double least;
|
||||
int number = 0;
|
||||
Stats(double t) { least = t; }
|
||||
}
|
||||
|
||||
static void shuffle(Object[] keys) {
|
||||
ThreadLocalRandom rnd = ThreadLocalRandom.current();
|
||||
int size = keys.length;
|
||||
for (int i=size; i>1; i--) {
|
||||
int r = rnd.nextInt(i);
|
||||
Object t = keys[i-1];
|
||||
keys[i-1] = keys[r];
|
||||
keys[r] = t;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
249
test/jdk/java/util/concurrent/ConcurrentHashMap/MapLoops.java
Normal file
249
test/jdk/java/util/concurrent/ConcurrentHashMap/MapLoops.java
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 4486658
|
||||
* @summary Exercise multithreaded maps, by default ConcurrentHashMap.
|
||||
* Multithreaded hash table test. Each thread does a random walk
|
||||
* though elements of "key" array. On each iteration, it checks if
|
||||
* table includes key. If absent, with probability pinsert it
|
||||
* inserts it, and if present, with probability premove it removes
|
||||
* it. (pinsert and premove are expressed as percentages to simplify
|
||||
* parsing from command line.)
|
||||
* @library /test/lib
|
||||
* @run main/timeout=1600 MapLoops
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SplittableRandom;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
public class MapLoops {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
static int nkeys = 1000; // 10_000
|
||||
static int pinsert = 60;
|
||||
static int premove = 2;
|
||||
static int maxThreads = 100;
|
||||
static int nops = 10000; // 100_000
|
||||
static int removesPerMaxRandom;
|
||||
static int insertsPerMaxRandom;
|
||||
|
||||
static final ExecutorService pool = Executors.newCachedThreadPool();
|
||||
|
||||
static final List<Throwable> throwables = new CopyOnWriteArrayList<>();
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
Class mapClass = null;
|
||||
if (args.length > 0) {
|
||||
try {
|
||||
mapClass = Class.forName(args[0]);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("Class " + args[0] + " not found.");
|
||||
}
|
||||
}
|
||||
else
|
||||
mapClass = java.util.concurrent.ConcurrentHashMap.class;
|
||||
|
||||
if (args.length > 1)
|
||||
maxThreads = Integer.parseInt(args[1]);
|
||||
|
||||
if (args.length > 2)
|
||||
nkeys = Integer.parseInt(args[2]);
|
||||
|
||||
if (args.length > 3)
|
||||
pinsert = Integer.parseInt(args[3]);
|
||||
|
||||
if (args.length > 4)
|
||||
premove = Integer.parseInt(args[4]);
|
||||
|
||||
if (args.length > 5)
|
||||
nops = Integer.parseInt(args[5]);
|
||||
|
||||
// normalize probabilities wrt random number generator
|
||||
removesPerMaxRandom = (int)((double)premove/100.0 * 0x7FFFFFFFL);
|
||||
insertsPerMaxRandom = (int)((double)pinsert/100.0 * 0x7FFFFFFFL);
|
||||
|
||||
System.out.print("Class: " + mapClass.getName());
|
||||
System.out.print(" threads: " + maxThreads);
|
||||
System.out.print(" size: " + nkeys);
|
||||
System.out.print(" ins: " + pinsert);
|
||||
System.out.print(" rem: " + premove);
|
||||
System.out.print(" ops: " + nops);
|
||||
System.out.println();
|
||||
|
||||
int k = 1;
|
||||
int warmups = 2;
|
||||
for (int i = 1; i <= maxThreads;) {
|
||||
test(i, nkeys, mapClass);
|
||||
if (warmups > 0)
|
||||
--warmups;
|
||||
else if (i == k) {
|
||||
k = i << 1;
|
||||
i = i + (i >>> 1);
|
||||
}
|
||||
else if (i == 1 && k == 2) {
|
||||
i = k;
|
||||
warmups = 1;
|
||||
}
|
||||
else
|
||||
i = k;
|
||||
}
|
||||
pool.shutdown();
|
||||
if (! pool.awaitTermination(LONG_DELAY_MS, MILLISECONDS))
|
||||
throw new Error();
|
||||
|
||||
if (! throwables.isEmpty())
|
||||
throw new Error
|
||||
(throwables.size() + " thread(s) terminated abruptly.");
|
||||
}
|
||||
|
||||
static Integer[] makeKeys(int n) {
|
||||
SplittableRandom rnd = new SplittableRandom();
|
||||
Integer[] key = new Integer[n];
|
||||
for (int i = 0; i < key.length; ++i)
|
||||
key[i] = new Integer(rnd.nextInt());
|
||||
return key;
|
||||
}
|
||||
|
||||
static void shuffleKeys(Integer[] key) {
|
||||
SplittableRandom rnd = new SplittableRandom();
|
||||
for (int i = key.length; i > 1; --i) {
|
||||
int j = rnd.nextInt(i);
|
||||
Integer tmp = key[j];
|
||||
key[j] = key[i-1];
|
||||
key[i-1] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
static void test(int i, int nkeys, Class mapClass) throws Exception {
|
||||
System.out.print("Threads: " + i + "\t:");
|
||||
Map<Integer, Integer> map = (Map<Integer, Integer>)
|
||||
mapClass.getDeclaredConstructor().newInstance();
|
||||
Integer[] key = makeKeys(nkeys);
|
||||
// Uncomment to start with a non-empty table
|
||||
// for (int j = 0; j < nkeys; j += 4) // start 1/4 occupied
|
||||
// map.put(key[j], key[j]);
|
||||
LoopHelpers.BarrierTimer timer = new LoopHelpers.BarrierTimer();
|
||||
CyclicBarrier barrier = new CyclicBarrier(i+1, timer);
|
||||
SplittableRandom rnd = new SplittableRandom();
|
||||
for (int t = 0; t < i; ++t)
|
||||
pool.execute(new Runner(map, key, barrier, rnd.split()));
|
||||
barrier.await();
|
||||
barrier.await();
|
||||
long time = timer.getTime();
|
||||
long tpo = time / (i * (long)nops);
|
||||
System.out.print(LoopHelpers.rightJustify(tpo) + " ns per op");
|
||||
double secs = (double)time / 1000000000.0;
|
||||
System.out.println("\t " + secs + "s run time");
|
||||
map.clear();
|
||||
}
|
||||
|
||||
static class Runner implements Runnable {
|
||||
final Map<Integer,Integer> map;
|
||||
final Integer[] key;
|
||||
final CyclicBarrier barrier;
|
||||
final SplittableRandom rnd;
|
||||
int position;
|
||||
int total;
|
||||
|
||||
Runner(Map<Integer,Integer> map,
|
||||
Integer[] key,
|
||||
CyclicBarrier barrier,
|
||||
SplittableRandom rnd) {
|
||||
this.map = map;
|
||||
this.key = key;
|
||||
this.barrier = barrier;
|
||||
this.rnd = rnd;
|
||||
position = key.length / 2;
|
||||
}
|
||||
|
||||
int step() {
|
||||
// random-walk around key positions, bunching accesses
|
||||
int r = rnd.nextInt(Integer.MAX_VALUE);
|
||||
position += (r & 7) - 3;
|
||||
while (position >= key.length) position -= key.length;
|
||||
while (position < 0) position += key.length;
|
||||
|
||||
Integer k = key[position];
|
||||
Integer x = map.get(k);
|
||||
|
||||
if (x != null) {
|
||||
if (x.intValue() != k.intValue())
|
||||
throw new Error("bad mapping: " + x + " to " + k);
|
||||
|
||||
if (r < removesPerMaxRandom) {
|
||||
if (map.remove(k) != null) {
|
||||
position = total % key.length; // move from position
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (r < insertsPerMaxRandom) {
|
||||
++position;
|
||||
map.put(k, k);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Uncomment to add a little computation between accesses
|
||||
// total += LoopHelpers.compute1(k.intValue());
|
||||
total += r;
|
||||
return 1;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
barrier.await();
|
||||
int ops = nops;
|
||||
while (ops > 0)
|
||||
ops -= step();
|
||||
barrier.await();
|
||||
}
|
||||
catch (Throwable throwable) {
|
||||
synchronized (System.err) {
|
||||
System.err.println("--------------------------------");
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
throwables.add(throwable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
104
test/jdk/java/util/concurrent/ConcurrentHashMap/ToArray.java
Normal file
104
test/jdk/java/util/concurrent/ConcurrentHashMap/ToArray.java
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 4486658 8010293
|
||||
* @summary thread safety of toArray methods of collection views
|
||||
* @author Martin Buchholz
|
||||
*/
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class ToArray {
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
final int runsPerTest = Integer.getInteger("jsr166.runsPerTest", 1);
|
||||
final int reps = 10 * runsPerTest;
|
||||
for (int i = reps; i--> 0; )
|
||||
executeTest();
|
||||
}
|
||||
|
||||
static void executeTest() throws Throwable {
|
||||
try (var executor = Executors.newCachedThreadPool()) {
|
||||
final ConcurrentHashMap<Integer, Integer> m = new ConcurrentHashMap<>();
|
||||
final ThreadLocalRandom rnd = ThreadLocalRandom.current();
|
||||
final int nCPU = Runtime.getRuntime().availableProcessors();
|
||||
final int minWorkers = 2;
|
||||
final int maxWorkers = Math.max(minWorkers, Math.min(32, nCPU));
|
||||
final int nWorkers = rnd.nextInt(minWorkers, maxWorkers + 1);
|
||||
final int sizePerWorker = 1024;
|
||||
final int maxSize = nWorkers * sizePerWorker;
|
||||
|
||||
// The foreman busy-checks that the size of the arrays obtained
|
||||
// from the keys and values views grows monotonically until it
|
||||
// reaches 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 boolean checkProgress(Object[] a) {
|
||||
int size = a.length;
|
||||
if (size < prevSize || size > maxSize)
|
||||
throw new AssertionError(
|
||||
String.format("prevSize=%d size=%d maxSize=%d",
|
||||
prevSize, size, maxSize));
|
||||
prevSize = size;
|
||||
return size == maxSize;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
Integer[] empty = new Integer[0];
|
||||
for (; ; )
|
||||
if (checkProgress(m.values().toArray())
|
||||
& checkProgress(m.keySet().toArray())
|
||||
& checkProgress(m.values().toArray(empty))
|
||||
& checkProgress(m.keySet().toArray(empty)))
|
||||
return;
|
||||
}
|
||||
}, executor);
|
||||
|
||||
// Each worker puts globally unique keys into the map
|
||||
List<CompletableFuture<?>> workers =
|
||||
IntStream.range(0, nWorkers)
|
||||
.mapToObj(w -> (Runnable) () -> {
|
||||
for (int i = 0, o = w * sizePerWorker; i < sizePerWorker; i++)
|
||||
m.put(o + i, i);
|
||||
})
|
||||
.map(r -> CompletableFuture.runAsync(r, executor))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Wait for workers and foreman to complete
|
||||
workers.forEach(CompletableFuture<?>::join);
|
||||
foreman.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
210
test/jdk/java/util/concurrent/ConcurrentHashMap/WhiteBox.java
Normal file
210
test/jdk/java/util/concurrent/ConcurrentHashMap/WhiteBox.java
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @modules java.base/java.util.concurrent:open
|
||||
* @run testng WhiteBox
|
||||
* @summary White box tests of implementation details
|
||||
*/
|
||||
|
||||
import static org.testng.Assert.*;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Test
|
||||
public class WhiteBox {
|
||||
final ThreadLocalRandom rnd = ThreadLocalRandom.current();
|
||||
final VarHandle TABLE, NEXTTABLE, SIZECTL;
|
||||
final MethodHandle TABLE_SIZE_FOR;
|
||||
|
||||
WhiteBox() throws ReflectiveOperationException {
|
||||
Class<?> mClass = ConcurrentHashMap.class;
|
||||
String nodeClassName = mClass.getName() + "$Node";
|
||||
Class<?> nodeClass = Class.forName(nodeClassName);
|
||||
Class<?> nodeArrayClass = Class.forName("[L" + nodeClassName + ";");
|
||||
MethodHandles.Lookup lookup
|
||||
= MethodHandles.privateLookupIn(mClass, MethodHandles.lookup());
|
||||
TABLE = lookup.findVarHandle(mClass, "table", nodeArrayClass);
|
||||
NEXTTABLE = lookup.findVarHandle(mClass, "nextTable", nodeArrayClass);
|
||||
SIZECTL = lookup.findVarHandle(mClass, "sizeCtl", int.class);
|
||||
TABLE_SIZE_FOR = lookup.findStatic(
|
||||
mClass, "tableSizeFor",
|
||||
MethodType.methodType(int.class, int.class));
|
||||
}
|
||||
|
||||
Object[] table(ConcurrentHashMap m) { return (Object[]) TABLE.getVolatile(m); }
|
||||
Object[] nextTable(ConcurrentHashMap m) { return (Object[]) NEXTTABLE.getVolatile(m); }
|
||||
int sizeCtl(ConcurrentHashMap m) { return (int) SIZECTL.getVolatile(m); }
|
||||
int tableSizeFor(int n) {
|
||||
try {
|
||||
return (int) TABLE_SIZE_FOR.invoke(n);
|
||||
} catch (Throwable t) { throw new AssertionError(t); }
|
||||
}
|
||||
|
||||
List<Supplier<ConcurrentHashMap>> newConcurrentHashMapSuppliers(
|
||||
int initialCapacity) {
|
||||
return List.of(
|
||||
() -> new ConcurrentHashMap(initialCapacity),
|
||||
() -> new ConcurrentHashMap(initialCapacity, 0.75f),
|
||||
() -> new ConcurrentHashMap(initialCapacity, 0.75f, 1));
|
||||
}
|
||||
|
||||
ConcurrentHashMap newConcurrentHashMap(int initialCapacity) {
|
||||
List<Supplier<ConcurrentHashMap>> suppliers
|
||||
= newConcurrentHashMapSuppliers(initialCapacity);
|
||||
return suppliers.get(rnd.nextInt(suppliers.size())).get();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultConstructor() {
|
||||
ConcurrentHashMap m = new ConcurrentHashMap();
|
||||
assertNull(table(m));
|
||||
assertEquals(sizeCtl(m), 0);
|
||||
assertResizeNotInProgress(m);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotResizeWhenInitialCapacityProvided() {
|
||||
int initialCapacity = rnd.nextInt(1, 100);
|
||||
Object[] initialTable = null;
|
||||
ConcurrentHashMap m = newConcurrentHashMap(initialCapacity);
|
||||
|
||||
// table is lazily initialized
|
||||
assertNull(table(m));
|
||||
int expectedInitialTableLength = sizeCtl(m);
|
||||
|
||||
assertInvariants(m);
|
||||
for (int i = 0; i < initialCapacity; i++) {
|
||||
m.put(i * 100 + rnd.nextInt(100), i);
|
||||
if (i == 0)
|
||||
initialTable = table(m);
|
||||
else
|
||||
assertSame(table(m), initialTable);
|
||||
assertInvariants(m);
|
||||
}
|
||||
assertEquals(initialTable.length, expectedInitialTableLength);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorsShouldGiveSameInitialCapacity() {
|
||||
int initialCapacity = rnd.nextInt(1, 256);
|
||||
long distinctTableLengths
|
||||
= newConcurrentHashMapSuppliers(initialCapacity).stream()
|
||||
.map(Supplier::get)
|
||||
.mapToInt(map -> { map.put(42, 42); return table(map).length; })
|
||||
.distinct()
|
||||
.count();
|
||||
assertEquals(1L, distinctTableLengths);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTableSizeFor() {
|
||||
assertEquals(1, tableSizeFor(0));
|
||||
assertEquals(1, tableSizeFor(1));
|
||||
assertEquals(2, tableSizeFor(2));
|
||||
assertEquals(4, tableSizeFor(3));
|
||||
assertEquals(16, tableSizeFor(15));
|
||||
assertEquals(16, tableSizeFor(16));
|
||||
assertEquals(32, tableSizeFor(17));
|
||||
int maxSize = 1 << 30;
|
||||
assertEquals(maxSize, tableSizeFor(maxSize - 1));
|
||||
assertEquals(maxSize, tableSizeFor(maxSize));
|
||||
assertEquals(maxSize, tableSizeFor(maxSize + 1));
|
||||
assertEquals(maxSize, tableSizeFor(Integer.MAX_VALUE));
|
||||
}
|
||||
|
||||
byte[] serialBytes(Object o) {
|
||||
try {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ObjectOutputStream oos = new ObjectOutputStream(bos);
|
||||
oos.writeObject(o);
|
||||
oos.flush();
|
||||
oos.close();
|
||||
return bos.toByteArray();
|
||||
} catch (Exception fail) {
|
||||
throw new AssertionError(fail);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
<T> T serialClone(T o) {
|
||||
try {
|
||||
ObjectInputStream ois = new ObjectInputStream
|
||||
(new ByteArrayInputStream(serialBytes(o)));
|
||||
T clone = (T) ois.readObject();
|
||||
assertNotSame(o, clone);
|
||||
assertSame(o.getClass(), clone.getClass());
|
||||
return clone;
|
||||
} catch (Exception fail) {
|
||||
throw new AssertionError(fail);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSerialization() {
|
||||
assertInvariants(serialClone(new ConcurrentHashMap()));
|
||||
|
||||
ConcurrentHashMap m = newConcurrentHashMap(rnd.nextInt(100));
|
||||
m.put(1, 1);
|
||||
ConcurrentHashMap clone = serialClone(m);
|
||||
assertInvariants(clone);
|
||||
assertNotSame(table(m), table(clone));
|
||||
assertEquals(m, clone);
|
||||
assertResizeNotInProgress(m);
|
||||
assertResizeNotInProgress(clone);
|
||||
}
|
||||
|
||||
/** Checks conditions which should always be true. */
|
||||
void assertInvariants(ConcurrentHashMap m) {
|
||||
if (!m.isEmpty())
|
||||
assertNotNull(table(m));
|
||||
}
|
||||
|
||||
void assertResizeNotInProgress(ConcurrentHashMap m) {
|
||||
assertTrue(sizeCtl(m) >= 0);
|
||||
assertNull(nextTable(m));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8054446 8137184 8137185
|
||||
* @modules java.base/java.util.concurrent:open
|
||||
* java.base/java.util.concurrent.locks:open
|
||||
* @summary Regression test for memory leak in remove(Object)
|
||||
*/
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
|
||||
public class RemoveLeak {
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new RemoveLeak().main();
|
||||
}
|
||||
|
||||
void main() throws Throwable {
|
||||
test(new ConcurrentLinkedDeque());
|
||||
test(new ConcurrentLinkedQueue());
|
||||
test(new LinkedBlockingDeque(10));
|
||||
test(new LinkedBlockingQueue(10));
|
||||
test(new LinkedTransferQueue());
|
||||
test(new ArrayBlockingQueue(10));
|
||||
test(new PriorityBlockingQueue());
|
||||
}
|
||||
|
||||
void test(Collection c) throws Throwable {
|
||||
assertNoLeak(c, () -> addRemove(c));
|
||||
|
||||
// A demo that the leak detection infrastructure works
|
||||
// assertNoLeak(c, () -> c.add(1));
|
||||
// printRetainedObjects(c);
|
||||
}
|
||||
|
||||
static void addRemove(Collection c) {
|
||||
c.add(1);
|
||||
c.remove(1);
|
||||
}
|
||||
|
||||
// -------- leak detection infrastructure ---------------
|
||||
void assertNoLeak(Object root, Runnable r) {
|
||||
int prev = retainedObjects(root).size();
|
||||
for (int i = 0; i < 3; i++) {
|
||||
for (int j = 0; j < 3; j++) r.run();
|
||||
int next = retainedObjects(root).size();
|
||||
if (next <= prev)
|
||||
return;
|
||||
prev = next;
|
||||
}
|
||||
throw new AssertionError(
|
||||
String.format("probable memory leak in %s: %s",
|
||||
root.getClass().getSimpleName(), root));
|
||||
}
|
||||
|
||||
ConcurrentHashMap<Class<?>, Collection<Field>> classFields
|
||||
= new ConcurrentHashMap<>();
|
||||
|
||||
Collection<Field> referenceFieldsOf(Class<?> k) {
|
||||
Collection<Field> fields = classFields.get(k);
|
||||
if (fields == null) {
|
||||
fields = new ArrayDeque<Field>();
|
||||
ArrayDeque<Field> allFields = new ArrayDeque<>();
|
||||
for (Class<?> c = k; c != null; c = c.getSuperclass())
|
||||
for (Field field : c.getDeclaredFields())
|
||||
if (!Modifier.isStatic(field.getModifiers())
|
||||
&& !field.getType().isPrimitive())
|
||||
fields.add(field);
|
||||
AccessibleObject.setAccessible(
|
||||
fields.toArray(new AccessibleObject[0]), true);
|
||||
classFields.put(k, fields);
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
static Object get(Field field, Object x) {
|
||||
try { return field.get(x); }
|
||||
catch (IllegalAccessException ex) { throw new AssertionError(ex); }
|
||||
}
|
||||
|
||||
Set<Object> retainedObjects(Object x) {
|
||||
ArrayDeque<Object> todo = new ArrayDeque<>() {
|
||||
public void push(Object x) { if (x != null) super.push(x); }};
|
||||
Set<Object> uniqueObjects = Collections.newSetFromMap(
|
||||
new IdentityHashMap<Object, Boolean>());
|
||||
todo.push(x);
|
||||
while (!todo.isEmpty()) {
|
||||
Object y = todo.pop();
|
||||
if (uniqueObjects.contains(y))
|
||||
continue;
|
||||
uniqueObjects.add(y);
|
||||
Class<?> k = y.getClass();
|
||||
if (k.isArray() && !k.getComponentType().isPrimitive()) {
|
||||
for (int i = 0, len = Array.getLength(y); i < len; i++)
|
||||
todo.push(Array.get(y, i));
|
||||
} else {
|
||||
for (Field field : referenceFieldsOf(k))
|
||||
todo.push(get(field, y));
|
||||
}
|
||||
}
|
||||
return uniqueObjects;
|
||||
}
|
||||
|
||||
/** for debugging the retained object graph */
|
||||
void printRetainedObjects(Object x) {
|
||||
for (Object y : retainedObjects(x))
|
||||
System.out.printf("%s : %s%n", y.getClass().getSimpleName(), y);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,325 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @modules java.base/java.util.concurrent:open
|
||||
* @run testng WhiteBox
|
||||
* @summary White box tests of implementation details
|
||||
*/
|
||||
|
||||
import static org.testng.Assert.*;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Test
|
||||
public class WhiteBox {
|
||||
final ThreadLocalRandom rnd = ThreadLocalRandom.current();
|
||||
final VarHandle HEAD, TAIL, ITEM, NEXT;
|
||||
|
||||
WhiteBox() throws ReflectiveOperationException {
|
||||
Class<?> qClass = ConcurrentLinkedQueue.class;
|
||||
Class<?> nodeClass = Class.forName(qClass.getName() + "$Node");
|
||||
MethodHandles.Lookup lookup
|
||||
= MethodHandles.privateLookupIn(qClass, MethodHandles.lookup());
|
||||
HEAD = lookup.findVarHandle(qClass, "head", nodeClass);
|
||||
TAIL = lookup.findVarHandle(qClass, "tail", nodeClass);
|
||||
NEXT = lookup.findVarHandle(nodeClass, "next", nodeClass);
|
||||
ITEM = lookup.findVarHandle(nodeClass, "item", Object.class);
|
||||
}
|
||||
|
||||
Object head(ConcurrentLinkedQueue q) { return HEAD.getVolatile(q); }
|
||||
Object tail(ConcurrentLinkedQueue q) { return TAIL.getVolatile(q); }
|
||||
Object item(Object node) { return ITEM.getVolatile(node); }
|
||||
Object next(Object node) { return NEXT.getVolatile(node); }
|
||||
|
||||
int nodeCount(ConcurrentLinkedQueue q) {
|
||||
int i = 0;
|
||||
for (Object p = head(q); p != null; ) {
|
||||
i++;
|
||||
if (p == (p = next(p))) p = head(q);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
void assertIsSelfLinked(Object node) {
|
||||
assertSame(next(node), node);
|
||||
assertNull(item(node));
|
||||
}
|
||||
|
||||
void assertIsNotSelfLinked(Object node) {
|
||||
assertNotSame(node, next(node));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addRemove() {
|
||||
ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
|
||||
assertInvariants(q);
|
||||
assertNull(item(head(q)));
|
||||
assertEquals(nodeCount(q), 1);
|
||||
q.add(1);
|
||||
assertEquals(nodeCount(q), 2);
|
||||
assertInvariants(q);
|
||||
q.remove(1);
|
||||
assertEquals(nodeCount(q), 1);
|
||||
assertInvariants(q);
|
||||
}
|
||||
|
||||
/**
|
||||
* Traversal actions that visit every node and do nothing, but
|
||||
* have side effect of squeezing out dead nodes.
|
||||
*/
|
||||
@DataProvider
|
||||
public Object[][] traversalActions() {
|
||||
return List.<Consumer<ConcurrentLinkedQueue>>of(
|
||||
q -> q.forEach(e -> {}),
|
||||
q -> assertFalse(q.contains(new Object())),
|
||||
q -> assertFalse(q.remove(new Object())),
|
||||
q -> q.spliterator().forEachRemaining(e -> {}),
|
||||
q -> q.stream().collect(toList()),
|
||||
q -> assertFalse(q.removeIf(e -> false)),
|
||||
q -> assertFalse(q.removeAll(List.of())))
|
||||
.stream().map(x -> new Object[]{ x }).toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "traversalActions")
|
||||
public void traversalOperationsCollapseNodes(
|
||||
Consumer<ConcurrentLinkedQueue> traversalAction) {
|
||||
ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
|
||||
Object oldHead;
|
||||
int n = 1 + rnd.nextInt(5);
|
||||
for (int i = 0; i < n; i++) q.add(i);
|
||||
assertInvariants(q);
|
||||
assertEquals(nodeCount(q), n + 1);
|
||||
oldHead = head(q);
|
||||
traversalAction.accept(q); // collapses head node
|
||||
assertIsSelfLinked(oldHead);
|
||||
assertInvariants(q);
|
||||
assertEquals(nodeCount(q), n);
|
||||
// Iterator.remove does not currently try to collapse dead nodes
|
||||
for (Iterator it = q.iterator(); it.hasNext(); ) {
|
||||
it.next();
|
||||
it.remove();
|
||||
}
|
||||
assertEquals(nodeCount(q), n);
|
||||
assertInvariants(q);
|
||||
oldHead = head(q);
|
||||
traversalAction.accept(q); // collapses all nodes
|
||||
if (n > 1) assertIsSelfLinked(oldHead);
|
||||
assertEquals(nodeCount(q), 1);
|
||||
assertInvariants(q);
|
||||
|
||||
for (int i = 0; i < n + 1; i++) q.add(i);
|
||||
assertEquals(nodeCount(q), n + 2);
|
||||
oldHead = head(q);
|
||||
assertEquals(0, q.poll()); // 2 leading nodes collapsed
|
||||
assertIsSelfLinked(oldHead);
|
||||
assertEquals(nodeCount(q), n);
|
||||
assertTrue(q.remove(n));
|
||||
assertEquals(nodeCount(q), n);
|
||||
traversalAction.accept(q); // trailing node is never collapsed
|
||||
}
|
||||
|
||||
@Test(dataProvider = "traversalActions")
|
||||
public void traversalOperationsCollapseLeadingNodes(
|
||||
Consumer<ConcurrentLinkedQueue> traversalAction) {
|
||||
ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
|
||||
Object oldHead;
|
||||
int n = 1 + rnd.nextInt(5);
|
||||
for (int i = 0; i < n; i++) q.add(i);
|
||||
assertEquals(nodeCount(q), n + 1);
|
||||
oldHead = head(q);
|
||||
traversalAction.accept(q);
|
||||
assertInvariants(q);
|
||||
assertEquals(nodeCount(q), n);
|
||||
assertIsSelfLinked(oldHead);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "traversalActions")
|
||||
public void traversalOperationsDoNotSelfLinkInteriorNodes(
|
||||
Consumer<ConcurrentLinkedQueue> traversalAction) {
|
||||
ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
|
||||
int c;
|
||||
int n = 3 + rnd.nextInt(3);
|
||||
for (int i = 0; i < n; i++) q.add(i);
|
||||
Object oneNode;
|
||||
for (oneNode = head(q);
|
||||
! (item(oneNode) != null && item(oneNode).equals(1));
|
||||
oneNode = next(oneNode))
|
||||
;
|
||||
Object next = next(oneNode);
|
||||
c = nodeCount(q);
|
||||
for (Iterator it = q.iterator(); it.hasNext(); )
|
||||
if (it.next().equals(1)) it.remove();
|
||||
assertEquals(nodeCount(q), c - 1); // iterator detached head!
|
||||
assertNull(item(oneNode));
|
||||
assertSame(next, next(oneNode));
|
||||
assertInvariants(q);
|
||||
c = nodeCount(q);
|
||||
traversalAction.accept(q);
|
||||
assertEquals(nodeCount(q), c - 1);
|
||||
assertSame(next, next(oneNode)); // un-linked, but not self-linked
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that traversal operations collapse a random pattern of
|
||||
* dead nodes as could normally only occur with a race.
|
||||
*/
|
||||
@Test(dataProvider = "traversalActions")
|
||||
public void traversalOperationsCollapseRandomNodes(
|
||||
Consumer<ConcurrentLinkedQueue> traversalAction) {
|
||||
ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
|
||||
int n = rnd.nextInt(6);
|
||||
for (int i = 0; i < n; i++) q.add(i);
|
||||
ArrayList nulledOut = new ArrayList();
|
||||
for (Object p = head(q); p != null; p = next(p))
|
||||
if (item(p) != null && rnd.nextBoolean()) {
|
||||
nulledOut.add(item(p));
|
||||
ITEM.setVolatile(p, null);
|
||||
}
|
||||
traversalAction.accept(q);
|
||||
int c = nodeCount(q);
|
||||
assertEquals(q.size(), c - (q.contains(n - 1) ? 0 : 1));
|
||||
for (int i = 0; i < n; i++)
|
||||
assertTrue(nulledOut.contains(i) ^ q.contains(i));
|
||||
}
|
||||
|
||||
/**
|
||||
* Traversal actions that remove every element, and are also
|
||||
* expected to squeeze out dead nodes.
|
||||
*/
|
||||
@DataProvider
|
||||
public Object[][] bulkRemovalActions() {
|
||||
return List.<Consumer<ConcurrentLinkedQueue>>of(
|
||||
q -> q.clear(),
|
||||
q -> assertTrue(q.removeIf(e -> true)),
|
||||
q -> assertTrue(q.retainAll(List.of())))
|
||||
.stream().map(x -> new Object[]{ x }).toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "bulkRemovalActions")
|
||||
public void bulkRemovalOperationsCollapseNodes(
|
||||
Consumer<ConcurrentLinkedQueue> bulkRemovalAction) {
|
||||
ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
|
||||
int n = 1 + rnd.nextInt(5);
|
||||
for (int i = 0; i < n; i++) q.add(i);
|
||||
bulkRemovalAction.accept(q);
|
||||
assertEquals(nodeCount(q), 1);
|
||||
assertInvariants(q);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions that remove the first element, and are expected to
|
||||
* leave at most one slack dead node at head.
|
||||
*/
|
||||
@DataProvider
|
||||
public Object[][] pollActions() {
|
||||
return List.<Consumer<ConcurrentLinkedQueue>>of(
|
||||
q -> assertNotNull(q.poll()),
|
||||
q -> assertNotNull(q.remove()))
|
||||
.stream().map(x -> new Object[]{ x }).toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "pollActions")
|
||||
public void pollActionsOneNodeSlack(
|
||||
Consumer<ConcurrentLinkedQueue> pollAction) {
|
||||
ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
|
||||
int n = 1 + rnd.nextInt(5);
|
||||
for (int i = 0; i < n; i++) q.add(i);
|
||||
assertEquals(nodeCount(q), n + 1);
|
||||
for (int i = 0; i < n; i++) {
|
||||
int c = nodeCount(q);
|
||||
boolean slack = item(head(q)) == null;
|
||||
if (slack) assertNotNull(item(next(head(q))));
|
||||
pollAction.accept(q);
|
||||
assertEquals(nodeCount(q), q.isEmpty() ? 1 : c - (slack ? 2 : 0));
|
||||
}
|
||||
assertInvariants(q);
|
||||
}
|
||||
|
||||
byte[] serialBytes(Object o) {
|
||||
try {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ObjectOutputStream oos = new ObjectOutputStream(bos);
|
||||
oos.writeObject(o);
|
||||
oos.flush();
|
||||
oos.close();
|
||||
return bos.toByteArray();
|
||||
} catch (Exception fail) {
|
||||
throw new AssertionError(fail);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
<T> T serialClone(T o) {
|
||||
try {
|
||||
ObjectInputStream ois = new ObjectInputStream
|
||||
(new ByteArrayInputStream(serialBytes(o)));
|
||||
T clone = (T) ois.readObject();
|
||||
assertNotSame(o, clone);
|
||||
assertSame(o.getClass(), clone.getClass());
|
||||
return clone;
|
||||
} catch (Exception fail) {
|
||||
throw new AssertionError(fail);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSerialization() {
|
||||
ConcurrentLinkedQueue q = serialClone(new ConcurrentLinkedQueue());
|
||||
assertInvariants(q);
|
||||
}
|
||||
|
||||
/** Checks conditions which should always be true. */
|
||||
void assertInvariants(ConcurrentLinkedQueue q) {
|
||||
assertNotNull(head(q));
|
||||
assertNotNull(tail(q));
|
||||
// head is never self-linked (but tail may!)
|
||||
for (Object h; next(h = head(q)) == h; )
|
||||
assertNotSame(h, head(q)); // must be update race
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6312056 4155650 4294891 4904074
|
||||
* @summary Reasonable things should happen if mutating while iterating.
|
||||
*/
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ConcurrentSkipListMap;
|
||||
|
||||
public class ConcurrentModification {
|
||||
static volatile int passed = 0, failed = 0;
|
||||
|
||||
static void fail(String msg) {
|
||||
failed++;
|
||||
new AssertionError(msg).printStackTrace();
|
||||
}
|
||||
|
||||
static void unexpected(Throwable t) {
|
||||
failed++;
|
||||
t.printStackTrace();
|
||||
}
|
||||
|
||||
static void check(boolean condition, String msg) {
|
||||
if (condition)
|
||||
passed++;
|
||||
else
|
||||
fail(msg);
|
||||
}
|
||||
|
||||
static void check(boolean condition) {
|
||||
check(condition, "Assertion failed");
|
||||
}
|
||||
|
||||
private static void test(ConcurrentMap<Integer, Integer> m)
|
||||
{
|
||||
try {
|
||||
m.clear();
|
||||
check(m.isEmpty());
|
||||
m.put(1,2);
|
||||
Iterator<Map.Entry<Integer,Integer>> it = m.entrySet().iterator();
|
||||
if (it.hasNext()) {
|
||||
m.remove(1); // sneaky
|
||||
Map.Entry<Integer, Integer> e = it.next();
|
||||
check(m.isEmpty());
|
||||
check(e.getKey() == 1);
|
||||
check(e.getValue() == 2);
|
||||
}
|
||||
} catch (Throwable t) {unexpected(t);}
|
||||
|
||||
try {
|
||||
m.clear();
|
||||
check(m.isEmpty());
|
||||
m.put(1,2);
|
||||
Iterator<Map.Entry<Integer,Integer>> it = m.entrySet().iterator();
|
||||
if (it.hasNext()) {
|
||||
m.put(1,3); // sneaky
|
||||
Map.Entry<Integer, Integer> e = it.next();
|
||||
check(e.getKey() == 1);
|
||||
check(e.getValue() == 2 || e.getValue() == 3);
|
||||
if (m instanceof ConcurrentHashMap) {
|
||||
e.setValue(4);
|
||||
check(m.get(1) == 4);
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {unexpected(t);}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
test(new ConcurrentHashMap<Integer,Integer>());
|
||||
test(new ConcurrentSkipListMap<Integer,Integer>());
|
||||
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new Error("Some tests failed");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @run testng ConcurrentRemoveIf
|
||||
* @bug 8078645
|
||||
* @summary Test removeIf on views of concurrent maps
|
||||
*/
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ConcurrentNavigableMap;
|
||||
import java.util.concurrent.ConcurrentSkipListMap;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Test
|
||||
public class ConcurrentRemoveIf {
|
||||
static final int K = 100;
|
||||
static final int SIZE = 1000;
|
||||
static final int HALF_SIZE = SIZE / 2;
|
||||
|
||||
@DataProvider()
|
||||
public static Object[][] concurrentMapViewRemoveIfActions() {
|
||||
List<Object[]> rows = new ArrayList<>();
|
||||
|
||||
// ConcurrentMap classes to test
|
||||
Map<String, Supplier<ConcurrentMap<Integer, Integer>>> maps = new HashMap<>();
|
||||
maps.put("ConcurrentHashMap", ConcurrentHashMap::new);
|
||||
maps.put("ConcurrentSkipListMap", ConcurrentSkipListMap::new);
|
||||
|
||||
// ConcurrentMap actions
|
||||
Map<String, Consumer<ConcurrentMap<Integer, Integer>>> actions = new HashMap<>();
|
||||
actions.put(".entrySet().removeIf()", m -> m.entrySet().removeIf(e -> e.getValue() == 0));
|
||||
actions.put(".values().removeIf()", m -> m.values().removeIf(v -> v == 0));
|
||||
|
||||
// ConcurrentNavigableMap actions
|
||||
Map<String, Consumer<ConcurrentNavigableMap<Integer, Integer>>> navActions = new HashMap<>();
|
||||
navActions.put(".headMap()/tailMap().entrySet().removeIf()",
|
||||
m -> {
|
||||
ConcurrentMap<Integer, Integer> left = m.headMap(HALF_SIZE, false);
|
||||
ConcurrentMap<Integer, Integer> right = m.tailMap(HALF_SIZE, true);
|
||||
left.entrySet().removeIf(e -> e.getValue() == 0);
|
||||
right.entrySet().removeIf(e -> e.getValue() == 0);
|
||||
});
|
||||
navActions.put(".headMap()/tailMap().values().removeIf()",
|
||||
m -> {
|
||||
ConcurrentMap<Integer, Integer> left = m.headMap(HALF_SIZE, false);
|
||||
ConcurrentMap<Integer, Integer> right = m.tailMap(HALF_SIZE, true);
|
||||
left.values().removeIf(v -> v == 0);
|
||||
right.values().removeIf(v -> v == 0);
|
||||
});
|
||||
navActions.put(".descendingMap().entrySet().removeIf()",
|
||||
m -> {
|
||||
ConcurrentMap<Integer, Integer> dm = m.descendingMap();
|
||||
dm.entrySet().removeIf(e -> e.getValue() == 0);
|
||||
});
|
||||
navActions.put(".descendingMap().values().removeIf()",
|
||||
m -> {
|
||||
ConcurrentMap<Integer, Integer> dm = m.descendingMap();
|
||||
dm.values().removeIf(v -> v == 0);
|
||||
});
|
||||
|
||||
maps.forEach((mapDescription, sm) -> {
|
||||
actions.forEach((actionDescription, action) -> {
|
||||
rows.add(new Object[] {mapDescription + actionDescription, sm, action});
|
||||
});
|
||||
|
||||
if (sm.get() instanceof ConcurrentNavigableMap) {
|
||||
navActions.forEach((actionDescription, action) -> {
|
||||
rows.add(new Object[] {mapDescription + actionDescription, sm, action});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return rows.toArray(new Object[0][]);
|
||||
}
|
||||
|
||||
ExecutorService executorService = Executors.newCachedThreadPool();
|
||||
|
||||
@AfterClass
|
||||
public void after() {
|
||||
executorService.shutdown();
|
||||
}
|
||||
|
||||
@Test(dataProvider = "concurrentMapViewRemoveIfActions")
|
||||
public void testMap(String desc, Supplier<ConcurrentMap<Integer, Integer>> ms, Consumer<ConcurrentMap<Integer, Integer>> action)
|
||||
throws InterruptedException {
|
||||
for (int i = 0; i < K; i++) {
|
||||
testMap(ms.get(), action);
|
||||
}
|
||||
}
|
||||
|
||||
private void testMap(ConcurrentMap<Integer, Integer> map, Consumer<ConcurrentMap<Integer, Integer>> action)
|
||||
throws InterruptedException {
|
||||
// put 0's
|
||||
fillMap(map, 0);
|
||||
|
||||
// To start working simultaneously
|
||||
CyclicBarrier threadStarted = new CyclicBarrier(2);
|
||||
|
||||
// This task puts 1's into map
|
||||
CompletableFuture<Void> putter = CompletableFuture.runAsync(
|
||||
awaitOn(threadStarted, () -> fillMap(map, 1)),
|
||||
executorService);
|
||||
|
||||
// This task performs the map action to remove all 0's from map
|
||||
CompletableFuture<Void> remover = CompletableFuture.runAsync(
|
||||
awaitOn(threadStarted, () -> action.accept(map)),
|
||||
executorService);
|
||||
|
||||
// Wait for both tasks to complete
|
||||
CompletableFuture.allOf(putter, remover).join();
|
||||
|
||||
assertEquals(map.size(), SIZE, "Map size incorrect");
|
||||
map.forEach((k, v) -> assertEquals(v, (Integer)1));
|
||||
}
|
||||
|
||||
static void fillMap(ConcurrentMap<Integer, Integer> map, int value) {
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
map.put(i, value);
|
||||
}
|
||||
}
|
||||
|
||||
static Runnable awaitOn(CyclicBarrier threadStarted, Runnable r) {
|
||||
return () -> {
|
||||
try {
|
||||
threadStarted.await();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
r.run();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 4486658 6785442
|
||||
* @summary Checks that a set of threads can repeatedly get and modify items
|
||||
* @library /test/lib
|
||||
* @run main ConcurrentQueueLoops 8 123456
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
public class ConcurrentQueueLoops {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
ExecutorService pool;
|
||||
AtomicInteger totalItems;
|
||||
boolean print;
|
||||
|
||||
// Suitable for benchmarking. Overridden by args[0] for testing.
|
||||
int maxStages = 20;
|
||||
|
||||
// Suitable for benchmarking. Overridden by args[1] for testing.
|
||||
int items = 1024 * 1024;
|
||||
|
||||
Collection<Queue<Integer>> concurrentQueues() {
|
||||
List<Queue<Integer>> queues = new ArrayList<>();
|
||||
queues.add(new ConcurrentLinkedDeque<Integer>());
|
||||
queues.add(new ConcurrentLinkedQueue<Integer>());
|
||||
queues.add(new ArrayBlockingQueue<Integer>(items, false));
|
||||
//queues.add(new ArrayBlockingQueue<Integer>(count, true));
|
||||
queues.add(new LinkedBlockingQueue<Integer>());
|
||||
queues.add(new LinkedBlockingDeque<Integer>());
|
||||
queues.add(new LinkedTransferQueue<Integer>());
|
||||
|
||||
// Following additional implementations are available from:
|
||||
// http://gee.cs.oswego.edu/dl/concurrency-interest/index.html
|
||||
// queues.add(new SynchronizedLinkedListQueue<Integer>());
|
||||
|
||||
// Avoid "first fast, second slow" benchmark effect.
|
||||
Collections.shuffle(queues);
|
||||
return queues;
|
||||
}
|
||||
|
||||
void test(String[] args) throws Throwable {
|
||||
if (args.length > 0)
|
||||
maxStages = Integer.parseInt(args[0]);
|
||||
if (args.length > 1)
|
||||
items = Integer.parseInt(args[1]);
|
||||
|
||||
for (Queue<Integer> queue : concurrentQueues())
|
||||
test(queue);
|
||||
}
|
||||
|
||||
void test(final Queue<Integer> q) throws Throwable {
|
||||
System.out.println(q.getClass().getSimpleName());
|
||||
pool = Executors.newCachedThreadPool();
|
||||
print = false;
|
||||
|
||||
print = false;
|
||||
System.out.println("Warmup...");
|
||||
oneRun(1, items, q);
|
||||
oneRun(3, items, q);
|
||||
print = true;
|
||||
|
||||
for (int i = 1; i <= maxStages; i += (i+1) >>> 1) {
|
||||
oneRun(i, items, q);
|
||||
}
|
||||
pool.shutdown();
|
||||
check(pool.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
|
||||
}
|
||||
|
||||
class Stage implements Callable<Integer> {
|
||||
final Queue<Integer> queue;
|
||||
final CyclicBarrier barrier;
|
||||
int items;
|
||||
Stage(Queue<Integer> q, CyclicBarrier b, int items) {
|
||||
queue = q;
|
||||
barrier = b;
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public Integer call() {
|
||||
// Repeatedly take something from queue if possible,
|
||||
// transform it, and put back in.
|
||||
try {
|
||||
barrier.await();
|
||||
int l = 4321;
|
||||
int takes = 0;
|
||||
for (;;) {
|
||||
Integer item = queue.poll();
|
||||
if (item != null) {
|
||||
++takes;
|
||||
l = LoopHelpers.compute2(item.intValue());
|
||||
}
|
||||
else if (takes != 0) {
|
||||
totalItems.getAndAdd(-takes);
|
||||
takes = 0;
|
||||
}
|
||||
else if (totalItems.get() <= 0)
|
||||
break;
|
||||
l = LoopHelpers.compute1(l);
|
||||
if (items > 0) {
|
||||
--items;
|
||||
queue.offer(new Integer(l));
|
||||
}
|
||||
else if ( (l & (3 << 5)) == 0) // spinwait
|
||||
Thread.sleep(1);
|
||||
}
|
||||
return new Integer(l);
|
||||
}
|
||||
catch (Throwable t) { unexpected(t); return null; }
|
||||
}
|
||||
}
|
||||
|
||||
void oneRun(int n, int items, final Queue<Integer> q) throws Exception {
|
||||
LoopHelpers.BarrierTimer timer = new LoopHelpers.BarrierTimer();
|
||||
CyclicBarrier barrier = new CyclicBarrier(n + 1, timer);
|
||||
totalItems = new AtomicInteger(n * items);
|
||||
ArrayList<Future<Integer>> results = new ArrayList<>(n);
|
||||
for (int i = 0; i < n; ++i)
|
||||
results.add(pool.submit(new Stage(q, barrier, items)));
|
||||
|
||||
if (print)
|
||||
System.out.print("Threads: " + n + "\t:");
|
||||
barrier.await();
|
||||
int total = 0;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Future<Integer> f = results.get(i);
|
||||
Integer r = f.get();
|
||||
total += r.intValue();
|
||||
}
|
||||
long endTime = System.nanoTime();
|
||||
long time = endTime - timer.startTime;
|
||||
if (print)
|
||||
System.out.println(LoopHelpers.rightJustify(time / (items * n)) + " ns per item");
|
||||
if (total == 0) // avoid overoptimization
|
||||
System.out.println("useless result: " + total);
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new ConcurrentQueueLoops().instanceMain(args);}
|
||||
public void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
189
test/jdk/java/util/concurrent/ConcurrentQueues/GCRetention.java
Normal file
189
test/jdk/java/util/concurrent/ConcurrentQueues/GCRetention.java
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
/*
|
||||
* @test
|
||||
* @bug 6785442
|
||||
* @summary Benchmark that tries to GC-tenure head, followed by
|
||||
* many add/remove operations.
|
||||
* @run main GCRetention just-testing
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
import java.util.LinkedList;
|
||||
import java.util.PriorityQueue;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.Map;
|
||||
|
||||
public class GCRetention {
|
||||
boolean benchmarkMode;
|
||||
int count;
|
||||
|
||||
/** No guarantees, but effective in practice. */
|
||||
static void forceFullGc() {
|
||||
long timeoutMillis = 1000L;
|
||||
CountDownLatch finalized = new CountDownLatch(1);
|
||||
ReferenceQueue<Object> queue = new ReferenceQueue<>();
|
||||
WeakReference<Object> ref = new WeakReference<>(
|
||||
new Object() { protected void finalize() { finalized.countDown(); }},
|
||||
queue);
|
||||
try {
|
||||
for (int tries = 3; tries--> 0; ) {
|
||||
System.gc();
|
||||
if (finalized.await(timeoutMillis, MILLISECONDS)
|
||||
&& queue.remove(timeoutMillis) != null
|
||||
&& ref.get() == null) {
|
||||
System.runFinalization(); // try to pick up stragglers
|
||||
return;
|
||||
}
|
||||
timeoutMillis *= 4;
|
||||
}
|
||||
} catch (InterruptedException unexpected) {
|
||||
throw new AssertionError("unexpected InterruptedException");
|
||||
}
|
||||
throw new AssertionError("failed to do a \"full\" gc");
|
||||
}
|
||||
|
||||
final Map<String,String> results = new ConcurrentHashMap<>();
|
||||
|
||||
Collection<Queue<Boolean>> queues() {
|
||||
List<Queue<Boolean>> queues = new ArrayList<>();
|
||||
queues.add(new ConcurrentLinkedDeque<Boolean>());
|
||||
queues.add(new ConcurrentLinkedQueue<Boolean>());
|
||||
queues.add(new ArrayBlockingQueue<Boolean>(count, false));
|
||||
queues.add(new ArrayBlockingQueue<Boolean>(count, true));
|
||||
queues.add(new LinkedBlockingQueue<Boolean>());
|
||||
queues.add(new LinkedBlockingDeque<Boolean>());
|
||||
queues.add(new PriorityBlockingQueue<Boolean>());
|
||||
queues.add(new PriorityQueue<Boolean>());
|
||||
queues.add(new LinkedList<Boolean>());
|
||||
queues.add(new LinkedTransferQueue<Boolean>());
|
||||
|
||||
// Following additional implementations are available from:
|
||||
// http://gee.cs.oswego.edu/dl/concurrency-interest/index.html
|
||||
// queues.add(new SynchronizedLinkedListQueue<Boolean>());
|
||||
|
||||
// Avoid "first fast, second slow" benchmark effect.
|
||||
Collections.shuffle(queues);
|
||||
return queues;
|
||||
}
|
||||
|
||||
void prettyPrintResults() {
|
||||
List<String> classNames = new ArrayList<>(results.keySet());
|
||||
Collections.sort(classNames);
|
||||
int maxClassNameLength = 0;
|
||||
int maxNanosLength = 0;
|
||||
for (String name : classNames) {
|
||||
if (maxClassNameLength < name.length())
|
||||
maxClassNameLength = name.length();
|
||||
if (maxNanosLength < results.get(name).length())
|
||||
maxNanosLength = results.get(name).length();
|
||||
}
|
||||
String format = String.format("%%%ds %%%ds nanos/item%%n",
|
||||
maxClassNameLength, maxNanosLength);
|
||||
for (String name : classNames)
|
||||
System.out.printf(format, name, results.get(name));
|
||||
}
|
||||
|
||||
void test(String[] args) {
|
||||
benchmarkMode = ! (args.length > 0 && args[0].equals("just-testing"));
|
||||
count = benchmarkMode ? 1024 * 1024 : 30;
|
||||
|
||||
// Warmup
|
||||
for (Queue<Boolean> queue : queues())
|
||||
test(queue);
|
||||
results.clear();
|
||||
for (Queue<Boolean> queue : queues())
|
||||
test(queue);
|
||||
|
||||
prettyPrintResults();
|
||||
}
|
||||
|
||||
void test(Queue<Boolean> q) {
|
||||
long t0 = System.nanoTime();
|
||||
for (int i = 0; i < count; i++)
|
||||
check(q.add(Boolean.TRUE));
|
||||
if (benchmarkMode) forceFullGc();
|
||||
// forceFullGc();
|
||||
Boolean x;
|
||||
while ((x = q.poll()) != null)
|
||||
equal(x, Boolean.TRUE);
|
||||
check(q.isEmpty());
|
||||
|
||||
for (int i = 0; i < 10 * count; i++) {
|
||||
for (int k = 0; k < 3; k++)
|
||||
check(q.add(Boolean.TRUE));
|
||||
for (int k = 0; k < 3; k++)
|
||||
if (q.poll() != Boolean.TRUE)
|
||||
fail();
|
||||
}
|
||||
check(q.isEmpty());
|
||||
|
||||
String className = q.getClass().getSimpleName();
|
||||
long elapsed = System.nanoTime() - t0;
|
||||
int nanos = (int) ((double) elapsed / (10 * 3 * count));
|
||||
results.put(className, String.valueOf(nanos));
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new GCRetention().instanceMain(args);}
|
||||
public void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
|
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6805775 6815766
|
||||
* @summary Check weak consistency of concurrent queue iterators
|
||||
*/
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public class IteratorWeakConsistency {
|
||||
final ThreadLocalRandom rnd = ThreadLocalRandom.current();
|
||||
|
||||
void test(String[] args) throws Throwable {
|
||||
test(new LinkedBlockingQueue());
|
||||
test(new LinkedBlockingQueue(20));
|
||||
test(new LinkedBlockingDeque());
|
||||
test(new LinkedBlockingDeque(20));
|
||||
test(new ConcurrentLinkedDeque());
|
||||
test(new ConcurrentLinkedQueue());
|
||||
test(new LinkedTransferQueue());
|
||||
test(new ArrayBlockingQueue(20));
|
||||
}
|
||||
|
||||
void checkExhausted(Iterator it) {
|
||||
if (rnd.nextBoolean()) {
|
||||
check(!it.hasNext());
|
||||
}
|
||||
if (rnd.nextBoolean()) {
|
||||
it.forEachRemaining(e -> { throw new AssertionError(); });
|
||||
}
|
||||
if (rnd.nextBoolean())
|
||||
try { it.next(); fail("should throw"); }
|
||||
catch (NoSuchElementException success) {}
|
||||
}
|
||||
|
||||
void checkRemoveThrowsISE(Iterator it) {
|
||||
if (rnd.nextBoolean()) {
|
||||
try { it.remove(); fail("should throw"); }
|
||||
catch (IllegalStateException success) {}
|
||||
}
|
||||
}
|
||||
|
||||
void checkRemoveHasNoEffect(Iterator it, Collection c) {
|
||||
if (rnd.nextBoolean()) {
|
||||
int size = c.size();
|
||||
it.remove(); // no effect
|
||||
equal(c.size(), size);
|
||||
checkRemoveThrowsISE(it);
|
||||
}
|
||||
}
|
||||
|
||||
void test(Queue q) {
|
||||
//----------------------------------------------------------------
|
||||
// Check iterators on an empty q
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Iterator it = q.iterator();
|
||||
if (rnd.nextBoolean()) checkExhausted(it);
|
||||
checkRemoveThrowsISE(it);
|
||||
}
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
// TODO: make this more general
|
||||
try {
|
||||
for (int i = 0; i < 10; i++)
|
||||
q.add(i);
|
||||
Iterator it = q.iterator();
|
||||
q.poll();
|
||||
q.poll();
|
||||
q.poll();
|
||||
q.remove(7);
|
||||
List list = new ArrayList();
|
||||
while (it.hasNext())
|
||||
list.add(it.next());
|
||||
equal(list, Arrays.asList(0, 3, 4, 5, 6, 8, 9));
|
||||
check(! list.contains(null));
|
||||
System.err.printf("%s: %s%n",
|
||||
q.getClass().getSimpleName(),
|
||||
list);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
try {
|
||||
q.clear();
|
||||
q.add(1);
|
||||
q.add(2);
|
||||
q.add(3);
|
||||
q.add(4);
|
||||
Iterator it = q.iterator();
|
||||
it.next();
|
||||
q.remove(2);
|
||||
q.remove(1);
|
||||
q.remove(3);
|
||||
boolean found4 = false;
|
||||
while (it.hasNext()) {
|
||||
found4 |= it.next().equals(4);
|
||||
}
|
||||
check(found4);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
try {
|
||||
q.clear();
|
||||
Object x = new Object();
|
||||
for (int i = 0; i < 20; i++)
|
||||
q.add(x);
|
||||
equal(20, q.size());
|
||||
Iterator it1 = q.iterator();
|
||||
Iterator it2 = q.iterator();
|
||||
try { it1.remove(); fail(); }
|
||||
catch (IllegalStateException success) {}
|
||||
try { it2.remove(); fail(); }
|
||||
catch (IllegalStateException success) {}
|
||||
|
||||
check(it1.next() == x);
|
||||
check(it2.hasNext());
|
||||
check(it2.next() == x);
|
||||
it1.remove();
|
||||
it2.remove();
|
||||
equal(19, q.size());
|
||||
try { it1.remove(); fail(); }
|
||||
catch (IllegalStateException success) {}
|
||||
try { it2.remove(); fail(); }
|
||||
catch (IllegalStateException success) {}
|
||||
equal(19, q.size());
|
||||
|
||||
it1.next();
|
||||
check(it2.hasNext());
|
||||
it2.next();
|
||||
it2.remove();
|
||||
it1.remove();
|
||||
equal(18, q.size());
|
||||
|
||||
it1.next();
|
||||
it2.next();
|
||||
check(q.remove() == x);
|
||||
equal(17, q.size());
|
||||
it1.remove();
|
||||
it2.remove();
|
||||
equal(17, q.size());
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Check "interior" removal of alternating elements
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
q.clear();
|
||||
final int remainingCapacity = (q instanceof BlockingQueue) ?
|
||||
((BlockingQueue)q).remainingCapacity() :
|
||||
Integer.MAX_VALUE;
|
||||
final int capacity = Math.min(20, remainingCapacity);
|
||||
List<Iterator> its = new ArrayList<>();
|
||||
// Move to "middle"
|
||||
for (int i = 0; i < capacity/2; i++) {
|
||||
check(q.add(i));
|
||||
equal(q.poll(), i);
|
||||
}
|
||||
for (int i = 0; i < capacity; i++)
|
||||
check(q.add(i));
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
Iterator it = q.iterator();
|
||||
its.add(it);
|
||||
for (int j = 0; j < i; j++)
|
||||
equal(j, it.next());
|
||||
}
|
||||
|
||||
// Remove all even elements, in either direction using
|
||||
// q.remove(), or iterator.remove()
|
||||
switch (rnd.nextInt(3)) {
|
||||
case 0:
|
||||
for (int i = 0; i < capacity; i+=2)
|
||||
check(q.remove(i));
|
||||
break;
|
||||
case 1:
|
||||
for (int i = capacity - 2; i >= 0; i-=2)
|
||||
check(q.remove(i));
|
||||
break;
|
||||
case 2:
|
||||
Iterator it = q.iterator();
|
||||
while (it.hasNext()) {
|
||||
int i = (Integer) it.next();
|
||||
if ((i & 1) == 0)
|
||||
it.remove();
|
||||
}
|
||||
break;
|
||||
default: throw new AssertionError();
|
||||
}
|
||||
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
Iterator it = its.get(i);
|
||||
boolean even = ((i & 1) == 0);
|
||||
if (even) {
|
||||
if (rnd.nextBoolean()) check(it.hasNext());
|
||||
equal(i, it.next());
|
||||
for (int j = i+1; j < capacity; j += 2)
|
||||
equal(j, it.next());
|
||||
check(!it.hasNext());
|
||||
} else { /* odd */
|
||||
if (rnd.nextBoolean()) check(it.hasNext());
|
||||
checkRemoveHasNoEffect(it, q);
|
||||
equal(i, it.next());
|
||||
for (int j = i+2; j < capacity; j += 2)
|
||||
equal(j, it.next());
|
||||
check(!it.hasNext());
|
||||
}
|
||||
}
|
||||
|
||||
// q only contains odd elements
|
||||
for (int i = 0; i < capacity; i++)
|
||||
check(q.contains(i) ^ ((i & 1) == 0));
|
||||
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new IteratorWeakConsistency().instanceMain(args);}
|
||||
public void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.err.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
103
test/jdk/java/util/concurrent/ConcurrentQueues/LoopHelpers.java
Normal file
103
test/jdk/java/util/concurrent/ConcurrentQueues/LoopHelpers.java
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/**
|
||||
* Misc utilities in JSR166 performance tests
|
||||
*/
|
||||
class LoopHelpers {
|
||||
|
||||
// Some mindless computation to do between synchronizations...
|
||||
|
||||
/**
|
||||
* generates 32 bit pseudo-random numbers.
|
||||
* Adapted from http://www.snippets.org
|
||||
*/
|
||||
public static int compute1(int x) {
|
||||
int lo = 16807 * (x & 0xFFFF);
|
||||
int hi = 16807 * (x >>> 16);
|
||||
lo += (hi & 0x7FFF) << 16;
|
||||
if ((lo & 0x80000000) != 0) {
|
||||
lo &= 0x7fffffff;
|
||||
++lo;
|
||||
}
|
||||
lo += hi >>> 15;
|
||||
if (lo == 0 || (lo & 0x80000000) != 0) {
|
||||
lo &= 0x7fffffff;
|
||||
++lo;
|
||||
}
|
||||
return lo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a linear congruential random number a random number
|
||||
* of times.
|
||||
*/
|
||||
public static int compute2(int x) {
|
||||
int loops = (x >>> 4) & 7;
|
||||
while (loops-- > 0) {
|
||||
x = (x * 2147483647) % 16807;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
public static class BarrierTimer implements Runnable {
|
||||
public volatile long startTime;
|
||||
public volatile long endTime;
|
||||
public void run() {
|
||||
long t = System.nanoTime();
|
||||
if (startTime == 0)
|
||||
startTime = t;
|
||||
else
|
||||
endTime = t;
|
||||
}
|
||||
public void clear() {
|
||||
startTime = 0;
|
||||
endTime = 0;
|
||||
}
|
||||
public long getTime() {
|
||||
return endTime - startTime;
|
||||
}
|
||||
}
|
||||
|
||||
public static String rightJustify(long n) {
|
||||
// There's probably a better way to do this...
|
||||
String field = " ";
|
||||
String num = Long.toString(n);
|
||||
if (num.length() >= field.length())
|
||||
return num;
|
||||
StringBuilder b = new StringBuilder(field);
|
||||
b.replace(b.length()-num.length(), b.length(), num);
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6316155 6595669 6871697 6868712
|
||||
* @summary Test concurrent offer vs. remove
|
||||
* @library /test/lib
|
||||
* @run main OfferRemoveLoops 100
|
||||
* @author Martin Buchholz
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Queue;
|
||||
import java.util.SplittableRandom;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes", "deprecation"})
|
||||
public class OfferRemoveLoops {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
final long testDurationMillisDefault = 10_000L;
|
||||
final long testDurationMillis;
|
||||
|
||||
OfferRemoveLoops(String[] args) {
|
||||
testDurationMillis = (args.length > 0) ?
|
||||
Long.valueOf(args[0]) : testDurationMillisDefault;
|
||||
}
|
||||
|
||||
void checkNotContainsNull(Iterable it) {
|
||||
for (Object x : it)
|
||||
check(x != null);
|
||||
}
|
||||
|
||||
void test(String[] args) throws Throwable {
|
||||
testQueue(new LinkedBlockingQueue(10));
|
||||
testQueue(new LinkedBlockingQueue());
|
||||
testQueue(new LinkedBlockingDeque(10));
|
||||
testQueue(new LinkedBlockingDeque());
|
||||
testQueue(new ArrayBlockingQueue(10));
|
||||
testQueue(new PriorityBlockingQueue(10));
|
||||
testQueue(new ConcurrentLinkedDeque());
|
||||
testQueue(new ConcurrentLinkedQueue());
|
||||
testQueue(new LinkedTransferQueue());
|
||||
}
|
||||
|
||||
void testQueue(final Queue q) throws Throwable {
|
||||
System.err.println(q.getClass().getSimpleName());
|
||||
final long testDurationNanos = testDurationMillis * 1000L * 1000L;
|
||||
final long quittingTimeNanos = System.nanoTime() + testDurationNanos;
|
||||
final int maxChunkSize = 1042;
|
||||
final int maxQueueSize = 10 * maxChunkSize;
|
||||
final CountDownLatch done = new CountDownLatch(3);
|
||||
final SplittableRandom rnd = new SplittableRandom();
|
||||
|
||||
// Poor man's bounded buffer; prevents unbounded queue expansion.
|
||||
final Semaphore offers = new Semaphore(maxQueueSize);
|
||||
|
||||
abstract class CheckedThread extends Thread {
|
||||
final SplittableRandom rnd;
|
||||
|
||||
CheckedThread(String name, SplittableRandom rnd) {
|
||||
super(name);
|
||||
this.rnd = rnd;
|
||||
setDaemon(true);
|
||||
start();
|
||||
}
|
||||
/** Polls for quitting time. */
|
||||
protected boolean quittingTime() {
|
||||
return System.nanoTime() - quittingTimeNanos > 0;
|
||||
}
|
||||
/** Polls occasionally for quitting time. */
|
||||
protected boolean quittingTime(long i) {
|
||||
return (i % 1024) == 0 && quittingTime();
|
||||
}
|
||||
protected abstract void realRun() throws Exception;
|
||||
public void run() {
|
||||
try { realRun(); } catch (Throwable t) { unexpected(t); }
|
||||
}
|
||||
}
|
||||
|
||||
Thread offerer = new CheckedThread("offerer", rnd.split()) {
|
||||
protected void realRun() throws InterruptedException {
|
||||
final int chunkSize = rnd.nextInt(maxChunkSize) + 20;
|
||||
long c = 0;
|
||||
while (! quittingTime()) {
|
||||
if (q.offer(Long.valueOf(c))) {
|
||||
if ((++c % chunkSize) == 0) {
|
||||
offers.acquire(chunkSize);
|
||||
}
|
||||
} else {
|
||||
Thread.yield();
|
||||
}
|
||||
}
|
||||
done.countDown();
|
||||
}};
|
||||
|
||||
Thread remover = new CheckedThread("remover", rnd.split()) {
|
||||
protected void realRun() {
|
||||
final int chunkSize = rnd.nextInt(maxChunkSize) + 20;
|
||||
long c = 0;
|
||||
while (! quittingTime()) {
|
||||
if (q.remove(Long.valueOf(c))) {
|
||||
if ((++c % chunkSize) == 0) {
|
||||
offers.release(chunkSize);
|
||||
}
|
||||
} else {
|
||||
Thread.yield();
|
||||
}
|
||||
}
|
||||
q.clear();
|
||||
offers.release(1<<30); // Releases waiting offerer thread
|
||||
done.countDown();
|
||||
}};
|
||||
|
||||
Thread scanner = new CheckedThread("scanner", rnd.split()) {
|
||||
protected void realRun() {
|
||||
while (! quittingTime()) {
|
||||
switch (rnd.nextInt(3)) {
|
||||
case 0: checkNotContainsNull(q); break;
|
||||
case 1: q.size(); break;
|
||||
case 2: checkNotContainsNull
|
||||
(Arrays.asList(q.toArray(new Long[0])));
|
||||
break;
|
||||
}
|
||||
Thread.yield();
|
||||
}
|
||||
done.countDown();
|
||||
}};
|
||||
|
||||
if (! done.await(LONG_DELAY_MS + testDurationMillis, MILLISECONDS)) {
|
||||
for (Thread thread : new Thread[] { offerer, remover, scanner }) {
|
||||
if (thread.isAlive()) {
|
||||
System.err.printf("Hung thread: %s%n", thread.getName());
|
||||
failed++;
|
||||
for (StackTraceElement e : thread.getStackTrace())
|
||||
System.err.println(e);
|
||||
thread.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new OfferRemoveLoops(args).instanceMain(args);}
|
||||
public void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
|
|
@ -0,0 +1,228 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6785442
|
||||
* @summary Checks race between poll and remove(Object), while
|
||||
* occasionally moonlighting as a microbenchmark.
|
||||
* @run main RemovePollRace 1234
|
||||
*/
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.Map;
|
||||
|
||||
public class RemovePollRace {
|
||||
// Suitable for benchmarking. Overridden by args[0] for testing.
|
||||
int count = 1024 * 1024;
|
||||
|
||||
final Map<String,String> results = new ConcurrentHashMap<>();
|
||||
|
||||
Collection<Queue<Boolean>> concurrentQueues() {
|
||||
List<Queue<Boolean>> queues = new ArrayList<>();
|
||||
queues.add(new ConcurrentLinkedDeque<Boolean>());
|
||||
queues.add(new ConcurrentLinkedQueue<Boolean>());
|
||||
queues.add(new ArrayBlockingQueue<Boolean>(count, false));
|
||||
queues.add(new ArrayBlockingQueue<Boolean>(count, true));
|
||||
queues.add(new LinkedBlockingQueue<Boolean>());
|
||||
queues.add(new LinkedBlockingDeque<Boolean>());
|
||||
queues.add(new LinkedTransferQueue<Boolean>());
|
||||
|
||||
// Following additional implementations are available from:
|
||||
// http://gee.cs.oswego.edu/dl/concurrency-interest/index.html
|
||||
// queues.add(new SynchronizedLinkedListQueue<Boolean>());
|
||||
|
||||
// Avoid "first fast, second slow" benchmark effect.
|
||||
Collections.shuffle(queues);
|
||||
return queues;
|
||||
}
|
||||
|
||||
void prettyPrintResults() {
|
||||
List<String> classNames = new ArrayList<>(results.keySet());
|
||||
Collections.sort(classNames);
|
||||
int maxClassNameLength = 0;
|
||||
int maxNanosLength = 0;
|
||||
for (String name : classNames) {
|
||||
if (maxClassNameLength < name.length())
|
||||
maxClassNameLength = name.length();
|
||||
if (maxNanosLength < results.get(name).length())
|
||||
maxNanosLength = results.get(name).length();
|
||||
}
|
||||
String format = String.format("%%%ds %%%ds nanos/item%%n",
|
||||
maxClassNameLength, maxNanosLength);
|
||||
for (String name : classNames)
|
||||
System.out.printf(format, name, results.get(name));
|
||||
}
|
||||
|
||||
void test(String[] args) throws Throwable {
|
||||
if (args.length > 0)
|
||||
count = Integer.valueOf(args[0]);
|
||||
|
||||
// Warmup
|
||||
for (Queue<Boolean> queue : concurrentQueues())
|
||||
test(queue);
|
||||
results.clear();
|
||||
for (Queue<Boolean> queue : concurrentQueues())
|
||||
test(queue);
|
||||
|
||||
prettyPrintResults();
|
||||
}
|
||||
|
||||
void await(CountDownLatch latch) {
|
||||
try { latch.await(); }
|
||||
catch (InterruptedException e) { unexpected(e); }
|
||||
}
|
||||
|
||||
void test(final Queue<Boolean> q) throws Throwable {
|
||||
long t0 = System.nanoTime();
|
||||
final int SPINS = 5;
|
||||
final AtomicLong removes = new AtomicLong(0);
|
||||
final AtomicLong polls = new AtomicLong(0);
|
||||
|
||||
// We need at least 3 threads, but we don't want to use too
|
||||
// many on massively multi-core systems.
|
||||
final int cpus = Runtime.getRuntime().availableProcessors();
|
||||
final int threadsToUse = Math.max(3, Math.min(cpus, 16));
|
||||
final int adderCount = threadsToUse / 3;
|
||||
final int removerCount = adderCount;
|
||||
final int pollerCount = removerCount;
|
||||
final int threadCount = adderCount + removerCount + pollerCount;
|
||||
|
||||
final CountDownLatch startingGate = new CountDownLatch(1);
|
||||
final CountDownLatch addersDone = new CountDownLatch(adderCount);
|
||||
final Runnable remover = new Runnable() {
|
||||
public void run() {
|
||||
await(startingGate);
|
||||
int spins = 0;
|
||||
for (;;) {
|
||||
boolean quittingTime = (addersDone.getCount() == 0);
|
||||
if (q.remove(Boolean.TRUE))
|
||||
removes.getAndIncrement();
|
||||
else if (quittingTime)
|
||||
break;
|
||||
else if (++spins > SPINS) {
|
||||
Thread.yield();
|
||||
spins = 0;
|
||||
}}}};
|
||||
final Runnable poller = new Runnable() {
|
||||
public void run() {
|
||||
await(startingGate);
|
||||
int spins = 0;
|
||||
for (;;) {
|
||||
boolean quittingTime = (addersDone.getCount() == 0);
|
||||
if (q.poll() == Boolean.TRUE)
|
||||
polls.getAndIncrement();
|
||||
else if (quittingTime)
|
||||
break;
|
||||
else if (++spins > SPINS) {
|
||||
Thread.yield();
|
||||
spins = 0;
|
||||
}}}};
|
||||
final Runnable adder = new Runnable() {
|
||||
public void run() {
|
||||
await(startingGate);
|
||||
for (int i = 0; i < count; i++) {
|
||||
for (;;) {
|
||||
try { q.add(Boolean.TRUE); break; }
|
||||
catch (IllegalStateException e) { Thread.yield(); }
|
||||
}
|
||||
}
|
||||
addersDone.countDown();
|
||||
}};
|
||||
|
||||
final List<Thread> adders = new ArrayList<>();
|
||||
final List<Thread> removers = new ArrayList<>();
|
||||
final List<Thread> pollers = new ArrayList<>();
|
||||
for (int i = 0; i < adderCount; i++)
|
||||
adders.add(checkedThread(adder));
|
||||
for (int i = 0; i < removerCount; i++)
|
||||
removers.add(checkedThread(remover));
|
||||
for (int i = 0; i < pollerCount; i++)
|
||||
pollers.add(checkedThread(poller));
|
||||
|
||||
final List<Thread> allThreads = new ArrayList<>();
|
||||
allThreads.addAll(removers);
|
||||
allThreads.addAll(pollers);
|
||||
allThreads.addAll(adders);
|
||||
|
||||
for (Thread t : allThreads)
|
||||
t.start();
|
||||
startingGate.countDown();
|
||||
for (Thread t : allThreads)
|
||||
t.join();
|
||||
|
||||
String className = q.getClass().getSimpleName();
|
||||
long elapsed = System.nanoTime() - t0;
|
||||
int nanos = (int) ((double) elapsed / (adderCount * count));
|
||||
results.put(className, String.valueOf(nanos));
|
||||
if (removes.get() + polls.get() != adderCount * count) {
|
||||
String msg = String.format
|
||||
("class=%s removes=%s polls=%d count=%d",
|
||||
className, removes.get(), polls.get(), count);
|
||||
fail(msg);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new RemovePollRace().instanceMain(args);}
|
||||
public void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
Thread checkedThread(final Runnable r) {
|
||||
return new Thread() {public void run() {
|
||||
try {r.run();} catch (Throwable t) {unexpected(t);}}};}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8011645
|
||||
* @summary CopyOnWriteArrayList.COWSubList.subList does not validate range properly
|
||||
*/
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class COWSubList {
|
||||
public static void main(String[] args) {
|
||||
List<String> list = new CopyOnWriteArrayList<>();
|
||||
list.add("A");
|
||||
list.add("B");
|
||||
list.add("C");
|
||||
list.add("D");
|
||||
list.add("E");
|
||||
|
||||
expectThrow(() -> list.subList(-1, 5));
|
||||
expectThrow(() -> list.subList(0, 6));
|
||||
expectThrow(() -> list.subList(4, 3));
|
||||
expectThrow(() -> list.subList(0, 5).subList(-1, 5));
|
||||
expectThrow(() -> list.subList(0, 5).subList(0, 6));
|
||||
expectThrow(() -> list.subList(0, 5).subList(4, 3));
|
||||
}
|
||||
|
||||
static void expectThrow(Runnable r) {
|
||||
try {
|
||||
r.run();
|
||||
throw new RuntimeException("Failed: expected IOOBE to be thrown");
|
||||
} catch (IndexOutOfBoundsException x) {
|
||||
// ok, expected
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6318638 6325166 6330307
|
||||
* @summary CopyOnWriteArrayList.equals should be thread-safe
|
||||
* @author Martin Buchholz
|
||||
*/
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class EqualsRace {
|
||||
private static void realMain(String[] args) throws Throwable {
|
||||
final int iterations = 100000;
|
||||
final List<Integer> list = new CopyOnWriteArrayList<>();
|
||||
final Integer one = Integer.valueOf(1);
|
||||
final List<Integer> oneElementList = Arrays.asList(one);
|
||||
final Thread t = new CheckedThread() { public void realRun() {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
list.add(one);
|
||||
list.remove(one);
|
||||
}}};
|
||||
t.start();
|
||||
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
list.equals(oneElementList);
|
||||
list.equals(Collections.EMPTY_LIST);
|
||||
}
|
||||
t.join();
|
||||
check(list.size() == 0);
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
static volatile int passed = 0, failed = 0;
|
||||
static void pass() {passed++;}
|
||||
static void fail() {failed++; Thread.dumpStack();}
|
||||
static void fail(String msg) {System.out.println(msg); fail();}
|
||||
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
static void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
static void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
try {realMain(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
private abstract static class CheckedThread extends Thread {
|
||||
public abstract void realRun() throws Throwable;
|
||||
public void run() {
|
||||
try { realRun(); } catch (Throwable t) { unexpected(t); }}}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6330307 6355645
|
||||
* @summary Check for race conditions in COWArray classes
|
||||
* @author Martin Buchholz
|
||||
*/
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
public class RacingCows {
|
||||
private static void realMain(String[] args) throws Throwable {
|
||||
final int iterations = 100000;
|
||||
final Integer two = Integer.valueOf(2);
|
||||
final Integer three = Integer.valueOf(3);
|
||||
|
||||
//------------ CopyOnWriteArraySet -------------------------------
|
||||
final Set<Integer> s1 = new CopyOnWriteArraySet<>();
|
||||
final Set<Integer> s2 = new CopyOnWriteArraySet<>();
|
||||
s1.add(1);
|
||||
|
||||
final Thread t1 = new CheckedThread() { public void realRun() {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
s2.add(two);
|
||||
s2.remove(two);
|
||||
}}};
|
||||
t1.start();
|
||||
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
check(! s1.equals(s2));
|
||||
check(! s2.equals(s1));
|
||||
}
|
||||
t1.join();
|
||||
|
||||
//------------ CopyOnWriteArrayList ------------------------------
|
||||
final List<Integer> l1 = new CopyOnWriteArrayList<>();
|
||||
final List<Integer> l2 = new CopyOnWriteArrayList<>();
|
||||
final List<Integer> l3 = new CopyOnWriteArrayList<>();
|
||||
l1.add(1);
|
||||
|
||||
final Thread t2 = new CheckedThread() { public void realRun() {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
switch (i%2) {
|
||||
case 0: l2.add(two); break;
|
||||
case 1: l2.add(0, two); break;
|
||||
}
|
||||
switch (i%3) {
|
||||
case 0: l2.remove(two); break;
|
||||
case 1: l2.remove(0); break;
|
||||
case 2: l2.clear(); break;
|
||||
}}}};
|
||||
t2.start();
|
||||
|
||||
final Thread t3 = new CheckedThread() { public void realRun() {
|
||||
l3.add(three);
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
switch (i%2) {
|
||||
case 0: l3.add(two); break;
|
||||
case 1: l3.add(0, two); break;
|
||||
}
|
||||
switch (i%2) {
|
||||
case 0: l3.remove(two); break;
|
||||
case 1: l3.remove(0); break;
|
||||
}}}};
|
||||
t3.start();
|
||||
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
check(! l1.equals(l2));
|
||||
check(! l2.equals(l1));
|
||||
|
||||
// CopyOnWriteArrayList(mutatingCollection)
|
||||
try { new CopyOnWriteArrayList<Integer>(l2); }
|
||||
catch (Throwable t) { unexpected(t); }
|
||||
|
||||
// addAllAbsent(mutatingCollection)
|
||||
try { new CopyOnWriteArrayList<Integer>().addAllAbsent(l3); }
|
||||
catch (Throwable t) { unexpected(t); }
|
||||
|
||||
// addAll(mutatingCollection)
|
||||
try { new CopyOnWriteArrayList<Integer>().addAll(l3); }
|
||||
catch (Throwable t) { unexpected(t); }
|
||||
|
||||
// addAll(int, mutatingCollection)
|
||||
try { new CopyOnWriteArrayList<Integer>().addAll(0,l3); }
|
||||
catch (Throwable t) { unexpected(t); }
|
||||
}
|
||||
t2.join();
|
||||
t3.join();
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
static volatile int passed = 0, failed = 0;
|
||||
static void pass() {passed++;}
|
||||
static void fail() {failed++; Thread.dumpStack();}
|
||||
static void fail(String msg) {System.out.println(msg); fail();}
|
||||
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
static void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
static void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
try {realMain(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
private abstract static class CheckedThread extends Thread {
|
||||
public abstract void realRun() throws Throwable;
|
||||
public void run() {
|
||||
try { realRun(); } catch (Throwable t) { unexpected(t); }}}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8365058
|
||||
* @summary Check basic correctness of de-serialization
|
||||
* @run junit SerializationTest
|
||||
*/
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class SerializationTest {
|
||||
|
||||
// Ensure basic serialization round trip correctness
|
||||
@ParameterizedTest
|
||||
@MethodSource
|
||||
void roundTripTest(CopyOnWriteArraySet<?> expected) {
|
||||
var bytes = ser(expected);
|
||||
var actual = deSer(bytes);
|
||||
assertEquals(CopyOnWriteArraySet.class, actual.getClass());
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
private static Stream<CopyOnWriteArraySet<?>> roundTripTest() {
|
||||
return Stream.of(
|
||||
new CopyOnWriteArraySet<>(),
|
||||
new CopyOnWriteArraySet<>(List.of(1, 2, 3)),
|
||||
new CopyOnWriteArraySet<>(Set.of("Foo", "Bar", "Baz"))
|
||||
);
|
||||
}
|
||||
|
||||
private static byte[] ser(Object obj) {
|
||||
return assertDoesNotThrow(() -> {
|
||||
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream)) {
|
||||
oos.writeObject(obj);
|
||||
return byteArrayOutputStream.toByteArray();
|
||||
}
|
||||
}, "Unexpected error during serialization");
|
||||
}
|
||||
|
||||
private static Object deSer(byte[] bytes) {
|
||||
return assertDoesNotThrow(() -> {
|
||||
try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
|
||||
ObjectInputStream ois = new ObjectInputStream(byteArrayInputStream)) {
|
||||
return ois.readObject();
|
||||
}
|
||||
}, "Unexpected error during de-serialization");
|
||||
}
|
||||
}
|
||||
200
test/jdk/java/util/concurrent/CountDownLatch/Basic.java
Normal file
200
test/jdk/java/util/concurrent/CountDownLatch/Basic.java
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6332435 8221168
|
||||
* @summary Basic tests for CountDownLatch
|
||||
* @library /test/lib
|
||||
* @author Seetharam Avadhanam, Martin Buchholz
|
||||
*/
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Supplier;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
public class Basic {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
|
||||
abstract static class Awaiter extends Thread {
|
||||
volatile Throwable exception;
|
||||
volatile boolean interrupted;
|
||||
abstract void realRun() throws Exception;
|
||||
public final void run() {
|
||||
try { realRun(); }
|
||||
catch (Throwable ex) { exception = ex; }
|
||||
interrupted = Thread.interrupted();
|
||||
};
|
||||
}
|
||||
|
||||
static Awaiter awaiter(CountDownLatch latch,
|
||||
CountDownLatch gate) {
|
||||
return new Awaiter() {
|
||||
public void realRun() throws InterruptedException {
|
||||
gate.countDown();
|
||||
latch.await();
|
||||
}};
|
||||
}
|
||||
|
||||
static Awaiter awaiter(CountDownLatch latch,
|
||||
CountDownLatch gate,
|
||||
long timeoutMillis) {
|
||||
return new Awaiter() {
|
||||
public void realRun() throws InterruptedException {
|
||||
gate.countDown();
|
||||
latch.await(timeoutMillis, TimeUnit.MILLISECONDS);
|
||||
}};
|
||||
}
|
||||
|
||||
static Supplier<Awaiter> randomAwaiterSupplier(
|
||||
CountDownLatch latch, CountDownLatch gate) {
|
||||
return () -> (ThreadLocalRandom.current().nextBoolean())
|
||||
? awaiter(latch, gate)
|
||||
: awaiter(latch, gate, LONG_DELAY_MS);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Normal use
|
||||
//----------------------------------------------------------------
|
||||
public static void normalUse() throws Throwable {
|
||||
int count = 0;
|
||||
CountDownLatch latch = new CountDownLatch(3);
|
||||
Awaiter[] a = new Awaiter[12];
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
CountDownLatch gate = new CountDownLatch(4);
|
||||
Supplier<Awaiter> s = randomAwaiterSupplier(latch, gate);
|
||||
a[count] = s.get(); a[count++].start();
|
||||
a[count] = s.get(); a[count++].start();
|
||||
a[count] = s.get(); a[count++].start();
|
||||
a[count] = s.get(); a[count++].start();
|
||||
gate.await();
|
||||
latch.countDown();
|
||||
checkCount(latch, 2-i);
|
||||
}
|
||||
for (Awaiter awaiter : a)
|
||||
awaiter.join();
|
||||
for (Awaiter awaiter : a)
|
||||
checkException(awaiter, null);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// One thread interrupted
|
||||
//----------------------------------------------------------------
|
||||
public static void threadInterrupted() throws Throwable {
|
||||
int count = 0;
|
||||
CountDownLatch latch = new CountDownLatch(3);
|
||||
Awaiter[] a = new Awaiter[12];
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
CountDownLatch gate = new CountDownLatch(4);
|
||||
Supplier<Awaiter> s = randomAwaiterSupplier(latch, gate);
|
||||
a[count] = s.get(); a[count++].start();
|
||||
a[count] = s.get(); a[count++].start();
|
||||
a[count] = s.get(); a[count++].start();
|
||||
a[count] = s.get(); a[count++].start();
|
||||
gate.await();
|
||||
a[count-1].interrupt();
|
||||
latch.countDown();
|
||||
checkCount(latch, 2-i);
|
||||
}
|
||||
for (Awaiter awaiter : a)
|
||||
awaiter.join();
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
Awaiter awaiter = a[i];
|
||||
Throwable ex = awaiter.exception;
|
||||
if ((i % 4) == 3 && !awaiter.interrupted)
|
||||
checkException(awaiter, InterruptedException.class);
|
||||
else
|
||||
checkException(awaiter, null);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// One thread timed out
|
||||
//----------------------------------------------------------------
|
||||
public static void timeOut() throws Throwable {
|
||||
int count = 0;
|
||||
CountDownLatch latch = new CountDownLatch(3);
|
||||
Awaiter[] a = new Awaiter[12];
|
||||
|
||||
long[] timeout = { 0L, 5L, 10L };
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
CountDownLatch gate = new CountDownLatch(4);
|
||||
Supplier<Awaiter> s = randomAwaiterSupplier(latch, gate);
|
||||
a[count] = awaiter(latch, gate, timeout[i]); a[count++].start();
|
||||
a[count] = s.get(); a[count++].start();
|
||||
a[count] = s.get(); a[count++].start();
|
||||
a[count] = s.get(); a[count++].start();
|
||||
gate.await();
|
||||
latch.countDown();
|
||||
checkCount(latch, 2-i);
|
||||
}
|
||||
for (Awaiter awaiter : a)
|
||||
awaiter.join();
|
||||
for (Awaiter awaiter : a)
|
||||
checkException(awaiter, null);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
try {
|
||||
normalUse();
|
||||
} catch (Throwable ex) { fail(ex); }
|
||||
try {
|
||||
threadInterrupted();
|
||||
} catch (Throwable ex) { fail(ex); }
|
||||
try {
|
||||
timeOut();
|
||||
} catch (Throwable ex) { fail(ex); }
|
||||
|
||||
if (failures.get() > 0L)
|
||||
throw new AssertionError(failures.get() + " failures");
|
||||
}
|
||||
|
||||
static final AtomicInteger failures = new AtomicInteger(0);
|
||||
|
||||
static void fail(String msg) {
|
||||
fail(new AssertionError(msg));
|
||||
}
|
||||
|
||||
static void fail(Throwable t) {
|
||||
t.printStackTrace();
|
||||
failures.getAndIncrement();
|
||||
}
|
||||
|
||||
static void checkCount(CountDownLatch b, int expected) {
|
||||
if (b.getCount() != expected)
|
||||
fail("Count = " + b.getCount() +
|
||||
", expected = " + expected);
|
||||
}
|
||||
|
||||
static void checkException(Awaiter awaiter, Class<? extends Throwable> c) {
|
||||
Throwable ex = awaiter.exception;
|
||||
if (! ((ex == null && c == null) || c.isInstance(ex)))
|
||||
fail("Expected: " + c + ", got: " + ex);
|
||||
}
|
||||
}
|
||||
444
test/jdk/java/util/concurrent/CyclicBarrier/Basic.java
Normal file
444
test/jdk/java/util/concurrent/CyclicBarrier/Basic.java
Normal file
|
|
@ -0,0 +1,444 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6253848 6366811
|
||||
* @summary Basic tests for CyclicBarrier
|
||||
* @library /test/lib
|
||||
* @author Martin Buchholz, David Holmes
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BrokenBarrierException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
public class Basic {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
|
||||
private static void checkBroken(final CyclicBarrier barrier) {
|
||||
check(barrier.isBroken());
|
||||
equal(barrier.getNumberWaiting(), 0);
|
||||
|
||||
THROWS(BrokenBarrierException.class,
|
||||
() -> barrier.await(),
|
||||
() -> barrier.await(100, MILLISECONDS));
|
||||
}
|
||||
|
||||
private static void reset(CyclicBarrier barrier) {
|
||||
barrier.reset();
|
||||
check(! barrier.isBroken());
|
||||
equal(barrier.getNumberWaiting(), 0);
|
||||
}
|
||||
|
||||
private static void checkResult(Awaiter a, Class<? extends Throwable> c) {
|
||||
Throwable t = a.result();
|
||||
if (! ((t == null && c == null) || (c != null && c.isInstance(t)))) {
|
||||
// t.printStackTrace();
|
||||
fail("Mismatch in thread " +
|
||||
a.getName() + ": " +
|
||||
t + ", " +
|
||||
(c == null ? "<null>" : c.getName()));
|
||||
} else {
|
||||
pass();
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Mechanism to get all victim threads into "running" mode.
|
||||
// The fact that this also uses CyclicBarrier is entirely coincidental.
|
||||
//----------------------------------------------------------------
|
||||
private static final CyclicBarrier atTheStartingGate = new CyclicBarrier(3);
|
||||
|
||||
private static void toTheStartingGate() {
|
||||
try { atTheStartingGate.await(LONG_DELAY_MS, MILLISECONDS); pass(); }
|
||||
catch (Throwable t) {
|
||||
unexpected(t);
|
||||
reset(atTheStartingGate);
|
||||
throw new Error(t);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Convenience methods for creating threads that call CyclicBarrier.await
|
||||
//----------------------------------------------------------------
|
||||
private abstract static class Awaiter extends Thread {
|
||||
static AtomicInteger count = new AtomicInteger(1);
|
||||
|
||||
{
|
||||
this.setName("Awaiter:"+count.getAndIncrement());
|
||||
this.setDaemon(true);
|
||||
}
|
||||
|
||||
private volatile Throwable result = null;
|
||||
protected void result(Throwable result) { this.result = result; }
|
||||
public Throwable result() { return this.result; }
|
||||
}
|
||||
|
||||
private static Awaiter awaiter(final CyclicBarrier barrier) {
|
||||
return new Awaiter() { public void run() {
|
||||
toTheStartingGate();
|
||||
|
||||
try { barrier.await(); }
|
||||
catch (Throwable result) { result(result); }}};
|
||||
}
|
||||
|
||||
private static Awaiter awaiter(final CyclicBarrier barrier,
|
||||
final long millis) {
|
||||
return new Awaiter() { public void run() {
|
||||
toTheStartingGate();
|
||||
|
||||
try { barrier.await(millis, MILLISECONDS); }
|
||||
catch (Throwable result) { result(result); }}};
|
||||
}
|
||||
|
||||
// Returns an infinite lazy list of all possible awaiter pair combinations.
|
||||
private static Iterator<Awaiter> awaiterIterator(final CyclicBarrier barrier) {
|
||||
return new Iterator<Awaiter>() {
|
||||
int i = 0;
|
||||
public boolean hasNext() { return true; }
|
||||
public Awaiter next() {
|
||||
switch ((i++)&7) {
|
||||
case 0: case 2: case 4: case 5:
|
||||
return awaiter(barrier);
|
||||
default:
|
||||
return awaiter(barrier, 10 * 1000); }}
|
||||
public void remove() {throw new UnsupportedOperationException();}};
|
||||
}
|
||||
|
||||
private static void realMain(String[] args) throws Throwable {
|
||||
|
||||
Thread.currentThread().setName("mainThread");
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Normal use
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
CyclicBarrier barrier = new CyclicBarrier(3);
|
||||
equal(barrier.getParties(), 3);
|
||||
Iterator<Awaiter> awaiters = awaiterIterator(barrier);
|
||||
for (boolean doReset : new boolean[] {false, true})
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Awaiter a1 = awaiters.next(); a1.start();
|
||||
Awaiter a2 = awaiters.next(); a2.start();
|
||||
toTheStartingGate();
|
||||
barrier.await();
|
||||
a1.join();
|
||||
a2.join();
|
||||
checkResult(a1, null);
|
||||
checkResult(a2, null);
|
||||
check(! barrier.isBroken());
|
||||
equal(barrier.getParties(), 3);
|
||||
equal(barrier.getNumberWaiting(), 0);
|
||||
if (doReset) reset(barrier);
|
||||
}
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// One thread interrupted
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
CyclicBarrier barrier = new CyclicBarrier(3);
|
||||
Iterator<Awaiter> awaiters = awaiterIterator(barrier);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Awaiter a1 = awaiters.next(); a1.start();
|
||||
Awaiter a2 = awaiters.next(); a2.start();
|
||||
toTheStartingGate();
|
||||
a1.interrupt();
|
||||
a1.join();
|
||||
a2.join();
|
||||
checkResult(a1, InterruptedException.class);
|
||||
checkResult(a2, BrokenBarrierException.class);
|
||||
checkBroken(barrier);
|
||||
reset(barrier);
|
||||
}
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Barrier is reset while threads are waiting
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
CyclicBarrier barrier = new CyclicBarrier(3);
|
||||
Iterator<Awaiter> awaiters = awaiterIterator(barrier);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Awaiter a1 = awaiters.next(); a1.start();
|
||||
Awaiter a2 = awaiters.next(); a2.start();
|
||||
toTheStartingGate();
|
||||
while (barrier.getNumberWaiting() < 2) Thread.yield();
|
||||
barrier.reset();
|
||||
a1.join();
|
||||
a2.join();
|
||||
checkResult(a1, BrokenBarrierException.class);
|
||||
checkResult(a2, BrokenBarrierException.class);
|
||||
check(! barrier.isBroken());
|
||||
equal(barrier.getParties(), 3);
|
||||
equal(barrier.getNumberWaiting(), 0);
|
||||
}
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// One thread timed out
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
CyclicBarrier barrier = new CyclicBarrier(3);
|
||||
Iterator<Awaiter> awaiters = awaiterIterator(barrier);
|
||||
for (long timeout : new long[] { 0L, 10L }) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
Awaiter a1 = awaiter(barrier, timeout); a1.start();
|
||||
Awaiter a2 = awaiters.next(); a2.start();
|
||||
toTheStartingGate();
|
||||
a1.join();
|
||||
a2.join();
|
||||
checkResult(a1, TimeoutException.class);
|
||||
checkResult(a2, BrokenBarrierException.class);
|
||||
checkBroken(barrier);
|
||||
equal(barrier.getParties(), 3);
|
||||
reset(barrier);
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Barrier action completed normally
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
final AtomicInteger count = new AtomicInteger(0);
|
||||
final CyclicBarrier[] kludge = new CyclicBarrier[1];
|
||||
Runnable action = new Runnable() { public void run() {
|
||||
count.incrementAndGet();
|
||||
equal(kludge[0].getNumberWaiting(),
|
||||
kludge[0].getParties());
|
||||
System.out.println("OK!"); }};
|
||||
CyclicBarrier barrier = new CyclicBarrier(3, action);
|
||||
kludge[0] = barrier;
|
||||
equal(barrier.getParties(), 3);
|
||||
Iterator<Awaiter> awaiters = awaiterIterator(barrier);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Awaiter a1 = awaiters.next(); a1.start();
|
||||
Awaiter a2 = awaiters.next(); a2.start();
|
||||
toTheStartingGate();
|
||||
while (barrier.getNumberWaiting() < 2) Thread.yield();
|
||||
try { barrier.await(); }
|
||||
catch (Throwable t) { unexpected(t); }
|
||||
a1.join();
|
||||
a2.join();
|
||||
checkResult(a1, null);
|
||||
checkResult(a2, null);
|
||||
check(! barrier.isBroken());
|
||||
equal(barrier.getNumberWaiting(), 0);
|
||||
reset(barrier);
|
||||
equal(count.get(), i+1);
|
||||
}
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Barrier action threw exception
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
Runnable action = new Runnable() {
|
||||
public void run() { throw new Error(); }};
|
||||
CyclicBarrier barrier = new CyclicBarrier(3, action);
|
||||
Iterator<Awaiter> awaiters = awaiterIterator(barrier);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Awaiter a1 = awaiters.next(); a1.start();
|
||||
Awaiter a2 = awaiters.next(); a2.start();
|
||||
toTheStartingGate();
|
||||
while (barrier.getNumberWaiting() < 2) Thread.yield();
|
||||
try {
|
||||
barrier.await();
|
||||
fail("Expected Error not thrown"); }
|
||||
catch (Error e) { pass(); }
|
||||
catch (Throwable t) { unexpected(t); }
|
||||
a1.join();
|
||||
a2.join();
|
||||
checkResult(a1, BrokenBarrierException.class);
|
||||
checkResult(a2, BrokenBarrierException.class);
|
||||
checkBroken(barrier);
|
||||
reset(barrier);
|
||||
}
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
testInterrupts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handling of extra interrupts while waiting - tests for bug 6366811
|
||||
*/
|
||||
private static void testInterrupts() {
|
||||
final int N = ThreadLocalRandom.current().nextInt(2, 10);
|
||||
final CyclicBarrier startingGate = new CyclicBarrier(N+1);
|
||||
|
||||
/**
|
||||
* A version of Awaiter that also records interrupted state.
|
||||
*/
|
||||
class Waiter extends CheckedThread {
|
||||
private final boolean timed;
|
||||
private final CyclicBarrier barrier;
|
||||
private final CountDownLatch doneSignal;
|
||||
volatile Throwable throwable;
|
||||
volatile boolean interruptStatusSetAfterAwait;
|
||||
|
||||
public Waiter(CountDownLatch doneSignal, CyclicBarrier barrier) {
|
||||
this.timed = ThreadLocalRandom.current().nextBoolean();
|
||||
this.doneSignal = doneSignal;
|
||||
this.barrier = barrier;
|
||||
}
|
||||
|
||||
void realRun() throws Throwable {
|
||||
startingGate.await(LONG_DELAY_MS, MILLISECONDS);
|
||||
|
||||
try {
|
||||
if (timed) barrier.await(LONG_DELAY_MS, MILLISECONDS);
|
||||
else barrier.await();
|
||||
} catch (Throwable throwable) {
|
||||
this.throwable = throwable;
|
||||
}
|
||||
|
||||
try {
|
||||
check(doneSignal.await(LONG_DELAY_MS, MILLISECONDS));
|
||||
if (Thread.interrupted())
|
||||
interruptStatusSetAfterAwait = true;
|
||||
} catch (InterruptedException e) {
|
||||
interruptStatusSetAfterAwait = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Interrupt occurs during barrier trip
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
final CountDownLatch doneSignal = new CountDownLatch(1);
|
||||
final List<Waiter> waiters = new ArrayList<>(N);
|
||||
|
||||
// work around finality of closed-over variables
|
||||
final Runnable[] realAction = new Runnable[1];
|
||||
final Runnable delegateAction =
|
||||
new Runnable() {public void run() {realAction[0].run();}};
|
||||
final CyclicBarrier barrier = new CyclicBarrier(N+1, delegateAction);
|
||||
|
||||
realAction[0] = new Runnable() { public void run() {
|
||||
try {
|
||||
for (int i = 0; i < N/2; i++)
|
||||
waiters.get(i).interrupt();
|
||||
// we need to try and ensure that the waiters get
|
||||
// to process their interruption before we do the
|
||||
// signalAll that trips the barrier. Using sleep
|
||||
// seems to work reliably while yield does not.
|
||||
Thread.sleep(100);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
}};
|
||||
for (int i = 0; i < N; i++) {
|
||||
Waiter waiter = new Waiter(doneSignal, barrier);
|
||||
waiter.start();
|
||||
waiters.add(waiter);
|
||||
}
|
||||
startingGate.await(LONG_DELAY_MS, MILLISECONDS);
|
||||
while (barrier.getNumberWaiting() < N) Thread.yield();
|
||||
barrier.await();
|
||||
doneSignal.countDown();
|
||||
int countInterruptStatusSetAfterAwait = 0;
|
||||
for (Waiter waiter : waiters) {
|
||||
waiter.join();
|
||||
equal(waiter.throwable, null);
|
||||
if (waiter.interruptStatusSetAfterAwait)
|
||||
countInterruptStatusSetAfterAwait++;
|
||||
}
|
||||
equal(countInterruptStatusSetAfterAwait, N/2);
|
||||
check(! barrier.isBroken());
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Multiple interrupts occur during barrier await
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
final CountDownLatch doneSignal = new CountDownLatch(1);
|
||||
final CyclicBarrier barrier = new CyclicBarrier(N+1);
|
||||
final List<Waiter> waiters = new ArrayList<>(N);
|
||||
for (int i = 0; i < N; i++) {
|
||||
Waiter waiter = new Waiter(doneSignal, barrier);
|
||||
waiter.start();
|
||||
waiters.add(waiter);
|
||||
}
|
||||
startingGate.await(LONG_DELAY_MS, MILLISECONDS);
|
||||
while (barrier.getNumberWaiting() < N) Thread.yield();
|
||||
for (int i = 0; i < N/2; i++) {
|
||||
Thread waiter = waiters.get(i);
|
||||
waiter.interrupt();
|
||||
}
|
||||
doneSignal.countDown();
|
||||
int countInterruptedException = 0;
|
||||
int countBrokenBarrierException = 0;
|
||||
int countInterruptStatusSetAfterAwait = 0;
|
||||
for (Waiter waiter : waiters) {
|
||||
waiter.join();
|
||||
if (waiter.throwable instanceof InterruptedException)
|
||||
countInterruptedException++;
|
||||
if (waiter.throwable instanceof BrokenBarrierException)
|
||||
countBrokenBarrierException++;
|
||||
if (waiter.interruptStatusSetAfterAwait)
|
||||
countInterruptStatusSetAfterAwait++;
|
||||
}
|
||||
equal(countInterruptedException, 1);
|
||||
equal(countBrokenBarrierException, N-1);
|
||||
checkBroken(barrier);
|
||||
equal(countInterruptStatusSetAfterAwait, N/2-1);
|
||||
reset(barrier);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
static volatile int passed = 0, failed = 0;
|
||||
static void pass() {passed++;}
|
||||
static void fail() {failed++; Thread.dumpStack();}
|
||||
static void fail(String msg) {System.out.println(msg); fail();}
|
||||
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
static void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
static void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
try {realMain(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
interface Fun {void f() throws Throwable;}
|
||||
private static void THROWS(Class<? extends Throwable> k, Fun... fs) {
|
||||
for (Fun f : fs)
|
||||
try { f.f(); fail("Expected " + k.getName() + " not thrown"); }
|
||||
catch (Throwable t) {
|
||||
if (k.isAssignableFrom(t.getClass())) pass();
|
||||
else unexpected(t);}}
|
||||
private abstract static class CheckedThread extends Thread {
|
||||
abstract void realRun() throws Throwable;
|
||||
public void run() {
|
||||
try {realRun();} catch (Throwable t) {unexpected(t);}}}
|
||||
}
|
||||
78
test/jdk/java/util/concurrent/DelayQueue/Iterate.java
Normal file
78
test/jdk/java/util/concurrent/DelayQueue/Iterate.java
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6355327
|
||||
* @summary DelayQueue iterators should support concurrent modification
|
||||
* @author Martin Buchholz
|
||||
*/
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.DelayQueue;
|
||||
import java.util.concurrent.Delayed;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class Iterate {
|
||||
private static class Godot implements Delayed {
|
||||
public long getDelay(TimeUnit unit) {return Long.MAX_VALUE;}
|
||||
public int compareTo(Delayed other) {return 0;}
|
||||
}
|
||||
|
||||
private static void realMain(String[] args) throws Throwable {
|
||||
Godot[] godots = new Godot[] { new Godot(), new Godot(), new Godot() };
|
||||
DelayQueue<Godot> q = new DelayQueue<>(Arrays.asList(godots));
|
||||
Iterator<Godot> it = q.iterator();
|
||||
q.clear();
|
||||
check(it.hasNext());
|
||||
equal(it.next(), godots[0]);
|
||||
it.remove();
|
||||
check(q.isEmpty());
|
||||
|
||||
q.addAll(Arrays.asList(godots));
|
||||
it = q.iterator();
|
||||
check(it.hasNext());
|
||||
it.next();
|
||||
equal(it.next(), godots[1]);
|
||||
it.remove();
|
||||
equal(q.size(), 2);
|
||||
check(q.contains(godots[0]));
|
||||
check(q.contains(godots[2]));
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
static volatile int passed = 0, failed = 0;
|
||||
static void pass() {passed++;}
|
||||
static void fail() {failed++; Thread.dumpStack();}
|
||||
static void fail(String msg) {System.out.println(msg); fail();}
|
||||
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
static void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
static void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
try {realMain(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
67
test/jdk/java/util/concurrent/DelayQueue/PollUnexpired.java
Normal file
67
test/jdk/java/util/concurrent/DelayQueue/PollUnexpired.java
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6327342
|
||||
* @summary Try to poll a DelayQueue with only unexpired elements
|
||||
* @author Martin Buchholz
|
||||
*/
|
||||
|
||||
import java.util.concurrent.DelayQueue;
|
||||
import java.util.concurrent.Delayed;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class PollUnexpired {
|
||||
private static class Godot implements Delayed {
|
||||
public long getDelay(TimeUnit unit) {return Long.MAX_VALUE;}
|
||||
public int compareTo(Delayed other) {return 0;}
|
||||
}
|
||||
|
||||
private static void realMain(String[] args) throws Throwable {
|
||||
DelayQueue<Godot> q = new DelayQueue<>();
|
||||
for (int i = 0; i < 3; i++) {
|
||||
equal(q.size(), i);
|
||||
equal(q.poll(), null);
|
||||
equal(q.size(), i);
|
||||
equal(q.poll(100, TimeUnit.MILLISECONDS), null);
|
||||
equal(q.size(), i);
|
||||
q.add(new Godot());
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
static volatile int passed = 0, failed = 0;
|
||||
static void pass() {passed++;}
|
||||
static void fail() {failed++; Thread.dumpStack();}
|
||||
static void fail(String msg) {System.out.println(msg); fail();}
|
||||
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
static void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
static void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
try {realMain(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
64
test/jdk/java/util/concurrent/DelayQueue/Stress.java
Normal file
64
test/jdk/java/util/concurrent/DelayQueue/Stress.java
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||
|
||||
import java.util.concurrent.DelayQueue;
|
||||
import java.util.concurrent.Delayed;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* This is not a regression test, but a stress benchmark test for
|
||||
* 6609775: Reduce context switches in DelayQueue due to signalAll
|
||||
*
|
||||
* This runs in the same wall clock time, but much reduced cpu time,
|
||||
* with the changes for 6609775.
|
||||
*/
|
||||
public class Stress {
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
|
||||
final DelayQueue<Delayed> q = new DelayQueue<>();
|
||||
final long t0 = System.nanoTime();
|
||||
for (long i = 0; i < 1000; i++) {
|
||||
final long expiry = t0 + i*10L*1000L*1000L;
|
||||
q.add(new Delayed() {
|
||||
public long getDelay(TimeUnit unit) {
|
||||
return unit.convert(expiry - System.nanoTime(),
|
||||
NANOSECONDS);
|
||||
}
|
||||
public int compareTo(Delayed x) {
|
||||
long d = getDelay(NANOSECONDS)
|
||||
- x.getDelay(NANOSECONDS);
|
||||
return d < 0 ? -1 : d > 0 ? 1 : 0; }});
|
||||
}
|
||||
|
||||
for (int i = 0; i < 300; i++)
|
||||
new Thread() { public void run() {
|
||||
try {
|
||||
while (!q.isEmpty())
|
||||
q.poll(10L, TimeUnit.SECONDS);
|
||||
} catch (Throwable t) { t.printStackTrace(); }
|
||||
}}.start();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8370887
|
||||
* @summary Test that cancelling a delayed task doesn't impact the ordering that other
|
||||
* delayed tasks execute
|
||||
*/
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
public class AscendingOrderAfterReplace {
|
||||
|
||||
private static final int[] DELAYS_IN_MS = { 3000, 3400, 3900, 3800, 3700, 3600, 3430, 3420, 3310, 3500, 3200 };
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
for (int i = 1; i < DELAYS_IN_MS.length; i++) {
|
||||
System.out.println("=== Test " + i + " ===");
|
||||
while (!testCancel(DELAYS_IN_MS, i)) { }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule the delayed tasks, cancel one of them, and check that the remaining tasks
|
||||
* execute in the ascending order of delay.
|
||||
* @return true if the test passed, false if a retry is needed
|
||||
* @throws RuntimeException if the test fails
|
||||
*/
|
||||
private static boolean testCancel(int[] delays, int indexToCancel) throws Exception {
|
||||
log("Delayed tasks: " + toString(delays));
|
||||
|
||||
// delayed tasks add to this queue when they execute
|
||||
var queue = new LinkedTransferQueue<Integer>();
|
||||
|
||||
// pool with one thread to ensure that delayed tasks don't execute concurrently
|
||||
try (var pool = new ForkJoinPool(1)) {
|
||||
long startNanos = System.nanoTime();
|
||||
Future<?>[] futures = Arrays.stream(delays)
|
||||
.mapToObj(d -> pool.schedule(() -> {
|
||||
log("Triggered " + d);
|
||||
queue.add(d);
|
||||
}, d, MILLISECONDS))
|
||||
.toArray(Future[]::new);
|
||||
long endNanos = System.nanoTime();
|
||||
log("Delayed tasks submitted");
|
||||
|
||||
// check submit took < min diffs between two delays
|
||||
long submitTime = Duration.ofNanos(endNanos - startNanos).toMillis();
|
||||
long minDiff = minDifference(delays);
|
||||
if (submitTime >= minDiff) {
|
||||
log("Submit took >= " + minDiff + " ms, need to retry");
|
||||
pool.shutdownNow();
|
||||
return false;
|
||||
}
|
||||
|
||||
// give a bit of time for -delayScheduler thread to process pending tasks
|
||||
Thread.sleep(minValue(delays) / 2);
|
||||
log("Cancel " + delays[indexToCancel]);
|
||||
futures[indexToCancel].cancel(true);
|
||||
}
|
||||
|
||||
// delayed tasks should have executed in ascending order of their delay
|
||||
int[] executed = queue.stream().mapToInt(Integer::intValue).toArray();
|
||||
log("Executed: " + toString(executed));
|
||||
if (!isAscendingOrder(executed)) {
|
||||
throw new RuntimeException("Not in ascending order!");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minimum element.
|
||||
*/
|
||||
private static int minValue(int[] array) {
|
||||
return IntStream.of(array).min().orElseThrow();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minimum difference between any two elements.
|
||||
*/
|
||||
private static int minDifference(int[] array) {
|
||||
int[] sorted = array.clone();
|
||||
Arrays.sort(sorted);
|
||||
return IntStream.range(1, sorted.length)
|
||||
.map(i -> sorted[i] - sorted[i - 1])
|
||||
.min()
|
||||
.orElse(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the array is in ascending order.
|
||||
*/
|
||||
private static boolean isAscendingOrder(int[] array) {
|
||||
return IntStream.range(1, array.length)
|
||||
.allMatch(i -> array[i - 1] <= array[i]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String containing the elements of an array in index order.
|
||||
*/
|
||||
private static String toString(int[] array) {
|
||||
return IntStream.of(array)
|
||||
.mapToObj(Integer::toString)
|
||||
.collect(Collectors.joining(", ", "[", "]"));
|
||||
}
|
||||
|
||||
private static void log(String message) {
|
||||
System.out.println(Instant.now() + " " + message);
|
||||
}
|
||||
}
|
||||
|
||||
143
test/jdk/java/util/concurrent/Exchanger/ExchangeLoops.java
Normal file
143
test/jdk/java/util/concurrent/Exchanger/ExchangeLoops.java
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 4486658
|
||||
* @summary checks to make sure a pipeline of exchangers passes data.
|
||||
* @library /test/lib
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.Exchanger;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
public class ExchangeLoops {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
static final ExecutorService pool = Executors.newCachedThreadPool();
|
||||
static boolean print = false;
|
||||
|
||||
static class Int {
|
||||
public int value;
|
||||
Int(int i) { value = i; }
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
int maxStages = 5;
|
||||
int iters = 2000;
|
||||
|
||||
if (args.length > 0)
|
||||
maxStages = Integer.parseInt(args[0]);
|
||||
|
||||
print = false;
|
||||
System.out.println("Warmup...");
|
||||
oneRun(2, iters);
|
||||
print = true;
|
||||
|
||||
for (int i = 2; i <= maxStages; i += (i+1) >>> 1) {
|
||||
System.out.print("Threads: " + i + "\t: ");
|
||||
oneRun(i, iters);
|
||||
}
|
||||
pool.shutdown();
|
||||
if (! pool.awaitTermination(LONG_DELAY_MS, MILLISECONDS))
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
static class Stage implements Runnable {
|
||||
final int iters;
|
||||
final Exchanger<Int> left;
|
||||
final Exchanger<Int> right;
|
||||
final CyclicBarrier barrier;
|
||||
volatile int result;
|
||||
Stage(Exchanger<Int> left,
|
||||
Exchanger<Int> right,
|
||||
CyclicBarrier b, int iters) {
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
barrier = b;
|
||||
this.iters = iters;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
barrier.await();
|
||||
Int item = new Int(hashCode());
|
||||
for (int i = 0; i < iters; ++i) {
|
||||
if (left != null) {
|
||||
item.value = LoopHelpers.compute1(item.value);
|
||||
Int other = left.exchange(item);
|
||||
if (other == item || other == null)
|
||||
throw new Error("Failed Exchange");
|
||||
item = other;
|
||||
|
||||
}
|
||||
if (right != null) {
|
||||
item.value = LoopHelpers.compute2(item.value);
|
||||
Int other = right.exchange(item);
|
||||
if (other == item || other == null)
|
||||
throw new Error("Failed Exchange");
|
||||
item = other;
|
||||
}
|
||||
}
|
||||
barrier.await();
|
||||
|
||||
}
|
||||
catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void oneRun(int nthreads, int iters) throws Exception {
|
||||
LoopHelpers.BarrierTimer timer = new LoopHelpers.BarrierTimer();
|
||||
CyclicBarrier barrier = new CyclicBarrier(nthreads + 1, timer);
|
||||
Exchanger<Int> l = null;
|
||||
Exchanger<Int> r = new Exchanger<>();
|
||||
for (int i = 0; i < nthreads; ++i) {
|
||||
pool.execute(new Stage(l, r, barrier, iters));
|
||||
l = r;
|
||||
r = (i+2 < nthreads) ? new Exchanger<Int>() : null;
|
||||
}
|
||||
barrier.await();
|
||||
barrier.await();
|
||||
long time = timer.getTime();
|
||||
if (print)
|
||||
System.out.println(LoopHelpers.rightJustify(time / (iters * nthreads + iters * (nthreads-2))) + " ns per transfer");
|
||||
}
|
||||
|
||||
}
|
||||
103
test/jdk/java/util/concurrent/Exchanger/LoopHelpers.java
Normal file
103
test/jdk/java/util/concurrent/Exchanger/LoopHelpers.java
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/**
|
||||
* Misc utilities in JSR166 performance tests
|
||||
*/
|
||||
class LoopHelpers {
|
||||
|
||||
// Some mindless computation to do between synchronizations...
|
||||
|
||||
/**
|
||||
* generates 32 bit pseudo-random numbers.
|
||||
* Adapted from http://www.snippets.org
|
||||
*/
|
||||
public static int compute1(int x) {
|
||||
int lo = 16807 * (x & 0xFFFF);
|
||||
int hi = 16807 * (x >>> 16);
|
||||
lo += (hi & 0x7FFF) << 16;
|
||||
if ((lo & 0x80000000) != 0) {
|
||||
lo &= 0x7fffffff;
|
||||
++lo;
|
||||
}
|
||||
lo += hi >>> 15;
|
||||
if (lo == 0 || (lo & 0x80000000) != 0) {
|
||||
lo &= 0x7fffffff;
|
||||
++lo;
|
||||
}
|
||||
return lo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a linear congruential random number a random number
|
||||
* of times.
|
||||
*/
|
||||
public static int compute2(int x) {
|
||||
int loops = (x >>> 4) & 7;
|
||||
while (loops-- > 0) {
|
||||
x = (x * 2147483647) % 16807;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
public static class BarrierTimer implements Runnable {
|
||||
public volatile long startTime;
|
||||
public volatile long endTime;
|
||||
public void run() {
|
||||
long t = System.nanoTime();
|
||||
if (startTime == 0)
|
||||
startTime = t;
|
||||
else
|
||||
endTime = t;
|
||||
}
|
||||
public void clear() {
|
||||
startTime = 0;
|
||||
endTime = 0;
|
||||
}
|
||||
public long getTime() {
|
||||
return endTime - startTime;
|
||||
}
|
||||
}
|
||||
|
||||
public static String rightJustify(long n) {
|
||||
// There's probably a better way to do this...
|
||||
String field = " ";
|
||||
String num = Long.toString(n);
|
||||
if (num.length() >= field.length())
|
||||
return num;
|
||||
StringBuilder b = new StringBuilder(field);
|
||||
b.replace(b.length()-num.length(), b.length(), num);
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 4965960
|
||||
* @summary Exercise ExecutorCompletionService
|
||||
* @library /test/lib
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorCompletionService;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
public class ExecutorCompletionServiceLoops {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
static final int POOLSIZE = 10;
|
||||
static final ExecutorService pool =
|
||||
Executors.newFixedThreadPool(POOLSIZE);
|
||||
static final ExecutorCompletionService<Integer> ecs =
|
||||
new ExecutorCompletionService<>(pool);
|
||||
static boolean print = false;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
int max = 8;
|
||||
int base = 2000;
|
||||
|
||||
if (args.length > 0)
|
||||
max = Integer.parseInt(args[0]);
|
||||
|
||||
System.out.println("Warmup...");
|
||||
oneTest(base);
|
||||
print = true;
|
||||
|
||||
for (int i = 1; i <= max; i += (i+1) >>> 1) {
|
||||
System.out.print("n: " + i * base);
|
||||
oneTest(i * base);
|
||||
}
|
||||
pool.shutdown();
|
||||
if (! pool.awaitTermination(LONG_DELAY_MS, MILLISECONDS))
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
static class Task implements Callable<Integer> {
|
||||
public Integer call() {
|
||||
int s = 0;
|
||||
int l = System.identityHashCode(this);
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
l = LoopHelpers.compute2(l);
|
||||
s += LoopHelpers.compute1(l);
|
||||
}
|
||||
return new Integer(s);
|
||||
}
|
||||
}
|
||||
|
||||
static class Producer implements Runnable {
|
||||
final ExecutorCompletionService cs;
|
||||
final int iters;
|
||||
Producer(ExecutorCompletionService ecs, int i) {
|
||||
cs = ecs;
|
||||
iters = i;
|
||||
}
|
||||
public void run() {
|
||||
for (int i = 0; i < iters; ++i)
|
||||
ecs.submit(new Task());
|
||||
}
|
||||
}
|
||||
|
||||
static void oneTest(int iters) throws Exception {
|
||||
long startTime = System.nanoTime();
|
||||
new Thread(new Producer(ecs, iters)).start();
|
||||
|
||||
int r = 0;
|
||||
for (int i = 0; i < iters; ++i)
|
||||
r += ecs.take().get().intValue();
|
||||
|
||||
long elapsed = System.nanoTime() - startTime;
|
||||
long tpi = elapsed/ iters;
|
||||
|
||||
if (print)
|
||||
System.out.println("\t: " + LoopHelpers.rightJustify(tpi) + " ns per task");
|
||||
|
||||
if (r == 0) // avoid overoptimization
|
||||
System.out.println("useless result: " + r);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/**
|
||||
* Misc utilities in JSR166 performance tests
|
||||
*/
|
||||
class LoopHelpers {
|
||||
|
||||
// Some mindless computation to do between synchronizations...
|
||||
|
||||
/**
|
||||
* generates 32 bit pseudo-random numbers.
|
||||
* Adapted from http://www.snippets.org
|
||||
*/
|
||||
public static int compute1(int x) {
|
||||
int lo = 16807 * (x & 0xFFFF);
|
||||
int hi = 16807 * (x >>> 16);
|
||||
lo += (hi & 0x7FFF) << 16;
|
||||
if ((lo & 0x80000000) != 0) {
|
||||
lo &= 0x7fffffff;
|
||||
++lo;
|
||||
}
|
||||
lo += hi >>> 15;
|
||||
if (lo == 0 || (lo & 0x80000000) != 0) {
|
||||
lo &= 0x7fffffff;
|
||||
++lo;
|
||||
}
|
||||
return lo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a linear congruential random number a random number
|
||||
* of times.
|
||||
*/
|
||||
public static int compute2(int x) {
|
||||
int loops = (x >>> 4) & 7;
|
||||
while (loops-- > 0) {
|
||||
x = (x * 2147483647) % 16807;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
public static class BarrierTimer implements Runnable {
|
||||
public volatile long startTime;
|
||||
public volatile long endTime;
|
||||
public void run() {
|
||||
long t = System.nanoTime();
|
||||
if (startTime == 0)
|
||||
startTime = t;
|
||||
else
|
||||
endTime = t;
|
||||
}
|
||||
public void clear() {
|
||||
startTime = 0;
|
||||
endTime = 0;
|
||||
}
|
||||
public long getTime() {
|
||||
return endTime - startTime;
|
||||
}
|
||||
}
|
||||
|
||||
public static String rightJustify(long n) {
|
||||
// There's probably a better way to do this...
|
||||
String field = " ";
|
||||
String num = Long.toString(n);
|
||||
if (num.length() >= field.length())
|
||||
return num;
|
||||
StringBuilder b = new StringBuilder(field);
|
||||
b.replace(b.length()-num.length(), b.length(), num);
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
}
|
||||
269
test/jdk/java/util/concurrent/ExecutorService/CloseTest.java
Normal file
269
test/jdk/java/util/concurrent/ExecutorService/CloseTest.java
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Test implementations of ExecutorService.close
|
||||
* @library ../lib
|
||||
* @run junit CloseTest
|
||||
*/
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.Phaser;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class CloseTest {
|
||||
|
||||
static Stream<ExecutorService> executors() {
|
||||
return Stream.of(
|
||||
// ensures that default close method is tested
|
||||
new DelegatingExecutorService(Executors.newCachedThreadPool()),
|
||||
|
||||
// implementations that may override close
|
||||
Executors.newCachedThreadPool(),
|
||||
Executors.newVirtualThreadPerTaskExecutor(),
|
||||
new ForkJoinPool()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test close with no tasks running.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testCloseWithNoTasks(ExecutorService executor) throws Exception {
|
||||
executor.close();
|
||||
assertTrue(executor.isShutdown());
|
||||
assertTrue(executor.isTerminated());
|
||||
assertTrue(executor.awaitTermination(10, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test close with tasks running.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testCloseWithRunningTasks(ExecutorService executor) throws Exception {
|
||||
Phaser phaser = new Phaser(2);
|
||||
Future<?> future = executor.submit(() -> {
|
||||
phaser.arriveAndAwaitAdvance();
|
||||
Thread.sleep(Duration.ofMillis(100));
|
||||
return "foo";
|
||||
});
|
||||
phaser.arriveAndAwaitAdvance(); // wait for task to start
|
||||
|
||||
executor.close(); // waits for task to complete
|
||||
assertFalse(Thread.interrupted());
|
||||
assertTrue(executor.isShutdown());
|
||||
assertTrue(executor.isTerminated());
|
||||
assertTrue(executor.awaitTermination(10, TimeUnit.MILLISECONDS));
|
||||
assertEquals("foo", future.resultNow());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test shutdown with tasks running.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testShutdownWithRunningTasks(ExecutorService executor) throws Exception {
|
||||
Phaser phaser = new Phaser(2);
|
||||
Future<?> future = executor.submit(() -> {
|
||||
phaser.arriveAndAwaitAdvance();
|
||||
Thread.sleep(Duration.ofMillis(100));
|
||||
return "foo";
|
||||
});
|
||||
phaser.arriveAndAwaitAdvance(); // wait for task to start
|
||||
|
||||
executor.shutdown();
|
||||
assertFalse(Thread.interrupted());
|
||||
assertTrue(executor.isShutdown());
|
||||
assertTrue(executor.awaitTermination(1, TimeUnit.MINUTES));
|
||||
assertTrue(executor.isTerminated());
|
||||
assertEquals("foo", future.resultNow());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test close with multiple tasks running
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testCloseWith2RunningTasks(ExecutorService executor) throws Exception {
|
||||
Phaser phaser = new Phaser(3);
|
||||
Future<?> f1 = executor.submit(() -> {
|
||||
phaser.arriveAndAwaitAdvance();
|
||||
Thread.sleep(Duration.ofMillis(100));
|
||||
return "foo";
|
||||
});
|
||||
Future<?> f2 = executor.submit(() -> {
|
||||
phaser.arriveAndAwaitAdvance();
|
||||
Thread.sleep(Duration.ofMillis(100));
|
||||
return "bar";
|
||||
});
|
||||
phaser.arriveAndAwaitAdvance(); // wait for tasks to start
|
||||
|
||||
executor.close(); // waits for task to complete
|
||||
assertFalse(Thread.interrupted());
|
||||
assertTrue(executor.isShutdown());
|
||||
assertTrue(executor.isTerminated());
|
||||
assertTrue(executor.awaitTermination(10, TimeUnit.MILLISECONDS));
|
||||
assertEquals("foo", f1.resultNow());
|
||||
assertEquals("bar", f2.resultNow());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test shutdown with multiple tasks running
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testShutdownWith2RunningTasks(ExecutorService executor) throws Exception {
|
||||
Phaser phaser = new Phaser(3);
|
||||
Future<?> f1 = executor.submit(() -> {
|
||||
phaser.arriveAndAwaitAdvance();
|
||||
Thread.sleep(Duration.ofMillis(100));
|
||||
return "foo";
|
||||
});
|
||||
Future<?> f2 = executor.submit(() -> {
|
||||
phaser.arriveAndAwaitAdvance();
|
||||
Thread.sleep(Duration.ofMillis(100));
|
||||
return "bar";
|
||||
});
|
||||
phaser.arriveAndAwaitAdvance(); // wait for tasks to start
|
||||
|
||||
executor.shutdown();
|
||||
assertFalse(Thread.interrupted());
|
||||
assertTrue(executor.isShutdown());
|
||||
assertTrue(executor.awaitTermination(1, TimeUnit.MINUTES));
|
||||
assertTrue(executor.isTerminated());
|
||||
assertEquals("foo", f1.resultNow());
|
||||
assertEquals("bar", f2.resultNow());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test close when executor is shutdown but not terminated.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testShutdownBeforeClose(ExecutorService executor) throws Exception {
|
||||
Phaser phaser = new Phaser(2);
|
||||
Future<?> future = executor.submit(() -> {
|
||||
phaser.arriveAndAwaitAdvance();
|
||||
Thread.sleep(Duration.ofMillis(100));
|
||||
return "foo";
|
||||
});
|
||||
phaser.arriveAndAwaitAdvance(); // wait for task to start
|
||||
|
||||
executor.shutdown(); // shutdown, will not immediately terminate
|
||||
executor.close();
|
||||
assertTrue(executor.isShutdown());
|
||||
assertTrue(executor.isTerminated());
|
||||
assertTrue(executor.awaitTermination(10, TimeUnit.MILLISECONDS));
|
||||
Object s = future.resultNow();
|
||||
assertEquals("foo", s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test close when terminated.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testTerminateBeforeClose(ExecutorService executor) throws Exception {
|
||||
executor.shutdown();
|
||||
assertTrue(executor.isTerminated());
|
||||
executor.close();
|
||||
assertTrue(executor.isShutdown());
|
||||
assertTrue(executor.isTerminated());
|
||||
assertTrue(executor.awaitTermination(10, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invoking close with interrupted status set.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInterruptBeforeClose(ExecutorService executor) throws Exception {
|
||||
Phaser phaser = new Phaser(2);
|
||||
Future<?> future = executor.submit(() -> {
|
||||
phaser.arriveAndAwaitAdvance();
|
||||
Thread.sleep(Duration.ofDays(1));
|
||||
return null;
|
||||
});
|
||||
phaser.arriveAndAwaitAdvance(); // wait for task to start
|
||||
|
||||
Thread.currentThread().interrupt();
|
||||
try {
|
||||
executor.close();
|
||||
assertTrue(Thread.currentThread().isInterrupted());
|
||||
} finally {
|
||||
Thread.interrupted(); // clear interrupted status
|
||||
}
|
||||
assertTrue(executor.isShutdown());
|
||||
assertTrue(executor.isTerminated());
|
||||
assertTrue(executor.awaitTermination(10, TimeUnit.MILLISECONDS));
|
||||
assertThrows(ExecutionException.class, future::get);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test interrupting thread blocked in close.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInterruptDuringClose(ExecutorService executor) throws Exception {
|
||||
Phaser phaser = new Phaser(2);
|
||||
Future<?> future = executor.submit(() -> {
|
||||
phaser.arriveAndAwaitAdvance();
|
||||
Thread.sleep(Duration.ofDays(1));
|
||||
return null;
|
||||
});
|
||||
phaser.arriveAndAwaitAdvance(); // wait for task to start
|
||||
|
||||
// schedule main thread to be interrupted
|
||||
Thread thread = Thread.currentThread();
|
||||
new Thread(() -> {
|
||||
try {
|
||||
Thread.sleep( Duration.ofMillis(100));
|
||||
} catch (Exception ignore) { }
|
||||
thread.interrupt();
|
||||
}).start();
|
||||
try {
|
||||
executor.close();
|
||||
assertTrue(Thread.currentThread().isInterrupted());
|
||||
} finally {
|
||||
Thread.interrupted(); // clear interrupted status
|
||||
}
|
||||
assertTrue(executor.isShutdown());
|
||||
assertTrue(executor.isTerminated());
|
||||
assertTrue(executor.awaitTermination(10, TimeUnit.MILLISECONDS));
|
||||
assertThrows(ExecutionException.class, future::get);
|
||||
}
|
||||
}
|
||||
235
test/jdk/java/util/concurrent/ExecutorService/Invoke.java
Normal file
235
test/jdk/java/util/concurrent/ExecutorService/Invoke.java
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
/*
|
||||
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6267833
|
||||
* @summary Tests for invokeAny, invokeAll
|
||||
* @author Martin Buchholz
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class Invoke {
|
||||
static volatile int passed = 0, failed = 0;
|
||||
|
||||
static void fail(String msg) {
|
||||
failed++;
|
||||
new AssertionError(msg).printStackTrace();
|
||||
}
|
||||
|
||||
static void pass() {
|
||||
passed++;
|
||||
}
|
||||
|
||||
static void unexpected(Throwable t) {
|
||||
failed++;
|
||||
t.printStackTrace();
|
||||
}
|
||||
|
||||
static void check(boolean condition, String msg) {
|
||||
if (condition) pass(); else fail(msg);
|
||||
}
|
||||
|
||||
static void check(boolean condition) {
|
||||
check(condition, "Assertion failure");
|
||||
}
|
||||
|
||||
static long secondsElapsedSince(long startTime) {
|
||||
return NANOSECONDS.toSeconds(System.nanoTime() - startTime);
|
||||
}
|
||||
|
||||
static void awaitInterrupt(long timeoutSeconds) {
|
||||
long startTime = System.nanoTime();
|
||||
try {
|
||||
Thread.sleep(SECONDS.toMillis(timeoutSeconds));
|
||||
fail("timed out waiting for interrupt");
|
||||
} catch (InterruptedException expected) {
|
||||
check(secondsElapsedSince(startTime) < timeoutSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
for (int nThreads = 1; nThreads <= 6; ++nThreads) {
|
||||
// untimed
|
||||
testInvokeAll(nThreads, false);
|
||||
testInvokeAny(nThreads, false);
|
||||
testInvokeAny_cancellationInterrupt(nThreads, false);
|
||||
// timed
|
||||
testInvokeAll(nThreads, true);
|
||||
testInvokeAny(nThreads, true);
|
||||
testInvokeAny_cancellationInterrupt(nThreads, true);
|
||||
}
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
if (failed > 0)
|
||||
throw new Error(
|
||||
String.format("Passed = %d, failed = %d", passed, failed));
|
||||
}
|
||||
|
||||
static final long timeoutSeconds = 10L;
|
||||
|
||||
static void testInvokeAll(int nThreads, boolean timed) throws Throwable {
|
||||
final ThreadLocalRandom rnd = ThreadLocalRandom.current();
|
||||
final ExecutorService pool = Executors.newFixedThreadPool(nThreads);
|
||||
final AtomicLong count = new AtomicLong(0);
|
||||
class Task implements Callable<Long> {
|
||||
public Long call() throws Exception {
|
||||
return count.incrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
final List<Task> tasks =
|
||||
IntStream.range(0, nThreads)
|
||||
.mapToObj(i -> new Task())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<Future<Long>> futures;
|
||||
if (timed) {
|
||||
long startTime = System.nanoTime();
|
||||
futures = pool.invokeAll(tasks, timeoutSeconds, SECONDS);
|
||||
check(secondsElapsedSince(startTime) < timeoutSeconds);
|
||||
}
|
||||
else
|
||||
futures = pool.invokeAll(tasks);
|
||||
check(futures.size() == tasks.size());
|
||||
check(count.get() == tasks.size());
|
||||
|
||||
long gauss = 0;
|
||||
for (Future<Long> future : futures) gauss += future.get();
|
||||
check(gauss == (tasks.size()+1)*tasks.size()/2);
|
||||
|
||||
pool.shutdown();
|
||||
check(pool.awaitTermination(10L, SECONDS));
|
||||
} finally {
|
||||
pool.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
static void testInvokeAny(int nThreads, boolean timed) throws Throwable {
|
||||
final ThreadLocalRandom rnd = ThreadLocalRandom.current();
|
||||
final ExecutorService pool = Executors.newFixedThreadPool(nThreads);
|
||||
final AtomicLong count = new AtomicLong(0);
|
||||
final CountDownLatch invokeAnyDone = new CountDownLatch(1);
|
||||
class Task implements Callable<Long> {
|
||||
public Long call() throws Exception {
|
||||
long x = count.incrementAndGet();
|
||||
if (x > 1) {
|
||||
// wait for main thread to interrupt us ...
|
||||
awaitInterrupt(timeoutSeconds);
|
||||
// ... and then for invokeAny to return
|
||||
check(invokeAnyDone.await(timeoutSeconds, SECONDS));
|
||||
}
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
final List<Task> tasks =
|
||||
IntStream.range(0, rnd.nextInt(1, 7))
|
||||
.mapToObj(i -> new Task())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
long val;
|
||||
if (timed) {
|
||||
long startTime = System.nanoTime();
|
||||
val = pool.invokeAny(tasks, timeoutSeconds, SECONDS);
|
||||
check(secondsElapsedSince(startTime) < timeoutSeconds);
|
||||
}
|
||||
else
|
||||
val = pool.invokeAny(tasks);
|
||||
check(val == 1);
|
||||
invokeAnyDone.countDown();
|
||||
|
||||
pool.shutdown();
|
||||
check(pool.awaitTermination(timeoutSeconds, SECONDS));
|
||||
|
||||
long c = count.get();
|
||||
check(c >= 1 && c <= tasks.size());
|
||||
|
||||
} finally {
|
||||
pool.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Every remaining running task is sent an interrupt for cancellation.
|
||||
*/
|
||||
static void testInvokeAny_cancellationInterrupt(int nThreads, boolean timed) throws Throwable {
|
||||
final ThreadLocalRandom rnd = ThreadLocalRandom.current();
|
||||
final ExecutorService pool = Executors.newFixedThreadPool(nThreads);
|
||||
final AtomicLong count = new AtomicLong(0);
|
||||
final AtomicLong interruptedCount = new AtomicLong(0);
|
||||
final CyclicBarrier allStarted = new CyclicBarrier(nThreads);
|
||||
class Task implements Callable<Long> {
|
||||
public Long call() throws Exception {
|
||||
long x = count.incrementAndGet();
|
||||
allStarted.await();
|
||||
if (x > 1)
|
||||
// main thread will interrupt us
|
||||
awaitInterrupt(timeoutSeconds);
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
final List<Task> tasks =
|
||||
IntStream.range(0, nThreads)
|
||||
.mapToObj(i -> new Task())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
long val;
|
||||
if (timed) {
|
||||
long startTime = System.nanoTime();
|
||||
val = pool.invokeAny(tasks, timeoutSeconds, SECONDS);
|
||||
check(secondsElapsedSince(startTime) < timeoutSeconds);
|
||||
}
|
||||
else
|
||||
val = pool.invokeAny(tasks);
|
||||
check(val == 1);
|
||||
|
||||
pool.shutdown();
|
||||
check(pool.awaitTermination(timeoutSeconds, SECONDS));
|
||||
|
||||
// Check after shutdown to avoid race
|
||||
check(count.get() == nThreads);
|
||||
} finally {
|
||||
pool.shutdownNow();
|
||||
}
|
||||
}
|
||||
}
|
||||
787
test/jdk/java/util/concurrent/ExecutorService/InvokeTest.java
Normal file
787
test/jdk/java/util/concurrent/ExecutorService/InvokeTest.java
Normal file
|
|
@ -0,0 +1,787 @@
|
|||
/*
|
||||
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary Test implementations of ExecutorService.invokeAll/invokeAny
|
||||
* @run junit InvokeTest
|
||||
*/
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Stream;
|
||||
import static java.lang.Thread.State.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class InvokeTest {
|
||||
|
||||
private static ScheduledExecutorService scheduler;
|
||||
|
||||
@BeforeAll
|
||||
static void setup() throws Exception {
|
||||
scheduler = Executors.newSingleThreadScheduledExecutor();
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void shutdown() {
|
||||
scheduler.shutdown();
|
||||
}
|
||||
|
||||
private static Stream<ExecutorService> executors() {
|
||||
return Stream.of(
|
||||
Executors.newCachedThreadPool(),
|
||||
Executors.newVirtualThreadPerTaskExecutor(),
|
||||
new ForkJoinPool()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invokeAny where all tasks complete normally.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAny1(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
Callable<String> task1 = () -> "foo";
|
||||
Callable<String> task2 = () -> "bar";
|
||||
String result = executor.invokeAny(List.of(task1, task2));
|
||||
assertTrue(Set.of("foo", "bar").contains(result));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invokeAny where all tasks complete normally. The completion of the
|
||||
* first task should cancel remaining tasks.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAny2(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
Callable<String> task1 = () -> "foo";
|
||||
|
||||
var task2Started = new AtomicBoolean();
|
||||
var task2Interrupted = new CountDownLatch(1);
|
||||
Callable<String> task2 = () -> {
|
||||
task2Started.set(true);
|
||||
try {
|
||||
Thread.sleep(Duration.ofDays(1));
|
||||
} catch (InterruptedException e) {
|
||||
task2Interrupted.countDown();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
String result = executor.invokeAny(List.of(task1, task2));
|
||||
assertEquals("foo", result);
|
||||
|
||||
// if task2 started then it should have been interrupted
|
||||
if (task2Started.get()) {
|
||||
task2Interrupted.await();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invokeAny where all tasks complete with exception.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAny3(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
class FooException extends Exception { }
|
||||
Callable<String> task1 = () -> { throw new FooException(); };
|
||||
Callable<String> task2 = () -> { throw new FooException(); };
|
||||
try {
|
||||
executor.invokeAny(List.of(task1, task2));
|
||||
fail("invokeAny did not throw");
|
||||
} catch (ExecutionException e) {
|
||||
Throwable cause = e.getCause();
|
||||
assertTrue(cause instanceof FooException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invokeAny where all tasks complete with exception. The completion
|
||||
* of the last task is delayed.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAny4(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
class FooException extends Exception { }
|
||||
Callable<String> task1 = () -> { throw new FooException(); };
|
||||
Callable<String> task2 = () -> {
|
||||
Thread.sleep(Duration.ofMillis(50));
|
||||
throw new FooException();
|
||||
};
|
||||
try {
|
||||
executor.invokeAny(List.of(task1, task2));
|
||||
fail("invokeAny did not throw");
|
||||
} catch (ExecutionException e) {
|
||||
Throwable cause = e.getCause();
|
||||
assertTrue(cause instanceof FooException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invokeAny where some, not all, tasks complete normally.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAny5(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
class FooException extends Exception { }
|
||||
Callable<String> task1 = () -> "foo";
|
||||
Callable<String> task2 = () -> { throw new FooException(); };
|
||||
String result = executor.invokeAny(List.of(task1, task2));
|
||||
assertEquals("foo", result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invokeAny where some, not all, tasks complete normally. The first
|
||||
* task to complete normally is delayed.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAny6(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
class FooException extends Exception { }
|
||||
Callable<String> task1 = () -> {
|
||||
Thread.sleep(Duration.ofMillis(50));
|
||||
return "foo";
|
||||
};
|
||||
Callable<String> task2 = () -> { throw new FooException(); };
|
||||
String result = executor.invokeAny(List.of(task1, task2));
|
||||
assertEquals("foo", result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test timed-invokeAny where all tasks complete normally before the timeout.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAnyWithTimeout1(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
Callable<String> task1 = () -> "foo";
|
||||
Callable<String> task2 = () -> "bar";
|
||||
String result = executor.invokeAny(List.of(task1, task2), 1, TimeUnit.MINUTES);
|
||||
assertTrue(Set.of("foo", "bar").contains(result));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test timed-invokeAny where one task completes normally before the timeout.
|
||||
* The remaining tests should be cancelled.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAnyWithTimeout2(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
Callable<String> task1 = () -> "foo";
|
||||
|
||||
var task2Started = new AtomicBoolean();
|
||||
var task2Interrupted = new CountDownLatch(1);
|
||||
Callable<String> task2 = () -> {
|
||||
task2Started.set(true);
|
||||
try {
|
||||
Thread.sleep(Duration.ofDays(1));
|
||||
} catch (InterruptedException e) {
|
||||
task2Interrupted.countDown();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
String result = executor.invokeAny(List.of(task1, task2), 1, TimeUnit.MINUTES);
|
||||
assertEquals("foo", result);
|
||||
|
||||
// if task2 started then it should have been interrupted
|
||||
if (task2Started.get()) {
|
||||
task2Interrupted.await();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test timed-invokeAny where timeout expires before any task completes.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAnyWithTimeout3(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
var task1Started = new AtomicBoolean();
|
||||
var task1Interrupted = new CountDownLatch(1);
|
||||
Callable<String> task1 = () -> {
|
||||
task1Started.set(true);
|
||||
try {
|
||||
Thread.sleep(Duration.ofDays(1));
|
||||
} catch (InterruptedException e) {
|
||||
task1Interrupted.countDown();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
var task2Started = new AtomicBoolean();
|
||||
var task2Interrupted = new CountDownLatch(1);
|
||||
Callable<String> task2 = () -> {
|
||||
task2Started.set(true);
|
||||
try {
|
||||
Thread.sleep(Duration.ofDays(1));
|
||||
} catch (InterruptedException e) {
|
||||
task2Interrupted.countDown();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// invokeAny should throw TimeoutException
|
||||
assertThrows(TimeoutException.class,
|
||||
() -> executor.invokeAny(List.of(task1, task2), 100, TimeUnit.MILLISECONDS));
|
||||
|
||||
// tasks that started should be interrupted
|
||||
if (task1Started.get()) {
|
||||
task1Interrupted.await();
|
||||
}
|
||||
if (task2Started.get()) {
|
||||
task2Interrupted.await();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invokeAny with interrupted status set.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAnyWithInterruptSet(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
Callable<String> task1 = () -> {
|
||||
Thread.sleep(Duration.ofMinutes(1));
|
||||
return "foo";
|
||||
};
|
||||
Callable<String> task2 = () -> {
|
||||
Thread.sleep(Duration.ofMinutes(1));
|
||||
return "bar";
|
||||
};
|
||||
Thread.currentThread().interrupt();
|
||||
try {
|
||||
executor.invokeAny(List.of(task1, task2));
|
||||
fail("invokeAny did not throw");
|
||||
} catch (InterruptedException expected) {
|
||||
assertFalse(Thread.currentThread().isInterrupted());
|
||||
} finally {
|
||||
Thread.interrupted(); // clear interrupt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test interrupting a thread blocked in invokeAny.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInterruptInvokeAny(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
var task1Started = new AtomicBoolean();
|
||||
var task1Interrupted = new CountDownLatch(1);
|
||||
Callable<String> task1 = () -> {
|
||||
task1Started.set(true);
|
||||
try {
|
||||
Thread.sleep(Duration.ofDays(1));
|
||||
} catch (InterruptedException e) {
|
||||
task1Interrupted.countDown();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
var task2Started = new AtomicBoolean();
|
||||
var task2Interrupted = new CountDownLatch(1);
|
||||
Callable<String> task2 = () -> {
|
||||
task2Started.set(true);
|
||||
try {
|
||||
Thread.sleep(Duration.ofDays(1));
|
||||
} catch (InterruptedException e) {
|
||||
task2Interrupted.countDown();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
scheduleInterruptAt("invokeAny");
|
||||
try {
|
||||
executor.invokeAny(List.of(task1, task2));
|
||||
fail("invokeAny did not throw");
|
||||
} catch (InterruptedException expected) {
|
||||
assertFalse(Thread.currentThread().isInterrupted());
|
||||
} finally {
|
||||
Thread.interrupted(); // clear interrupt
|
||||
}
|
||||
|
||||
// tasks that started should be interrupted
|
||||
if (task1Started.get()) {
|
||||
task1Interrupted.await();
|
||||
}
|
||||
if (task2Started.get()) {
|
||||
task2Interrupted.await();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invokeAny after ExecutorService has been shutdown.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAnyAfterShutdown(ExecutorService executor) throws Exception {
|
||||
executor.shutdown();
|
||||
Callable<String> task1 = () -> "foo";
|
||||
Callable<String> task2 = () -> "bar";
|
||||
assertThrows(RejectedExecutionException.class,
|
||||
() -> executor.invokeAny(List.of(task1, task2)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invokeAny with empty collection.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAnyEmpty1(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
assertThrows(IllegalArgumentException.class, () -> executor.invokeAny(List.of()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test timed-invokeAny with empty collection.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAnyEmpty2(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> executor.invokeAny(List.of(), 1, TimeUnit.MINUTES));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invokeAny with null.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAnyNull1(ExecutorService executor)throws Exception {
|
||||
try (executor) {
|
||||
assertThrows(NullPointerException.class, () -> executor.invokeAny(null));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invokeAny with null element
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAnyNull2(ExecutorService executor)throws Exception {
|
||||
try (executor) {
|
||||
List<Callable<String>> list = new ArrayList<>();
|
||||
list.add(() -> "foo");
|
||||
list.add(null);
|
||||
assertThrows(NullPointerException.class, () -> executor.invokeAny(null));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invokeAll where all tasks complete normally.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAll1(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
Callable<String> task1 = () -> "foo";
|
||||
Callable<String> task2 = () -> {
|
||||
Thread.sleep(Duration.ofMillis(50));
|
||||
return "bar";
|
||||
};
|
||||
|
||||
List<Future<String>> futures = executor.invokeAll(List.of(task1, task2));
|
||||
assertTrue(futures.size() == 2);
|
||||
|
||||
// check results
|
||||
List<String> results = futures.stream().map(Future::resultNow).toList();
|
||||
assertEquals(results, List.of("foo", "bar"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invokeAll where all tasks complete with exception.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAll2(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
class FooException extends Exception { }
|
||||
class BarException extends Exception { }
|
||||
Callable<String> task1 = () -> { throw new FooException(); };
|
||||
Callable<String> task2 = () -> {
|
||||
Thread.sleep(Duration.ofMillis(50));
|
||||
throw new BarException();
|
||||
};
|
||||
|
||||
List<Future<String>> futures = executor.invokeAll(List.of(task1, task2));
|
||||
assertTrue(futures.size() == 2);
|
||||
|
||||
// check results
|
||||
Throwable e1 = assertThrows(ExecutionException.class, () -> futures.get(0).get());
|
||||
assertTrue(e1.getCause() instanceof FooException);
|
||||
Throwable e2 = assertThrows(ExecutionException.class, () -> futures.get(1).get());
|
||||
assertTrue(e2.getCause() instanceof BarException);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invokeAll where all tasks complete normally before the timeout expires.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAll3(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
Callable<String> task1 = () -> "foo";
|
||||
Callable<String> task2 = () -> {
|
||||
Thread.sleep(Duration.ofMillis(50));
|
||||
return "bar";
|
||||
};
|
||||
|
||||
List<Future<String>> futures = executor.invokeAll(List.of(task1, task2), 1, TimeUnit.MINUTES);
|
||||
assertTrue(futures.size() == 2);
|
||||
|
||||
// check results
|
||||
List<String> results = futures.stream().map(Future::resultNow).toList();
|
||||
assertEquals(results, List.of("foo", "bar"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invokeAll where some tasks do not complete before the timeout expires.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAll4(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
Callable<String> task1 = () -> "foo";
|
||||
|
||||
var task2Started = new AtomicBoolean();
|
||||
var task2Interrupted = new CountDownLatch(1);
|
||||
Callable<String> task2 = () -> {
|
||||
task2Started.set(true);
|
||||
try {
|
||||
Thread.sleep(Duration.ofDays(1));
|
||||
} catch (InterruptedException e) {
|
||||
task2Interrupted.countDown();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
List<Future<String>> futures = executor.invokeAll(List.of(task1, task2), 1, TimeUnit.SECONDS);
|
||||
assertTrue(futures.size() == 2);
|
||||
|
||||
// task1 should be done
|
||||
assertTrue(futures.get(0).isDone());
|
||||
|
||||
// task2 should be cancelled and interrupted
|
||||
assertTrue(futures.get(1).isCancelled());
|
||||
|
||||
// if task2 started then it should have been interrupted
|
||||
if (task2Started.get()) {
|
||||
task2Interrupted.await();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invokeAll with interrupted status set.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAllInterrupt1(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
Callable<String> task1 = () -> "foo";
|
||||
Callable<String> task2 = () -> {
|
||||
Thread.sleep(Duration.ofMinutes(1));
|
||||
return "bar";
|
||||
};
|
||||
|
||||
Thread.currentThread().interrupt();
|
||||
try {
|
||||
executor.invokeAll(List.of(task1, task2));
|
||||
fail("invokeAll did not throw");
|
||||
} catch (InterruptedException expected) {
|
||||
assertFalse(Thread.currentThread().isInterrupted());
|
||||
} finally {
|
||||
Thread.interrupted(); // clear interrupt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test timed-invokeAll with interrupted status set.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAllInterrupt3(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
Callable<String> task1 = () -> "foo";
|
||||
Callable<String> task2 = () -> {
|
||||
Thread.sleep(Duration.ofMinutes(1));
|
||||
return "bar";
|
||||
};
|
||||
|
||||
Thread.currentThread().interrupt();
|
||||
try {
|
||||
executor.invokeAll(List.of(task1, task2), 1, TimeUnit.MINUTES);
|
||||
fail("invokeAll did not throw");
|
||||
} catch (InterruptedException expected) {
|
||||
assertFalse(Thread.currentThread().isInterrupted());
|
||||
} finally {
|
||||
Thread.interrupted(); // clear interrupt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test interrupt with thread blocked in invokeAll.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAllInterrupt4(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
Callable<String> task1 = () -> "foo";
|
||||
|
||||
var task2Started = new AtomicBoolean();
|
||||
var task2Interrupted = new CountDownLatch(1);
|
||||
Callable<String> task2 = () -> {
|
||||
task2Started.set(true);
|
||||
try {
|
||||
Thread.sleep(Duration.ofDays(1));
|
||||
} catch (InterruptedException e) {
|
||||
task2Interrupted.countDown();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
scheduleInterruptAt("invokeAll");
|
||||
try {
|
||||
executor.invokeAll(List.of(task1, task2));
|
||||
fail("invokeAll did not throw");
|
||||
} catch (InterruptedException expected) {
|
||||
assertFalse(Thread.currentThread().isInterrupted());
|
||||
} finally {
|
||||
Thread.interrupted(); // clear interrupt
|
||||
}
|
||||
|
||||
// if task2 started then it should have been interrupted
|
||||
if (task2Started.get()) {
|
||||
task2Interrupted.await();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test interrupt with thread blocked in timed-invokeAll.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAllInterrupt6(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
Callable<String> task1 = () -> "foo";
|
||||
|
||||
var task2Started = new AtomicBoolean();
|
||||
var task2Interrupted = new CountDownLatch(1);
|
||||
Callable<String> task2 = () -> {
|
||||
task2Started.set(true);
|
||||
try {
|
||||
Thread.sleep(Duration.ofDays(1));
|
||||
} catch (InterruptedException e) {
|
||||
task2Interrupted.countDown();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
scheduleInterruptAt("invokeAll");
|
||||
try {
|
||||
executor.invokeAll(List.of(task1, task2), 1, TimeUnit.MINUTES);
|
||||
fail("invokeAll did not throw");
|
||||
} catch (InterruptedException expected) {
|
||||
assertFalse(Thread.currentThread().isInterrupted());
|
||||
} finally {
|
||||
Thread.interrupted(); // clear interrupt
|
||||
}
|
||||
|
||||
// if task2 started then it should have been interrupted
|
||||
if (task2Started.get()) {
|
||||
task2Interrupted.await();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invokeAll after ExecutorService has been shutdown.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAllAfterShutdown1(ExecutorService executor) throws Exception {
|
||||
executor.shutdown();
|
||||
|
||||
Callable<String> task1 = () -> "foo";
|
||||
Callable<String> task2 = () -> "bar";
|
||||
assertThrows(RejectedExecutionException.class,
|
||||
() -> executor.invokeAll(List.of(task1, task2)));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAllAfterShutdown2(ExecutorService executor) throws Exception {
|
||||
executor.shutdown();
|
||||
|
||||
Callable<String> task1 = () -> "foo";
|
||||
Callable<String> task2 = () -> "bar";
|
||||
assertThrows(RejectedExecutionException.class,
|
||||
() -> executor.invokeAll(List.of(task1, task2), 1, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invokeAll with empty collection.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAllEmpty1(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
List<Future<Object>> list = executor.invokeAll(List.of());
|
||||
assertTrue(list.size() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAllEmpty2(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
List<Future<Object>> list = executor.invokeAll(List.of(), 1, TimeUnit.SECONDS);
|
||||
assertTrue(list.size() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAllNull1(ExecutorService executor)throws Exception {
|
||||
try (executor) {
|
||||
assertThrows(NullPointerException.class, () -> executor.invokeAll(null));
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAllNull2(ExecutorService executor)throws Exception {
|
||||
try (executor) {
|
||||
List<Callable<String>> tasks = new ArrayList<>();
|
||||
tasks.add(() -> "foo");
|
||||
tasks.add(null);
|
||||
assertThrows(NullPointerException.class, () -> executor.invokeAll(tasks));
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAllNull3(ExecutorService executor)throws Exception {
|
||||
try (executor) {
|
||||
assertThrows(NullPointerException.class,
|
||||
() -> executor.invokeAll(null, 1, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAllNull4(ExecutorService executor)throws Exception {
|
||||
try (executor) {
|
||||
Callable<String> task = () -> "foo";
|
||||
assertThrows(NullPointerException.class,
|
||||
() -> executor.invokeAll(List.of(task), 1, null));
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testInvokeAllNull5(ExecutorService executor)throws Exception {
|
||||
try (executor) {
|
||||
List<Callable<String>> tasks = new ArrayList<>();
|
||||
tasks.add(() -> "foo");
|
||||
tasks.add(null);
|
||||
assertThrows(NullPointerException.class,
|
||||
() -> executor.invokeAll(tasks, 1, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules the current thread to be interrupted when it waits (timed or untimed)
|
||||
* at the given method name.
|
||||
*/
|
||||
private void scheduleInterruptAt(String methodName) {
|
||||
Thread target = Thread.currentThread();
|
||||
scheduler.submit(() -> {
|
||||
try {
|
||||
boolean found = false;
|
||||
while (!found) {
|
||||
Thread.State state = target.getState();
|
||||
assertTrue(state != TERMINATED);
|
||||
if ((state == WAITING || state == TIMED_WAITING)
|
||||
&& contains(target.getStackTrace(), methodName)) {
|
||||
found = true;
|
||||
} else {
|
||||
Thread.sleep(20);
|
||||
}
|
||||
}
|
||||
target.interrupt();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given stack trace contains an element for the given method name.
|
||||
*/
|
||||
private boolean contains(StackTraceElement[] stack, String methodName) {
|
||||
return Arrays.stream(stack)
|
||||
.anyMatch(e -> methodName.equals(e.getMethodName()));
|
||||
}
|
||||
}
|
||||
370
test/jdk/java/util/concurrent/ExecutorService/SubmitTest.java
Normal file
370
test/jdk/java/util/concurrent/ExecutorService/SubmitTest.java
Normal file
|
|
@ -0,0 +1,370 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary Test implementations of ExecutorService.submit/execute
|
||||
* @run junit SubmitTest
|
||||
*/
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class SubmitTest {
|
||||
|
||||
private static Stream<ExecutorService> executors() {
|
||||
return Stream.of(
|
||||
Executors.newCachedThreadPool(),
|
||||
Executors.newVirtualThreadPerTaskExecutor(),
|
||||
new ForkJoinPool()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test submit(Runnable) executes the task.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testSubmitRunnable(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
var latch = new CountDownLatch(1);
|
||||
Future<?> future = executor.submit(latch::countDown);
|
||||
latch.await();
|
||||
assertNull(future.get());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test submit(Runnable) throws if executor is shutdown.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testSubmitRunnableAfterShutdown(ExecutorService executor) {
|
||||
executor.shutdown();
|
||||
assertThrows(RejectedExecutionException.class, () -> executor.submit(() -> { }));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test task submitted with submit(Runnable) is not interrupted by cancel(false).
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testSubmitRunnableWithCancelFalse(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
var started = new CountDownLatch(1);
|
||||
var stop = new CountDownLatch(1);
|
||||
var done = new CountDownLatch(1);
|
||||
Future<?> future = executor.submit(() -> {
|
||||
started.countDown();
|
||||
try {
|
||||
stop.await();
|
||||
} catch (InterruptedException e) {
|
||||
// ignore
|
||||
} finally {
|
||||
done.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
// wait for task to start
|
||||
started.await();
|
||||
|
||||
// cancel(false), task should not be interrupted
|
||||
future.cancel(false);
|
||||
assertFalse(done.await(500, TimeUnit.MILLISECONDS));
|
||||
|
||||
// let task finish
|
||||
stop.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test task submitted with submit(Runnable) is interrupted by cancel(true).
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testSubmitRunnableWithCancelTrue(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
var started = new CountDownLatch(1);
|
||||
var interrupted = new CountDownLatch(1);
|
||||
Future<?> future = executor.submit(() -> {
|
||||
started.countDown();
|
||||
try {
|
||||
Thread.sleep(Duration.ofDays(1));
|
||||
} catch (InterruptedException e) {
|
||||
interrupted.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
// wait for task to start
|
||||
started.await();
|
||||
|
||||
// cancel(true), task should be interrupted
|
||||
future.cancel(true);
|
||||
interrupted.await();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test task submitted with submit(Runnable) is interrupted if executor is
|
||||
* stopped with shutdownNow.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testSubmitRunnableWithShutdownNow(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
var started = new CountDownLatch(1);
|
||||
var interrupted = new CountDownLatch(1);
|
||||
Future<?> future = executor.submit(() -> {
|
||||
started.countDown();
|
||||
try {
|
||||
Thread.sleep(Duration.ofDays(1));
|
||||
} catch (InterruptedException e) {
|
||||
interrupted.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
// wait for task to start
|
||||
started.await();
|
||||
|
||||
// shutdown forcefully, task should be interrupted
|
||||
executor.shutdownNow();
|
||||
interrupted.await();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test submit(Runnable) throws if task is null.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testSubmitRunnableNull(ExecutorService executor) {
|
||||
try (executor) {
|
||||
Runnable nullTask = null;
|
||||
assertThrows(NullPointerException.class, () -> executor.submit(nullTask));
|
||||
assertThrows(NullPointerException.class, () -> executor.submit(nullTask, Void.class));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
/**
|
||||
* Test submit(Callable) executes the task.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testSubmitCallable(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
var latch = new CountDownLatch(1);
|
||||
Future<String> future = executor.submit(() -> {
|
||||
latch.countDown();
|
||||
return "foo";
|
||||
});
|
||||
latch.await();
|
||||
assertEquals("foo", future.get());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test submit(Callable) throws if executor is shutdown.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testSubmitCallableAfterShutdown(ExecutorService executor) {
|
||||
executor.shutdown();
|
||||
assertThrows(RejectedExecutionException.class, () -> executor.submit(() -> null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test task submitted with submit(Callable) is not interrupted by cancel(false).
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testSubmitCallableWithCancelFalse(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
var started = new CountDownLatch(1);
|
||||
var stop = new CountDownLatch(1);
|
||||
var done = new CountDownLatch(1);
|
||||
Future<Void> future = executor.submit(() -> {
|
||||
started.countDown();
|
||||
try {
|
||||
stop.await();
|
||||
} finally {
|
||||
done.countDown();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
// wait for task to start
|
||||
started.await();
|
||||
|
||||
// cancel(false), task should not be interrupted
|
||||
future.cancel(false);
|
||||
assertFalse(done.await(500, TimeUnit.MILLISECONDS));
|
||||
|
||||
// let task finish
|
||||
stop.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test task submitted with submit(Callable) is interrupted by cancel(true).
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testSubmitCallableWithCancelTrue(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
var started = new CountDownLatch(1);
|
||||
var interrupted = new CountDownLatch(1);
|
||||
Future<Void> future = executor.submit(() -> {
|
||||
started.countDown();
|
||||
try {
|
||||
Thread.sleep(Duration.ofDays(1));
|
||||
} catch (InterruptedException e) {
|
||||
interrupted.countDown();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
// wait for task to start
|
||||
started.await();
|
||||
|
||||
// cancel(true), task should be interrupted
|
||||
future.cancel(true);
|
||||
interrupted.await();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test task submitted with submit(Callable) is interrupted if executor is
|
||||
* stopped with shutdownNow.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testSubmitCallableWithShutdownNow(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
var started = new CountDownLatch(1);
|
||||
var interrupted = new CountDownLatch(1);
|
||||
Future<Void> future = executor.submit(() -> {
|
||||
started.countDown();
|
||||
try {
|
||||
Thread.sleep(Duration.ofDays(1));
|
||||
} catch (InterruptedException e) {
|
||||
interrupted.countDown();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
// wait for task to start
|
||||
started.await();
|
||||
|
||||
// shutdown forcefully, task should be interrupted
|
||||
executor.shutdownNow();
|
||||
interrupted.await();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test submit(Callable) throws if task is null.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testSubmitCallableNull(ExecutorService executor) {
|
||||
try (executor) {
|
||||
Callable<Void> nullTask = null;
|
||||
assertThrows(NullPointerException.class, () -> executor.submit(nullTask));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
/**
|
||||
* Test execute(Runnable) executes the task.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testExecute(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
var latch = new CountDownLatch(1);
|
||||
executor.execute(latch::countDown);
|
||||
latch.await();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test execute(Runnable) throws if executor is shutdown.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testExecuteAfterShutdown(ExecutorService executor) {
|
||||
executor.shutdown();
|
||||
assertThrows(RejectedExecutionException.class, () -> executor.execute(() -> { }));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test task submitted with execute(Runnable) is interrupted if executor is
|
||||
* stopped with shutdownNow.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testExecuteWithShutdownNow(ExecutorService executor) throws Exception {
|
||||
try (executor) {
|
||||
var started = new CountDownLatch(1);
|
||||
var interrupted = new CountDownLatch(1);
|
||||
executor.execute(() -> {
|
||||
started.countDown();
|
||||
try {
|
||||
Thread.sleep(Duration.ofDays(1));
|
||||
} catch (InterruptedException e) {
|
||||
interrupted.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
// wait for task to start
|
||||
started.await();
|
||||
|
||||
// shutdown forcefully, task should be interrupted
|
||||
executor.shutdownNow();
|
||||
interrupted.await();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test execute(Runnable) throws if task is null.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testExecuteNull(ExecutorService executor) {
|
||||
try (executor) {
|
||||
Runnable nullTask = null;
|
||||
assertThrows(NullPointerException.class, () -> executor.execute(nullTask));
|
||||
}
|
||||
}
|
||||
}
|
||||
191
test/jdk/java/util/concurrent/Executors/AutoShutdown.java
Normal file
191
test/jdk/java/util/concurrent/Executors/AutoShutdown.java
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6399443 8302899 8362123
|
||||
* @summary Test that Executors.newSingleThreadExecutor wraps an ExecutorService that
|
||||
* automatically shuts down and terminates when the wrapper is GC'ed
|
||||
* @library /test/lib/
|
||||
* @modules java.base/java.util.concurrent:+open
|
||||
* @run junit AutoShutdown
|
||||
*/
|
||||
|
||||
import java.lang.ref.PhantomReference;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.reflect.Field;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import jdk.test.lib.Utils;
|
||||
import jdk.test.lib.util.ForceGC;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class AutoShutdown {
|
||||
|
||||
private static Stream<Supplier<ExecutorService>> executors() {
|
||||
return Stream.of(
|
||||
() -> Executors.newSingleThreadExecutor(),
|
||||
() -> Executors.newSingleThreadExecutor(Executors.defaultThreadFactory())
|
||||
);
|
||||
}
|
||||
|
||||
private static Stream<Arguments> executorAndQueuedTaskCounts() {
|
||||
int[] queuedTaskCounts = { 0, 1, 2 };
|
||||
return executors().flatMap(s -> IntStream.of(queuedTaskCounts)
|
||||
.mapToObj(i -> Arguments.of(s, i)));
|
||||
}
|
||||
|
||||
private static Stream<Arguments> shutdownMethods() {
|
||||
return Stream.<Consumer<ExecutorService>>of(
|
||||
e -> e.shutdown(),
|
||||
e -> e.shutdownNow()
|
||||
).map(Arguments::of);
|
||||
}
|
||||
|
||||
/**
|
||||
* SingleThreadExecutor with no worker threads.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testNoWorker(Supplier<ExecutorService> supplier) throws Exception {
|
||||
ExecutorService executor = supplier.get();
|
||||
ExecutorService delegate = getDelegate(executor);
|
||||
executor = null;
|
||||
gcAndAwaitTermination(delegate);
|
||||
}
|
||||
|
||||
/**
|
||||
* SingleThreadExecutor with an idle worker thread.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testIdleWorker(Supplier<ExecutorService> supplier) throws Exception {
|
||||
ExecutorService executor = supplier.get();
|
||||
// submit a task to get a worker to start
|
||||
executor.submit(() -> null).get();
|
||||
ExecutorService delegate = getDelegate(executor);
|
||||
executor = null;
|
||||
gcAndAwaitTermination(delegate);
|
||||
}
|
||||
|
||||
/**
|
||||
* SingleThreadExecutor with an active worker and queued tasks.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executorAndQueuedTaskCounts")
|
||||
void testActiveWorker(Supplier<ExecutorService> supplier,int queuedTaskCount) throws Exception {
|
||||
ExecutorService executor = supplier.get();
|
||||
// the worker will execute one task, the other tasks will be queued
|
||||
int ntasks = 1 + queuedTaskCount;
|
||||
AtomicInteger completedTaskCount = new AtomicInteger();
|
||||
for (int i = 0; i < ntasks; i++) {
|
||||
executor.submit(() -> {
|
||||
Thread.sleep(Duration.ofMillis(500));
|
||||
completedTaskCount.incrementAndGet();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
ExecutorService delegate = getDelegate(executor);
|
||||
executor = null;
|
||||
gcAndAwaitTermination(delegate);
|
||||
assertEquals(ntasks, completedTaskCount.get());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("shutdownMethods")
|
||||
void testShutdownUnlinksCleaner(Consumer<ExecutorService> shutdown) throws Exception {
|
||||
ClassLoader classLoader =
|
||||
Utils.getTestClassPathURLClassLoader(ClassLoader.getPlatformClassLoader());
|
||||
|
||||
ReferenceQueue<?> queue = new ReferenceQueue<>();
|
||||
Reference<?> reference = new PhantomReference(classLoader, queue);
|
||||
try {
|
||||
Class<?> isolatedClass = classLoader.loadClass("AutoShutdown$IsolatedClass");
|
||||
assertSame(isolatedClass.getClassLoader(), classLoader);
|
||||
isolatedClass.getDeclaredMethod("shutdown", Consumer.class).invoke(null, shutdown);
|
||||
|
||||
isolatedClass = null;
|
||||
classLoader = null;
|
||||
|
||||
assertTrue(ForceGC.wait(() -> queue.poll() != null));
|
||||
} finally {
|
||||
Reference.reachabilityFence(reference);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the delegate for the given ExecutorService. The given ExecutorService
|
||||
* must be a Executors$DelegatedExecutorService.
|
||||
*/
|
||||
private ExecutorService getDelegate(ExecutorService executor) throws Exception {
|
||||
Field eField = Class.forName("java.util.concurrent.Executors$DelegatedExecutorService")
|
||||
.getDeclaredField("e");
|
||||
eField.setAccessible(true);
|
||||
return (ExecutorService) eField.get(executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes System.gc and waits for the given ExecutorService to terminate.
|
||||
*/
|
||||
private void gcAndAwaitTermination(ExecutorService executor) throws Exception {
|
||||
System.err.println(executor);
|
||||
boolean terminated = false;
|
||||
while (!terminated) {
|
||||
System.gc();
|
||||
terminated = executor.awaitTermination(100, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
public static class IsolatedClass {
|
||||
|
||||
private static final ExecutorService executor =
|
||||
Executors.newSingleThreadExecutor(new IsolatedThreadFactory());
|
||||
|
||||
public static void shutdown(Consumer<ExecutorService> shutdown) {
|
||||
shutdown.accept(executor);
|
||||
}
|
||||
}
|
||||
|
||||
public static class IsolatedThreadFactory implements ThreadFactory {
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
return new Thread(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
117
test/jdk/java/util/concurrent/Executors/PrivilegedCallables.java
Normal file
117
test/jdk/java/util/concurrent/Executors/PrivilegedCallables.java
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6552961 6558429
|
||||
* @summary Test privilegedCallable, privilegedCallableUsingCurrentClassLoader
|
||||
* @run main PrivilegedCallables
|
||||
* @author Martin Buchholz
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.Executors.privilegedCallable;
|
||||
import static java.util.concurrent.Executors.privilegedCallableUsingCurrentClassLoader;
|
||||
import static java.util.concurrent.Executors.privilegedThreadFactory;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class PrivilegedCallables {
|
||||
Callable<Integer> real;
|
||||
|
||||
final Callable<Integer> realCaller = new Callable<>() {
|
||||
public Integer call() throws Exception {
|
||||
return real.call(); }};
|
||||
|
||||
final Random rnd = new Random();
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
final Throwable[] throwables = {
|
||||
new Exception() {},
|
||||
new RuntimeException() {},
|
||||
new Error() {}
|
||||
};
|
||||
Throwable randomThrowable() {
|
||||
return throwables[rnd.nextInt(throwables.length)];
|
||||
}
|
||||
void throwThrowable(Throwable t) throws Exception {
|
||||
if (t instanceof Error) throw (Error) t;
|
||||
if (t instanceof RuntimeException) throw (RuntimeException) t;
|
||||
throw (Exception) t;
|
||||
}
|
||||
|
||||
void test(String[] args) {
|
||||
try { test(privilegedCallable(realCaller)); }
|
||||
catch (Throwable t) { unexpected(t); }
|
||||
|
||||
try { test(privilegedCallableUsingCurrentClassLoader(realCaller)); }
|
||||
catch (Throwable t) { unexpected(t); }
|
||||
|
||||
try { privilegedThreadFactory(); }
|
||||
catch (Throwable t) { unexpected(t); }
|
||||
}
|
||||
|
||||
void test(final Callable<Integer> c) throws Throwable {
|
||||
for (int i = 0; i < 20; i++)
|
||||
if (rnd.nextBoolean()) {
|
||||
final Throwable t = randomThrowable();
|
||||
real = new Callable<>() {
|
||||
public Integer call() throws Exception {
|
||||
throwThrowable(t);
|
||||
return null; }};
|
||||
try {
|
||||
c.call();
|
||||
fail("Expected exception not thrown");
|
||||
} catch (Throwable tt) { check(t == tt); }
|
||||
} else {
|
||||
final int n = rnd.nextInt();
|
||||
real = new Callable<>() {
|
||||
public Integer call() { return n; }};
|
||||
equal(c.call(), n);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new PrivilegedCallables().instanceMain(args);}
|
||||
void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
abstract class F {abstract void f() throws Throwable;}
|
||||
void THROWS(Class<? extends Throwable> k, F... fs) {
|
||||
for (F f : fs)
|
||||
try {f.f(); fail("Expected " + k.getName() + " not thrown");}
|
||||
catch (Throwable t) {
|
||||
if (k.isAssignableFrom(t.getClass())) pass();
|
||||
else unexpected(t);}}
|
||||
}
|
||||
142
test/jdk/java/util/concurrent/Executors/Throws.java
Normal file
142
test/jdk/java/util/concurrent/Executors/Throws.java
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6398290
|
||||
* @summary Check Executors/STPE Exception specifications
|
||||
* @author Martin Buchholz
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.Executors.callable;
|
||||
import static java.util.concurrent.Executors.defaultThreadFactory;
|
||||
import static java.util.concurrent.Executors.newCachedThreadPool;
|
||||
import static java.util.concurrent.Executors.newFixedThreadPool;
|
||||
import static java.util.concurrent.Executors.newScheduledThreadPool;
|
||||
import static java.util.concurrent.Executors.newSingleThreadExecutor;
|
||||
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
|
||||
import static java.util.concurrent.Executors.privilegedCallable;
|
||||
import static java.util.concurrent.Executors.unconfigurableExecutorService;
|
||||
import static java.util.concurrent.Executors.unconfigurableScheduledExecutorService;
|
||||
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
public class Throws {
|
||||
private static void realMain(String[] args) throws Throwable {
|
||||
final ThreadFactory fac = defaultThreadFactory();
|
||||
final ThreadFactory nullFactory = null;
|
||||
final RejectedExecutionHandler reh
|
||||
= new RejectedExecutionHandler() {
|
||||
public void rejectedExecution(Runnable r,
|
||||
ThreadPoolExecutor executor) {}};
|
||||
final RejectedExecutionHandler nullHandler = null;
|
||||
|
||||
THROWS(NullPointerException.class,
|
||||
() -> newFixedThreadPool(3, null),
|
||||
() -> newCachedThreadPool(null),
|
||||
() -> newSingleThreadScheduledExecutor(null),
|
||||
() -> newScheduledThreadPool(0, null),
|
||||
() -> unconfigurableExecutorService(null),
|
||||
() -> unconfigurableScheduledExecutorService(null),
|
||||
() -> callable(null, "foo"),
|
||||
() -> callable((Runnable) null),
|
||||
() -> callable((PrivilegedAction<?>) null),
|
||||
() -> callable((PrivilegedExceptionAction<?>) null),
|
||||
() -> privilegedCallable((Callable<?>) null),
|
||||
() -> new ScheduledThreadPoolExecutor(0, nullFactory),
|
||||
() -> new ScheduledThreadPoolExecutor(0, nullFactory, reh),
|
||||
() -> new ScheduledThreadPoolExecutor(0, fac, nullHandler));
|
||||
|
||||
THROWS(IllegalArgumentException.class,
|
||||
() -> newFixedThreadPool(-42),
|
||||
() -> newFixedThreadPool(0),
|
||||
() -> newFixedThreadPool(-42, fac),
|
||||
() -> newFixedThreadPool(0, fac),
|
||||
() -> newScheduledThreadPool(-42),
|
||||
() -> new ScheduledThreadPoolExecutor(-42),
|
||||
() -> new ScheduledThreadPoolExecutor(-42, reh),
|
||||
() -> new ScheduledThreadPoolExecutor(-42, fac, reh));
|
||||
|
||||
try { newFixedThreadPool(1).shutdownNow(); pass(); }
|
||||
catch (Throwable t) { unexpected(t); }
|
||||
|
||||
try { newFixedThreadPool(1, fac).shutdownNow(); pass(); }
|
||||
catch (Throwable t) { unexpected(t); }
|
||||
|
||||
try { newSingleThreadExecutor().shutdownNow(); pass(); }
|
||||
catch (Throwable t) { unexpected(t); }
|
||||
|
||||
try { newCachedThreadPool().shutdownNow(); pass(); }
|
||||
catch (Throwable t) { unexpected(t); }
|
||||
|
||||
try { newSingleThreadScheduledExecutor().shutdownNow(); pass(); }
|
||||
catch (Throwable t) { unexpected(t); }
|
||||
|
||||
try { newSingleThreadScheduledExecutor(fac).shutdownNow(); pass(); }
|
||||
catch (Throwable t) { unexpected(t); }
|
||||
|
||||
try { newScheduledThreadPool(0).shutdownNow(); pass(); }
|
||||
catch (Throwable t) { unexpected(t); }
|
||||
|
||||
try { newScheduledThreadPool(0, fac).shutdownNow(); pass(); }
|
||||
catch (Throwable t) { unexpected(t); }
|
||||
|
||||
try { new ScheduledThreadPoolExecutor(0).shutdownNow(); pass(); }
|
||||
catch (Throwable t) { unexpected(t); }
|
||||
|
||||
try { new ScheduledThreadPoolExecutor(0, fac).shutdownNow(); pass(); }
|
||||
catch (Throwable t) { unexpected(t); }
|
||||
|
||||
try { new ScheduledThreadPoolExecutor(0, fac, reh).shutdownNow(); pass(); }
|
||||
catch (Throwable t) { unexpected(t); }
|
||||
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
static volatile int passed = 0, failed = 0;
|
||||
static void pass() {passed++;}
|
||||
static void fail() {failed++; Thread.dumpStack();}
|
||||
static void fail(String msg) {System.out.println(msg); fail();}
|
||||
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
static void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
static void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
try {realMain(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
interface Fun {void f() throws Throwable;}
|
||||
static void THROWS(Class<? extends Throwable> k, Fun... fs) {
|
||||
for (Fun f : fs)
|
||||
try { f.f(); fail("Expected " + k.getName() + " not thrown"); }
|
||||
catch (Throwable t) {
|
||||
if (k.isAssignableFrom(t.getClass())) pass();
|
||||
else unexpected(t);}}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8308235
|
||||
* @summary Unreference ExecutorService objects returned by the Executors without shutdown
|
||||
* and termination, this should not leak memory
|
||||
* @run main/othervm -Xmx32m UnreferencedExecutor
|
||||
*/
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class UnreferencedExecutor {
|
||||
|
||||
private static final int DURATION_IN_SECONDS = 5;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
int ncores = Runtime.getRuntime().availableProcessors();
|
||||
long durationNanos = Duration.ofSeconds(DURATION_IN_SECONDS).toNanos();
|
||||
long start = System.nanoTime();
|
||||
while (System.nanoTime() - start < durationNanos) {
|
||||
Executors.newFixedThreadPool(ncores);
|
||||
Executors.newCachedThreadPool();
|
||||
Executors.newVirtualThreadPerTaskExecutor();
|
||||
Executors.newWorkStealingPool(ncores);
|
||||
}
|
||||
}
|
||||
}
|
||||
240
test/jdk/java/util/concurrent/Future/DefaultMethods.java
Normal file
240
test/jdk/java/util/concurrent/Future/DefaultMethods.java
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Test Future's default methods
|
||||
* @library ../lib
|
||||
* @run junit DefaultMethods
|
||||
*/
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.stream.Stream;
|
||||
import static java.util.concurrent.Future.State.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class DefaultMethods {
|
||||
|
||||
static Stream<ExecutorService> executors() {
|
||||
return Stream.of(
|
||||
// ensures that default close method is tested
|
||||
new DelegatingExecutorService(Executors.newCachedThreadPool()),
|
||||
|
||||
// executors that may return a Future that overrides the methods
|
||||
Executors.newCachedThreadPool(),
|
||||
Executors.newVirtualThreadPerTaskExecutor(),
|
||||
new ForkJoinPool()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test methods when the task has not completed.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testRunningTask(ExecutorService executor) {
|
||||
try (executor) {
|
||||
var latch = new CountDownLatch(1);
|
||||
Future<?> future = executor.submit(() -> { latch.await(); return null; });
|
||||
try {
|
||||
assertTrue(future.state() == RUNNING);
|
||||
assertThrows(IllegalStateException.class, future::resultNow);
|
||||
assertThrows(IllegalStateException.class, future::exceptionNow);
|
||||
} finally {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test methods when the task has already completed with a result.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testCompletedTask1(ExecutorService executor) {
|
||||
try (executor) {
|
||||
Future<String> future = executor.submit(() -> "foo");
|
||||
awaitDone(future);
|
||||
assertTrue(future.state() == SUCCESS);
|
||||
assertEquals("foo", future.resultNow());
|
||||
assertThrows(IllegalStateException.class, future::exceptionNow);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test methods when the task has already completed with null.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testCompletedTask2(ExecutorService executor) {
|
||||
try (executor) {
|
||||
Future<String> future = executor.submit(() -> null);
|
||||
awaitDone(future);
|
||||
assertTrue(future.state() == SUCCESS);
|
||||
assertNull(future.resultNow());
|
||||
assertThrows(IllegalStateException.class, future::exceptionNow);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test methods when the task has completed with an exception.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testFailedTask(ExecutorService executor) {
|
||||
try (executor) {
|
||||
Future<?> future = executor.submit(() -> { throw new ArithmeticException(); });
|
||||
awaitDone(future);
|
||||
assertTrue(future.state() == FAILED);
|
||||
assertThrows(IllegalStateException.class, future::resultNow);
|
||||
Throwable ex = future.exceptionNow();
|
||||
assertTrue(ex instanceof ArithmeticException);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test methods when the task has been cancelled (mayInterruptIfRunning=false)
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testCancelledTask1(ExecutorService executor) {
|
||||
try (executor) {
|
||||
var latch = new CountDownLatch(1);
|
||||
Future<?> future = executor.submit(() -> { latch.await(); return null; });
|
||||
future.cancel(false);
|
||||
try {
|
||||
assertTrue(future.state() == CANCELLED);
|
||||
assertThrows(IllegalStateException.class, future::resultNow);
|
||||
assertThrows(IllegalStateException.class, future::exceptionNow);
|
||||
} finally {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test methods when the task has been cancelled (mayInterruptIfRunning=true)
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void testCancelledTask2(ExecutorService executor) {
|
||||
try (executor) {
|
||||
var latch = new CountDownLatch(1);
|
||||
Future<?> future = executor.submit(() -> { latch.await(); return null; });
|
||||
future.cancel(true);
|
||||
try {
|
||||
assertTrue(future.state() == CANCELLED);
|
||||
assertThrows(IllegalStateException.class, future::resultNow);
|
||||
assertThrows(IllegalStateException.class, future::exceptionNow);
|
||||
} finally {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test CompletableFuture with the task has not completed.
|
||||
*/
|
||||
@Test
|
||||
void testCompletableFuture1() {
|
||||
var future = new CompletableFuture<String>();
|
||||
assertTrue(future.state() == RUNNING);
|
||||
assertThrows(IllegalStateException.class, future::resultNow);
|
||||
assertThrows(IllegalStateException.class, future::exceptionNow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test CompletableFuture with the task that completed with result.
|
||||
*/
|
||||
@Test
|
||||
void testCompletableFuture2() {
|
||||
var future = new CompletableFuture<String>();
|
||||
future.complete("foo");
|
||||
assertTrue(future.state() == SUCCESS);
|
||||
assertEquals("foo", future.resultNow());
|
||||
assertThrows(IllegalStateException.class, future::exceptionNow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test CompletableFuture with the task that completed with null.
|
||||
*/
|
||||
@Test
|
||||
void testCompletableFuture3() {
|
||||
var future = new CompletableFuture<String>();
|
||||
future.complete(null);
|
||||
assertTrue(future.state() == SUCCESS);
|
||||
assertNull(future.resultNow());
|
||||
assertThrows(IllegalStateException.class, future::exceptionNow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test CompletableFuture with the task that completed with exception.
|
||||
*/
|
||||
@Test
|
||||
void testCompletableFuture4() {
|
||||
var future = new CompletableFuture<String>();
|
||||
future.completeExceptionally(new ArithmeticException());
|
||||
assertTrue(future.state() == FAILED);
|
||||
assertThrows(IllegalStateException.class, future::resultNow);
|
||||
Throwable ex = future.exceptionNow();
|
||||
assertTrue(ex instanceof ArithmeticException);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test CompletableFuture with the task that was cancelled.
|
||||
*/
|
||||
@Test
|
||||
void testCompletableFuture5() {
|
||||
var future = new CompletableFuture<String>();
|
||||
future.cancel(false);
|
||||
assertTrue(future.state() == CANCELLED);
|
||||
assertThrows(IllegalStateException.class, future::resultNow);
|
||||
assertThrows(IllegalStateException.class, future::exceptionNow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the future to be done.
|
||||
*/
|
||||
private static void awaitDone(Future<?> future) {
|
||||
boolean interrupted = false;
|
||||
while (!future.isDone()) {
|
||||
try {
|
||||
Thread.sleep(10);
|
||||
} catch (InterruptedException e) {
|
||||
interrupted = true;
|
||||
}
|
||||
}
|
||||
if (interrupted) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6431315
|
||||
* @summary ExecutorService.invokeAll might hang
|
||||
* @author Martin Buchholz
|
||||
* @library /test/lib
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
/**
|
||||
* Adapted from Doug Lea, which was...
|
||||
* adapted from a posting by Tom Sugden tom at epcc.ed.ac.uk
|
||||
*/
|
||||
public class BlockingTaskExecutor {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
|
||||
static void realMain(String[] args) throws Throwable {
|
||||
for (int i = 1; i <= 100; i++) {
|
||||
System.out.print(".");
|
||||
test();
|
||||
}
|
||||
}
|
||||
|
||||
static void test() throws Throwable {
|
||||
final ExecutorService executor = Executors.newCachedThreadPool();
|
||||
|
||||
final NotificationReceiver notifiee1 = new NotificationReceiver();
|
||||
final NotificationReceiver notifiee2 = new NotificationReceiver();
|
||||
|
||||
final Collection<Callable<Object>> tasks = new ArrayList<>();
|
||||
tasks.add(new BlockingTask(notifiee1));
|
||||
tasks.add(new BlockingTask(notifiee2));
|
||||
tasks.add(new NonBlockingTask());
|
||||
|
||||
// start a thread to invoke the tasks
|
||||
Thread thread = new Thread() { public void run() {
|
||||
try { executor.invokeAll(tasks); }
|
||||
catch (RejectedExecutionException t) {/* OK */}
|
||||
catch (Throwable t) { unexpected(t); }}};
|
||||
thread.start();
|
||||
|
||||
// Wait until tasks begin execution
|
||||
notifiee1.waitForNotification();
|
||||
notifiee2.waitForNotification();
|
||||
|
||||
// Now try to shutdown the executor service while tasks
|
||||
// are blocked. This should cause the tasks to be
|
||||
// interrupted.
|
||||
executor.shutdownNow();
|
||||
if (! executor.awaitTermination(LONG_DELAY_MS, MILLISECONDS))
|
||||
throw new Error(
|
||||
String.format("Executor termination timed out after %d ms",
|
||||
LONG_DELAY_MS));
|
||||
|
||||
// Wait for the invocation thread to complete.
|
||||
thread.join(LONG_DELAY_MS);
|
||||
if (thread.isAlive()) {
|
||||
thread.interrupt();
|
||||
thread.join(LONG_DELAY_MS);
|
||||
throw new Error(
|
||||
String.format("invokeAll timed out after %d ms",
|
||||
LONG_DELAY_MS));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper class with a method to wait for a notification.
|
||||
*
|
||||
* The notification is received via the
|
||||
* {@code sendNotification} method.
|
||||
*/
|
||||
static class NotificationReceiver {
|
||||
/** Has the notifiee been notified? */
|
||||
boolean notified;
|
||||
|
||||
/**
|
||||
* Notify the notification receiver.
|
||||
*/
|
||||
public synchronized void sendNotification() {
|
||||
notified = true;
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until a notification has been received.
|
||||
*
|
||||
* @throws InterruptedException if the wait is interrupted
|
||||
*/
|
||||
public synchronized void waitForNotification()
|
||||
throws InterruptedException {
|
||||
while (! notified)
|
||||
wait();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A callable task that blocks until it is interrupted.
|
||||
* This task sends a notification to a notification receiver when
|
||||
* it is first called.
|
||||
*/
|
||||
static class BlockingTask implements Callable<Object> {
|
||||
private final NotificationReceiver notifiee;
|
||||
|
||||
BlockingTask(NotificationReceiver notifiee) {
|
||||
this.notifiee = notifiee;
|
||||
}
|
||||
|
||||
public Object call() throws InterruptedException {
|
||||
notifiee.sendNotification();
|
||||
|
||||
// wait indefinitely until task is interrupted
|
||||
while (true) {
|
||||
synchronized (this) {
|
||||
wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A callable task that simply returns a string result.
|
||||
*/
|
||||
static class NonBlockingTask implements Callable<Object> {
|
||||
public Object call() {
|
||||
return "NonBlockingTaskResult";
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
static volatile int passed = 0, failed = 0;
|
||||
static void pass() {passed++;}
|
||||
static void fail() {failed++; Thread.dumpStack();}
|
||||
static void fail(String msg) {System.out.println(msg); fail();}
|
||||
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
static void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
static void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
try {realMain(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 4486658
|
||||
* @summary Checks for responsiveness of futures to cancellation.
|
||||
* Runs under the assumption that ITERS computations require more than
|
||||
* TIMEOUT msecs to complete.
|
||||
* @library /test/lib
|
||||
* @run main/timeout=2000 CancelledFutureLoops
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import java.util.SplittableRandom;
|
||||
import java.util.concurrent.BrokenBarrierException;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
public final class CancelledFutureLoops {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
static final ExecutorService pool = Executors.newCachedThreadPool();
|
||||
static final SplittableRandom rnd = new SplittableRandom();
|
||||
static boolean print = false;
|
||||
static final int ITERS = 1000000;
|
||||
static final long TIMEOUT = 100;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
int maxThreads = 5;
|
||||
if (args.length > 0)
|
||||
maxThreads = Integer.parseInt(args[0]);
|
||||
|
||||
print = true;
|
||||
|
||||
for (int i = 2; i <= maxThreads; i += (i+1) >>> 1) {
|
||||
System.out.print("Threads: " + i);
|
||||
try {
|
||||
new FutureLoop(i, rnd.split()).test();
|
||||
}
|
||||
catch (BrokenBarrierException bb) {
|
||||
// OK; ignore
|
||||
}
|
||||
catch (ExecutionException ee) {
|
||||
// OK; ignore
|
||||
}
|
||||
Thread.sleep(TIMEOUT);
|
||||
}
|
||||
pool.shutdown();
|
||||
if (! pool.awaitTermination(6 * LONG_DELAY_MS, MILLISECONDS))
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
static final class FutureLoop implements Callable {
|
||||
private final int nthreads;
|
||||
private final SplittableRandom rnd;
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
private final LoopHelpers.BarrierTimer timer = new LoopHelpers.BarrierTimer();
|
||||
private final CyclicBarrier barrier;
|
||||
private int v;
|
||||
FutureLoop(int nthreads, SplittableRandom rnd) {
|
||||
this.nthreads = nthreads;
|
||||
this.rnd = rnd;
|
||||
barrier = new CyclicBarrier(nthreads+1, timer);
|
||||
v = rnd.nextInt();
|
||||
}
|
||||
|
||||
final void test() throws Exception {
|
||||
Future[] futures = new Future[nthreads];
|
||||
for (int i = 0; i < nthreads; ++i)
|
||||
futures[i] = pool.submit(this);
|
||||
|
||||
barrier.await();
|
||||
Thread.sleep(TIMEOUT);
|
||||
boolean tooLate = false;
|
||||
for (int i = 1; i < nthreads; ++i) {
|
||||
if (!futures[i].cancel(true))
|
||||
tooLate = true;
|
||||
// Unbunch some of the cancels
|
||||
if ( (i & 3) == 0)
|
||||
Thread.sleep(1 + rnd.nextInt(5));
|
||||
}
|
||||
|
||||
Object f0 = futures[0].get();
|
||||
if (!tooLate) {
|
||||
for (int i = 1; i < nthreads; ++i) {
|
||||
if (!futures[i].isDone() || !futures[i].isCancelled())
|
||||
throw new Error("Only one thread should complete");
|
||||
}
|
||||
}
|
||||
else
|
||||
System.out.print("(cancelled too late) ");
|
||||
|
||||
long endTime = System.nanoTime();
|
||||
long time = endTime - timer.startTime;
|
||||
if (print) {
|
||||
double secs = (double)time / 1000000000.0;
|
||||
System.out.println("\t " + secs + "s run time");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public final Object call() throws Exception {
|
||||
barrier.await();
|
||||
int sum = v;
|
||||
int x = 0;
|
||||
int n = ITERS;
|
||||
while (n-- > 0) {
|
||||
lock.lockInterruptibly();
|
||||
try {
|
||||
v = x = LoopHelpers.compute1(v);
|
||||
}
|
||||
finally {
|
||||
lock.unlock();
|
||||
}
|
||||
sum += LoopHelpers.compute2(LoopHelpers.compute2(x));
|
||||
}
|
||||
return new Integer(sum);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
211
test/jdk/java/util/concurrent/FutureTask/Customized.java
Normal file
211
test/jdk/java/util/concurrent/FutureTask/Customized.java
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6464365
|
||||
* @summary Test state transitions; check protected methods are called
|
||||
* @author Martin Buchholz
|
||||
*/
|
||||
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class Customized {
|
||||
static final AtomicLong doneCount = new AtomicLong(0);
|
||||
static final AtomicLong setCount = new AtomicLong(0);
|
||||
static final AtomicLong setExceptionCount = new AtomicLong(0);
|
||||
|
||||
static void equal(long expected, AtomicLong actual) {
|
||||
equal(expected, actual.get());
|
||||
}
|
||||
|
||||
static void equalCounts(long x, long y, long z) {
|
||||
equal(x, doneCount);
|
||||
equal(y, setCount);
|
||||
equal(z, setExceptionCount);
|
||||
}
|
||||
|
||||
static class MyFutureTask<V> extends FutureTask<V> {
|
||||
MyFutureTask(Runnable r, V result) { super(r, result); }
|
||||
protected void done() {
|
||||
doneCount.getAndIncrement();
|
||||
super.done();
|
||||
}
|
||||
protected void set(V v) {
|
||||
setCount.getAndIncrement();
|
||||
super.set(v);
|
||||
}
|
||||
protected void setException(Throwable t) {
|
||||
setExceptionCount.getAndIncrement();
|
||||
super.setException(t);
|
||||
}
|
||||
public boolean runAndReset() {
|
||||
return super.runAndReset();
|
||||
}
|
||||
}
|
||||
|
||||
static <V> void checkReady(final FutureTask<V> task) {
|
||||
check(! task.isDone());
|
||||
check(! task.isCancelled());
|
||||
THROWS(TimeoutException.class,
|
||||
() -> task.get(0L, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
static <V> void checkDone(final FutureTask<V> task) {
|
||||
try {
|
||||
check(task.isDone());
|
||||
check(! task.isCancelled());
|
||||
check(task.get() != null);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
}
|
||||
|
||||
static <V> void checkCancelled(final FutureTask<V> task) {
|
||||
check(task.isDone());
|
||||
check(task.isCancelled());
|
||||
THROWS(CancellationException.class,
|
||||
() -> task.get(0L, TimeUnit.SECONDS),
|
||||
() -> task.get());
|
||||
}
|
||||
|
||||
static <V> void checkThrew(final FutureTask<V> task) {
|
||||
check(task.isDone());
|
||||
check(! task.isCancelled());
|
||||
THROWS(ExecutionException.class,
|
||||
() -> task.get(0L, TimeUnit.SECONDS),
|
||||
() -> task.get());
|
||||
}
|
||||
|
||||
static <V> void cancel(FutureTask<V> task, boolean mayInterruptIfRunning) {
|
||||
task.cancel(mayInterruptIfRunning);
|
||||
checkCancelled(task);
|
||||
}
|
||||
|
||||
static <V> void run(FutureTask<V> task) {
|
||||
boolean isCancelled = task.isCancelled();
|
||||
task.run();
|
||||
check(task.isDone());
|
||||
equal(isCancelled, task.isCancelled());
|
||||
}
|
||||
|
||||
static void realMain(String[] args) throws Throwable {
|
||||
final Runnable nop = new Runnable() {
|
||||
public void run() {}};
|
||||
final Runnable bad = new Runnable() {
|
||||
public void run() { throw new Error(); }};
|
||||
|
||||
try {
|
||||
final MyFutureTask<Long> task = new MyFutureTask<>(nop, 42L);
|
||||
checkReady(task);
|
||||
equalCounts(0,0,0);
|
||||
check(task.runAndReset());
|
||||
checkReady(task);
|
||||
equalCounts(0,0,0);
|
||||
run(task);
|
||||
checkDone(task);
|
||||
equalCounts(1,1,0);
|
||||
equal(42L, task.get());
|
||||
run(task);
|
||||
checkDone(task);
|
||||
equalCounts(1,1,0);
|
||||
equal(42L, task.get());
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
try {
|
||||
final MyFutureTask<Long> task = new MyFutureTask<>(nop, 42L);
|
||||
cancel(task, false);
|
||||
equalCounts(2,1,0);
|
||||
cancel(task, false);
|
||||
equalCounts(2,1,0);
|
||||
run(task);
|
||||
equalCounts(2,1,0);
|
||||
check(! task.runAndReset());
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
try {
|
||||
final MyFutureTask<Long> task = new MyFutureTask<>(bad, 42L);
|
||||
checkReady(task);
|
||||
run(task);
|
||||
checkThrew(task);
|
||||
equalCounts(3,1,1);
|
||||
run(task);
|
||||
equalCounts(3,1,1);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
try {
|
||||
final MyFutureTask<Long> task = new MyFutureTask<>(nop, 42L);
|
||||
checkReady(task);
|
||||
task.set(99L);
|
||||
checkDone(task);
|
||||
equalCounts(4,2,1);
|
||||
run(task);
|
||||
equalCounts(4,2,1);
|
||||
task.setException(new Throwable());
|
||||
checkDone(task);
|
||||
equalCounts(4,2,2);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
try {
|
||||
final MyFutureTask<Long> task = new MyFutureTask<>(nop, 42L);
|
||||
checkReady(task);
|
||||
task.setException(new Throwable());
|
||||
checkThrew(task);
|
||||
equalCounts(5,2,3);
|
||||
run(task);
|
||||
equalCounts(5,2,3);
|
||||
task.set(99L);
|
||||
checkThrew(task);
|
||||
equalCounts(5,3,3);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
System.out.printf("doneCount=%d%n", doneCount.get());
|
||||
System.out.printf("setCount=%d%n", setCount.get());
|
||||
System.out.printf("setExceptionCount=%d%n", setExceptionCount.get());
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
static volatile int passed = 0, failed = 0;
|
||||
static void pass() {passed++;}
|
||||
static void fail() {failed++; Thread.dumpStack();}
|
||||
static void fail(String msg) {System.out.println(msg); fail();}
|
||||
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
static void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
static void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
try {realMain(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
interface Fun {void f() throws Throwable;}
|
||||
static void THROWS(Class<? extends Throwable> k, Fun... fs) {
|
||||
for (Fun f : fs)
|
||||
try { f.f(); fail("Expected " + k.getName() + " not thrown"); }
|
||||
catch (Throwable t) {
|
||||
if (k.isAssignableFrom(t.getClass())) pass();
|
||||
else unexpected(t);}}
|
||||
}
|
||||
106
test/jdk/java/util/concurrent/FutureTask/DoneMeansDone.java
Normal file
106
test/jdk/java/util/concurrent/FutureTask/DoneMeansDone.java
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8073704
|
||||
* @summary Checks that once isDone() returns true,
|
||||
* get() never throws InterruptedException or TimeoutException
|
||||
* @library /test/lib
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
public class DoneMeansDone {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
final int iters = 1000;
|
||||
final int nThreads = 2;
|
||||
final AtomicBoolean done = new AtomicBoolean(false);
|
||||
final AtomicReference<FutureTask<Boolean>> a = new AtomicReference<>();
|
||||
final CountDownLatch threadsStarted = new CountDownLatch(nThreads);
|
||||
final Callable<Boolean> alwaysTrue = new Callable<>() {
|
||||
public Boolean call() {
|
||||
return true;
|
||||
}};
|
||||
|
||||
final Runnable observer = new Runnable() { public void run() {
|
||||
threadsStarted.countDown();
|
||||
final ThreadLocalRandom rnd = ThreadLocalRandom.current();
|
||||
try {
|
||||
for (FutureTask<Boolean> f; !done.get();) {
|
||||
f = a.get();
|
||||
if (f != null) {
|
||||
do {} while (!f.isDone());
|
||||
Thread.currentThread().interrupt();
|
||||
if (!(rnd.nextBoolean()
|
||||
? f.get()
|
||||
: f.get(-1L, TimeUnit.DAYS)))
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
} catch (Exception t) { throw new AssertionError(t); }
|
||||
}};
|
||||
|
||||
final ArrayList<Future<?>> futures = new ArrayList<>(nThreads);
|
||||
final ExecutorService pool = Executors.newCachedThreadPool();
|
||||
for (int i = 0; i < nThreads; i++)
|
||||
futures.add(pool.submit(observer));
|
||||
threadsStarted.await();
|
||||
for (int i = 0; i < iters; i++) {
|
||||
FutureTask<Boolean> f = new FutureTask<>(alwaysTrue);
|
||||
a.set(f);
|
||||
f.run();
|
||||
}
|
||||
done.set(true);
|
||||
pool.shutdown();
|
||||
if (!pool.awaitTermination(LONG_DELAY_MS, MILLISECONDS))
|
||||
throw new AssertionError();
|
||||
for (Future<?> future : futures)
|
||||
future.get();
|
||||
}
|
||||
}
|
||||
168
test/jdk/java/util/concurrent/FutureTask/DoneTimedGetLoops.java
Normal file
168
test/jdk/java/util/concurrent/FutureTask/DoneTimedGetLoops.java
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @library /test/lib
|
||||
* @run main DoneTimedGetLoops 300
|
||||
* @summary isDone returning true guarantees that subsequent timed get
|
||||
* will never throw TimeoutException.
|
||||
*/
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes", "deprecation"})
|
||||
public class DoneTimedGetLoops {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
final long testDurationMillisDefault = 10_000L;
|
||||
final long testDurationMillis;
|
||||
|
||||
static class PublicFutureTask extends FutureTask<Boolean> {
|
||||
static final Runnable noop = new Runnable() { public void run() {} };
|
||||
PublicFutureTask() { super(noop, null); }
|
||||
public void set(Boolean v) { super.set(v); }
|
||||
public void setException(Throwable t) { super.setException(t); }
|
||||
}
|
||||
|
||||
DoneTimedGetLoops(String[] args) {
|
||||
testDurationMillis = (args.length > 0) ?
|
||||
Long.valueOf(args[0]) : testDurationMillisDefault;
|
||||
}
|
||||
|
||||
void test(String[] args) throws Throwable {
|
||||
final long testDurationNanos = testDurationMillis * 1000L * 1000L;
|
||||
final long quittingTimeNanos = System.nanoTime() + testDurationNanos;
|
||||
|
||||
final AtomicReference<PublicFutureTask> normalRef
|
||||
= new AtomicReference<>();
|
||||
final AtomicReference<PublicFutureTask> abnormalRef
|
||||
= new AtomicReference<>();
|
||||
|
||||
final Throwable throwable = new Throwable();
|
||||
|
||||
abstract class CheckedThread extends Thread {
|
||||
CheckedThread(String name) {
|
||||
super(name);
|
||||
setDaemon(true);
|
||||
start();
|
||||
}
|
||||
/** Polls for quitting time. */
|
||||
protected boolean quittingTime() {
|
||||
return System.nanoTime() - quittingTimeNanos > 0;
|
||||
}
|
||||
/** Polls occasionally for quitting time. */
|
||||
protected boolean quittingTime(long i) {
|
||||
return (i % 1024) == 0 && quittingTime();
|
||||
}
|
||||
protected abstract void realRun() throws Exception;
|
||||
public void run() {
|
||||
try { realRun(); } catch (Throwable t) { unexpected(t); }
|
||||
}
|
||||
}
|
||||
|
||||
Thread setter = new CheckedThread("setter") {
|
||||
protected void realRun() {
|
||||
while (! quittingTime()) {
|
||||
PublicFutureTask future = new PublicFutureTask();
|
||||
normalRef.set(future);
|
||||
future.set(Boolean.TRUE);
|
||||
}}};
|
||||
|
||||
Thread setterException = new CheckedThread("setterException") {
|
||||
protected void realRun() {
|
||||
while (! quittingTime()) {
|
||||
PublicFutureTask future = new PublicFutureTask();
|
||||
abnormalRef.set(future);
|
||||
future.setException(throwable);
|
||||
}}};
|
||||
|
||||
Thread doneTimedGetNormal = new CheckedThread("doneTimedGetNormal") {
|
||||
protected void realRun() throws Exception {
|
||||
while (! quittingTime()) {
|
||||
PublicFutureTask future = normalRef.get();
|
||||
if (future != null) {
|
||||
while (!future.isDone())
|
||||
;
|
||||
check(future.get(0L, TimeUnit.HOURS) == Boolean.TRUE);
|
||||
}}}};
|
||||
|
||||
Thread doneTimedGetAbnormal = new CheckedThread("doneTimedGetAbnormal") {
|
||||
protected void realRun() throws Exception {
|
||||
while (! quittingTime()) {
|
||||
PublicFutureTask future = abnormalRef.get();
|
||||
if (future != null) {
|
||||
while (!future.isDone())
|
||||
;
|
||||
try { future.get(0L, TimeUnit.HOURS); fail(); }
|
||||
catch (ExecutionException t) {
|
||||
check(t.getCause() == throwable);
|
||||
}
|
||||
}}}};
|
||||
|
||||
for (Thread thread : new Thread[] {
|
||||
setter,
|
||||
setterException,
|
||||
doneTimedGetNormal,
|
||||
doneTimedGetAbnormal }) {
|
||||
thread.join(LONG_DELAY_MS + testDurationMillis);
|
||||
if (thread.isAlive()) {
|
||||
System.err.printf("Hung thread: %s%n", thread.getName());
|
||||
failed++;
|
||||
for (StackTraceElement e : thread.getStackTrace())
|
||||
System.err.println(e);
|
||||
thread.join(LONG_DELAY_MS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new DoneTimedGetLoops(args).instanceMain(args);}
|
||||
public void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
87
test/jdk/java/util/concurrent/FutureTask/ExplicitSet.java
Normal file
87
test/jdk/java/util/concurrent/FutureTask/ExplicitSet.java
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 7132378
|
||||
* @summary Race in FutureTask if used with explicit set ( not Runnable )
|
||||
* @author Chris Hegarty
|
||||
*/
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
public class ExplicitSet {
|
||||
|
||||
static void realMain(String[] args) throws Throwable {
|
||||
for (int i = 1; i <= 10000; i++) {
|
||||
//System.out.print(".");
|
||||
test();
|
||||
}
|
||||
}
|
||||
|
||||
static void test() throws Throwable {
|
||||
final SettableTask task = new SettableTask();
|
||||
|
||||
Thread thread = new Thread() { public void run() {
|
||||
try {
|
||||
check(task.get() != null);
|
||||
} catch (Exception e) { unexpected(e); }
|
||||
}};
|
||||
thread.start();
|
||||
|
||||
task.set(Boolean.TRUE);
|
||||
thread.join(5000);
|
||||
}
|
||||
|
||||
static class SettableTask extends FutureTask<Boolean> {
|
||||
SettableTask() {
|
||||
super(new Callable<Boolean>() {
|
||||
public Boolean call() {
|
||||
fail("The task should never be run!");
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(Boolean b) {
|
||||
super.set(b);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
static volatile int passed = 0, failed = 0;
|
||||
static void pass() {passed++;}
|
||||
static void fail() {failed++; Thread.dumpStack();}
|
||||
static void fail(String msg) {System.out.println(msg); fail();}
|
||||
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
static void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
static void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
try {realMain(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
103
test/jdk/java/util/concurrent/FutureTask/LoopHelpers.java
Normal file
103
test/jdk/java/util/concurrent/FutureTask/LoopHelpers.java
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/**
|
||||
* Misc utilities in JSR166 performance tests
|
||||
*/
|
||||
class LoopHelpers {
|
||||
|
||||
// Some mindless computation to do between synchronizations...
|
||||
|
||||
/**
|
||||
* generates 32 bit pseudo-random numbers.
|
||||
* Adapted from http://www.snippets.org
|
||||
*/
|
||||
public static int compute1(int x) {
|
||||
int lo = 16807 * (x & 0xFFFF);
|
||||
int hi = 16807 * (x >>> 16);
|
||||
lo += (hi & 0x7FFF) << 16;
|
||||
if ((lo & 0x80000000) != 0) {
|
||||
lo &= 0x7fffffff;
|
||||
++lo;
|
||||
}
|
||||
lo += hi >>> 15;
|
||||
if (lo == 0 || (lo & 0x80000000) != 0) {
|
||||
lo &= 0x7fffffff;
|
||||
++lo;
|
||||
}
|
||||
return lo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a linear congruential random number a random number
|
||||
* of times.
|
||||
*/
|
||||
public static int compute2(int x) {
|
||||
int loops = (x >>> 4) & 7;
|
||||
while (loops-- > 0) {
|
||||
x = (x * 2147483647) % 16807;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
public static class BarrierTimer implements Runnable {
|
||||
public volatile long startTime;
|
||||
public volatile long endTime;
|
||||
public void run() {
|
||||
long t = System.nanoTime();
|
||||
if (startTime == 0)
|
||||
startTime = t;
|
||||
else
|
||||
endTime = t;
|
||||
}
|
||||
public void clear() {
|
||||
startTime = 0;
|
||||
endTime = 0;
|
||||
}
|
||||
public long getTime() {
|
||||
return endTime - startTime;
|
||||
}
|
||||
}
|
||||
|
||||
public static String rightJustify(long n) {
|
||||
// There's probably a better way to do this...
|
||||
String field = " ";
|
||||
String num = Long.toString(n);
|
||||
if (num.length() >= field.length())
|
||||
return num;
|
||||
StringBuilder b = new StringBuilder(field);
|
||||
b.replace(b.length()-num.length(), b.length(), num);
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8060052
|
||||
* @summary FutureTask; fix underflow when timeout = Long.MIN_VALUE
|
||||
* @author Chris Hegarty
|
||||
*/
|
||||
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
// If the bug exists the test will eventually be interrupted by the
|
||||
// test harness and fail with an InterruptedException, otherwise it
|
||||
// will throw a TimeoutException almost immediately and return silently.
|
||||
|
||||
public class NegativeTimeout {
|
||||
public static void main(String[] args) throws Exception {
|
||||
FutureTask<Void> task = new FutureTask<>(() -> null);
|
||||
try {
|
||||
task.get(Long.MIN_VALUE, TimeUnit.NANOSECONDS);
|
||||
} catch (TimeoutException success) {}
|
||||
}
|
||||
}
|
||||
147
test/jdk/java/util/concurrent/FutureTask/Throw.java
Normal file
147
test/jdk/java/util/concurrent/FutureTask/Throw.java
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6415572
|
||||
* @summary Check exceptional behavior in run and done methods
|
||||
*/
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
public class Throw {
|
||||
|
||||
static void THROW(final Throwable t) {
|
||||
if (t != null)
|
||||
Throw.<RuntimeException>uncheckedThrow(t);
|
||||
}
|
||||
|
||||
Callable<Void> thrower(final Throwable t) {
|
||||
return new Callable<Void>() { public Void call() {
|
||||
THROW(t); return null; }};
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class DoneError extends Error {}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class DoneException extends RuntimeException {}
|
||||
|
||||
static class MyFutureTask extends FutureTask<Void> {
|
||||
MyFutureTask(Callable<Void> task) { super(task); }
|
||||
public boolean runAndReset() { return super.runAndReset(); }
|
||||
}
|
||||
|
||||
MyFutureTask checkTask(final MyFutureTask task) {
|
||||
check(! task.isCancelled());
|
||||
check(! task.isDone());
|
||||
return task;
|
||||
}
|
||||
|
||||
MyFutureTask taskFor(final Throwable callableThrowable,
|
||||
final Throwable doneThrowable) {
|
||||
return checkTask(
|
||||
new MyFutureTask(thrower(callableThrowable)) {
|
||||
protected void done() { THROW(doneThrowable); }});
|
||||
}
|
||||
|
||||
void test(String[] args) throws Throwable {
|
||||
final Throwable[] callableThrowables = {
|
||||
null, new Exception(), new Error(), new RuntimeException() };
|
||||
final Throwable[] doneThrowables = {
|
||||
new DoneError(), new DoneException() };
|
||||
for (final Throwable c : callableThrowables) {
|
||||
for (final Throwable d : doneThrowables) {
|
||||
THROWS(d.getClass(),
|
||||
new F(){void f(){
|
||||
taskFor(c, d).cancel(false);}},
|
||||
new F(){void f(){
|
||||
taskFor(c, d).run();}});
|
||||
if (c != null)
|
||||
THROWS(d.getClass(),
|
||||
new F(){void f(){
|
||||
taskFor(c, d).runAndReset();}});
|
||||
}
|
||||
|
||||
try {
|
||||
final MyFutureTask task = taskFor(c, null);
|
||||
check(task.cancel(false));
|
||||
THROWS(CancellationException.class,
|
||||
new F(){void f() throws Throwable { task.get(); }});
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
if (c != null) {
|
||||
final MyFutureTask task = taskFor(c, null);
|
||||
task.run();
|
||||
try {
|
||||
task.get();
|
||||
fail("Expected ExecutionException");
|
||||
} catch (ExecutionException ee) {
|
||||
equal(c.getClass(), ee.getCause().getClass());
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
}
|
||||
|
||||
if (c != null) {
|
||||
final MyFutureTask task = taskFor(c, null);
|
||||
task.runAndReset();
|
||||
try {
|
||||
task.get();
|
||||
fail("Expected ExecutionException");
|
||||
} catch (ExecutionException ee) {
|
||||
check(c.getClass().isInstance(ee.getCause()));
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new Throw().instanceMain(args);}
|
||||
void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
abstract class F {abstract void f() throws Throwable;}
|
||||
void THROWS(Class<? extends Throwable> k, F... fs) {
|
||||
for (F f : fs)
|
||||
try {f.f(); fail("Expected " + k.getName() + " not thrown");}
|
||||
catch (Throwable t) {
|
||||
if (k.isAssignableFrom(t.getClass())) pass();
|
||||
else unexpected(t);}}
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T extends Throwable> void uncheckedThrow(Throwable t) throws T {
|
||||
throw (T)t; // rely on vacuous cast
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6307455
|
||||
* @summary toArray(a) must set "after-end" element to null
|
||||
* @author Martin Buchholz
|
||||
*/
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
public class ToArray {
|
||||
public static void main(String[] args) throws Throwable {
|
||||
Collection<Integer> c = new LinkedBlockingQueue<>();
|
||||
if (c.toArray(new Integer[]{42})[0] != null)
|
||||
throw new Error("should be null");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @run testng SpliteratorTraverseAddRemoveTest
|
||||
* @bug 8085978
|
||||
* @summary repeatedly traverse the queue using the spliterator while
|
||||
* concurrently adding and removing an element to test that self-linked
|
||||
* nodes are never erroneously reported on traversal
|
||||
*/
|
||||
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.Spliterator;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Test
|
||||
public class SpliteratorTraverseAddRemoveTest {
|
||||
|
||||
static Object[] of(String desc, Consumer<Queue<?>> c) {
|
||||
return new Object[]{desc, c};
|
||||
}
|
||||
|
||||
static void assertIsString(Object e) {
|
||||
Assert.assertTrue(e instanceof String,
|
||||
String.format("Object instanceof %s (actual: instanceof %s)",
|
||||
String.class.getName(),
|
||||
e.getClass().getName()));
|
||||
}
|
||||
|
||||
@DataProvider()
|
||||
public static Object[][] spliteratorTraversers() {
|
||||
return new Object[][]{
|
||||
of("forEachRemaining", q -> {
|
||||
q.spliterator().forEachRemaining(SpliteratorTraverseAddRemoveTest::assertIsString);
|
||||
}),
|
||||
of("tryAdvance", q -> {
|
||||
Spliterator<?> s = q.spliterator();
|
||||
while (s.tryAdvance(SpliteratorTraverseAddRemoveTest::assertIsString))
|
||||
;
|
||||
}),
|
||||
of("trySplit then forEachRemaining", q -> {
|
||||
Spliterator<?> r = q.spliterator();
|
||||
|
||||
List<Spliterator<?>> ss = new ArrayList<>();
|
||||
Spliterator<?> l;
|
||||
while ((l = r.trySplit()) != null) {
|
||||
ss.add(l);
|
||||
}
|
||||
ss.add(r);
|
||||
|
||||
ss.forEach(s -> s.forEachRemaining(SpliteratorTraverseAddRemoveTest::assertIsString));
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@Test(dataProvider = "spliteratorTraversers")
|
||||
public void testQueue(String desc, Consumer<Queue<String>> c)
|
||||
throws InterruptedException {
|
||||
AtomicBoolean done = new AtomicBoolean(false);
|
||||
Queue<String> msgs = new LinkedTransferQueue<>();
|
||||
|
||||
CompletableFuture<Void> traversalTask = CompletableFuture.runAsync(() -> {
|
||||
while (!done.get()) {
|
||||
// Traversal will fail if self-linked nodes of
|
||||
// LinkedTransferQueue are erroneously reported
|
||||
c.accept(msgs);
|
||||
}
|
||||
});
|
||||
CompletableFuture<Void> addAndRemoveTask = CompletableFuture.runAsync(() -> {
|
||||
while (!traversalTask.isDone()) {
|
||||
msgs.add("msg");
|
||||
msgs.remove("msg");
|
||||
}
|
||||
});
|
||||
|
||||
Thread.sleep(TimeUnit.SECONDS.toMillis(1));
|
||||
done.set(true);
|
||||
|
||||
addAndRemoveTask.join();
|
||||
Assert.assertTrue(traversalTask.isDone());
|
||||
traversalTask.join();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8323659
|
||||
* @summary Ensures that the implementation of LTQ add and put methods does
|
||||
* not call overridable offer. This test specifically asserts implementation
|
||||
* details of LTQ. It's not that such impl details cannot change, just that
|
||||
* such a change should be deliberately done with suitable consideration
|
||||
* to compatibility.
|
||||
* @run testng SubclassTest
|
||||
*/
|
||||
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.testng.annotations.Test;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
@Test
|
||||
public class SubclassTest {
|
||||
|
||||
public void testPut() {
|
||||
var queue = new TestLinkedTransferQueue();
|
||||
queue.put(new Object());
|
||||
assertEquals(queue.size(), 1);
|
||||
}
|
||||
|
||||
public void testAdd() {
|
||||
var queue = new TestLinkedTransferQueue();
|
||||
queue.add(new Object());
|
||||
assertEquals(queue.size(), 1);
|
||||
}
|
||||
|
||||
public void testTimedOffer() {
|
||||
var queue = new TestLinkedTransferQueue();
|
||||
queue.offer(new Object(), 60, TimeUnit.SECONDS);
|
||||
assertEquals(queue.size(), 1);
|
||||
}
|
||||
|
||||
static class TestLinkedTransferQueue extends LinkedTransferQueue<Object> {
|
||||
@Override
|
||||
public boolean offer(Object obj) {
|
||||
return false; // simulate fails to add the given obj
|
||||
}
|
||||
}
|
||||
}
|
||||
392
test/jdk/java/util/concurrent/LinkedTransferQueue/WhiteBox.java
Normal file
392
test/jdk/java/util/concurrent/LinkedTransferQueue/WhiteBox.java
Normal file
|
|
@ -0,0 +1,392 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @modules java.base/java.util.concurrent:open
|
||||
* @run testng WhiteBox
|
||||
* @summary White box tests of implementation details
|
||||
*/
|
||||
|
||||
import static org.testng.Assert.*;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Test
|
||||
public class WhiteBox {
|
||||
|
||||
final ThreadLocalRandom rnd = ThreadLocalRandom.current();
|
||||
final VarHandle HEAD, TAIL, ITEM, NEXT;
|
||||
|
||||
public WhiteBox() throws Throwable { // throws ReflectiveOperationException {
|
||||
try {
|
||||
Class<?> qClass = LinkedTransferQueue.class;
|
||||
Class<?> nodeClass = Class.forName(qClass.getName() + "$DualNode");
|
||||
MethodHandles.Lookup lookup
|
||||
= MethodHandles.privateLookupIn(qClass, MethodHandles.lookup());
|
||||
HEAD = lookup.findVarHandle(qClass, "head", nodeClass);
|
||||
TAIL = lookup.findVarHandle(qClass, "tail", nodeClass);
|
||||
NEXT = lookup.findVarHandle(nodeClass, "next", nodeClass);
|
||||
ITEM = lookup.findVarHandle(nodeClass, "item", Object.class);
|
||||
} catch (Throwable ex) {
|
||||
ex.printStackTrace();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
Object head(LinkedTransferQueue q) { return HEAD.getVolatile(q); }
|
||||
Object tail(LinkedTransferQueue q) { return TAIL.getVolatile(q); }
|
||||
Object item(Object node) { return ITEM.getVolatile(node); }
|
||||
Object next(Object node) { return NEXT.getVolatile(node); }
|
||||
|
||||
/*
|
||||
* Modified for jdk22: Accommodate lazy initialization, so counts
|
||||
* may vary by 1, and some nodes become headers vs unlinked,
|
||||
* compared to previous versions.
|
||||
*/
|
||||
|
||||
static void checkCount(int val, int expect) {
|
||||
assertTrue(val == expect || val == expect - 1);
|
||||
}
|
||||
|
||||
int nodeCount(LinkedTransferQueue q) {
|
||||
int i = 0;
|
||||
for (Object p = head(q); p != null; ) {
|
||||
i++;
|
||||
if (p == (p = next(p))) p = head(q);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int tailCount(LinkedTransferQueue q) {
|
||||
int i = 0;
|
||||
for (Object p = tail(q); p != null; ) {
|
||||
i++;
|
||||
if (p == (p = next(p))) p = head(q);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
Object findNode(LinkedTransferQueue q, Object e) {
|
||||
for (Object p = head(q); p != null; ) {
|
||||
if (item(p) != null && e.equals(item(p)))
|
||||
return p;
|
||||
if (p == (p = next(p))) p = head(q);
|
||||
}
|
||||
throw new AssertionError("not found");
|
||||
}
|
||||
|
||||
Iterator iteratorAt(LinkedTransferQueue q, Object e) {
|
||||
for (Iterator it = q.iterator(); it.hasNext(); )
|
||||
if (it.next().equals(e))
|
||||
return it;
|
||||
throw new AssertionError("not found");
|
||||
}
|
||||
|
||||
void assertIsSelfLinked(Object node) {
|
||||
assertSame(next(node), node);
|
||||
assertNull(item(node));
|
||||
}
|
||||
void assertIsNotSelfLinked(Object node) {
|
||||
assertNotSame(node, next(node));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addRemove() {
|
||||
LinkedTransferQueue q = new LinkedTransferQueue();
|
||||
assertInvariants(q);
|
||||
if (head(q) != null) {
|
||||
assertNull(next(head(q)));
|
||||
assertNull(item(head(q)));
|
||||
}
|
||||
q.add(1);
|
||||
checkCount(nodeCount(q), 2);
|
||||
assertInvariants(q);
|
||||
q.remove(1);
|
||||
checkCount(nodeCount(q), 1);
|
||||
assertInvariants(q);
|
||||
}
|
||||
|
||||
/**
|
||||
* Traversal actions that visit every node and do nothing, but
|
||||
* have side effect of squeezing out dead nodes.
|
||||
*/
|
||||
@DataProvider
|
||||
public Object[][] traversalActions() {
|
||||
return List.<Consumer<LinkedTransferQueue>>of(
|
||||
q -> q.forEach(e -> {}),
|
||||
q -> assertFalse(q.contains(new Object())),
|
||||
q -> assertFalse(q.remove(new Object())),
|
||||
q -> q.spliterator().forEachRemaining(e -> {}),
|
||||
q -> q.stream().collect(toList()),
|
||||
q -> assertFalse(q.removeIf(e -> false)),
|
||||
q -> assertFalse(q.removeAll(List.of())))
|
||||
.stream().map(x -> new Object[]{ x }).toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "traversalActions")
|
||||
public void traversalOperationsCollapseLeadingNodes(
|
||||
Consumer<LinkedTransferQueue> traversalAction) {
|
||||
LinkedTransferQueue q = new LinkedTransferQueue();
|
||||
Object oldHead;
|
||||
int n = 1 + rnd.nextInt(5);
|
||||
for (int i = 0; i < n; i++) q.add(i);
|
||||
checkCount(nodeCount(q), n + 1);
|
||||
oldHead = head(q);
|
||||
traversalAction.accept(q);
|
||||
assertInvariants(q);
|
||||
checkCount(nodeCount(q), n);
|
||||
// assertIsSelfLinked(oldHead);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "traversalActions")
|
||||
public void traversalOperationsCollapseInteriorNodes(
|
||||
Consumer<LinkedTransferQueue> traversalAction) {
|
||||
LinkedTransferQueue q = new LinkedTransferQueue();
|
||||
int n = 6;
|
||||
for (int i = 0; i < n; i++) q.add(i);
|
||||
|
||||
// We must be quite devious to reliably create an interior dead node
|
||||
Object p0 = findNode(q, 0);
|
||||
Object p1 = findNode(q, 1);
|
||||
Object p2 = findNode(q, 2);
|
||||
Object p3 = findNode(q, 3);
|
||||
Object p4 = findNode(q, 4);
|
||||
Object p5 = findNode(q, 5);
|
||||
|
||||
Iterator it1 = iteratorAt(q, 1);
|
||||
Iterator it2 = iteratorAt(q, 2);
|
||||
|
||||
it2.remove(); // causes it2's ancestor to advance to 1
|
||||
assertSame(next(p1), p3);
|
||||
assertSame(next(p2), p3);
|
||||
assertNull(item(p2));
|
||||
it1.remove(); // removes it2's ancestor
|
||||
assertSame(next(p0), p3);
|
||||
assertSame(next(p1), p3);
|
||||
assertSame(next(p2), p3);
|
||||
assertNull(item(p1));
|
||||
assertEquals(it2.next(), 3);
|
||||
it2.remove(); // it2's ancestor can't unlink
|
||||
|
||||
assertSame(next(p0), p3); // p3 is now interior dead node
|
||||
assertSame(next(p1), p4); // it2 uselessly CASed p1.next
|
||||
assertSame(next(p2), p3);
|
||||
assertSame(next(p3), p4);
|
||||
assertInvariants(q);
|
||||
|
||||
int c = nodeCount(q);
|
||||
traversalAction.accept(q);
|
||||
checkCount(nodeCount(q), c - 1);
|
||||
|
||||
assertSame(next(p0), p4);
|
||||
assertSame(next(p1), p4);
|
||||
assertSame(next(p2), p3);
|
||||
assertSame(next(p3), p4);
|
||||
assertInvariants(q);
|
||||
|
||||
// trailing nodes are not unlinked
|
||||
Iterator it5 = iteratorAt(q, 5); it5.remove();
|
||||
traversalAction.accept(q);
|
||||
assertSame(next(p4), p5);
|
||||
assertNull(next(p5));
|
||||
checkCount(nodeCount(q), c - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that traversal operations collapse a random pattern of
|
||||
* dead nodes as could normally only occur with a race.
|
||||
*/
|
||||
@Test(dataProvider = "traversalActions")
|
||||
public void traversalOperationsCollapseRandomNodes(
|
||||
Consumer<LinkedTransferQueue> traversalAction) {
|
||||
LinkedTransferQueue q = new LinkedTransferQueue();
|
||||
int n = 1 + rnd.nextInt(6);
|
||||
for (int i = 0; i < n; i++) q.add(i);
|
||||
ArrayList nulledOut = new ArrayList();
|
||||
for (Object p = head(q); p != null; p = next(p))
|
||||
if (rnd.nextBoolean()) {
|
||||
nulledOut.add(item(p));
|
||||
ITEM.setVolatile(p, null);
|
||||
}
|
||||
traversalAction.accept(q);
|
||||
int c = nodeCount(q);
|
||||
checkCount(c - (q.contains(n - 1) ? 0 : 1), q.size() + 1);
|
||||
for (int i = 0; i < n; i++)
|
||||
assertTrue(nulledOut.contains(i) ^ q.contains(i));
|
||||
}
|
||||
|
||||
/**
|
||||
* Traversal actions that remove every element, and are also
|
||||
* expected to squeeze out dead nodes.
|
||||
*/
|
||||
@DataProvider
|
||||
public Object[][] bulkRemovalActions() {
|
||||
return List.<Consumer<LinkedTransferQueue>>of(
|
||||
q -> q.clear(),
|
||||
q -> assertTrue(q.removeIf(e -> true)),
|
||||
q -> assertTrue(q.retainAll(List.of())))
|
||||
.stream().map(x -> new Object[]{ x }).toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "bulkRemovalActions")
|
||||
public void bulkRemovalOperationsCollapseNodes(
|
||||
Consumer<LinkedTransferQueue> bulkRemovalAction) {
|
||||
LinkedTransferQueue q = new LinkedTransferQueue();
|
||||
int n = 1 + rnd.nextInt(5);
|
||||
for (int i = 0; i < n; i++) q.add(i);
|
||||
bulkRemovalAction.accept(q);
|
||||
checkCount(nodeCount(q), 1);
|
||||
assertInvariants(q);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions that remove the first element, and are expected to
|
||||
* leave at most one slack dead node at head.
|
||||
*/
|
||||
@DataProvider
|
||||
public Object[][] pollActions() {
|
||||
return List.<Consumer<LinkedTransferQueue>>of(
|
||||
q -> assertNotNull(q.poll()),
|
||||
q -> { try { assertNotNull(q.poll(1L, TimeUnit.DAYS)); }
|
||||
catch (Throwable x) { throw new AssertionError(x); }},
|
||||
q -> { try { assertNotNull(q.take()); }
|
||||
catch (Throwable x) { throw new AssertionError(x); }},
|
||||
q -> assertNotNull(q.remove()))
|
||||
.stream().map(x -> new Object[]{ x }).toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "pollActions")
|
||||
public void pollActionsOneNodeSlack(
|
||||
Consumer<LinkedTransferQueue> pollAction) {
|
||||
LinkedTransferQueue q = new LinkedTransferQueue();
|
||||
int n = 1 + rnd.nextInt(5);
|
||||
for (int i = 0; i < n; i++) q.add(i);
|
||||
checkCount(nodeCount(q), n + 1);
|
||||
for (int i = 0; i < n; i++) {
|
||||
int c = nodeCount(q);
|
||||
boolean slack = item(head(q)) == null;
|
||||
if (slack) assertNotNull(item(next(head(q))));
|
||||
pollAction.accept(q);
|
||||
checkCount(nodeCount(q), q.isEmpty() ? 1 : c - (slack ? 2 : 0));
|
||||
}
|
||||
assertInvariants(q);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions that append an element, and are expected to
|
||||
* leave at most one slack node at tail.
|
||||
*/
|
||||
@DataProvider
|
||||
public Object[][] addActions() {
|
||||
return List.<Consumer<LinkedTransferQueue>>of(
|
||||
q -> q.add(1),
|
||||
q -> q.offer(1))
|
||||
.stream().map(x -> new Object[]{ x }).toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "addActions")
|
||||
public void addActionsOneNodeSlack(
|
||||
Consumer<LinkedTransferQueue> addAction) {
|
||||
LinkedTransferQueue q = new LinkedTransferQueue();
|
||||
int n = 1 + rnd.nextInt(9);
|
||||
for (int i = 0; i < n; i++) {
|
||||
boolean empty = (tail(q) == null);
|
||||
boolean slack = !empty && (next(tail(q)) != null);
|
||||
addAction.accept(q);
|
||||
if (slack)
|
||||
assertNull(next(tail(q)));
|
||||
else if (!empty) {
|
||||
assertNotNull(next(tail(q)));
|
||||
assertNull(next(next(tail(q))));
|
||||
}
|
||||
assertInvariants(q);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] serialBytes(Object o) {
|
||||
try {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ObjectOutputStream oos = new ObjectOutputStream(bos);
|
||||
oos.writeObject(o);
|
||||
oos.flush();
|
||||
oos.close();
|
||||
return bos.toByteArray();
|
||||
} catch (Exception fail) {
|
||||
throw new AssertionError(fail);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
<T> T serialClone(T o) {
|
||||
try {
|
||||
ObjectInputStream ois = new ObjectInputStream
|
||||
(new ByteArrayInputStream(serialBytes(o)));
|
||||
T clone = (T) ois.readObject();
|
||||
assertNotSame(o, clone);
|
||||
assertSame(o.getClass(), clone.getClass());
|
||||
return clone;
|
||||
} catch (Exception fail) {
|
||||
throw new AssertionError(fail);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSerialization() {
|
||||
LinkedTransferQueue q = serialClone(new LinkedTransferQueue());
|
||||
assertInvariants(q);
|
||||
}
|
||||
|
||||
/** Checks conditions which should always be true. */
|
||||
void assertInvariants(LinkedTransferQueue q) {
|
||||
// head is never self-linked (but tail may!)
|
||||
Object h;
|
||||
if ((h = head(q)) != null)
|
||||
assertNotSame(h, next(h));
|
||||
}
|
||||
}
|
||||
97
test/jdk/java/util/concurrent/Phaser/Arrive.java
Normal file
97
test/jdk/java/util/concurrent/Phaser/Arrive.java
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6445158
|
||||
* @summary tests for Phaser.arrive()
|
||||
*/
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Phaser;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class Arrive {
|
||||
void test(String[] args) throws Throwable {
|
||||
for (int i = 0; i < 100; ++i)
|
||||
doTest(args);
|
||||
}
|
||||
void doTest(String[] args) throws Throwable {
|
||||
final int n = ThreadLocalRandom.current().nextInt(1, 10);
|
||||
final Phaser startingGate = new Phaser(n);
|
||||
final Phaser phaser = new Phaser(n);
|
||||
final List<Thread> threads = new ArrayList<>();
|
||||
final AtomicInteger count0 = new AtomicInteger(0);
|
||||
final AtomicInteger count1 = new AtomicInteger(0);
|
||||
final Runnable task = new Runnable() { public void run() {
|
||||
equal(startingGate.getPhase(), 0);
|
||||
startingGate.arriveAndAwaitAdvance();
|
||||
equal(startingGate.getPhase(), 1);
|
||||
int phase = phaser.arrive();
|
||||
if (phase == 0)
|
||||
count0.getAndIncrement();
|
||||
else if (phase == 1)
|
||||
count1.getAndIncrement();
|
||||
else
|
||||
fail();
|
||||
}};
|
||||
for (int i = 0; i < n; i++)
|
||||
threads.add(new Thread(task));
|
||||
for (Thread thread : threads)
|
||||
thread.start();
|
||||
for (Thread thread : threads)
|
||||
thread.join();
|
||||
equal(count0.get(), n);
|
||||
equal(count1.get(), 0);
|
||||
equal(phaser.getPhase(), 1);
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new Arrive().instanceMain(args);}
|
||||
public void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
463
test/jdk/java/util/concurrent/Phaser/Basic.java
Normal file
463
test/jdk/java/util/concurrent/Phaser/Basic.java
Normal file
|
|
@ -0,0 +1,463 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6445158
|
||||
* @summary Basic tests for Phaser
|
||||
* @author Chris Hegarty
|
||||
* @modules java.management
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.ThreadInfo;
|
||||
import java.lang.management.ThreadMXBean;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.Phaser;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class Basic {
|
||||
|
||||
private static void checkTerminated(final Phaser phaser) {
|
||||
check(phaser.isTerminated());
|
||||
int unarriverParties = phaser.getUnarrivedParties();
|
||||
int registeredParties = phaser.getRegisteredParties();
|
||||
int phase = phaser.getPhase();
|
||||
check(phase < 0);
|
||||
equal(phase, phaser.arrive());
|
||||
equal(phase, phaser.arriveAndDeregister());
|
||||
equal(phase, phaser.arriveAndAwaitAdvance());
|
||||
equal(phase, phaser.bulkRegister(10));
|
||||
equal(phase, phaser.register());
|
||||
try {
|
||||
equal(phase, phaser.awaitAdvanceInterruptibly(0));
|
||||
equal(phase, phaser.awaitAdvanceInterruptibly(0, 10, SECONDS));
|
||||
} catch (Exception ex) {
|
||||
unexpected(ex);
|
||||
}
|
||||
equal(phaser.getUnarrivedParties(), unarriverParties);
|
||||
equal(phaser.getRegisteredParties(), registeredParties);
|
||||
}
|
||||
|
||||
private static void checkResult(Arriver a, Class<? extends Throwable> c) {
|
||||
Throwable t = a.result();
|
||||
if (! ((t == null && c == null) || (c != null && c.isInstance(t)))) {
|
||||
// t.printStackTrace();
|
||||
fail("Mismatch in thread " +
|
||||
a.getName() + ": " +
|
||||
t + ", " +
|
||||
(c == null ? "<null>" : c.getName()));
|
||||
} else {
|
||||
pass();
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Mechanism to get all test threads into "running" mode.
|
||||
//----------------------------------------------------------------
|
||||
private static Phaser startingGate = new Phaser(3);
|
||||
|
||||
private static void toTheStartingGate() {
|
||||
try {
|
||||
boolean expectNextPhase = (startingGate.getUnarrivedParties() == 1);
|
||||
int phase = startingGate.getPhase();
|
||||
equal(phase, startingGate.arrive());
|
||||
int awaitPhase;
|
||||
for (boolean interrupted = false;;) {
|
||||
try {
|
||||
awaitPhase = startingGate.awaitAdvanceInterruptibly
|
||||
(phase, 30, SECONDS);
|
||||
if (interrupted) Thread.currentThread().interrupt();
|
||||
break;
|
||||
} catch (InterruptedException ie) {
|
||||
interrupted = true;
|
||||
}
|
||||
}
|
||||
if (expectNextPhase) check(awaitPhase == phase + 1);
|
||||
else check(awaitPhase == phase || awaitPhase == phase + 1);
|
||||
pass();
|
||||
} catch (Throwable t) {
|
||||
unexpected(t);
|
||||
// reset(startingGate);
|
||||
throw new Error(t);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Convenience methods for creating threads that call arrive,
|
||||
// awaitAdvance, arriveAndAwaitAdvance, awaitAdvanceInterruptibly
|
||||
//----------------------------------------------------------------
|
||||
private abstract static class Arriver extends Thread {
|
||||
static AtomicInteger count = new AtomicInteger(1);
|
||||
|
||||
Arriver() {
|
||||
this("Arriver");
|
||||
}
|
||||
|
||||
Arriver(String name) {
|
||||
this.setName(name + ":" + count.getAndIncrement());
|
||||
this.setDaemon(true);
|
||||
}
|
||||
|
||||
private volatile Throwable result;
|
||||
private volatile int phase;
|
||||
protected void result(Throwable result) { this.result = result; }
|
||||
public Throwable result() { return this.result; }
|
||||
protected void phase(int phase) { this.phase = phase; }
|
||||
public int phase() { return this.phase; }
|
||||
}
|
||||
|
||||
private abstract static class Awaiter extends Arriver {
|
||||
Awaiter() { super("Awaiter"); }
|
||||
Awaiter(String name) { super(name); }
|
||||
}
|
||||
|
||||
private static Arriver arriver(final Phaser phaser) {
|
||||
return new Arriver() { public void run() {
|
||||
toTheStartingGate();
|
||||
|
||||
try { phase(phaser.arrive()); }
|
||||
catch (Throwable result) { result(result); }}};
|
||||
}
|
||||
|
||||
private static AtomicInteger cycleArriveAwaitAdvance = new AtomicInteger(1);
|
||||
|
||||
private static Awaiter awaiter(final Phaser phaser) {
|
||||
return new Awaiter() { public void run() {
|
||||
toTheStartingGate();
|
||||
|
||||
try {
|
||||
if (cycleArriveAwaitAdvance.getAndIncrement() % 2 == 0)
|
||||
phase(phaser.awaitAdvance(phaser.arrive()));
|
||||
else
|
||||
phase(phaser.arriveAndAwaitAdvance());
|
||||
} catch (Throwable result) { result(result); }}};
|
||||
}
|
||||
|
||||
private static Awaiter awaiter(final Phaser phaser,
|
||||
final long timeout,
|
||||
final TimeUnit unit) {
|
||||
return new Awaiter("InterruptibleWaiter") { public void run() {
|
||||
toTheStartingGate();
|
||||
|
||||
try {
|
||||
if (timeout < 0)
|
||||
phase(phaser.awaitAdvanceInterruptibly(phaser.arrive()));
|
||||
else
|
||||
phase(phaser.awaitAdvanceInterruptibly(phaser.arrive(),
|
||||
timeout,
|
||||
unit));
|
||||
} catch (Throwable result) { result(result); }}};
|
||||
}
|
||||
|
||||
// Returns an infinite lazy list of all possible arriver/awaiter combinations.
|
||||
private static Iterator<Arriver> arriverIterator(final Phaser phaser) {
|
||||
return new Iterator<Arriver>() {
|
||||
int i = 0;
|
||||
public boolean hasNext() { return true; }
|
||||
public Arriver next() {
|
||||
switch ((i++)&7) {
|
||||
case 0: case 4:
|
||||
return arriver(phaser);
|
||||
case 1: case 5:
|
||||
return awaiter(phaser);
|
||||
case 2: case 6: case 7:
|
||||
return awaiter(phaser, -1, SECONDS);
|
||||
default:
|
||||
return awaiter(phaser, 30, SECONDS); }}
|
||||
public void remove() {throw new UnsupportedOperationException();}};
|
||||
}
|
||||
|
||||
// Returns an infinite lazy list of all possible awaiter only combinations.
|
||||
private static Iterator<Awaiter> awaiterIterator(final Phaser phaser) {
|
||||
return new Iterator<Awaiter>() {
|
||||
int i = 0;
|
||||
public boolean hasNext() { return true; }
|
||||
public Awaiter next() {
|
||||
switch ((i++)&7) {
|
||||
case 1: case 4: case 7:
|
||||
return awaiter(phaser);
|
||||
case 2: case 5:
|
||||
return awaiter(phaser, -1, SECONDS);
|
||||
default:
|
||||
return awaiter(phaser, 30, SECONDS); }}
|
||||
public void remove() {throw new UnsupportedOperationException();}};
|
||||
}
|
||||
|
||||
static class SimpleTimer {
|
||||
long startTime = System.nanoTime();
|
||||
long elapsedMillis() {
|
||||
long now = System.nanoTime();
|
||||
long elapsed = NANOSECONDS.toMillis(now - startTime);
|
||||
startTime = now;
|
||||
return elapsed;
|
||||
}
|
||||
void printElapsed() { System.out.println(elapsedMillis() + " ms"); }
|
||||
}
|
||||
|
||||
private static void realMain(String[] args) throws Throwable {
|
||||
SimpleTimer timer = new SimpleTimer();
|
||||
Thread.currentThread().setName("mainThread");
|
||||
|
||||
//----------------------------------------------------------------
|
||||
System.out.print("Normal use: ");
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
Phaser phaser = new Phaser(3);
|
||||
equal(phaser.getRegisteredParties(), 3);
|
||||
equal(phaser.getArrivedParties(), 0);
|
||||
equal(phaser.getPhase(), 0);
|
||||
check(phaser.getRoot().equals(phaser));
|
||||
equal(phaser.getParent(), null);
|
||||
check(!phaser.isTerminated());
|
||||
|
||||
Iterator<Arriver> arrivers = arriverIterator(phaser);
|
||||
int phase = 0;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
equal(phaser.getPhase(), phase++);
|
||||
Arriver a1 = arrivers.next(); a1.start();
|
||||
Arriver a2 = arrivers.next(); a2.start();
|
||||
toTheStartingGate();
|
||||
phaser.arriveAndAwaitAdvance();
|
||||
a1.join();
|
||||
a2.join();
|
||||
checkResult(a1, null);
|
||||
checkResult(a2, null);
|
||||
check(!phaser.isTerminated());
|
||||
equal(phaser.getRegisteredParties(), 3);
|
||||
equal(phaser.getArrivedParties(), 0);
|
||||
}
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
timer.printElapsed();
|
||||
|
||||
//----------------------------------------------------------------
|
||||
System.out.print("One thread interrupted: ");
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
Phaser phaser = new Phaser(3);
|
||||
Iterator<Arriver> arrivers = arriverIterator(phaser);
|
||||
int phase = phaser.getPhase();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
check(phaser.getPhase() == phase);
|
||||
Awaiter a1 = awaiter(phaser, 30, SECONDS); a1.start();
|
||||
Arriver a2 = arrivers.next(); a2.start();
|
||||
toTheStartingGate();
|
||||
a1.interrupt();
|
||||
a1.join();
|
||||
phaser.arriveAndAwaitAdvance();
|
||||
a2.join();
|
||||
checkResult(a1, InterruptedException.class);
|
||||
checkResult(a2, null);
|
||||
check(!phaser.isTerminated());
|
||||
equal(phaser.getRegisteredParties(), 3);
|
||||
equal(phaser.getArrivedParties(), 0);
|
||||
phase++;
|
||||
}
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
timer.printElapsed();
|
||||
|
||||
//----------------------------------------------------------------
|
||||
System.out.print("Phaser is terminated while threads are waiting: ");
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Phaser phaser = new Phaser(3);
|
||||
Iterator<Awaiter> awaiters = awaiterIterator(phaser);
|
||||
Arriver a1 = awaiters.next(); a1.start();
|
||||
Arriver a2 = awaiters.next(); a2.start();
|
||||
toTheStartingGate();
|
||||
while (phaser.getArrivedParties() < 2) Thread.yield();
|
||||
equal(0, phaser.getPhase());
|
||||
phaser.forceTermination();
|
||||
a1.join();
|
||||
a2.join();
|
||||
equal(0 + Integer.MIN_VALUE, a1.phase);
|
||||
equal(0 + Integer.MIN_VALUE, a2.phase);
|
||||
int arrivedParties = phaser.getArrivedParties();
|
||||
checkTerminated(phaser);
|
||||
equal(phaser.getArrivedParties(), arrivedParties);
|
||||
}
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
timer.printElapsed();
|
||||
|
||||
//----------------------------------------------------------------
|
||||
System.out.print("Adds new unarrived parties to this phaser: ");
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
Phaser phaser = new Phaser(1);
|
||||
Iterator<Arriver> arrivers = arriverIterator(phaser);
|
||||
LinkedList<Arriver> arriverList = new LinkedList<>();
|
||||
int phase = phaser.getPhase();
|
||||
for (int i = 1; i < 5; i++) {
|
||||
startingGate = new Phaser(1+(3*i));
|
||||
check(phaser.getPhase() == phase);
|
||||
// register 3 more
|
||||
phaser.register(); phaser.register(); phaser.register();
|
||||
for (int z=0; z<(3*i); z++) {
|
||||
arriverList.add(arrivers.next());
|
||||
}
|
||||
for (Arriver arriver : arriverList)
|
||||
arriver.start();
|
||||
|
||||
toTheStartingGate();
|
||||
phaser.arriveAndAwaitAdvance();
|
||||
|
||||
for (Arriver arriver : arriverList) {
|
||||
arriver.join();
|
||||
checkResult(arriver, null);
|
||||
}
|
||||
equal(phaser.getRegisteredParties(), 1 + (3*i));
|
||||
equal(phaser.getArrivedParties(), 0);
|
||||
arriverList.clear();
|
||||
phase++;
|
||||
}
|
||||
startingGate = new Phaser(3);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
timer.printElapsed();
|
||||
|
||||
//----------------------------------------------------------------
|
||||
System.out.print("One thread timed out: ");
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
Phaser phaser = new Phaser(3);
|
||||
Iterator<Arriver> arrivers = arriverIterator(phaser);
|
||||
for (long timeout : new long[] { 0L, 12L }) {
|
||||
Awaiter a1 = awaiter(phaser, timeout, MILLISECONDS);
|
||||
a1.start();
|
||||
Arriver a2 = arrivers.next();
|
||||
a2.start();
|
||||
toTheStartingGate();
|
||||
a1.join();
|
||||
checkResult(a1, TimeoutException.class);
|
||||
phaser.arrive();
|
||||
a2.join();
|
||||
checkResult(a2, null);
|
||||
check(!phaser.isTerminated());
|
||||
}
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
timer.printElapsed();
|
||||
|
||||
//----------------------------------------------------------------
|
||||
System.out.print("Barrier action completed normally: ");
|
||||
//----------------------------------------------------------------
|
||||
try {
|
||||
final AtomicInteger count = new AtomicInteger(0);
|
||||
final Phaser[] kludge = new Phaser[1];
|
||||
Phaser phaser = new Phaser(3) {
|
||||
@Override
|
||||
protected boolean onAdvance(int phase, int registeredParties) {
|
||||
int countPhase = count.getAndIncrement();
|
||||
equal(countPhase, phase);
|
||||
equal(kludge[0].getPhase(), phase);
|
||||
equal(kludge[0].getRegisteredParties(), registeredParties);
|
||||
if (phase >= 3)
|
||||
return true; // terminate
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
kludge[0] = phaser;
|
||||
equal(phaser.getRegisteredParties(), 3);
|
||||
Iterator<Awaiter> awaiters = awaiterIterator(phaser);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Awaiter a1 = awaiters.next(); a1.start();
|
||||
Awaiter a2 = awaiters.next(); a2.start();
|
||||
toTheStartingGate();
|
||||
while (phaser.getArrivedParties() < 2) Thread.yield();
|
||||
phaser.arrive();
|
||||
a1.join();
|
||||
a2.join();
|
||||
checkResult(a1, null);
|
||||
checkResult(a2, null);
|
||||
equal(count.get(), i+1);
|
||||
if (i < 3) {
|
||||
check(!phaser.isTerminated());
|
||||
equal(phaser.getRegisteredParties(), 3);
|
||||
equal(phaser.getArrivedParties(), 0);
|
||||
equal(phaser.getUnarrivedParties(), 3);
|
||||
equal(phaser.getPhase(), count.get());
|
||||
} else
|
||||
checkTerminated(phaser);
|
||||
}
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
timer.printElapsed();
|
||||
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
|
||||
/**
|
||||
* A debugging tool to print stack traces of most threads, as jstack does.
|
||||
* Uninteresting threads are filtered out.
|
||||
*/
|
||||
static void dumpTestThreads() {
|
||||
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
|
||||
System.err.println("------ stacktrace dump start ------");
|
||||
for (ThreadInfo info : threadMXBean.dumpAllThreads(true, true)) {
|
||||
String name = info.getThreadName();
|
||||
if ("Signal Dispatcher".equals(name))
|
||||
continue;
|
||||
if ("Reference Handler".equals(name))
|
||||
continue;
|
||||
if ("Finalizer".equals(name))
|
||||
continue;
|
||||
if (name.startsWith("process reaper"))
|
||||
continue;
|
||||
if (name != null && name.startsWith("ForkJoinPool.commonPool-worker"))
|
||||
continue;
|
||||
System.err.print(info);
|
||||
}
|
||||
System.err.println("------ stacktrace dump end ------");
|
||||
}
|
||||
|
||||
static volatile int passed = 0, failed = 0;
|
||||
static void pass() {passed++;}
|
||||
static void fail() {failed++; Thread.dumpStack();}
|
||||
static void fail(String msg) {System.out.println(msg); fail();}
|
||||
static void unexpected(Throwable t) {failed++; t.printStackTrace(); dumpTestThreads();}
|
||||
static void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
static void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
try {realMain(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
150
test/jdk/java/util/concurrent/Phaser/FickleRegister.java
Normal file
150
test/jdk/java/util/concurrent/Phaser/FickleRegister.java
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary stress test for register/arriveAndDeregister
|
||||
* @run main FickleRegister 300
|
||||
*/
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.Phaser;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class FickleRegister {
|
||||
final AtomicLong count = new AtomicLong(0);
|
||||
final long testDurationMillisDefault = 10_000L;
|
||||
final long testDurationMillis;
|
||||
final long quittingTimeNanos;
|
||||
final int chunkSize = 1000;
|
||||
|
||||
FickleRegister(String[] args) {
|
||||
testDurationMillis = (args.length > 0) ?
|
||||
Long.valueOf(args[0]) : testDurationMillisDefault;
|
||||
quittingTimeNanos = System.nanoTime() +
|
||||
testDurationMillis * 1000L * 1000L;
|
||||
}
|
||||
|
||||
class Runner extends CheckedRunnable {
|
||||
final Phaser p;
|
||||
Runner(Phaser phaser) { p = phaser; }
|
||||
public void realRun() {
|
||||
int prevPhase = -1;
|
||||
for (int k = 1;; k++) {
|
||||
for (int i = 0; i < chunkSize; i++) {
|
||||
int phase = p.register();
|
||||
if (phase < 0) break;
|
||||
check(phase > prevPhase);
|
||||
prevPhase = phase;
|
||||
equal(phase, p.arriveAndDeregister());
|
||||
check(phase < p.awaitAdvance(phase));
|
||||
}
|
||||
if (System.nanoTime() - quittingTimeNanos > 0) {
|
||||
count.getAndAdd(k * chunkSize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test(String[] args) throws Throwable {
|
||||
final Phaser parent = new Phaser() {
|
||||
protected boolean onAdvance(int phase, int parties) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
final Phaser child1 = new Phaser(parent);
|
||||
final Phaser child2 = new Phaser(parent);
|
||||
final Phaser subchild1 = new Phaser(child1);
|
||||
final Phaser subchild2 = new Phaser(child2);
|
||||
final Phaser[] phasers = {
|
||||
parent, child1, child2, subchild1, subchild2
|
||||
};
|
||||
|
||||
int reps = 4;
|
||||
ArrayList<Thread> threads = new ArrayList<>();
|
||||
for (int j = 0; j < reps; ++j) {
|
||||
threads.add(new Thread(new Runner(subchild1)));
|
||||
threads.add(new Thread(new Runner(child1)));
|
||||
threads.add(new Thread(new Runner(parent)));
|
||||
threads.add(new Thread(new Runner(child2)));
|
||||
threads.add(new Thread(new Runner(subchild2)));
|
||||
}
|
||||
|
||||
for (Thread thread : threads)
|
||||
thread.start();
|
||||
|
||||
for (Thread thread : threads)
|
||||
thread.join();
|
||||
|
||||
System.out.println("Parent: " + parent);
|
||||
System.out.println("Child1: " + child1);
|
||||
System.out.println("Child2: " + child2);
|
||||
System.out.println("Subchild1: " + subchild1);
|
||||
System.out.println("Subchild2: " + subchild2);
|
||||
System.out.println("Iterations:" + count.get());
|
||||
|
||||
for (Phaser phaser : phasers) {
|
||||
check(phaser.getPhase() > 0);
|
||||
equal(0, phaser.getRegisteredParties());
|
||||
equal(0, phaser.getUnarrivedParties());
|
||||
equal(parent.getPhase(), phaser.getPhase());
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new FickleRegister(args).instanceMain(args);}
|
||||
public void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
|
||||
abstract class CheckedRunnable implements Runnable {
|
||||
protected abstract void realRun() throws Throwable;
|
||||
|
||||
public final void run() {
|
||||
try {realRun();} catch (Throwable t) {unexpected(t);}
|
||||
}
|
||||
}
|
||||
}
|
||||
159
test/jdk/java/util/concurrent/Phaser/PhaseOverflow.java
Normal file
159
test/jdk/java/util/concurrent/Phaser/PhaseOverflow.java
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Martin Buchholz and Doug Lea 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @modules java.base/java.util.concurrent:open
|
||||
* @summary Test Phaser phase integer overflow behavior
|
||||
*/
|
||||
|
||||
import java.util.concurrent.Phaser;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class PhaseOverflow {
|
||||
Field stateField;
|
||||
|
||||
void checkState(Phaser phaser,
|
||||
int phase, int parties, int unarrived) {
|
||||
equal(phase, phaser.getPhase());
|
||||
equal(parties, phaser.getRegisteredParties());
|
||||
equal(unarrived, phaser.getUnarrivedParties());
|
||||
}
|
||||
|
||||
void test(String[] args) throws Throwable {
|
||||
stateField = Phaser.class.getDeclaredField("state");
|
||||
stateField.setAccessible(true);
|
||||
testLeaf();
|
||||
testTiered();
|
||||
}
|
||||
|
||||
void testLeaf() throws Throwable {
|
||||
Phaser phaser = new Phaser();
|
||||
// this is extremely dependent on internal representation
|
||||
stateField.setLong(phaser, ((Integer.MAX_VALUE - 1L) << 32) | 1L);
|
||||
checkState(phaser, Integer.MAX_VALUE - 1, 0, 0);
|
||||
phaser.register();
|
||||
checkState(phaser, Integer.MAX_VALUE - 1, 1, 1);
|
||||
phaser.arrive();
|
||||
checkState(phaser, Integer.MAX_VALUE, 1, 1);
|
||||
phaser.arrive();
|
||||
checkState(phaser, 0, 1, 1);
|
||||
phaser.arrive();
|
||||
checkState(phaser, 1, 1, 1);
|
||||
}
|
||||
|
||||
int phaseInc(int phase) { return (phase + 1) & Integer.MAX_VALUE; }
|
||||
|
||||
void testTiered() throws Throwable {
|
||||
Phaser root = new Phaser();
|
||||
// this is extremely dependent on internal representation
|
||||
stateField.setLong(root, ((Integer.MAX_VALUE - 1L) << 32) | 1L);
|
||||
checkState(root, Integer.MAX_VALUE - 1, 0, 0);
|
||||
Phaser p1 = new Phaser(root, 1);
|
||||
checkState(root, Integer.MAX_VALUE - 1, 1, 1);
|
||||
checkState(p1, Integer.MAX_VALUE - 1, 1, 1);
|
||||
Phaser p2 = new Phaser(root, 2);
|
||||
checkState(root, Integer.MAX_VALUE - 1, 2, 2);
|
||||
checkState(p2, Integer.MAX_VALUE - 1, 2, 2);
|
||||
int ph = Integer.MAX_VALUE - 1;
|
||||
for (int k = 0; k < 5; k++) {
|
||||
checkState(root, ph, 2, 2);
|
||||
checkState(p1, ph, 1, 1);
|
||||
checkState(p2, ph, 2, 2);
|
||||
p1.arrive();
|
||||
checkState(root, ph, 2, 1);
|
||||
checkState(p1, ph, 1, 0);
|
||||
checkState(p2, ph, 2, 2);
|
||||
p2.arrive();
|
||||
checkState(root, ph, 2, 1);
|
||||
checkState(p1, ph, 1, 0);
|
||||
checkState(p2, ph, 2, 1);
|
||||
p2.arrive();
|
||||
ph = phaseInc(ph);
|
||||
checkState(root, ph, 2, 2);
|
||||
checkState(p1, ph, 1, 1);
|
||||
checkState(p2, ph, 2, 2);
|
||||
}
|
||||
equal(3, ph);
|
||||
}
|
||||
|
||||
void xtestTiered() throws Throwable {
|
||||
Phaser root = new Phaser();
|
||||
stateField.setLong(root, ((Integer.MAX_VALUE - 1L) << 32) | 1L);
|
||||
checkState(root, Integer.MAX_VALUE - 1, 0, 0);
|
||||
Phaser p1 = new Phaser(root, 1);
|
||||
checkState(root, Integer.MAX_VALUE - 1, 1, 1);
|
||||
checkState(p1, Integer.MAX_VALUE - 1, 1, 1);
|
||||
Phaser p2 = new Phaser(root, 2);
|
||||
checkState(root, Integer.MAX_VALUE - 1, 2, 2);
|
||||
checkState(p2, Integer.MAX_VALUE - 1, 2, 2);
|
||||
int ph = Integer.MAX_VALUE - 1;
|
||||
for (int k = 0; k < 5; k++) {
|
||||
checkState(root, ph, 2, 2);
|
||||
checkState(p1, ph, 1, 1);
|
||||
checkState(p2, ph, 2, 2);
|
||||
p1.arrive();
|
||||
checkState(root, ph, 2, 1);
|
||||
checkState(p1, ph, 1, 0);
|
||||
checkState(p2, ph, 2, 2);
|
||||
p2.arrive();
|
||||
checkState(root, ph, 2, 1);
|
||||
checkState(p1, ph, 1, 0);
|
||||
checkState(p2, ph, 2, 1);
|
||||
p2.arrive();
|
||||
ph = phaseInc(ph);
|
||||
checkState(root, ph, 2, 2);
|
||||
checkState(p1, ph, 1, 1);
|
||||
checkState(p2, ph, 2, 2);
|
||||
}
|
||||
equal(3, ph);
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new PhaseOverflow().instanceMain(args);}
|
||||
public void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
117
test/jdk/java/util/concurrent/Phaser/TieredArriveLoops.java
Normal file
117
test/jdk/java/util/concurrent/Phaser/TieredArriveLoops.java
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary stress test for arrivals in a tiered phaser
|
||||
* @run main TieredArriveLoops 300
|
||||
*/
|
||||
|
||||
import java.util.concurrent.Phaser;
|
||||
|
||||
public class TieredArriveLoops {
|
||||
final long testDurationMillisDefault = 10_000L;
|
||||
final long testDurationMillis;
|
||||
final long quittingTimeNanos;
|
||||
|
||||
TieredArriveLoops(String[] args) {
|
||||
testDurationMillis = (args.length > 0) ?
|
||||
Long.valueOf(args[0]) : testDurationMillisDefault;
|
||||
quittingTimeNanos = System.nanoTime() +
|
||||
testDurationMillis * 1000L * 1000L;
|
||||
}
|
||||
|
||||
Runnable runner(final Phaser p) {
|
||||
return new CheckedRunnable() { public void realRun() {
|
||||
int prevPhase = p.register();
|
||||
while (!p.isTerminated()) {
|
||||
int phase = p.awaitAdvance(p.arrive());
|
||||
if (phase < 0)
|
||||
return;
|
||||
equal(phase, (prevPhase + 1) & Integer.MAX_VALUE);
|
||||
int ph = p.getPhase();
|
||||
check(ph < 0 || ph == phase);
|
||||
prevPhase = phase;
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
void test(String[] args) throws Throwable {
|
||||
final Phaser parent = new Phaser();
|
||||
final Phaser child1 = new Phaser(parent);
|
||||
final Phaser child2 = new Phaser(parent);
|
||||
|
||||
Thread t1 = new Thread(runner(child1));
|
||||
Thread t2 = new Thread(runner(child2));
|
||||
t1.start();
|
||||
t2.start();
|
||||
|
||||
for (int prevPhase = 0, phase; ; prevPhase = phase) {
|
||||
phase = child2.getPhase();
|
||||
check(phase >= prevPhase);
|
||||
if (System.nanoTime() - quittingTimeNanos > 0) {
|
||||
System.err.printf("phase=%d%n", phase);
|
||||
child1.forceTermination();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
t1.join();
|
||||
t2.join();
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new TieredArriveLoops(args).instanceMain(args);}
|
||||
public void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
|
||||
abstract class CheckedRunnable implements Runnable {
|
||||
protected abstract void realRun() throws Throwable;
|
||||
|
||||
public final void run() {
|
||||
try {realRun();} catch (Throwable t) {unexpected(t);}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @modules java.base/java.util.concurrent:open
|
||||
* java.base/java.util:open
|
||||
* @run testng WhiteBox
|
||||
* @summary White box tests of implementation details
|
||||
*/
|
||||
|
||||
import static org.testng.Assert.*;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.PriorityQueue;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.PriorityBlockingQueue;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Test
|
||||
public class WhiteBox {
|
||||
final ThreadLocalRandom rnd = ThreadLocalRandom.current();
|
||||
final VarHandle PQ_QUEUE = queueVarHandle(PriorityQueue.class);
|
||||
final VarHandle PBQ_QUEUE = queueVarHandle(PriorityBlockingQueue.class);
|
||||
|
||||
VarHandle queueVarHandle(Class klazz) {
|
||||
return findVarHandle(klazz, "queue", Object[].class);
|
||||
}
|
||||
|
||||
VarHandle findVarHandle(Class klazz, String fieldName, Class type) {
|
||||
try {
|
||||
return MethodHandles.privateLookupIn(klazz, MethodHandles.lookup())
|
||||
.findVarHandle(klazz, fieldName, type);
|
||||
} catch (ReflectiveOperationException ex) { throw new Error(ex); }
|
||||
}
|
||||
|
||||
Object[] queue(PriorityQueue q) {
|
||||
return (Object[]) PQ_QUEUE.getOpaque(q);
|
||||
}
|
||||
|
||||
Object[] queue(PriorityBlockingQueue q) {
|
||||
return (Object[]) PBQ_QUEUE.getOpaque(q);
|
||||
}
|
||||
|
||||
/** PriorityQueue and PriorityBlockingQueue have identical heap management. */
|
||||
@Test
|
||||
public void randomLockstep() {
|
||||
PriorityBlockingQueue pbq = new PriorityBlockingQueue();
|
||||
PriorityQueue pq = new PriorityQueue();
|
||||
List<Consumer<Queue>> frobbers = List.of(
|
||||
q -> q.add(42),
|
||||
q -> assertTrue(q.offer(86)),
|
||||
q -> q.poll(),
|
||||
q -> q.peek(),
|
||||
q -> {
|
||||
Iterator it = q.iterator();
|
||||
if (it.hasNext()) {
|
||||
it.next();
|
||||
it.remove();
|
||||
}});
|
||||
for (int i = 0; i < 100; i++) {
|
||||
Consumer<Queue> frobber = frobbers.get(rnd.nextInt(frobbers.size()));
|
||||
frobber.accept(pq);
|
||||
frobber.accept(pbq);
|
||||
assertInvariants(pbq);
|
||||
assertInvariants(pq);
|
||||
assertTrue(Arrays.equals(pq.toArray(), pbq.toArray()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forgetMeNot_PriorityQueue() {
|
||||
forgetMeNot(() -> new PriorityQueue());
|
||||
forgetMeNot(() -> new PriorityQueue(rnd.nextInt(1, 100)));
|
||||
forgetMeNot(() -> new PriorityQueue(rnd.nextInt(1, 100), Collections.reverseOrder()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forgetMeNot_PriorityBlockingQueue() {
|
||||
forgetMeNot(() -> new PriorityBlockingQueue());
|
||||
forgetMeNot(() -> new PriorityBlockingQueue(rnd.nextInt(1, 100)));
|
||||
forgetMeNot(() -> new PriorityBlockingQueue(rnd.nextInt(1, 100), Collections.reverseOrder()));
|
||||
}
|
||||
|
||||
void forgetMeNot(Supplier<Queue> qMaker) {
|
||||
Queue q = qMaker.get();
|
||||
Set replay = Collections.newSetFromMap(new IdentityHashMap());
|
||||
int size = rnd.nextInt(64);
|
||||
for (int i = 0; i < size; i++) {
|
||||
Object e = new Integer(rnd.nextInt());
|
||||
q.add(e);
|
||||
replay.add(e);
|
||||
}
|
||||
Iterator it = q.iterator();
|
||||
while (it.hasNext()) {
|
||||
Object e = it.next();
|
||||
assertTrue(replay.contains(e));
|
||||
if (rnd.nextBoolean()) {
|
||||
it.remove();
|
||||
if (rnd.nextBoolean())
|
||||
assertThrows(IllegalStateException.class,
|
||||
() -> it.remove());
|
||||
assertTrue(replay.remove(e));
|
||||
}
|
||||
assertInvariants(q);
|
||||
}
|
||||
for (Object e; (e = q.poll()) != null; )
|
||||
assertTrue(replay.remove(e));
|
||||
assertTrue(replay.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveIf_PriorityQueue() {
|
||||
testRemoveIf(() -> new PriorityQueue());
|
||||
testRemoveIf(() -> new PriorityQueue(rnd.nextInt(1, 100)));
|
||||
testRemoveIf(() -> new PriorityQueue(rnd.nextInt(1, 100), Collections.reverseOrder()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveIf_PriorityBlockingQueue() {
|
||||
testRemoveIf(() -> new PriorityBlockingQueue());
|
||||
testRemoveIf(() -> new PriorityBlockingQueue(rnd.nextInt(1, 100)));
|
||||
testRemoveIf(() -> new PriorityBlockingQueue(rnd.nextInt(1, 100), Collections.reverseOrder()));
|
||||
}
|
||||
|
||||
void testRemoveIf(Supplier<Queue> qMaker) {
|
||||
Queue q = qMaker.get();
|
||||
Set replay = Collections.newSetFromMap(new IdentityHashMap());
|
||||
int size = rnd.nextInt(64);
|
||||
for (int i = 0; i < size; i++) {
|
||||
Object e = new Integer(rnd.nextInt());
|
||||
q.add(e);
|
||||
replay.add(e);
|
||||
}
|
||||
q.removeIf(
|
||||
e -> {
|
||||
if (rnd.nextBoolean())
|
||||
return false;
|
||||
assertTrue(replay.remove(e));
|
||||
return true;
|
||||
});
|
||||
assertInvariants(q);
|
||||
for (Object e; (e = q.poll()) != null; )
|
||||
assertTrue(replay.remove(e));
|
||||
assertTrue(replay.isEmpty());
|
||||
}
|
||||
|
||||
byte[] serialBytes(Object o) {
|
||||
try {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ObjectOutputStream oos = new ObjectOutputStream(bos);
|
||||
oos.writeObject(o);
|
||||
oos.flush();
|
||||
oos.close();
|
||||
return bos.toByteArray();
|
||||
} catch (Exception fail) {
|
||||
throw new AssertionError(fail);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
<T> T serialClone(T o) {
|
||||
try {
|
||||
ObjectInputStream ois = new ObjectInputStream
|
||||
(new ByteArrayInputStream(serialBytes(o)));
|
||||
T clone = (T) ois.readObject();
|
||||
assertNotSame(o, clone);
|
||||
assertSame(o.getClass(), clone.getClass());
|
||||
return clone;
|
||||
} catch (Exception fail) {
|
||||
throw new AssertionError(fail);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSerialization() {
|
||||
assertInvariants(serialClone(new PriorityQueue()));
|
||||
assertInvariants(serialClone(new PriorityQueue(1)));
|
||||
assertInvariants(serialClone(new PriorityQueue(new ArrayList())));
|
||||
assertInvariants(serialClone(new PriorityBlockingQueue()));
|
||||
assertInvariants(serialClone(new PriorityBlockingQueue(1)));
|
||||
assertInvariants(serialClone(new PriorityBlockingQueue(new ArrayList())));
|
||||
}
|
||||
|
||||
void assertInvariants(Queue q) {
|
||||
if (q instanceof PriorityQueue)
|
||||
assertInvariants((PriorityQueue) q);
|
||||
else
|
||||
assertInvariants((PriorityBlockingQueue) q);
|
||||
}
|
||||
|
||||
void assertInvariants(PriorityBlockingQueue q) {
|
||||
assertHeap(queue(q), q.size(), q.comparator());
|
||||
}
|
||||
|
||||
void assertInvariants(PriorityQueue q) {
|
||||
assertHeap(queue(q), q.size(), q.comparator());
|
||||
}
|
||||
|
||||
void assertHeap(Object[] es, int size, Comparator cmp) {
|
||||
assertTrue(es.length > 0);
|
||||
assertTrue(size >= 0 && size <= es.length);
|
||||
if (size < es.length)
|
||||
assertNull(es[size]);
|
||||
if (size > 0)
|
||||
assertNotNull(es[size - 1]);
|
||||
for (int i = 0; i <= size / 2; i++) {
|
||||
int leftChild = 2 * i + 1;
|
||||
int rightChild = leftChild + 1;
|
||||
if (leftChild < size)
|
||||
assertTrue(cmp(es[i], es[leftChild], cmp) <= 0);
|
||||
if (rightChild < size)
|
||||
assertTrue(cmp(es[i], es[rightChild], cmp) <= 0);
|
||||
}
|
||||
}
|
||||
|
||||
int cmp(Object x, Object y, Comparator cmp) {
|
||||
return (cmp == null)
|
||||
? ((Comparable) x).compareTo(y)
|
||||
: cmp.compare(x, y);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6602600
|
||||
* @run main/othervm/timeout=480 -Xmx64m BasicCancelTest
|
||||
* @summary Check effectiveness of RemoveOnCancelPolicy
|
||||
*/
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Simple timer cancellation test. Submits tasks to a scheduled executor
|
||||
* service and immediately cancels them.
|
||||
*/
|
||||
public class BasicCancelTest {
|
||||
|
||||
void checkShutdown(final ExecutorService es) {
|
||||
final Runnable nop = new Runnable() {public void run() {}};
|
||||
try {
|
||||
if (new Random().nextBoolean()) {
|
||||
check(es.isShutdown());
|
||||
if (es instanceof ThreadPoolExecutor)
|
||||
check(((ThreadPoolExecutor) es).isTerminating()
|
||||
|| es.isTerminated());
|
||||
THROWS(RejectedExecutionException.class,
|
||||
new F(){void f(){es.execute(nop);}});
|
||||
}
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
}
|
||||
|
||||
void checkTerminated(final ThreadPoolExecutor tpe) {
|
||||
try {
|
||||
checkShutdown(tpe);
|
||||
check(tpe.getQueue().isEmpty());
|
||||
check(tpe.isTerminated());
|
||||
check(! tpe.isTerminating());
|
||||
equal(tpe.getActiveCount(), 0);
|
||||
equal(tpe.getPoolSize(), 0);
|
||||
equal(tpe.getTaskCount(), tpe.getCompletedTaskCount());
|
||||
check(tpe.awaitTermination(0L, TimeUnit.SECONDS));
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
}
|
||||
|
||||
void test(String[] args) throws Throwable {
|
||||
|
||||
final ScheduledThreadPoolExecutor pool =
|
||||
new ScheduledThreadPoolExecutor(1);
|
||||
|
||||
// Needed to avoid OOME
|
||||
pool.setRemoveOnCancelPolicy(true);
|
||||
|
||||
final long moreThanYouCanChew = Runtime.getRuntime().maxMemory() / 32;
|
||||
System.out.printf("moreThanYouCanChew=%d%n", moreThanYouCanChew);
|
||||
|
||||
Runnable noopTask = new Runnable() { public void run() {}};
|
||||
|
||||
for (long i = 0; i < moreThanYouCanChew; i++)
|
||||
pool.schedule(noopTask, 10, TimeUnit.MINUTES).cancel(true);
|
||||
|
||||
pool.shutdown();
|
||||
check(pool.awaitTermination(1L, TimeUnit.DAYS));
|
||||
checkTerminated(pool);
|
||||
equal(pool.getTaskCount(), 0L);
|
||||
equal(pool.getCompletedTaskCount(), 0L);
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new BasicCancelTest().instanceMain(args);}
|
||||
void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
abstract class F {abstract void f() throws Throwable;}
|
||||
void THROWS(Class<? extends Throwable> k, F... fs) {
|
||||
for (F f : fs)
|
||||
try {f.f(); fail("Expected " + k.getName() + " not thrown");}
|
||||
catch (Throwable t) {
|
||||
if (k.isAssignableFrom(t.getClass())) pass();
|
||||
else unexpected(t);}}
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6560953
|
||||
* @summary Test ScheduledThreadPoolExecutor.decorateTask
|
||||
*/
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Delayed;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.RunnableScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class DecorateTask {
|
||||
Runnable countDownTask(final CountDownLatch latch) {
|
||||
return new Runnable() { public void run() {
|
||||
latch.countDown();
|
||||
if (latch.getCount() <= 0)
|
||||
throw new RuntimeException("done");
|
||||
}};}
|
||||
|
||||
void test(String[] args) throws Throwable {
|
||||
final int jobs = 100;
|
||||
final AtomicInteger decoratorCount = new AtomicInteger(0);
|
||||
final ScheduledThreadPoolExecutor pool =
|
||||
new ScheduledThreadPoolExecutor(10) {
|
||||
protected <V> RunnableScheduledFuture<V> decorateTask(
|
||||
final Runnable runnable,
|
||||
final RunnableScheduledFuture<V> task) {
|
||||
return new RunnableScheduledFuture<V>() {
|
||||
public void run() {
|
||||
decoratorCount.incrementAndGet();
|
||||
task.run();
|
||||
}
|
||||
public boolean isPeriodic() {
|
||||
return task.isPeriodic();
|
||||
}
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
return task.cancel(mayInterruptIfRunning);
|
||||
}
|
||||
public boolean isCancelled() {
|
||||
return task.isCancelled();
|
||||
}
|
||||
public boolean isDone() {
|
||||
return task.isDone();
|
||||
}
|
||||
public V get()
|
||||
throws InterruptedException, ExecutionException {
|
||||
return task.get();
|
||||
}
|
||||
public V get(long timeout, TimeUnit unit)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
return task.get(timeout, unit);
|
||||
}
|
||||
public long getDelay(TimeUnit unit) {
|
||||
return task.getDelay(unit);
|
||||
}
|
||||
public int compareTo(Delayed o) {
|
||||
return task.compareTo(o);
|
||||
}};}};
|
||||
final CountDownLatch latch1 = new CountDownLatch(jobs);
|
||||
final CountDownLatch latch2 = new CountDownLatch(jobs);
|
||||
pool.scheduleAtFixedRate(countDownTask(latch1), 0L, 1L, TimeUnit.NANOSECONDS);
|
||||
pool.scheduleWithFixedDelay(countDownTask(latch2), 0L, 1L, TimeUnit.NANOSECONDS);
|
||||
latch1.await();
|
||||
latch2.await();
|
||||
pool.shutdown();
|
||||
pool.awaitTermination(1L, TimeUnit.MINUTES);
|
||||
equal(decoratorCount.get(), 2 * jobs);
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new DecorateTask().instanceMain(args);}
|
||||
void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6725789
|
||||
* @summary Check for long overflow in task time comparison.
|
||||
* @library /test/lib
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.DAYS;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
public class DelayOverflow {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
|
||||
static void waitForNanoTimeTick() {
|
||||
for (long t0 = System.nanoTime(); t0 == System.nanoTime(); )
|
||||
;
|
||||
}
|
||||
|
||||
void scheduleNow(ScheduledThreadPoolExecutor pool,
|
||||
Runnable r, int how) {
|
||||
switch (how) {
|
||||
case 0:
|
||||
pool.schedule(r, 0, MILLISECONDS);
|
||||
break;
|
||||
case 1:
|
||||
pool.schedule(Executors.callable(r), 0, DAYS);
|
||||
break;
|
||||
case 2:
|
||||
pool.scheduleWithFixedDelay(r, 0, 1000, NANOSECONDS);
|
||||
break;
|
||||
case 3:
|
||||
pool.scheduleAtFixedRate(r, 0, 1000, MILLISECONDS);
|
||||
break;
|
||||
default:
|
||||
fail(String.valueOf(how));
|
||||
}
|
||||
}
|
||||
|
||||
void scheduleAtTheEndOfTime(ScheduledThreadPoolExecutor pool,
|
||||
Runnable r, int how) {
|
||||
switch (how) {
|
||||
case 0:
|
||||
pool.schedule(r, Long.MAX_VALUE, MILLISECONDS);
|
||||
break;
|
||||
case 1:
|
||||
pool.schedule(Executors.callable(r), Long.MAX_VALUE, DAYS);
|
||||
break;
|
||||
case 2:
|
||||
pool.scheduleWithFixedDelay(r, Long.MAX_VALUE, 1000, NANOSECONDS);
|
||||
break;
|
||||
case 3:
|
||||
pool.scheduleAtFixedRate(r, Long.MAX_VALUE, 1000, MILLISECONDS);
|
||||
break;
|
||||
default:
|
||||
fail(String.valueOf(how));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to test exhaustively and deterministically, all 20
|
||||
* possible ways that one task can be scheduled in the maximal
|
||||
* distant future, while at the same time an existing task's time
|
||||
* has already expired.
|
||||
*/
|
||||
void test(String[] args) throws Throwable {
|
||||
for (int nowHow = 0; nowHow < 4; nowHow++) {
|
||||
for (int thenHow = 0; thenHow < 4; thenHow++) {
|
||||
|
||||
final ScheduledThreadPoolExecutor pool
|
||||
= new ScheduledThreadPoolExecutor(1);
|
||||
final CountDownLatch runLatch = new CountDownLatch(1);
|
||||
final CountDownLatch busyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch proceedLatch = new CountDownLatch(1);
|
||||
final Runnable notifier = new Runnable() {
|
||||
public void run() { runLatch.countDown(); }};
|
||||
final Runnable neverRuns = new Runnable() {
|
||||
public void run() { fail(); }};
|
||||
final Runnable keepPoolBusy = new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
busyLatch.countDown();
|
||||
proceedLatch.await();
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
}};
|
||||
pool.schedule(keepPoolBusy, 0, DAYS);
|
||||
busyLatch.await();
|
||||
scheduleNow(pool, notifier, nowHow);
|
||||
waitForNanoTimeTick();
|
||||
scheduleAtTheEndOfTime(pool, neverRuns, thenHow);
|
||||
proceedLatch.countDown();
|
||||
|
||||
check(runLatch.await(LONG_DELAY_MS, MILLISECONDS));
|
||||
equal(runLatch.getCount(), 0L);
|
||||
|
||||
pool.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
||||
pool.shutdown();
|
||||
}
|
||||
|
||||
final int nowHowCopy = nowHow;
|
||||
final ScheduledThreadPoolExecutor pool
|
||||
= new ScheduledThreadPoolExecutor(1);
|
||||
final CountDownLatch runLatch = new CountDownLatch(1);
|
||||
final Runnable notifier = new Runnable() {
|
||||
public void run() { runLatch.countDown(); }};
|
||||
final Runnable scheduleNowScheduler = new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
scheduleNow(pool, notifier, nowHowCopy);
|
||||
waitForNanoTimeTick();
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
}};
|
||||
pool.scheduleWithFixedDelay(scheduleNowScheduler,
|
||||
0, Long.MAX_VALUE, NANOSECONDS);
|
||||
|
||||
check(runLatch.await(LONG_DELAY_MS, MILLISECONDS));
|
||||
equal(runLatch.getCount(), 0L);
|
||||
|
||||
pool.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
||||
pool.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new DelayOverflow().instanceMain(args);}
|
||||
void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Ensure that waiting pool threads don't retain refs to tasks.
|
||||
*/
|
||||
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Delayed;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.RunnableScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
public class GCRetention {
|
||||
|
||||
/**
|
||||
* A custom thread pool with a custom RunnableScheduledFuture, for the
|
||||
* sole purpose of ensuring that the task retains a strong reference to
|
||||
* the Runnable even after cancellation.
|
||||
*/
|
||||
static class CustomPool extends ScheduledThreadPoolExecutor {
|
||||
CustomPool(int n) { super(n); }
|
||||
protected <V> RunnableScheduledFuture<V> decorateTask(
|
||||
final Runnable r,
|
||||
final RunnableScheduledFuture<V> task) {
|
||||
return new RunnableScheduledFuture<V>() {
|
||||
public void run() { System.err.println(r); task.run(); }
|
||||
public boolean isPeriodic() { return task.isPeriodic(); }
|
||||
public V get()
|
||||
throws InterruptedException,ExecutionException
|
||||
{ return task.get(); }
|
||||
public V get(long x, TimeUnit y)
|
||||
throws InterruptedException,ExecutionException,TimeoutException
|
||||
{ return task.get(x, y); }
|
||||
public boolean isDone() { return task.isDone(); }
|
||||
public boolean isCancelled() { return task.isCancelled(); }
|
||||
public boolean cancel(boolean x) { return task.cancel(x); }
|
||||
public long getDelay(TimeUnit x) { return task.getDelay(x); }
|
||||
public int compareTo(Delayed x) { return task.compareTo(x); }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void removeAll(ReferenceQueue<?> q, int n) throws InterruptedException {
|
||||
for (int j = n; j--> 0; ) {
|
||||
if (q.poll() == null) {
|
||||
for (;;) {
|
||||
System.gc();
|
||||
if (q.remove(1000) != null)
|
||||
break;
|
||||
System.out.printf(
|
||||
"%d/%d unqueued references remaining%n", j + 1, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
check(q.poll() == null);
|
||||
}
|
||||
|
||||
void test(String[] args) throws Throwable {
|
||||
final CustomPool pool = new CustomPool(10);
|
||||
final int size = 100;
|
||||
final ReferenceQueue<Object> q = new ReferenceQueue<>();
|
||||
final List<WeakReference<?>> refs = new ArrayList<>(size);
|
||||
final List<Future<?>> futures = new ArrayList<>(size);
|
||||
|
||||
// Schedule custom tasks with strong references.
|
||||
class Task implements Runnable {
|
||||
final Object x;
|
||||
Task() { refs.add(new WeakReference<>(x = new Object(), q)); }
|
||||
public void run() { System.out.println(x); }
|
||||
}
|
||||
// Give tasks added later earlier expiration, to ensure
|
||||
// multiple residents of queue head.
|
||||
for (int i = size; i--> 0; )
|
||||
futures.add(pool.schedule(new Task(), i + 1, TimeUnit.MINUTES));
|
||||
futures.forEach(future -> future.cancel(false));
|
||||
futures.clear();
|
||||
|
||||
pool.purge();
|
||||
removeAll(q, size);
|
||||
for (WeakReference<?> ref : refs) check(ref.get() == null);
|
||||
|
||||
pool.shutdown();
|
||||
// rely on test harness to handle timeout
|
||||
pool.awaitTermination(1L, TimeUnit.DAYS);
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new GCRetention().instanceMain(args);}
|
||||
void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
|
||||
/**
|
||||
* This is not a regression test, but a stress benchmark test for
|
||||
* 6602600: Fast removal of cancelled scheduled thread pool tasks
|
||||
*
|
||||
* This runs in the same wall clock time, but much reduced cpu time,
|
||||
* with the changes for 6602600.
|
||||
*/
|
||||
public class Stress {
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
|
||||
final CountDownLatch count = new CountDownLatch(1000);
|
||||
|
||||
final ScheduledThreadPoolExecutor pool =
|
||||
new ScheduledThreadPoolExecutor(100);
|
||||
pool.prestartAllCoreThreads();
|
||||
|
||||
final Runnable incTask = new Runnable() { public void run() {
|
||||
count.countDown();
|
||||
}};
|
||||
|
||||
pool.scheduleAtFixedRate(incTask, 0, 10, TimeUnit.MILLISECONDS);
|
||||
|
||||
count.await();
|
||||
|
||||
pool.shutdown();
|
||||
pool.awaitTermination(1L, TimeUnit.DAYS);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 7091003
|
||||
* @summary ScheduledExecutorService never executes Runnable
|
||||
* with corePoolSize of zero
|
||||
* @library /test/lib
|
||||
* @author Chris Hegarty
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
/**
|
||||
* Verify that tasks can be run even with a core pool size of 0.
|
||||
*/
|
||||
public class ZeroCorePoolSize {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
|
||||
volatile boolean taskRun;
|
||||
|
||||
void test(String[] args) throws Throwable {
|
||||
|
||||
ScheduledThreadPoolExecutor pool = new ScheduledThreadPoolExecutor(0);
|
||||
Runnable task = new Runnable() {
|
||||
public void run() {
|
||||
taskRun = true;
|
||||
}
|
||||
};
|
||||
check(pool.getCorePoolSize() == 0);
|
||||
|
||||
pool.schedule(task, 12L, MILLISECONDS);
|
||||
|
||||
pool.shutdown();
|
||||
check(pool.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
|
||||
check(pool.getCorePoolSize() == 0);
|
||||
check(taskRun);
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new ZeroCorePoolSize().instanceMain(args);}
|
||||
void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
abstract class F {abstract void f() throws Throwable;}
|
||||
void THROWS(Class<? extends Throwable> k, F... fs) {
|
||||
for (F f : fs)
|
||||
try {f.f(); fail("Expected " + k.getName() + " not thrown");}
|
||||
catch (Throwable t) {
|
||||
if (k.isAssignableFrom(t.getClass())) pass();
|
||||
else unexpected(t);}}
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8022642 8065320 8129861
|
||||
* @summary Ensure relative sanity when zero core threads
|
||||
* @library /test/lib
|
||||
* @modules java.base/java.util.concurrent:open
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.HOURS;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
public class ZeroCoreThreads {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
|
||||
static long millisElapsedSince(long startTime) {
|
||||
return (System.nanoTime() - startTime) / (1000L * 1000L);
|
||||
}
|
||||
|
||||
static void spinWaitUntil(BooleanSupplier predicate, long timeoutMillis) {
|
||||
long startTime = -1L;
|
||||
while (!predicate.getAsBoolean()) {
|
||||
if (startTime == -1L)
|
||||
startTime = System.nanoTime();
|
||||
else if (millisElapsedSince(startTime) > timeoutMillis)
|
||||
throw new AssertionError(
|
||||
String.format("timed out after %s ms", timeoutMillis));
|
||||
Thread.yield();
|
||||
}
|
||||
}
|
||||
|
||||
static boolean hasWaiters(ReentrantLock lock, Condition condition) {
|
||||
lock.lock();
|
||||
try {
|
||||
return lock.hasWaiters(condition);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
static void awaitHasWaiters(ReentrantLock lock, Condition condition,
|
||||
long timeoutMillis) {
|
||||
spinWaitUntil(() -> hasWaiters(lock, condition), timeoutMillis);
|
||||
}
|
||||
|
||||
static <T> T getField(Object x, String fieldName) {
|
||||
try {
|
||||
Field field = x.getClass().getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return (T) field.get(x);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new AssertionError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
void test(String[] args) throws Throwable {
|
||||
ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(0);
|
||||
try {
|
||||
test(p);
|
||||
} finally {
|
||||
p.shutdownNow();
|
||||
check(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
|
||||
}
|
||||
}
|
||||
|
||||
void test(ScheduledThreadPoolExecutor p) throws Throwable {
|
||||
Runnable dummy = new Runnable() { public void run() {
|
||||
throw new AssertionError("shouldn't get here"); }};
|
||||
BlockingQueue q = p.getQueue();
|
||||
ReentrantLock lock = getField(q, "lock");
|
||||
Condition available = getField(q, "available");
|
||||
|
||||
equal(0, p.getPoolSize());
|
||||
equal(0, p.getLargestPoolSize());
|
||||
equal(0L, p.getTaskCount());
|
||||
equal(0L, p.getCompletedTaskCount());
|
||||
p.schedule(dummy, 1L, HOURS);
|
||||
// Ensure one pool thread actually waits in timed queue poll
|
||||
awaitHasWaiters(lock, available, LONG_DELAY_MS);
|
||||
equal(1, p.getPoolSize());
|
||||
equal(1, p.getLargestPoolSize());
|
||||
equal(1L, p.getTaskCount());
|
||||
equal(0L, p.getCompletedTaskCount());
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new ZeroCoreThreads().instanceMain(args);}
|
||||
void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
102
test/jdk/java/util/concurrent/Semaphore/PermitOverflow.java
Normal file
102
test/jdk/java/util/concurrent/Semaphore/PermitOverflow.java
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6941130
|
||||
* @summary Numeric overflow/underflow of permits causes Error throw
|
||||
*/
|
||||
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
public class PermitOverflow {
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
for (boolean fair : new boolean[] { true, false }) {
|
||||
Semaphore sem = new Semaphore(Integer.MAX_VALUE - 1, fair);
|
||||
if (sem.availablePermits() != Integer.MAX_VALUE - 1)
|
||||
throw new RuntimeException();
|
||||
try {
|
||||
sem.release(2);
|
||||
throw new RuntimeException();
|
||||
} catch (Error expected) {
|
||||
}
|
||||
sem.release(1);
|
||||
if (sem.availablePermits() != Integer.MAX_VALUE)
|
||||
throw new RuntimeException();
|
||||
try {
|
||||
sem.release(1);
|
||||
throw new RuntimeException();
|
||||
} catch (Error expected) {
|
||||
}
|
||||
try {
|
||||
sem.release(Integer.MAX_VALUE);
|
||||
throw new RuntimeException();
|
||||
} catch (Error expected) {
|
||||
}
|
||||
}
|
||||
|
||||
class Sem extends Semaphore {
|
||||
public Sem(int permits, boolean fair) {
|
||||
super(permits, fair);
|
||||
}
|
||||
public void reducePermits(int reduction) {
|
||||
super.reducePermits(reduction);
|
||||
}
|
||||
}
|
||||
|
||||
for (boolean fair : new boolean[] { true, false }) {
|
||||
Sem sem = new Sem(Integer.MIN_VALUE + 1, fair);
|
||||
if (sem.availablePermits() != Integer.MIN_VALUE + 1)
|
||||
throw new RuntimeException();
|
||||
try {
|
||||
sem.reducePermits(2);
|
||||
throw new RuntimeException();
|
||||
} catch (Error expected) {
|
||||
}
|
||||
sem.reducePermits(1);
|
||||
if (sem.availablePermits() != Integer.MIN_VALUE)
|
||||
throw new RuntimeException();
|
||||
try {
|
||||
sem.reducePermits(1);
|
||||
throw new RuntimeException();
|
||||
} catch (Error expected) {
|
||||
}
|
||||
try {
|
||||
sem.reducePermits(Integer.MAX_VALUE);
|
||||
throw new RuntimeException();
|
||||
} catch (Error expected) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
116
test/jdk/java/util/concurrent/Semaphore/RacingReleases.java
Normal file
116
test/jdk/java/util/concurrent/Semaphore/RacingReleases.java
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Doug Lea 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6801020 6803402
|
||||
* @summary Try to tickle race conditions in
|
||||
* AbstractQueuedSynchronizer "shared" code
|
||||
*/
|
||||
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
public class RacingReleases {
|
||||
|
||||
/** Increase this for better chance of tickling races */
|
||||
static final int iterations = 1000;
|
||||
|
||||
public static void test(final boolean fair,
|
||||
final boolean interruptibly)
|
||||
throws Throwable {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
final Semaphore sem = new Semaphore(0, fair);
|
||||
final Throwable[] badness = new Throwable[1];
|
||||
Runnable blocker = interruptibly ?
|
||||
new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
sem.acquire();
|
||||
} catch (Throwable t) {
|
||||
badness[0] = t;
|
||||
throw new Error(t);
|
||||
}}}
|
||||
:
|
||||
new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
sem.acquireUninterruptibly();
|
||||
} catch (Throwable t) {
|
||||
badness[0] = t;
|
||||
throw new Error(t);
|
||||
}}};
|
||||
|
||||
Thread b1 = new Thread(blocker);
|
||||
Thread b2 = new Thread(blocker);
|
||||
Runnable signaller = new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
sem.release();
|
||||
} catch (Throwable t) {
|
||||
badness[0] = t;
|
||||
throw new Error(t);
|
||||
}}};
|
||||
Thread s1 = new Thread(signaller);
|
||||
Thread s2 = new Thread(signaller);
|
||||
Thread[] threads = { b1, b2, s1, s2 };
|
||||
java.util.Collections.shuffle(java.util.Arrays.asList(threads));
|
||||
for (Thread thread : threads)
|
||||
thread.start();
|
||||
for (Thread thread : threads) {
|
||||
thread.join(60 * 1000);
|
||||
if (thread.isAlive())
|
||||
throw new Error
|
||||
(String.format
|
||||
("Semaphore stuck: permits %d, thread waiting %s%n",
|
||||
sem.availablePermits(),
|
||||
sem.hasQueuedThreads() ? "true" : "false"));
|
||||
}
|
||||
if (badness[0] != null)
|
||||
throw new Error(badness[0]);
|
||||
if (sem.availablePermits() != 0)
|
||||
throw new Error(String.valueOf(sem.availablePermits()));
|
||||
if (sem.hasQueuedThreads())
|
||||
throw new Error(String.valueOf(sem.hasQueuedThreads()));
|
||||
if (sem.getQueueLength() != 0)
|
||||
throw new Error(String.valueOf(sem.getQueueLength()));
|
||||
if (sem.isFair() != fair)
|
||||
throw new Error(String.valueOf(sem.isFair()));
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
for (boolean fair : new boolean[] { true, false })
|
||||
for (boolean interruptibly : new boolean[] { true, false })
|
||||
test(fair, interruptibly);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Stress test of StructuredTaskScope cancellation with running and starting threads
|
||||
* @enablePreview
|
||||
* @run junit StressCancellation
|
||||
*/
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.StructuredTaskScope;
|
||||
import java.util.concurrent.StructuredTaskScope.Joiner;
|
||||
import java.util.concurrent.StructuredTaskScope.Subtask;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class StressCancellation {
|
||||
|
||||
static Stream<Arguments> testCases() {
|
||||
Stream<ThreadFactory> factories = Stream.of(
|
||||
Thread.ofPlatform().factory(),
|
||||
Thread.ofVirtual().factory()
|
||||
);
|
||||
// 0..15 forks before shutdown, 0..15 forks after shutdown
|
||||
return factories.flatMap(f -> IntStream.range(0, 256)
|
||||
.mapToObj(x -> Arguments.of(f, x & 0x0F, (x & 0xF0) >> 4)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test StructuredTaskScope cancellation with running threads and concurrently with
|
||||
* threads that are starting. The cancellation should interrupt all running threads,
|
||||
* join should wakeup, and close would complete quickly.
|
||||
*
|
||||
* @param factory the ThreadFactory to use
|
||||
* @param beforeCancel the number of subtasks to fork before cancel
|
||||
* @param afterCancel the number of subtasks to fork after cancel
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("testCases")
|
||||
void test(ThreadFactory factory, int beforeCancel, int afterCancel) throws Exception {
|
||||
var joiner = new Joiner<Boolean, Void>() {
|
||||
@Override
|
||||
public boolean onComplete(Subtask<Boolean> subtask) {
|
||||
boolean cancel = subtask.get();
|
||||
return cancel;
|
||||
}
|
||||
@Override
|
||||
public Void result() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
try (var scope = StructuredTaskScope.open(joiner, cf -> cf.withThreadFactory(factory))) {
|
||||
// fork subtasks
|
||||
for (int i = 0; i < beforeCancel; i++) {
|
||||
scope.fork(() -> {
|
||||
Thread.sleep(Duration.ofDays(1));
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// fork subtask to cancel
|
||||
scope.fork(() -> true);
|
||||
|
||||
// fork after forking subtask to cancel
|
||||
for (int i = 0; i < afterCancel; i++) {
|
||||
scope.fork(() -> {
|
||||
Thread.sleep(Duration.ofDays(1));
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
scope.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8284199 8296779 8306647
|
||||
* @summary Test thread dumps with StructuredTaskScope
|
||||
* @enablePreview
|
||||
* @library /test/lib
|
||||
* @run junit/othervm StructuredThreadDumpTest
|
||||
*/
|
||||
|
||||
import java.util.concurrent.StructuredTaskScope;
|
||||
import java.util.concurrent.StructuredTaskScope.Joiner;
|
||||
import java.io.IOException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
import java.util.stream.Stream;
|
||||
import com.sun.management.HotSpotDiagnosticMXBean;
|
||||
import com.sun.management.HotSpotDiagnosticMXBean.ThreadDumpFormat;
|
||||
import jdk.test.lib.threaddump.ThreadDump;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class StructuredThreadDumpTest {
|
||||
|
||||
/**
|
||||
* Test that a thread dump with a tree of task scopes contains a thread grouping for
|
||||
* each task scope.
|
||||
*/
|
||||
@Test
|
||||
void testTree() throws Exception {
|
||||
try (var scope = StructuredTaskScope.open(Joiner.awaitAll(), cf -> cf.withName("scope"))) {
|
||||
Thread thread1 = fork(scope, "child-scope-A");
|
||||
Thread thread2 = fork(scope, "child-scope-B");
|
||||
try {
|
||||
ThreadDump threadDump = threadDump();
|
||||
|
||||
// thread dump should have a thread container for each scope
|
||||
var rootContainer = threadDump.rootThreadContainer();
|
||||
var container1 = threadDump.findThreadContainer("scope").orElseThrow();
|
||||
var container2 = threadDump.findThreadContainer("child-scope-A").orElseThrow();
|
||||
var container3 = threadDump.findThreadContainer("child-scope-B").orElseThrow();
|
||||
|
||||
// check parents
|
||||
assertFalse(rootContainer.parent().isPresent());
|
||||
assertTrue(container1.parent().get() == rootContainer);
|
||||
assertTrue(container2.parent().get() == container1);
|
||||
assertTrue(container3.parent().get() == container1);
|
||||
|
||||
// check owners
|
||||
assertFalse(rootContainer.owner().isPresent());
|
||||
assertTrue(container1.owner().getAsLong() == Thread.currentThread().threadId());
|
||||
assertTrue(container2.owner().getAsLong() == thread1.threadId());
|
||||
assertTrue(container3.owner().getAsLong() == thread2.threadId());
|
||||
|
||||
// thread1 and threads2 should be in threads array of "scope"
|
||||
container1.findThread(thread1.threadId()).orElseThrow();
|
||||
container1.findThread(thread2.threadId()).orElseThrow();
|
||||
|
||||
} finally {
|
||||
LockSupport.unpark(thread1);
|
||||
LockSupport.unpark(thread2);
|
||||
scope.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a thread dump with nested tasks scopes contains a thread grouping for
|
||||
* each task scope.
|
||||
*/
|
||||
@Test
|
||||
void testNested() throws Exception {
|
||||
try (var scope1 = StructuredTaskScope.open(Joiner.awaitAll(), cf -> cf.withName("scope-A"))) {
|
||||
Thread thread1 = fork(scope1);
|
||||
|
||||
try (var scope2 = StructuredTaskScope.open(Joiner.awaitAll(), cf -> cf.withName("scope-B"))) {
|
||||
Thread thread2 = fork(scope2);
|
||||
try {
|
||||
ThreadDump threadDump = threadDump();
|
||||
|
||||
// thread dump should have a thread container for both scopes
|
||||
var rootContainer = threadDump.rootThreadContainer();
|
||||
var container1 = threadDump.findThreadContainer("scope-A").orElseThrow();
|
||||
var container2 = threadDump.findThreadContainer("scope-B").orElseThrow();
|
||||
|
||||
// check parents
|
||||
assertFalse(rootContainer.parent().isPresent());
|
||||
assertTrue(container1.parent().get() == rootContainer);
|
||||
assertTrue(container2.parent().get() == container1);
|
||||
|
||||
// check owners
|
||||
long tid = Thread.currentThread().threadId();
|
||||
assertFalse(rootContainer.owner().isPresent());
|
||||
assertTrue(container1.owner().getAsLong() == tid);
|
||||
assertTrue(container2.owner().getAsLong() == tid);
|
||||
|
||||
// thread1 should be in threads array of "scope-A"
|
||||
container1.findThread(thread1.threadId()).orElseThrow();
|
||||
|
||||
// thread2 should be in threads array of "scope-B"
|
||||
container2.findThread(thread2.threadId()).orElseThrow();
|
||||
|
||||
} finally {
|
||||
LockSupport.unpark(thread2);
|
||||
scope2.join();
|
||||
}
|
||||
} finally {
|
||||
LockSupport.unpark(thread1);
|
||||
scope1.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a JSON formatted thread dump to a temporary file, prints it to standard
|
||||
* output, parses the JSON text and returns a ThreadDump object for the thread dump.
|
||||
*/
|
||||
private static ThreadDump threadDump() throws IOException {
|
||||
Path dir = Path.of(".").toAbsolutePath();
|
||||
Path file = Files.createTempFile(dir, "threadump", "json");
|
||||
Files.delete(file);
|
||||
ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class)
|
||||
.dumpThreads(file.toString(), ThreadDumpFormat.JSON);
|
||||
|
||||
try (Stream<String> stream = Files.lines(file)) {
|
||||
stream.forEach(System.out::println);
|
||||
}
|
||||
|
||||
String jsonText = Files.readString(file);
|
||||
return ThreadDump.parse(jsonText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forks a subtask in the given scope that parks, returning the Thread that executes
|
||||
* the subtask.
|
||||
*/
|
||||
private static Thread fork(StructuredTaskScope<Object, Void> scope) throws Exception {
|
||||
var ref = new AtomicReference<Thread>();
|
||||
scope.fork(() -> {
|
||||
ref.set(Thread.currentThread());
|
||||
LockSupport.park();
|
||||
return null;
|
||||
});
|
||||
Thread thread;
|
||||
while ((thread = ref.get()) == null) {
|
||||
Thread.sleep(10);
|
||||
}
|
||||
return thread;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forks a subtask in the given scope. The subtask creates a new child scope with
|
||||
* the given name, then parks. This method returns Thread that executes the subtask.
|
||||
*/
|
||||
private static Thread fork(StructuredTaskScope<Object, Void> scope,
|
||||
String childScopeName) throws Exception {
|
||||
var ref = new AtomicReference<Thread>();
|
||||
scope.fork(() -> {
|
||||
try (var childScope = StructuredTaskScope.open(Joiner.awaitAll(),
|
||||
cf -> cf.withName(childScopeName))) {
|
||||
ref.set(Thread.currentThread());
|
||||
LockSupport.park();
|
||||
}
|
||||
});
|
||||
Thread thread;
|
||||
while ((thread = ref.get()) == null) {
|
||||
Thread.sleep(10);
|
||||
}
|
||||
return thread;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8284199 8296779 8306647
|
||||
* @summary Basic tests for StructuredTaskScope with scoped values
|
||||
* @enablePreview
|
||||
* @run junit WithScopedValue
|
||||
*/
|
||||
|
||||
import java.util.concurrent.StructuredTaskScope;
|
||||
import java.util.concurrent.StructuredTaskScope.Subtask;
|
||||
import java.util.concurrent.StructuredTaskScope.Joiner;
|
||||
import java.util.concurrent.StructureViolationException;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class WithScopedValue {
|
||||
|
||||
private static Stream<ThreadFactory> factories() {
|
||||
return Stream.of(Thread.ofPlatform().factory(), Thread.ofVirtual().factory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that fork inherits a scoped value into a child thread.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("factories")
|
||||
void testForkInheritsScopedValue1(ThreadFactory factory) throws Exception {
|
||||
ScopedValue<String> name = ScopedValue.newInstance();
|
||||
String value = ScopedValue.where(name, "x").call(() -> {
|
||||
try (var scope = StructuredTaskScope.open(Joiner.awaitAll(),
|
||||
cf -> cf.withThreadFactory(factory))) {
|
||||
Subtask<String> subtask = scope.fork(() -> {
|
||||
return name.get(); // child should read "x"
|
||||
});
|
||||
scope.join();
|
||||
return subtask.get();
|
||||
}
|
||||
});
|
||||
assertEquals(value, "x");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that fork inherits a scoped value into a grandchild thread.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("factories")
|
||||
void testForkInheritsScopedValue2(ThreadFactory factory) throws Exception {
|
||||
ScopedValue<String> name = ScopedValue.newInstance();
|
||||
String value = ScopedValue.where(name, "x").call(() -> {
|
||||
try (var scope1 = StructuredTaskScope.open(Joiner.awaitAll(),
|
||||
cf -> cf.withThreadFactory(factory))) {
|
||||
Subtask<String> subtask1 = scope1.fork(() -> {
|
||||
try (var scope2 = StructuredTaskScope.open(Joiner.awaitAll(),
|
||||
cf -> cf.withThreadFactory(factory))) {
|
||||
Subtask<String> subtask2 = scope2.fork(() -> {
|
||||
return name.get(); // grandchild should read "x"
|
||||
});
|
||||
scope2.join();
|
||||
return subtask2.get();
|
||||
}
|
||||
});
|
||||
scope1.join();
|
||||
return subtask1.get();
|
||||
}
|
||||
});
|
||||
assertEquals(value, "x");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that fork inherits a rebound scoped value into a grandchild thread.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("factories")
|
||||
void testForkInheritsScopedValue3(ThreadFactory factory) throws Exception {
|
||||
ScopedValue<String> name = ScopedValue.newInstance();
|
||||
String value = ScopedValue.where(name, "x").call(() -> {
|
||||
try (var scope1 = StructuredTaskScope.open(Joiner.awaitAll(),
|
||||
cf -> cf.withThreadFactory(factory))) {
|
||||
Subtask<String> subtask1 = scope1.fork(() -> {
|
||||
assertEquals(name.get(), "x"); // child should read "x"
|
||||
|
||||
// rebind name to "y"
|
||||
String grandchildValue = ScopedValue.where(name, "y").call(() -> {
|
||||
try (var scope2 = StructuredTaskScope.open(Joiner.awaitAll(),
|
||||
cf -> cf.withThreadFactory(factory))) {
|
||||
Subtask<String> subtask2 = scope2.fork(() -> {
|
||||
return name.get(); // grandchild should read "y"
|
||||
});
|
||||
scope2.join();
|
||||
return subtask2.get();
|
||||
}
|
||||
});
|
||||
|
||||
assertEquals(name.get(), "x"); // child should read "x"
|
||||
return grandchildValue;
|
||||
});
|
||||
scope1.join();
|
||||
return subtask1.get();
|
||||
}
|
||||
});
|
||||
assertEquals(value, "y");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test exiting a dynamic scope with an open task scope.
|
||||
*/
|
||||
@Test
|
||||
void testStructureViolation1() throws Exception {
|
||||
ScopedValue<String> name = ScopedValue.newInstance();
|
||||
class Box {
|
||||
StructuredTaskScope<Object, Void> scope;
|
||||
}
|
||||
var box = new Box();
|
||||
try {
|
||||
try {
|
||||
ScopedValue.where(name, "x").run(() -> {
|
||||
box.scope = StructuredTaskScope.open(Joiner.awaitAll());
|
||||
});
|
||||
fail();
|
||||
} catch (StructureViolationException expected) { }
|
||||
|
||||
// underlying flock should be closed and fork should fail to start a thread
|
||||
StructuredTaskScope<Object, Void> scope = box.scope;
|
||||
AtomicBoolean ran = new AtomicBoolean();
|
||||
Subtask<Object> subtask = scope.fork(() -> {
|
||||
ran.set(true);
|
||||
return null;
|
||||
});
|
||||
scope.join();
|
||||
assertEquals(Subtask.State.UNAVAILABLE, subtask.state());
|
||||
assertFalse(ran.get());
|
||||
} finally {
|
||||
StructuredTaskScope<Object, Void> scope = box.scope;
|
||||
if (scope != null) {
|
||||
scope.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test closing a StructuredTaskScope while executing in a dynamic scope.
|
||||
*/
|
||||
@Test
|
||||
void testStructureViolation2() throws Exception {
|
||||
ScopedValue<String> name = ScopedValue.newInstance();
|
||||
try (var scope = StructuredTaskScope.open(Joiner.awaitAll())) {
|
||||
ScopedValue.where(name, "x").run(() -> {
|
||||
assertThrows(StructureViolationException.class, scope::close);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test fork when a scoped value is bound after a StructuredTaskScope is created.
|
||||
*/
|
||||
@Test
|
||||
void testStructureViolation3() throws Exception {
|
||||
ScopedValue<String> name = ScopedValue.newInstance();
|
||||
try (var scope = StructuredTaskScope.open(Joiner.awaitAll())) {
|
||||
ScopedValue.where(name, "x").run(() -> {
|
||||
assertThrows(StructureViolationException.class,
|
||||
() -> scope.fork(() -> "foo"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test fork when a scoped value is re-bound after a StructuredTaskScope is created.
|
||||
*/
|
||||
@Test
|
||||
void testStructureViolation4() throws Exception {
|
||||
ScopedValue<String> name1 = ScopedValue.newInstance();
|
||||
ScopedValue<String> name2 = ScopedValue.newInstance();
|
||||
|
||||
// rebind
|
||||
ScopedValue.where(name1, "x").run(() -> {
|
||||
try (var scope = StructuredTaskScope.open(Joiner.awaitAll())) {
|
||||
ScopedValue.where(name1, "y").run(() -> {
|
||||
assertThrows(StructureViolationException.class,
|
||||
() -> scope.fork(() -> "foo"));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// new binding
|
||||
ScopedValue.where(name1, "x").run(() -> {
|
||||
try (var scope = StructuredTaskScope.open(Joiner.awaitAll())) {
|
||||
ScopedValue.where(name2, "y").run(() -> {
|
||||
assertThrows(StructureViolationException.class,
|
||||
() -> scope.fork(() -> "foo"));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
103
test/jdk/java/util/concurrent/SynchronousQueue/Fairness.java
Normal file
103
test/jdk/java/util/concurrent/SynchronousQueue/Fairness.java
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @modules java.base/java.util.concurrent:open
|
||||
* @bug 4992438 6633113
|
||||
* @summary Checks that fairness setting is respected.
|
||||
*/
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class Fairness {
|
||||
private final static VarHandle underlyingTransferQueueAccess;
|
||||
|
||||
static {
|
||||
try {
|
||||
underlyingTransferQueueAccess =
|
||||
MethodHandles.privateLookupIn(
|
||||
SynchronousQueue.class,
|
||||
MethodHandles.lookup()
|
||||
).findVarHandle(
|
||||
SynchronousQueue.class,
|
||||
"transferer",
|
||||
Class.forName(SynchronousQueue.class.getName() + "$Transferer")
|
||||
);
|
||||
} catch (Exception ex) {
|
||||
throw new ExceptionInInitializerError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void testFairness(boolean fair, final SynchronousQueue<Integer> q)
|
||||
throws Throwable
|
||||
{
|
||||
final LinkedTransferQueue<Integer> underlying =
|
||||
(LinkedTransferQueue<Integer>)underlyingTransferQueueAccess.get(q);
|
||||
|
||||
final ReentrantLock lock = new ReentrantLock();
|
||||
final Condition ready = lock.newCondition();
|
||||
final int threadCount = 10;
|
||||
final Throwable[] badness = new Throwable[1];
|
||||
lock.lock();
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
final Integer I = i;
|
||||
Thread t = new Thread() { public void run() {
|
||||
try {
|
||||
lock.lock();
|
||||
ready.signal();
|
||||
lock.unlock();
|
||||
q.put(I);
|
||||
} catch (Throwable t) { badness[0] = t; }}};
|
||||
t.start();
|
||||
ready.await();
|
||||
// Wait until previous put:ing thread is provably parked
|
||||
while (underlying.size() < (i + 1))
|
||||
Thread.yield();
|
||||
|
||||
if (underlying.size() > (i + 1))
|
||||
throw new Error("Unexpected number of waiting producers: " + i);
|
||||
}
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
int j = q.take();
|
||||
// Non-fair queues are lifo in our implementation
|
||||
if (fair ? j != i : j != threadCount - 1 - i)
|
||||
throw new Error(String.format("fair=%b i=%d j=%d%n",
|
||||
fair, i, j));
|
||||
}
|
||||
if (badness[0] != null) throw new Error(badness[0]);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
testFairness(false, new SynchronousQueue<>());
|
||||
testFairness(false, new SynchronousQueue<>(false));
|
||||
testFairness(true, new SynchronousQueue<>(true));
|
||||
}
|
||||
}
|
||||
1
test/jdk/java/util/concurrent/TEST.properties
Normal file
1
test/jdk/java/util/concurrent/TEST.properties
Normal file
|
|
@ -0,0 +1 @@
|
|||
maxOutputSize=2000000
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
* Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6450200
|
||||
* @summary Test proper handling of pool state changes
|
||||
* @library /test/lib
|
||||
* @build jdk.test.lib.RandomFactory
|
||||
* @run main ConfigChanges
|
||||
* @key randomness
|
||||
* @author Martin Buchholz
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.function.Supplier;
|
||||
import jdk.test.lib.RandomFactory;
|
||||
|
||||
public class ConfigChanges {
|
||||
static final ThreadGroup tg = new ThreadGroup("pool");
|
||||
|
||||
static final Random rnd = RandomFactory.getRandom();
|
||||
|
||||
static void report(ThreadPoolExecutor tpe) {
|
||||
try {
|
||||
System.out.printf(
|
||||
"active=%d submitted=%d completed=%d queued=%d sizes=%d/%d/%d%n",
|
||||
tg.activeCount(),
|
||||
tpe.getTaskCount(),
|
||||
tpe.getCompletedTaskCount(),
|
||||
tpe.getQueue().size(),
|
||||
tpe.getPoolSize(),
|
||||
tpe.getCorePoolSize(),
|
||||
tpe.getMaximumPoolSize());
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
}
|
||||
|
||||
static void report(String label, ThreadPoolExecutor tpe) {
|
||||
System.out.printf("%10s ", label);
|
||||
report(tpe);
|
||||
}
|
||||
|
||||
static void checkShutdown(final ExecutorService es) {
|
||||
final Runnable nop = new Runnable() {public void run() {}};
|
||||
try {
|
||||
if (new Random().nextBoolean()) {
|
||||
check(es.isShutdown());
|
||||
if (es instanceof ThreadPoolExecutor)
|
||||
check(((ThreadPoolExecutor) es).isTerminating()
|
||||
|| es.isTerminated());
|
||||
THROWS(RejectedExecutionException.class,
|
||||
() -> es.execute(nop));
|
||||
}
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
}
|
||||
|
||||
static void checkTerminated(final ThreadPoolExecutor tpe) {
|
||||
try {
|
||||
checkShutdown(tpe);
|
||||
check(tpe.getQueue().isEmpty());
|
||||
check(tpe.isTerminated());
|
||||
check(! tpe.isTerminating());
|
||||
equal(0, tpe.getActiveCount());
|
||||
equal(0, tpe.getPoolSize());
|
||||
equal(tpe.getTaskCount(), tpe.getCompletedTaskCount());
|
||||
check(tpe.awaitTermination(0L, MINUTES));
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
}
|
||||
|
||||
static Runnable waiter(final CyclicBarrier barrier) {
|
||||
return new Runnable() { public void run() {
|
||||
try { barrier.await(); barrier.await(); }
|
||||
catch (Throwable t) { unexpected(t); }}};
|
||||
}
|
||||
|
||||
static volatile Runnable runnableDuJour;
|
||||
|
||||
static void awaitIdleness(ThreadPoolExecutor tpe, long taskCount) {
|
||||
restart: for (;;) {
|
||||
// check twice to make chance of race vanishingly small
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (tpe.getQueue().size() != 0 ||
|
||||
tpe.getActiveCount() != 0 ||
|
||||
tpe.getCompletedTaskCount() != taskCount) {
|
||||
Thread.yield();
|
||||
continue restart;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for condition to become true, first spin-polling, then sleep-polling.
|
||||
*/
|
||||
static void spinAwait(Supplier<Boolean> waitingForGodot) {
|
||||
for (int spins = 0; !waitingForGodot.get(); ) {
|
||||
if ((spins = (spins + 1) & 3) > 0) {
|
||||
Thread.yield();
|
||||
} else {
|
||||
try { Thread.sleep(4); }
|
||||
catch (InterruptedException unexpected) {
|
||||
throw new AssertionError(unexpected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void realMain(String[] args) throws Throwable {
|
||||
final boolean prestart = rnd.nextBoolean();
|
||||
|
||||
final Thread.UncaughtExceptionHandler handler
|
||||
= new Thread.UncaughtExceptionHandler() {
|
||||
public void uncaughtException(Thread t, Throwable e) {
|
||||
check(! Thread.currentThread().isInterrupted());
|
||||
unexpected(e);
|
||||
}};
|
||||
|
||||
final int n = 3;
|
||||
final ThreadPoolExecutor tpe
|
||||
= new ThreadPoolExecutor(n, 3*n,
|
||||
3L, MINUTES,
|
||||
new ArrayBlockingQueue<Runnable>(3*n));
|
||||
tpe.setThreadFactory(new ThreadFactory() {
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread t = new Thread(tg, r);
|
||||
t.setUncaughtExceptionHandler(handler);
|
||||
return t;
|
||||
}});
|
||||
|
||||
if (prestart) {
|
||||
tpe.prestartAllCoreThreads();
|
||||
equal(n, tg.activeCount());
|
||||
equal(n, tpe.getCorePoolSize());
|
||||
equal(n, tpe.getLargestPoolSize());
|
||||
}
|
||||
|
||||
final Runnable runRunnableDuJour =
|
||||
new Runnable() { public void run() {
|
||||
// Delay choice of action till last possible moment.
|
||||
runnableDuJour.run(); }};
|
||||
final CyclicBarrier pumpedUp = new CyclicBarrier(3*n + 1);
|
||||
runnableDuJour = waiter(pumpedUp);
|
||||
|
||||
if (prestart) {
|
||||
for (int i = 0; i < 1*n; i++)
|
||||
tpe.execute(runRunnableDuJour);
|
||||
// Wait for prestarted threads to dequeue their initial tasks.
|
||||
while (! tpe.getQueue().isEmpty())
|
||||
Thread.sleep(1);
|
||||
for (int i = 0; i < 5*n; i++)
|
||||
tpe.execute(runRunnableDuJour);
|
||||
} else {
|
||||
for (int i = 0; i < 6*n; i++)
|
||||
tpe.execute(runRunnableDuJour);
|
||||
}
|
||||
|
||||
//report("submitted", tpe);
|
||||
pumpedUp.await();
|
||||
equal(3*n, tg.activeCount());
|
||||
equal(3*n, tpe.getMaximumPoolSize());
|
||||
equal(3*n, tpe.getLargestPoolSize());
|
||||
equal(n, tpe.getCorePoolSize());
|
||||
equal(3*n, tpe.getActiveCount());
|
||||
equal(6L*n, tpe.getTaskCount());
|
||||
equal(0L, tpe.getCompletedTaskCount());
|
||||
|
||||
//report("pumped up", tpe);
|
||||
tpe.setMaximumPoolSize(4*n);
|
||||
equal(4*n, tpe.getMaximumPoolSize());
|
||||
//report("pumped up2", tpe);
|
||||
final CyclicBarrier pumpedUp2 = new CyclicBarrier(n + 1);
|
||||
runnableDuJour = waiter(pumpedUp2);
|
||||
for (int i = 0; i < 1*n; i++)
|
||||
tpe.execute(runRunnableDuJour);
|
||||
pumpedUp2.await();
|
||||
equal(4*n, tg.activeCount());
|
||||
equal(4*n, tpe.getMaximumPoolSize());
|
||||
equal(4*n, tpe.getLargestPoolSize());
|
||||
equal(4*n, tpe.getActiveCount());
|
||||
equal(7L*n, tpe.getTaskCount());
|
||||
equal(0L, tpe.getCompletedTaskCount());
|
||||
//report("pumped up2", tpe);
|
||||
runnableDuJour = new Runnable() { public void run() {}};
|
||||
|
||||
tpe.setMaximumPoolSize(2*n);
|
||||
//report("after setMaximumPoolSize", tpe);
|
||||
|
||||
pumpedUp2.await();
|
||||
pumpedUp.await();
|
||||
|
||||
spinAwait(() -> tg.activeCount() == 2*n);
|
||||
equal(2*n, tpe.getMaximumPoolSize());
|
||||
equal(4*n, tpe.getLargestPoolSize());
|
||||
|
||||
//report("draining", tpe);
|
||||
awaitIdleness(tpe, 7L*n);
|
||||
|
||||
equal(2*n, tg.activeCount());
|
||||
equal(2*n, tpe.getMaximumPoolSize());
|
||||
equal(4*n, tpe.getLargestPoolSize());
|
||||
|
||||
equal(7L*n, tpe.getTaskCount());
|
||||
equal(7L*n, tpe.getCompletedTaskCount());
|
||||
equal(0, tpe.getActiveCount());
|
||||
|
||||
equal(3L, tpe.getKeepAliveTime(MINUTES));
|
||||
long t0 = System.nanoTime();
|
||||
tpe.setKeepAliveTime(7L, MILLISECONDS);
|
||||
equal(7L, tpe.getKeepAliveTime(MILLISECONDS));
|
||||
spinAwait(() -> tg.activeCount() == n);
|
||||
check(System.nanoTime() - t0 >= tpe.getKeepAliveTime(NANOSECONDS));
|
||||
|
||||
//report("idle", tpe);
|
||||
check(! tpe.allowsCoreThreadTimeOut());
|
||||
t0 = System.nanoTime();
|
||||
tpe.allowCoreThreadTimeOut(true);
|
||||
check(tpe.allowsCoreThreadTimeOut());
|
||||
spinAwait(() -> tg.activeCount() == 0);
|
||||
|
||||
// The following assertion is almost always true, but may
|
||||
// exceptionally not be during a transition from core count
|
||||
// too high to allowCoreThreadTimeOut. Users will never
|
||||
// notice, and we accept the small loss of testability.
|
||||
//
|
||||
// check(System.nanoTime() - t0 >= tpe.getKeepAliveTime(NANOSECONDS));
|
||||
|
||||
//report("idle", tpe);
|
||||
|
||||
tpe.shutdown();
|
||||
checkShutdown(tpe);
|
||||
check(tpe.awaitTermination(3L, MINUTES));
|
||||
checkTerminated(tpe);
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
static volatile int passed = 0, failed = 0;
|
||||
static void pass() {passed++;}
|
||||
static void fail() {failed++; Thread.dumpStack();}
|
||||
static void fail(String msg) {System.out.println(msg); fail();}
|
||||
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
static void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
static void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
try {realMain(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
interface Fun {void f() throws Throwable;}
|
||||
static void THROWS(Class<? extends Throwable> k, Fun... fs) {
|
||||
for (Fun f : fs)
|
||||
try { f.f(); fail("Expected " + k.getName() + " not thrown"); }
|
||||
catch (Throwable t) {
|
||||
if (k.isAssignableFrom(t.getClass())) pass();
|
||||
else unexpected(t);}}
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6233235 6268386
|
||||
* @summary Test allowsCoreThreadTimeOut
|
||||
* @library /test/lib
|
||||
* @author Martin Buchholz
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
public class CoreThreadTimeOut {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
|
||||
static class IdentifiableThreadFactory implements ThreadFactory {
|
||||
static ThreadFactory defaultThreadFactory
|
||||
= Executors.defaultThreadFactory();
|
||||
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread t = defaultThreadFactory.newThread(r);
|
||||
t.setName("CoreThreadTimeOut-" + t.getName());
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
int countExecutorThreads() {
|
||||
Thread[] threads = new Thread[Thread.activeCount()+100];
|
||||
Thread.enumerate(threads);
|
||||
int count = 0;
|
||||
for (Thread t : threads)
|
||||
if (t != null &&
|
||||
t.getName().matches
|
||||
("CoreThreadTimeOut-pool-[0-9]+-thread-[0-9]+"))
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
static long millisElapsedSince(long startTime) {
|
||||
return (System.nanoTime() - startTime) / (1000L * 1000L);
|
||||
}
|
||||
|
||||
void test(String[] args) throws Throwable {
|
||||
final int threadCount = 10;
|
||||
final int timeoutMillis = 30;
|
||||
BlockingQueue<Runnable> q = new ArrayBlockingQueue<>(2*threadCount);
|
||||
ThreadPoolExecutor tpe
|
||||
= new ThreadPoolExecutor(threadCount, threadCount,
|
||||
timeoutMillis, TimeUnit.MILLISECONDS,
|
||||
q, new IdentifiableThreadFactory());
|
||||
equal(tpe.getCorePoolSize(), threadCount);
|
||||
check(! tpe.allowsCoreThreadTimeOut());
|
||||
tpe.allowCoreThreadTimeOut(true);
|
||||
check(tpe.allowsCoreThreadTimeOut());
|
||||
equal(countExecutorThreads(), 0);
|
||||
long startTime = System.nanoTime();
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
tpe.submit(() -> {});
|
||||
int count = countExecutorThreads();
|
||||
if (millisElapsedSince(startTime) < timeoutMillis)
|
||||
equal(count, i + 1);
|
||||
}
|
||||
while (countExecutorThreads() > 0 &&
|
||||
millisElapsedSince(startTime) < LONG_DELAY_MS)
|
||||
Thread.yield();
|
||||
equal(countExecutorThreads(), 0);
|
||||
check(millisElapsedSince(startTime) >= timeoutMillis);
|
||||
tpe.shutdown();
|
||||
check(tpe.allowsCoreThreadTimeOut());
|
||||
check(tpe.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
|
||||
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new Exception("Some tests failed");
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new CoreThreadTimeOut().instanceMain(args);}
|
||||
public void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
146
test/jdk/java/util/concurrent/ThreadPoolExecutor/Custom.java
Normal file
146
test/jdk/java/util/concurrent/ThreadPoolExecutor/Custom.java
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6277663
|
||||
* @summary Test TPE extensibility framework
|
||||
* @library /test/lib
|
||||
* @author Martin Buchholz
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.RunnableFuture;
|
||||
import java.util.concurrent.RunnableScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
public class Custom {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
static volatile int passed = 0, failed = 0;
|
||||
static void pass() { passed++; }
|
||||
static void fail() { failed++; Thread.dumpStack(); }
|
||||
static void unexpected(Throwable t) { failed++; t.printStackTrace(); }
|
||||
static void check(boolean cond) { if (cond) pass(); else fail(); }
|
||||
static void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else {System.out.println(x + " not equal to " + y); fail(); }}
|
||||
|
||||
private static class CustomTask<V> extends FutureTask<V> {
|
||||
public static final AtomicInteger births = new AtomicInteger(0);
|
||||
CustomTask(Callable<V> c) { super(c); births.getAndIncrement(); }
|
||||
CustomTask(Runnable r, V v) { super(r, v); births.getAndIncrement(); }
|
||||
}
|
||||
|
||||
private static class CustomTPE extends ThreadPoolExecutor {
|
||||
CustomTPE() {
|
||||
super(threadCount, threadCount,
|
||||
30, TimeUnit.MILLISECONDS,
|
||||
new ArrayBlockingQueue<Runnable>(2*threadCount));
|
||||
}
|
||||
protected <V> RunnableFuture<V> newTaskFor(Callable<V> c) {
|
||||
return new CustomTask<V>(c);
|
||||
}
|
||||
protected <V> RunnableFuture<V> newTaskFor(Runnable r, V v) {
|
||||
return new CustomTask<V>(r, v);
|
||||
}
|
||||
}
|
||||
|
||||
private static class CustomSTPE extends ScheduledThreadPoolExecutor {
|
||||
public static final AtomicInteger decorations = new AtomicInteger(0);
|
||||
CustomSTPE() {
|
||||
super(threadCount);
|
||||
}
|
||||
protected <V> RunnableScheduledFuture<V> decorateTask(
|
||||
Runnable r, RunnableScheduledFuture<V> task) {
|
||||
decorations.getAndIncrement();
|
||||
return task;
|
||||
}
|
||||
protected <V> RunnableScheduledFuture<V> decorateTask(
|
||||
Callable<V> c, RunnableScheduledFuture<V> task) {
|
||||
decorations.getAndIncrement();
|
||||
return task;
|
||||
}
|
||||
}
|
||||
|
||||
static int countExecutorThreads() {
|
||||
Thread[] threads = new Thread[Thread.activeCount()+100];
|
||||
Thread.enumerate(threads);
|
||||
int count = 0;
|
||||
for (Thread t : threads)
|
||||
if (t != null && t.getName().matches("pool-[0-9]+-thread-[0-9]+"))
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
private static final int threadCount = 10;
|
||||
|
||||
static long millisElapsedSince(long startTime) {
|
||||
return (System.nanoTime() - startTime) / (1000L * 1000L);
|
||||
}
|
||||
|
||||
static void spinWaitUntil(BooleanSupplier predicate, long timeoutMillis) {
|
||||
long startTime = -1L;
|
||||
while (!predicate.getAsBoolean()) {
|
||||
if (startTime == -1L)
|
||||
startTime = System.nanoTime();
|
||||
else if (millisElapsedSince(startTime) > timeoutMillis)
|
||||
throw new AssertionError(
|
||||
String.format("timed out after %s ms", timeoutMillis));
|
||||
Thread.yield();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
CustomTPE tpe = new CustomTPE();
|
||||
equal(tpe.getCorePoolSize(), threadCount);
|
||||
equal(countExecutorThreads(), 0);
|
||||
for (int i = 0; i < threadCount; i++)
|
||||
tpe.submit(new Runnable() { public void run() {}});
|
||||
equal(countExecutorThreads(), threadCount);
|
||||
equal(CustomTask.births.get(), threadCount);
|
||||
tpe.shutdown();
|
||||
tpe.awaitTermination(LONG_DELAY_MS, MILLISECONDS);
|
||||
spinWaitUntil(() -> countExecutorThreads() == 0, LONG_DELAY_MS);
|
||||
|
||||
CustomSTPE stpe = new CustomSTPE();
|
||||
for (int i = 0; i < threadCount; i++)
|
||||
stpe.submit(new Runnable() { public void run() {}});
|
||||
equal(CustomSTPE.decorations.get(), threadCount);
|
||||
equal(countExecutorThreads(), threadCount);
|
||||
stpe.shutdown();
|
||||
stpe.awaitTermination(LONG_DELAY_MS, MILLISECONDS);
|
||||
spinWaitUntil(() -> countExecutorThreads() == 0, LONG_DELAY_MS);
|
||||
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new Exception("Some tests failed");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file:
|
||||
*
|
||||
* Written by Martin Buchholz and Doug Lea 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/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Should be able to shutdown a pool when worker creation failed.
|
||||
* @library /test/lib
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
public class FlakyThreadFactory {
|
||||
static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
|
||||
|
||||
void test(String[] args) throws Throwable {
|
||||
test(NullPointerException.class,
|
||||
new ThreadFactory() {
|
||||
public Thread newThread(Runnable r) {
|
||||
throw new NullPointerException();
|
||||
}});
|
||||
test(OutOfMemoryError.class,
|
||||
new ThreadFactory() {
|
||||
@SuppressWarnings("DeadThread")
|
||||
public Thread newThread(Runnable r) {
|
||||
// We expect this to throw OOME, but ...
|
||||
new Thread(null, r, "a natural OOME", 1L << 60);
|
||||
// """On some platforms, the value of the stackSize
|
||||
// parameter may have no effect whatsoever."""
|
||||
throw new OutOfMemoryError("artificial OOME");
|
||||
}});
|
||||
test(null,
|
||||
new ThreadFactory() {
|
||||
public Thread newThread(Runnable r) {
|
||||
return null;
|
||||
}});
|
||||
}
|
||||
|
||||
void test(final Class<?> exceptionClass,
|
||||
final ThreadFactory failingThreadFactory)
|
||||
throws Throwable {
|
||||
ThreadFactory flakyThreadFactory = new ThreadFactory() {
|
||||
int seq = 0;
|
||||
public Thread newThread(Runnable r) {
|
||||
if (seq++ < 4)
|
||||
return new Thread(r);
|
||||
else
|
||||
return failingThreadFactory.newThread(r);
|
||||
}};
|
||||
ThreadPoolExecutor pool =
|
||||
new ThreadPoolExecutor(10, 10,
|
||||
0L, TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue(),
|
||||
flakyThreadFactory);
|
||||
try {
|
||||
for (int i = 0; i < 8; i++)
|
||||
pool.submit(new Runnable() { public void run() {} });
|
||||
check(exceptionClass == null);
|
||||
} catch (Throwable t) {
|
||||
/* t.printStackTrace(); */
|
||||
check(exceptionClass.isInstance(t));
|
||||
}
|
||||
pool.shutdown();
|
||||
check(pool.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
volatile int passed = 0, failed = 0;
|
||||
void pass() {passed++;}
|
||||
void fail() {failed++; Thread.dumpStack();}
|
||||
void fail(String msg) {System.err.println(msg); fail();}
|
||||
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
new FlakyThreadFactory().instanceMain(args);}
|
||||
public void instanceMain(String[] args) throws Throwable {
|
||||
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6522773
|
||||
* @summary Test changes to STPE core pool size
|
||||
* @author Martin Buchholz
|
||||
*/
|
||||
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ModifyCorePoolSize {
|
||||
static void awaitPoolSize(ThreadPoolExecutor pool, int n) {
|
||||
while (pool.getPoolSize() != n) Thread.yield();
|
||||
pass();
|
||||
}
|
||||
|
||||
static void setCorePoolSize(ThreadPoolExecutor pool, int n) {
|
||||
pool.setCorePoolSize(n);
|
||||
equal(pool.getCorePoolSize(), n);
|
||||
awaitPoolSize(pool, n);
|
||||
}
|
||||
|
||||
static void realMain(String[] args) throws Throwable {
|
||||
final int size = 10;
|
||||
final ScheduledThreadPoolExecutor pool
|
||||
= new ScheduledThreadPoolExecutor(size);
|
||||
final Runnable nop = new Runnable() { public void run() {}};
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
pool.scheduleAtFixedRate(nop, 100L * (i + 1),
|
||||
1000L, TimeUnit.MILLISECONDS);
|
||||
awaitPoolSize(pool, size);
|
||||
setCorePoolSize(pool, size - 3);
|
||||
setCorePoolSize(pool, size + 3);
|
||||
pool.shutdownNow();
|
||||
check(pool.awaitTermination(1L, TimeUnit.DAYS));
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
static volatile int passed = 0, failed = 0;
|
||||
static void pass() {passed++;}
|
||||
static void fail() {failed++; Thread.dumpStack();}
|
||||
static void fail(String msg) {System.out.println(msg); fail();}
|
||||
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
static void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
static void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
try {realMain(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6362121
|
||||
* @summary Test one ScheduledThreadPoolExecutor extension scenario
|
||||
* @author Martin Buchholz
|
||||
*/
|
||||
|
||||
// based on a test kindly provided by Holger Hoffstaette <holger@wizards.de>
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Delayed;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.RunnableScheduledFuture;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
public class ScheduledTickleService {
|
||||
|
||||
// We get intermittent ClassCastException if greater than 1
|
||||
// because of calls to compareTo
|
||||
private static final int concurrency = 2;
|
||||
|
||||
// Record when tasks are done
|
||||
public static final CountDownLatch done = new CountDownLatch(concurrency);
|
||||
|
||||
public static void realMain(String... args) throws InterruptedException {
|
||||
// our tickle service
|
||||
ScheduledExecutorService tickleService =
|
||||
new ScheduledThreadPoolExecutor(concurrency) {
|
||||
// We override decorateTask() to return a custom
|
||||
// RunnableScheduledFuture which explicitly removes
|
||||
// itself from the queue after cancellation.
|
||||
protected <V> RunnableScheduledFuture<V>
|
||||
decorateTask(Runnable runnable,
|
||||
RunnableScheduledFuture<V> task) {
|
||||
final ScheduledThreadPoolExecutor exec = this;
|
||||
return new CustomRunnableScheduledFuture<V>(task) {
|
||||
// delegate to wrapped task, except for:
|
||||
public boolean cancel(boolean b) {
|
||||
// cancel wrapped task & remove myself from the queue
|
||||
return (task().cancel(b)
|
||||
&& exec.remove(this));}};}};
|
||||
|
||||
for (int i = 0; i < concurrency; i++)
|
||||
new ScheduledTickle(i, tickleService)
|
||||
.setUpdateInterval(25, MILLISECONDS);
|
||||
|
||||
done.await();
|
||||
tickleService.shutdown();
|
||||
pass();
|
||||
}
|
||||
|
||||
// our Runnable
|
||||
static class ScheduledTickle implements Runnable {
|
||||
public volatile int failures = 0;
|
||||
|
||||
// my tickle service
|
||||
private final ScheduledExecutorService service;
|
||||
|
||||
// remember my own scheduled ticket
|
||||
private ScheduledFuture ticket = null;
|
||||
|
||||
// remember the number of times I've been tickled
|
||||
private int numTickled = 0;
|
||||
|
||||
// my private name
|
||||
private final String name;
|
||||
|
||||
public ScheduledTickle(int i, ScheduledExecutorService service) {
|
||||
super();
|
||||
this.name = "Tickler-"+i;
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
// set my tickle interval; 0 to disable further tickling.
|
||||
public synchronized void setUpdateInterval(long interval,
|
||||
TimeUnit unit) {
|
||||
// cancel & remove previously created ticket
|
||||
if (ticket != null) {
|
||||
ticket.cancel(false);
|
||||
ticket = null;
|
||||
}
|
||||
|
||||
if (interval > 0 && ! service.isShutdown()) {
|
||||
// requeue with new interval
|
||||
ticket = service.scheduleAtFixedRate(this, interval,
|
||||
interval, unit);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void run() {
|
||||
try {
|
||||
check(numTickled < 6);
|
||||
numTickled++;
|
||||
System.out.println(name + ": Run " + numTickled);
|
||||
|
||||
// tickle 3 times and then slow down
|
||||
if (numTickled == 3) {
|
||||
System.out.println(name + ": slower please!");
|
||||
this.setUpdateInterval(100, MILLISECONDS);
|
||||
}
|
||||
// ..but only 5 times max.
|
||||
else if (numTickled == 5) {
|
||||
System.out.println(name + ": OK that's enough.");
|
||||
this.setUpdateInterval(0, MILLISECONDS);
|
||||
ScheduledTickleService.done.countDown();
|
||||
}
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
}
|
||||
}
|
||||
|
||||
// This is just a generic wrapper to make up for the private ScheduledFutureTask
|
||||
static class CustomRunnableScheduledFuture<V>
|
||||
implements RunnableScheduledFuture<V> {
|
||||
// the wrapped future
|
||||
private RunnableScheduledFuture<V> task;
|
||||
|
||||
public CustomRunnableScheduledFuture(RunnableScheduledFuture<V> task) {
|
||||
super();
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
public RunnableScheduledFuture<V> task() { return task; }
|
||||
|
||||
// Forwarding methods
|
||||
public boolean isPeriodic() { return task.isPeriodic(); }
|
||||
public boolean isCancelled() { return task.isCancelled(); }
|
||||
public boolean isDone() { return task.isDone(); }
|
||||
public boolean cancel(boolean b) { return task.cancel(b); }
|
||||
public long getDelay(TimeUnit unit) { return task.getDelay(unit); }
|
||||
public void run() { task.run(); }
|
||||
|
||||
public V get()
|
||||
throws InterruptedException, ExecutionException {
|
||||
return task.get();
|
||||
}
|
||||
|
||||
public V get(long timeout, TimeUnit unit)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
return task.get(timeout, unit);
|
||||
}
|
||||
|
||||
public int compareTo(Delayed other) {
|
||||
if (this == other)
|
||||
return 0;
|
||||
else if (other instanceof CustomRunnableScheduledFuture)
|
||||
return task.compareTo(((CustomRunnableScheduledFuture)other).task());
|
||||
else
|
||||
return task.compareTo(other);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
static volatile int passed = 0, failed = 0;
|
||||
static void pass() {passed++;}
|
||||
static void fail() {failed++; Thread.dumpStack();}
|
||||
static void fail(String msg) {System.out.println(msg); fail();}
|
||||
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||
static void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
static void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(x + " not equal to " + y);}
|
||||
public static void main(String[] args) throws Throwable {
|
||||
try {realMain(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue