Merge "JSR-166 update without java 1.9 method/classes"
diff --git a/JavaLibrary.mk b/JavaLibrary.mk
index 21ad635..756ec02 100644
--- a/JavaLibrary.mk
+++ b/JavaLibrary.mk
@@ -215,7 +215,7 @@
LOCAL_SRC_FILES := $(call all-test-java-files-under, jsr166-tests)
LOCAL_JAVA_RESOURCE_DIRS := $(test_resource_dirs)
LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := core-oj core-libart core-junit
+LOCAL_JAVA_LIBRARIES := core-oj core-libart core-lambda-stubs core-junit
LOCAL_JAVACFLAGS := $(local_javac_flags)
LOCAL_MODULE := jsr166-tests
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/JavaLibrary.mk
diff --git a/jsr166-tests/src/test/java/jsr166/AbstractExecutorServiceTest.java b/jsr166-tests/src/test/java/jsr166/AbstractExecutorServiceTest.java
index 9e83de2..c293f13 100644
--- a/jsr166-tests/src/test/java/jsr166/AbstractExecutorServiceTest.java
+++ b/jsr166-tests/src/test/java/jsr166/AbstractExecutorServiceTest.java
@@ -18,6 +18,7 @@
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
@@ -38,7 +39,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(AbstractExecutorServiceTest.class);
// }
/**
@@ -196,28 +197,25 @@
public void testInterruptedSubmit() throws InterruptedException {
final CountDownLatch submitted = new CountDownLatch(1);
final CountDownLatch quittingTime = new CountDownLatch(1);
+ final Callable<Void> awaiter = new CheckedCallable<Void>() {
+ public Void realCall() throws InterruptedException {
+ assertTrue(quittingTime.await(2*LONG_DELAY_MS, MILLISECONDS));
+ return null;
+ }};
final ExecutorService p
= new ThreadPoolExecutor(1,1,60, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(10));
- final Callable<Void> awaiter = new CheckedCallable<Void>() {
- public Void realCall() throws InterruptedException {
- quittingTime.await();
- return null;
- }};
- try {
- Thread t = new Thread(new CheckedInterruptedRunnable() {
+ try (PoolCleaner cleaner = cleaner(p, quittingTime)) {
+ Thread t = newStartedThread(new CheckedInterruptedRunnable() {
public void realRun() throws Exception {
Future<Void> future = p.submit(awaiter);
submitted.countDown();
future.get();
}});
- t.start();
- submitted.await();
+
+ await(submitted);
t.interrupt();
- t.join();
- } finally {
- quittingTime.countDown();
- joinPool(p);
+ awaitTermination(t);
}
}
@@ -226,34 +224,32 @@
* throws exception
*/
public void testSubmitEE() throws InterruptedException {
- ThreadPoolExecutor p =
+ final ThreadPoolExecutor p =
new ThreadPoolExecutor(1, 1,
60, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(10));
-
- Callable c = new Callable() {
- public Object call() { throw new ArithmeticException(); }};
-
- try {
- p.submit(c).get();
- shouldThrow();
- } catch (ExecutionException success) {
- assertTrue(success.getCause() instanceof ArithmeticException);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ Callable c = new Callable() {
+ public Object call() { throw new ArithmeticException(); }};
+ try {
+ p.submit(c).get();
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertTrue(success.getCause() instanceof ArithmeticException);
+ }
}
- joinPool(p);
}
/**
* invokeAny(null) throws NPE
*/
public void testInvokeAny1() throws Exception {
- ExecutorService e = new DirectExecutorService();
- try {
- e.invokeAny(null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new DirectExecutorService();
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAny(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -261,13 +257,12 @@
* invokeAny(empty collection) throws IAE
*/
public void testInvokeAny2() throws Exception {
- ExecutorService e = new DirectExecutorService();
- try {
- e.invokeAny(new ArrayList<Callable<String>>());
- shouldThrow();
- } catch (IllegalArgumentException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new DirectExecutorService();
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAny(new ArrayList<Callable<String>>());
+ shouldThrow();
+ } catch (IllegalArgumentException success) {}
}
}
@@ -275,17 +270,16 @@
* invokeAny(c) throws NPE if c has null elements
*/
public void testInvokeAny3() throws Exception {
- ExecutorService e = new DirectExecutorService();
- List<Callable<Long>> l = new ArrayList<Callable<Long>>();
- l.add(new Callable<Long>() {
- public Long call() { throw new ArithmeticException(); }});
- l.add(null);
- try {
- e.invokeAny(l);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new DirectExecutorService();
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<Long>> l = new ArrayList<Callable<Long>>();
+ l.add(new Callable<Long>() {
+ public Long call() { throw new ArithmeticException(); }});
+ l.add(null);
+ try {
+ e.invokeAny(l);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -293,16 +287,16 @@
* invokeAny(c) throws ExecutionException if no task in c completes
*/
public void testInvokeAny4() throws InterruptedException {
- ExecutorService e = new DirectExecutorService();
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new NPETask());
- try {
- e.invokeAny(l);
- shouldThrow();
- } catch (ExecutionException success) {
- assertTrue(success.getCause() instanceof NullPointerException);
- } finally {
- joinPool(e);
+ final ExecutorService e = new DirectExecutorService();
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new NPETask());
+ try {
+ e.invokeAny(l);
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertTrue(success.getCause() instanceof NullPointerException);
+ }
}
}
@@ -310,15 +304,13 @@
* invokeAny(c) returns result of some task in c if at least one completes
*/
public void testInvokeAny5() throws Exception {
- ExecutorService e = new DirectExecutorService();
- try {
+ final ExecutorService e = new DirectExecutorService();
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
String result = e.invokeAny(l);
assertSame(TEST_STRING, result);
- } finally {
- joinPool(e);
}
}
@@ -326,13 +318,12 @@
* invokeAll(null) throws NPE
*/
public void testInvokeAll1() throws InterruptedException {
- ExecutorService e = new DirectExecutorService();
- try {
- e.invokeAll(null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new DirectExecutorService();
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAll(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -340,12 +331,10 @@
* invokeAll(empty collection) returns empty collection
*/
public void testInvokeAll2() throws InterruptedException {
- ExecutorService e = new DirectExecutorService();
- try {
+ final ExecutorService e = new DirectExecutorService();
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>());
assertTrue(r.isEmpty());
- } finally {
- joinPool(e);
}
}
@@ -353,16 +342,15 @@
* invokeAll(c) throws NPE if c has null elements
*/
public void testInvokeAll3() throws InterruptedException {
- ExecutorService e = new DirectExecutorService();
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- l.add(null);
- try {
- e.invokeAll(l);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new DirectExecutorService();
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new StringTask());
+ l.add(null);
+ try {
+ e.invokeAll(l);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -370,8 +358,8 @@
* get of returned element of invokeAll(c) throws exception on failed task
*/
public void testInvokeAll4() throws Exception {
- ExecutorService e = new DirectExecutorService();
- try {
+ final ExecutorService e = new DirectExecutorService();
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new NPETask());
List<Future<String>> futures = e.invokeAll(l);
@@ -382,8 +370,6 @@
} catch (ExecutionException success) {
assertTrue(success.getCause() instanceof NullPointerException);
}
- } finally {
- joinPool(e);
}
}
@@ -391,8 +377,8 @@
* invokeAll(c) returns results of all completed tasks in c
*/
public void testInvokeAll5() throws Exception {
- ExecutorService e = new DirectExecutorService();
- try {
+ final ExecutorService e = new DirectExecutorService();
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
@@ -400,8 +386,6 @@
assertEquals(2, futures.size());
for (Future<String> future : futures)
assertSame(TEST_STRING, future.get());
- } finally {
- joinPool(e);
}
}
@@ -409,13 +393,12 @@
* timed invokeAny(null) throws NPE
*/
public void testTimedInvokeAny1() throws Exception {
- ExecutorService e = new DirectExecutorService();
- try {
- e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new DirectExecutorService();
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -423,15 +406,14 @@
* timed invokeAny(null time unit) throws NPE
*/
public void testTimedInvokeAnyNullTimeUnit() throws Exception {
- ExecutorService e = new DirectExecutorService();
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- try {
- e.invokeAny(l, MEDIUM_DELAY_MS, null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new DirectExecutorService();
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new StringTask());
+ try {
+ e.invokeAny(l, MEDIUM_DELAY_MS, null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -439,13 +421,13 @@
* timed invokeAny(empty collection) throws IAE
*/
public void testTimedInvokeAny2() throws Exception {
- ExecutorService e = new DirectExecutorService();
- try {
- e.invokeAny(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (IllegalArgumentException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new DirectExecutorService();
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAny(new ArrayList<Callable<String>>(),
+ MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (IllegalArgumentException success) {}
}
}
@@ -453,17 +435,16 @@
* timed invokeAny(c) throws NPE if c has null elements
*/
public void testTimedInvokeAny3() throws Exception {
- ExecutorService e = new DirectExecutorService();
- List<Callable<Long>> l = new ArrayList<Callable<Long>>();
- l.add(new Callable<Long>() {
- public Long call() { throw new ArithmeticException(); }});
- l.add(null);
- try {
- e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new DirectExecutorService();
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<Long>> l = new ArrayList<Callable<Long>>();
+ l.add(new Callable<Long>() {
+ public Long call() { throw new ArithmeticException(); }});
+ l.add(null);
+ try {
+ e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -471,16 +452,18 @@
* timed invokeAny(c) throws ExecutionException if no task completes
*/
public void testTimedInvokeAny4() throws Exception {
- ExecutorService e = new DirectExecutorService();
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new NPETask());
- try {
- e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (ExecutionException success) {
- assertTrue(success.getCause() instanceof NullPointerException);
- } finally {
- joinPool(e);
+ final ExecutorService e = new DirectExecutorService();
+ try (PoolCleaner cleaner = cleaner(e)) {
+ long startTime = System.nanoTime();
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new NPETask());
+ try {
+ e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertTrue(success.getCause() instanceof NullPointerException);
+ }
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}
}
@@ -488,15 +471,15 @@
* timed invokeAny(c) returns result of some task in c
*/
public void testTimedInvokeAny5() throws Exception {
- ExecutorService e = new DirectExecutorService();
- try {
+ final ExecutorService e = new DirectExecutorService();
+ try (PoolCleaner cleaner = cleaner(e)) {
+ long startTime = System.nanoTime();
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
- String result = e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
assertSame(TEST_STRING, result);
- } finally {
- joinPool(e);
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}
}
@@ -504,13 +487,12 @@
* timed invokeAll(null) throws NPE
*/
public void testTimedInvokeAll1() throws InterruptedException {
- ExecutorService e = new DirectExecutorService();
- try {
- e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new DirectExecutorService();
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -518,15 +500,14 @@
* timed invokeAll(null time unit) throws NPE
*/
public void testTimedInvokeAllNullTimeUnit() throws InterruptedException {
- ExecutorService e = new DirectExecutorService();
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- try {
- e.invokeAll(l, MEDIUM_DELAY_MS, null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new DirectExecutorService();
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new StringTask());
+ try {
+ e.invokeAll(l, MEDIUM_DELAY_MS, null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -534,12 +515,10 @@
* timed invokeAll(empty collection) returns empty collection
*/
public void testTimedInvokeAll2() throws InterruptedException {
- ExecutorService e = new DirectExecutorService();
- try {
+ final ExecutorService e = new DirectExecutorService();
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
assertTrue(r.isEmpty());
- } finally {
- joinPool(e);
}
}
@@ -547,16 +526,15 @@
* timed invokeAll(c) throws NPE if c has null elements
*/
public void testTimedInvokeAll3() throws InterruptedException {
- ExecutorService e = new DirectExecutorService();
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- l.add(null);
- try {
- e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new DirectExecutorService();
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new StringTask());
+ l.add(null);
+ try {
+ e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -564,12 +542,12 @@
* get of returned element of invokeAll(c) throws exception on failed task
*/
public void testTimedInvokeAll4() throws Exception {
- ExecutorService e = new DirectExecutorService();
- try {
+ final ExecutorService e = new DirectExecutorService();
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new NPETask());
List<Future<String>> futures =
- e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
assertEquals(1, futures.size());
try {
futures.get(0).get();
@@ -577,8 +555,6 @@
} catch (ExecutionException success) {
assertTrue(success.getCause() instanceof NullPointerException);
}
- } finally {
- joinPool(e);
}
}
@@ -586,41 +562,51 @@
* timed invokeAll(c) returns results of all completed tasks in c
*/
public void testTimedInvokeAll5() throws Exception {
- ExecutorService e = new DirectExecutorService();
- try {
+ final ExecutorService e = new DirectExecutorService();
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
List<Future<String>> futures =
- e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
assertEquals(2, futures.size());
for (Future<String> future : futures)
assertSame(TEST_STRING, future.get());
- } finally {
- joinPool(e);
}
}
/**
* timed invokeAll cancels tasks not completed by timeout
*/
- public void testTimedInvokeAll6() throws InterruptedException {
- ExecutorService e = new DirectExecutorService();
- try {
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- l.add(Executors.callable(possiblyInterruptedRunnable(2 * SHORT_DELAY_MS), TEST_STRING));
- l.add(new StringTask());
- List<Future<String>> futures =
- e.invokeAll(l, SHORT_DELAY_MS, MILLISECONDS);
- assertEquals(l.size(), futures.size());
- for (Future future : futures)
- assertTrue(future.isDone());
- assertFalse(futures.get(0).isCancelled());
- assertFalse(futures.get(1).isCancelled());
- assertTrue(futures.get(2).isCancelled());
- } finally {
- joinPool(e);
+ public void testTimedInvokeAll6() throws Exception {
+ final ExecutorService e = new DirectExecutorService();
+ try (PoolCleaner cleaner = cleaner(e)) {
+ for (long timeout = timeoutMillis();;) {
+ List<Callable<String>> tasks = new ArrayList<>();
+ tasks.add(new StringTask("0"));
+ tasks.add(Executors.callable(possiblyInterruptedRunnable(timeout),
+ TEST_STRING));
+ tasks.add(new StringTask("2"));
+ long startTime = System.nanoTime();
+ List<Future<String>> futures =
+ e.invokeAll(tasks, timeout, MILLISECONDS);
+ assertEquals(tasks.size(), futures.size());
+ assertTrue(millisElapsedSince(startTime) >= timeout);
+ for (Future future : futures)
+ assertTrue(future.isDone());
+ try {
+ assertEquals("0", futures.get(0).get());
+ assertEquals(TEST_STRING, futures.get(1).get());
+ } catch (CancellationException retryWithLongerTimeout) {
+ // unusual delay before starting second task
+ timeout *= 2;
+ if (timeout >= LONG_DELAY_MS / 2)
+ fail("expected exactly one task to be cancelled");
+ continue;
+ }
+ assertTrue(futures.get(2).isCancelled());
+ break;
+ }
}
}
diff --git a/jsr166-tests/src/test/java/jsr166/AbstractQueueTest.java b/jsr166-tests/src/test/java/jsr166/AbstractQueueTest.java
index 2aa7326..bf25668 100644
--- a/jsr166-tests/src/test/java/jsr166/AbstractQueueTest.java
+++ b/jsr166-tests/src/test/java/jsr166/AbstractQueueTest.java
@@ -24,7 +24,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(AbstractQueueTest.class);
// }
static class Succeed extends AbstractQueue<Integer> {
@@ -158,7 +158,7 @@
public void testAddAll3() {
Succeed q = new Succeed();
Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
+ for (int i = 0; i < SIZE - 1; ++i)
ints[i] = new Integer(i);
try {
q.addAll(Arrays.asList(ints));
diff --git a/jsr166-tests/src/test/java/jsr166/AbstractQueuedLongSynchronizerTest.java b/jsr166-tests/src/test/java/jsr166/AbstractQueuedLongSynchronizerTest.java
index 8604d86..c462c73 100644
--- a/jsr166-tests/src/test/java/jsr166/AbstractQueuedLongSynchronizerTest.java
+++ b/jsr166-tests/src/test/java/jsr166/AbstractQueuedLongSynchronizerTest.java
@@ -29,7 +29,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(AbstractQueuedLongSynchronizerTest.class);
// }
/**
@@ -241,25 +241,33 @@
*/
void assertAwaitTimesOut(ConditionObject c, AwaitMethod awaitMethod) {
long timeoutMillis = timeoutMillis();
- long startTime = System.nanoTime();
+ long startTime;
try {
switch (awaitMethod) {
case awaitTimed:
+ startTime = System.nanoTime();
assertFalse(c.await(timeoutMillis, MILLISECONDS));
+ assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
break;
case awaitNanos:
+ startTime = System.nanoTime();
long nanosTimeout = MILLISECONDS.toNanos(timeoutMillis);
long nanosRemaining = c.awaitNanos(nanosTimeout);
assertTrue(nanosRemaining <= 0);
+ assertTrue(nanosRemaining > -MILLISECONDS.toNanos(LONG_DELAY_MS));
+ assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
break;
case awaitUntil:
+ // We shouldn't assume that nanoTime and currentTimeMillis
+ // use the same time source, so don't use nanoTime here.
+ java.util.Date delayedDate = delayedDate(timeoutMillis());
assertFalse(c.awaitUntil(delayedDate(timeoutMillis)));
+ assertTrue(new java.util.Date().getTime() >= delayedDate.getTime());
break;
default:
throw new UnsupportedOperationException();
}
} catch (InterruptedException ie) { threadUnexpectedException(ie); }
- assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
}
/**
diff --git a/jsr166-tests/src/test/java/jsr166/AbstractQueuedSynchronizerTest.java b/jsr166-tests/src/test/java/jsr166/AbstractQueuedSynchronizerTest.java
index b3c4110..d102fc6 100644
--- a/jsr166-tests/src/test/java/jsr166/AbstractQueuedSynchronizerTest.java
+++ b/jsr166-tests/src/test/java/jsr166/AbstractQueuedSynchronizerTest.java
@@ -29,7 +29,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(AbstractQueuedSynchronizerTest.class);
// }
/**
@@ -244,25 +244,33 @@
*/
void assertAwaitTimesOut(ConditionObject c, AwaitMethod awaitMethod) {
long timeoutMillis = timeoutMillis();
- long startTime = System.nanoTime();
+ long startTime;
try {
switch (awaitMethod) {
case awaitTimed:
+ startTime = System.nanoTime();
assertFalse(c.await(timeoutMillis, MILLISECONDS));
+ assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
break;
case awaitNanos:
+ startTime = System.nanoTime();
long nanosTimeout = MILLISECONDS.toNanos(timeoutMillis);
long nanosRemaining = c.awaitNanos(nanosTimeout);
assertTrue(nanosRemaining <= 0);
+ assertTrue(nanosRemaining > -MILLISECONDS.toNanos(LONG_DELAY_MS));
+ assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
break;
case awaitUntil:
+ // We shouldn't assume that nanoTime and currentTimeMillis
+ // use the same time source, so don't use nanoTime here.
+ java.util.Date delayedDate = delayedDate(timeoutMillis());
assertFalse(c.awaitUntil(delayedDate(timeoutMillis)));
+ assertTrue(new java.util.Date().getTime() >= delayedDate.getTime());
break;
default:
throw new UnsupportedOperationException();
}
} catch (InterruptedException ie) { threadUnexpectedException(ie); }
- assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
}
/**
diff --git a/jsr166-tests/src/test/java/jsr166/ArrayBlockingQueueTest.java b/jsr166-tests/src/test/java/jsr166/ArrayBlockingQueueTest.java
index 247c90e..902ae40 100644
--- a/jsr166-tests/src/test/java/jsr166/ArrayBlockingQueueTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ArrayBlockingQueueTest.java
@@ -26,7 +26,7 @@
public class ArrayBlockingQueueTest extends JSR166TestCase {
- // android-note: These tests have been moved into their own separate
+ // android-note: These tests have been moved into their own separate
// classes to work around CTS issues.
//
// public static class Fair extends BlockingQueueTest {
@@ -34,17 +34,19 @@
// return new ArrayBlockingQueue(SIZE, true);
// }
// }
- //
+
// public static class NonFair extends BlockingQueueTest {
// protected BlockingQueue emptyCollection() {
// return new ArrayBlockingQueue(SIZE, false);
// }
// }
+
+ // android-note: Removed because the CTS runner does a bad job of
+ // retrying tests that have suite() declarations.
//
// public static void main(String[] args) {
// main(suite(), args);
// }
- //
// public static Test suite() {
// return newTestSuite(ArrayBlockingQueueTest.class,
// new Fair().testSuite(),
@@ -109,7 +111,7 @@
*/
public void testConstructor5() {
Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
+ for (int i = 0; i < SIZE - 1; ++i)
ints[i] = i;
Collection<Integer> elements = Arrays.asList(ints);
try {
@@ -171,7 +173,7 @@
assertEquals(i, q.remove());
}
for (int i = 0; i < SIZE; ++i) {
- assertEquals(SIZE-i, q.remainingCapacity());
+ assertEquals(SIZE - i, q.remainingCapacity());
assertEquals(SIZE, q.size() + q.remainingCapacity());
assertTrue(q.add(i));
}
@@ -219,7 +221,7 @@
public void testAddAll3() {
ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE);
Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
+ for (int i = 0; i < SIZE - 1; ++i)
ints[i] = new Integer(i);
try {
q.addAll(Arrays.asList(ints));
@@ -456,25 +458,23 @@
final CountDownLatch aboutToWait = new CountDownLatch(1);
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
+ long startTime = System.nanoTime();
for (int i = 0; i < SIZE; ++i) {
- long t0 = System.nanoTime();
assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
- assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
}
- long t0 = System.nanoTime();
aboutToWait.countDown();
try {
- q.poll(MEDIUM_DELAY_MS, MILLISECONDS);
+ q.poll(LONG_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {
- assertTrue(millisElapsedSince(t0) < MEDIUM_DELAY_MS);
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}
}});
- aboutToWait.await();
- waitForThreadToEnterWaitState(t, SMALL_DELAY_MS);
+ await(aboutToWait);
+ waitForThreadToEnterWaitState(t, LONG_DELAY_MS);
t.interrupt();
- awaitTermination(t, MEDIUM_DELAY_MS);
+ awaitTermination(t);
checkEmpty(q);
}
@@ -577,7 +577,7 @@
assertTrue(changed);
assertTrue(q.containsAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
p.remove();
}
}
@@ -590,7 +590,7 @@
ArrayBlockingQueue q = populatedQueue(SIZE);
ArrayBlockingQueue p = populatedQueue(i);
assertTrue(q.removeAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
for (int j = 0; j < i; ++j) {
Integer x = (Integer)(p.remove());
assertFalse(q.contains(x));
@@ -624,23 +624,23 @@
checkToArray(q);
assertEquals(i, q.poll());
checkToArray(q);
- q.add(SIZE+i);
+ q.add(SIZE + i);
}
for (int i = 0; i < SIZE; i++) {
checkToArray(q);
- assertEquals(SIZE+i, q.poll());
+ assertEquals(SIZE + i, q.poll());
}
}
void checkToArray2(ArrayBlockingQueue q) {
int size = q.size();
- Integer[] a1 = size == 0 ? null : new Integer[size-1];
+ Integer[] a1 = (size == 0) ? null : new Integer[size - 1];
Integer[] a2 = new Integer[size];
- Integer[] a3 = new Integer[size+2];
+ Integer[] a3 = new Integer[size + 2];
if (size > 0) Arrays.fill(a1, 42);
Arrays.fill(a2, 42);
Arrays.fill(a3, 42);
- Integer[] b1 = size == 0 ? null : (Integer[]) q.toArray(a1);
+ Integer[] b1 = (size == 0) ? null : (Integer[]) q.toArray(a1);
Integer[] b2 = (Integer[]) q.toArray(a2);
Integer[] b3 = (Integer[]) q.toArray(a3);
assertSame(a2, b2);
@@ -654,7 +654,7 @@
assertSame(b3[i], x);
}
assertNull(a3[size]);
- assertEquals(42, (int) a3[size+1]);
+ assertEquals(42, (int) a3[size + 1]);
if (size > 0) {
assertNotSame(a1, b1);
assertEquals(size, b1.length);
@@ -678,11 +678,11 @@
checkToArray2(q);
assertEquals(i, q.poll());
checkToArray2(q);
- q.add(SIZE+i);
+ q.add(SIZE + i);
}
for (int i = 0; i < SIZE; i++) {
checkToArray2(q);
- assertEquals(SIZE+i, q.poll());
+ assertEquals(SIZE + i, q.poll());
}
}
@@ -793,24 +793,24 @@
final ArrayBlockingQueue q = new ArrayBlockingQueue(2);
q.add(one);
q.add(two);
- ExecutorService executor = Executors.newFixedThreadPool(2);
final CheckedBarrier threadsStarted = new CheckedBarrier(2);
- executor.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- assertFalse(q.offer(three));
- threadsStarted.await();
- assertTrue(q.offer(three, LONG_DELAY_MS, MILLISECONDS));
- assertEquals(0, q.remainingCapacity());
- }});
+ final ExecutorService executor = Executors.newFixedThreadPool(2);
+ try (PoolCleaner cleaner = cleaner(executor)) {
+ executor.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ assertFalse(q.offer(three));
+ threadsStarted.await();
+ assertTrue(q.offer(three, LONG_DELAY_MS, MILLISECONDS));
+ assertEquals(0, q.remainingCapacity());
+ }});
- executor.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- threadsStarted.await();
- assertEquals(0, q.remainingCapacity());
- assertSame(one, q.take());
- }});
-
- joinPool(executor);
+ executor.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ threadsStarted.await();
+ assertEquals(0, q.remainingCapacity());
+ assertSame(one, q.take());
+ }});
+ }
}
/**
@@ -819,22 +819,22 @@
public void testPollInExecutor() {
final ArrayBlockingQueue q = new ArrayBlockingQueue(2);
final CheckedBarrier threadsStarted = new CheckedBarrier(2);
- ExecutorService executor = Executors.newFixedThreadPool(2);
- executor.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- assertNull(q.poll());
- threadsStarted.await();
- assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
- checkEmpty(q);
- }});
+ final ExecutorService executor = Executors.newFixedThreadPool(2);
+ try (PoolCleaner cleaner = cleaner(executor)) {
+ executor.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ assertNull(q.poll());
+ threadsStarted.await();
+ assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+ checkEmpty(q);
+ }});
- executor.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- threadsStarted.await();
- q.put(one);
- }});
-
- joinPool(executor);
+ executor.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ threadsStarted.await();
+ q.put(one);
+ }});
+ }
}
/**
@@ -886,7 +886,7 @@
final ArrayBlockingQueue q = populatedQueue(SIZE);
Thread t = new Thread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
- q.put(new Integer(SIZE+1));
+ q.put(new Integer(SIZE + 1));
}});
t.start();
@@ -903,7 +903,7 @@
* drainTo(c, n) empties first min(n, size) elements of queue into c
*/
public void testDrainToN() {
- ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE*2);
+ ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE * 2);
for (int i = 0; i < SIZE + 2; ++i) {
for (int j = 0; j < SIZE; j++)
assertTrue(q.offer(new Integer(j)));
@@ -911,7 +911,7 @@
q.drainTo(l, i);
int k = (i < SIZE) ? i : SIZE;
assertEquals(k, l.size());
- assertEquals(SIZE-k, q.size());
+ assertEquals(SIZE - k, q.size());
for (int j = 0; j < k; ++j)
assertEquals(l.get(j), new Integer(j));
do {} while (q.poll() != null);
diff --git a/jsr166-tests/src/test/java/jsr166/ArrayDequeTest.java b/jsr166-tests/src/test/java/jsr166/ArrayDequeTest.java
index 16290e9..23cc6b9 100644
--- a/jsr166-tests/src/test/java/jsr166/ArrayDequeTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ArrayDequeTest.java
@@ -26,7 +26,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(ArrayDequeTest.class);
// }
/**
@@ -65,8 +65,7 @@
*/
public void testConstructor4() {
try {
- Integer[] ints = new Integer[SIZE];
- new ArrayDeque(Arrays.asList(ints));
+ new ArrayDeque(Arrays.asList(new Integer[SIZE]));
shouldThrow();
} catch (NullPointerException success) {}
}
@@ -75,10 +74,10 @@
* Initializing from Collection with some null elements throws NPE
*/
public void testConstructor5() {
+ Integer[] ints = new Integer[SIZE];
+ for (int i = 0; i < SIZE - 1; ++i)
+ ints[i] = new Integer(i);
try {
- Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
- ints[i] = new Integer(i);
new ArrayDeque(Arrays.asList(ints));
shouldThrow();
} catch (NullPointerException success) {}
@@ -116,7 +115,7 @@
public void testSize() {
ArrayDeque q = populatedDeque(SIZE);
for (int i = 0; i < SIZE; ++i) {
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
q.removeFirst();
}
for (int i = 0; i < SIZE; ++i) {
@@ -129,8 +128,8 @@
* push(null) throws NPE
*/
public void testPushNull() {
+ ArrayDeque q = new ArrayDeque(1);
try {
- ArrayDeque q = new ArrayDeque(1);
q.push(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -164,8 +163,8 @@
* offer(null) throws NPE
*/
public void testOfferNull() {
+ ArrayDeque q = new ArrayDeque();
try {
- ArrayDeque q = new ArrayDeque();
q.offer(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -175,8 +174,8 @@
* offerFirst(null) throws NPE
*/
public void testOfferFirstNull() {
+ ArrayDeque q = new ArrayDeque();
try {
- ArrayDeque q = new ArrayDeque();
q.offerFirst(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -186,8 +185,8 @@
* offerLast(null) throws NPE
*/
public void testOfferLastNull() {
+ ArrayDeque q = new ArrayDeque();
try {
- ArrayDeque q = new ArrayDeque();
q.offerLast(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -230,8 +229,8 @@
* add(null) throws NPE
*/
public void testAddNull() {
+ ArrayDeque q = new ArrayDeque();
try {
- ArrayDeque q = new ArrayDeque();
q.add(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -241,8 +240,8 @@
* addFirst(null) throws NPE
*/
public void testAddFirstNull() {
+ ArrayDeque q = new ArrayDeque();
try {
- ArrayDeque q = new ArrayDeque();
q.addFirst(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -252,8 +251,8 @@
* addLast(null) throws NPE
*/
public void testAddLastNull() {
+ ArrayDeque q = new ArrayDeque();
try {
- ArrayDeque q = new ArrayDeque();
q.addLast(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -296,8 +295,8 @@
* addAll(null) throws NPE
*/
public void testAddAll1() {
+ ArrayDeque q = new ArrayDeque();
try {
- ArrayDeque q = new ArrayDeque();
q.addAll(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -307,10 +306,9 @@
* addAll of a collection with null elements throws NPE
*/
public void testAddAll2() {
+ ArrayDeque q = new ArrayDeque();
try {
- ArrayDeque q = new ArrayDeque();
- Integer[] ints = new Integer[SIZE];
- q.addAll(Arrays.asList(ints));
+ q.addAll(Arrays.asList(new Integer[SIZE]));
shouldThrow();
} catch (NullPointerException success) {}
}
@@ -320,11 +318,11 @@
* possibly adding some elements
*/
public void testAddAll3() {
+ ArrayDeque q = new ArrayDeque();
+ Integer[] ints = new Integer[SIZE];
+ for (int i = 0; i < SIZE - 1; ++i)
+ ints[i] = new Integer(i);
try {
- ArrayDeque q = new ArrayDeque();
- Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
- ints[i] = new Integer(i);
q.addAll(Arrays.asList(ints));
shouldThrow();
} catch (NullPointerException success) {}
@@ -361,7 +359,7 @@
*/
public void testPollLast() {
ArrayDeque q = populatedDeque(SIZE);
- for (int i = SIZE-1; i >= 0; --i) {
+ for (int i = SIZE - 1; i >= 0; --i) {
assertEquals(i, q.pollLast());
}
assertNull(q.pollLast());
@@ -401,14 +399,14 @@
assertTrue(q.contains(i));
assertTrue(q.remove(i));
assertFalse(q.contains(i));
- assertTrue(q.contains(i-1));
+ assertTrue(q.contains(i - 1));
}
for (int i = 0; i < SIZE; i += 2) {
assertTrue(q.contains(i));
assertTrue(q.remove(i));
assertFalse(q.contains(i));
- assertFalse(q.remove(i+1));
- assertFalse(q.contains(i+1));
+ assertFalse(q.remove(i + 1));
+ assertFalse(q.contains(i + 1));
}
assertTrue(q.isEmpty());
}
@@ -446,7 +444,7 @@
*/
public void testPeekLast() {
ArrayDeque q = populatedDeque(SIZE);
- for (int i = SIZE-1; i >= 0; --i) {
+ for (int i = SIZE - 1; i >= 0; --i) {
assertEquals(i, q.peekLast());
assertEquals(i, q.pollLast());
assertTrue(q.peekLast() == null ||
@@ -490,7 +488,7 @@
*/
public void testLastElement() {
ArrayDeque q = populatedDeque(SIZE);
- for (int i = SIZE-1; i >= 0; --i) {
+ for (int i = SIZE - 1; i >= 0; --i) {
assertEquals(i, q.getLast());
assertEquals(i, q.pollLast());
}
@@ -541,7 +539,7 @@
}
for (int i = 0; i < SIZE; i += 2) {
assertTrue(q.removeFirstOccurrence(new Integer(i)));
- assertFalse(q.removeFirstOccurrence(new Integer(i+1)));
+ assertFalse(q.removeFirstOccurrence(new Integer(i + 1)));
}
assertTrue(q.isEmpty());
}
@@ -556,7 +554,7 @@
}
for (int i = 0; i < SIZE; i += 2) {
assertTrue(q.removeLastOccurrence(new Integer(i)));
- assertFalse(q.removeLastOccurrence(new Integer(i+1)));
+ assertFalse(q.removeLastOccurrence(new Integer(i + 1)));
}
assertTrue(q.isEmpty());
}
@@ -611,7 +609,7 @@
boolean changed = q.retainAll(p);
assertEquals(changed, (i > 0));
assertTrue(q.containsAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
p.removeFirst();
}
}
@@ -624,7 +622,7 @@
ArrayDeque q = populatedDeque(SIZE);
ArrayDeque p = populatedDeque(i);
assertTrue(q.removeAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
for (int j = 0; j < i; ++j) {
assertFalse(q.contains(p.removeFirst()));
}
@@ -656,23 +654,23 @@
for (int i = 0; i < SIZE; i++) {
checkToArray(q);
assertEquals(i, q.poll());
- q.addLast(SIZE+i);
+ q.addLast(SIZE + i);
}
for (int i = 0; i < SIZE; i++) {
checkToArray(q);
- assertEquals(SIZE+i, q.poll());
+ assertEquals(SIZE + i, q.poll());
}
}
void checkToArray2(ArrayDeque q) {
int size = q.size();
- Integer[] a1 = size == 0 ? null : new Integer[size-1];
+ Integer[] a1 = (size == 0) ? null : new Integer[size - 1];
Integer[] a2 = new Integer[size];
- Integer[] a3 = new Integer[size+2];
+ Integer[] a3 = new Integer[size + 2];
if (size > 0) Arrays.fill(a1, 42);
Arrays.fill(a2, 42);
Arrays.fill(a3, 42);
- Integer[] b1 = size == 0 ? null : (Integer[]) q.toArray(a1);
+ Integer[] b1 = (size == 0) ? null : (Integer[]) q.toArray(a1);
Integer[] b2 = (Integer[]) q.toArray(a2);
Integer[] b3 = (Integer[]) q.toArray(a3);
assertSame(a2, b2);
@@ -686,7 +684,7 @@
assertSame(b3[i], x);
}
assertNull(a3[size]);
- assertEquals(42, (int) a3[size+1]);
+ assertEquals(42, (int) a3[size + 1]);
if (size > 0) {
assertNotSame(a1, b1);
assertEquals(size, b1.length);
@@ -709,11 +707,11 @@
for (int i = 0; i < SIZE; i++) {
checkToArray2(q);
assertEquals(i, q.poll());
- q.addLast(SIZE+i);
+ q.addLast(SIZE + i);
}
for (int i = 0; i < SIZE; i++) {
checkToArray2(q);
- assertEquals(SIZE+i, q.poll());
+ assertEquals(SIZE + i, q.poll());
}
}
@@ -787,18 +785,18 @@
final Random rng = new Random();
for (int iters = 0; iters < 100; ++iters) {
int max = rng.nextInt(5) + 2;
- int split = rng.nextInt(max-1) + 1;
+ int split = rng.nextInt(max - 1) + 1;
for (int j = 1; j <= max; ++j)
q.add(new Integer(j));
Iterator it = q.iterator();
for (int j = 1; j <= split; ++j)
assertEquals(it.next(), new Integer(j));
it.remove();
- assertEquals(it.next(), new Integer(split+1));
+ assertEquals(it.next(), new Integer(split + 1));
for (int j = 1; j <= split; ++j)
q.remove(new Integer(j));
it = q.iterator();
- for (int j = split+1; j <= max; ++j) {
+ for (int j = split + 1; j <= max; ++j) {
assertEquals(it.next(), new Integer(j));
it.remove();
}
@@ -855,18 +853,18 @@
final Random rng = new Random();
for (int iters = 0; iters < 100; ++iters) {
int max = rng.nextInt(5) + 2;
- int split = rng.nextInt(max-1) + 1;
+ int split = rng.nextInt(max - 1) + 1;
for (int j = max; j >= 1; --j)
q.add(new Integer(j));
Iterator it = q.descendingIterator();
for (int j = 1; j <= split; ++j)
assertEquals(it.next(), new Integer(j));
it.remove();
- assertEquals(it.next(), new Integer(split+1));
+ assertEquals(it.next(), new Integer(split + 1));
for (int j = 1; j <= split; ++j)
q.remove(new Integer(j));
it = q.descendingIterator();
- for (int j = split+1; j <= max; ++j) {
+ for (int j = split + 1; j <= max; ++j) {
assertEquals(it.next(), new Integer(j));
it.remove();
}
diff --git a/jsr166-tests/src/test/java/jsr166/Atomic8Test.java b/jsr166-tests/src/test/java/jsr166/Atomic8Test.java
new file mode 100644
index 0000000..f81c44f
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/Atomic8Test.java
@@ -0,0 +1,574 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicIntegerArray;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicLongArray;
+import java.util.concurrent.atomic.AtomicLongFieldUpdater;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.atomic.AtomicReferenceArray;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class Atomic8Test extends JSR166TestCase {
+
+ // android-note: Removed because the CTS runner does a bad job of
+ // retrying tests that have suite() declarations.
+ //
+ // public static void main(String[] args) {
+ // main(suite(), args);
+ // }
+ // public static Test suite() {
+ // return new TestSuite(Atomic8Test.class);
+ // }
+
+ /*
+ * Tests of atomic class methods accepting lambdas
+ * introduced in JDK8.
+ */
+
+ static long addLong17(long x) { return x + 17; }
+ static int addInt17(int x) { return x + 17; }
+ static Integer addInteger17(Integer x) {
+ return new Integer(x.intValue() + 17);
+ }
+ static Integer sumInteger(Integer x, Integer y) {
+ return new Integer(x.intValue() + y.intValue());
+ }
+
+ volatile long aLongField;
+ volatile int anIntField;
+ volatile Integer anIntegerField;
+
+ AtomicLongFieldUpdater aLongFieldUpdater() {
+ return AtomicLongFieldUpdater.newUpdater
+ (Atomic8Test.class, "aLongField");
+ }
+
+ AtomicIntegerFieldUpdater anIntFieldUpdater() {
+ return AtomicIntegerFieldUpdater.newUpdater
+ (Atomic8Test.class, "anIntField");
+ }
+
+ AtomicReferenceFieldUpdater<Atomic8Test,Integer> anIntegerFieldUpdater() {
+ return AtomicReferenceFieldUpdater.newUpdater
+ (Atomic8Test.class, Integer.class, "anIntegerField");
+ }
+
+ /**
+ * AtomicLong getAndUpdate returns previous value and updates
+ * result of supplied function
+ */
+ public void testLongGetAndUpdate() {
+ AtomicLong a = new AtomicLong(1L);
+ assertEquals(1L, a.getAndUpdate(Atomic8Test::addLong17));
+ assertEquals(18L, a.getAndUpdate(Atomic8Test::addLong17));
+ assertEquals(35L, a.get());
+ }
+
+ /**
+ * AtomicLong updateAndGet updates with supplied function and
+ * returns result.
+ */
+ public void testLongUpdateAndGet() {
+ AtomicLong a = new AtomicLong(1L);
+ assertEquals(18L, a.updateAndGet(Atomic8Test::addLong17));
+ assertEquals(35L, a.updateAndGet(Atomic8Test::addLong17));
+ }
+
+ /**
+ * AtomicLong getAndAccumulate returns previous value and updates
+ * with supplied function.
+ */
+ public void testLongGetAndAccumulate() {
+ AtomicLong a = new AtomicLong(1L);
+ assertEquals(1L, a.getAndAccumulate(2L, Long::sum));
+ assertEquals(3L, a.getAndAccumulate(3L, Long::sum));
+ assertEquals(6L, a.get());
+ }
+
+ /**
+ * AtomicLong accumulateAndGet updates with supplied function and
+ * returns result.
+ */
+ public void testLongAccumulateAndGet() {
+ AtomicLong a = new AtomicLong(1L);
+ assertEquals(7L, a.accumulateAndGet(6L, Long::sum));
+ assertEquals(10L, a.accumulateAndGet(3L, Long::sum));
+ assertEquals(10L, a.get());
+ }
+
+ /**
+ * AtomicInteger getAndUpdate returns previous value and updates
+ * result of supplied function
+ */
+ public void testIntGetAndUpdate() {
+ AtomicInteger a = new AtomicInteger(1);
+ assertEquals(1, a.getAndUpdate(Atomic8Test::addInt17));
+ assertEquals(18, a.getAndUpdate(Atomic8Test::addInt17));
+ assertEquals(35, a.get());
+ }
+
+ /**
+ * AtomicInteger updateAndGet updates with supplied function and
+ * returns result.
+ */
+ public void testIntUpdateAndGet() {
+ AtomicInteger a = new AtomicInteger(1);
+ assertEquals(18, a.updateAndGet(Atomic8Test::addInt17));
+ assertEquals(35, a.updateAndGet(Atomic8Test::addInt17));
+ assertEquals(35, a.get());
+ }
+
+ /**
+ * AtomicInteger getAndAccumulate returns previous value and updates
+ * with supplied function.
+ */
+ public void testIntGetAndAccumulate() {
+ AtomicInteger a = new AtomicInteger(1);
+ assertEquals(1, a.getAndAccumulate(2, Integer::sum));
+ assertEquals(3, a.getAndAccumulate(3, Integer::sum));
+ assertEquals(6, a.get());
+ }
+
+ /**
+ * AtomicInteger accumulateAndGet updates with supplied function and
+ * returns result.
+ */
+ public void testIntAccumulateAndGet() {
+ AtomicInteger a = new AtomicInteger(1);
+ assertEquals(7, a.accumulateAndGet(6, Integer::sum));
+ assertEquals(10, a.accumulateAndGet(3, Integer::sum));
+ assertEquals(10, a.get());
+ }
+
+ /**
+ * AtomicReference getAndUpdate returns previous value and updates
+ * result of supplied function
+ */
+ public void testReferenceGetAndUpdate() {
+ AtomicReference<Integer> a = new AtomicReference<Integer>(one);
+ assertEquals(new Integer(1), a.getAndUpdate(Atomic8Test::addInteger17));
+ assertEquals(new Integer(18), a.getAndUpdate(Atomic8Test::addInteger17));
+ assertEquals(new Integer(35), a.get());
+ }
+
+ /**
+ * AtomicReference updateAndGet updates with supplied function and
+ * returns result.
+ */
+ public void testReferenceUpdateAndGet() {
+ AtomicReference<Integer> a = new AtomicReference<Integer>(one);
+ assertEquals(new Integer(18), a.updateAndGet(Atomic8Test::addInteger17));
+ assertEquals(new Integer(35), a.updateAndGet(Atomic8Test::addInteger17));
+ assertEquals(new Integer(35), a.get());
+ }
+
+ /**
+ * AtomicReference getAndAccumulate returns previous value and updates
+ * with supplied function.
+ */
+ public void testReferenceGetAndAccumulate() {
+ AtomicReference<Integer> a = new AtomicReference<Integer>(one);
+ assertEquals(new Integer(1), a.getAndAccumulate(2, Atomic8Test::sumInteger));
+ assertEquals(new Integer(3), a.getAndAccumulate(3, Atomic8Test::sumInteger));
+ assertEquals(new Integer(6), a.get());
+ }
+
+ /**
+ * AtomicReference accumulateAndGet updates with supplied function and
+ * returns result.
+ */
+ public void testReferenceAccumulateAndGet() {
+ AtomicReference<Integer> a = new AtomicReference<Integer>(one);
+ assertEquals(new Integer(7), a.accumulateAndGet(6, Atomic8Test::sumInteger));
+ assertEquals(new Integer(10), a.accumulateAndGet(3, Atomic8Test::sumInteger));
+ assertEquals(new Integer(10), a.get());
+ }
+
+ /**
+ * AtomicLongArray getAndUpdate returns previous value and updates
+ * result of supplied function
+ */
+ public void testLongArrayGetAndUpdate() {
+ AtomicLongArray a = new AtomicLongArray(1);
+ a.set(0, 1);
+ assertEquals(1L, a.getAndUpdate(0, Atomic8Test::addLong17));
+ assertEquals(18L, a.getAndUpdate(0, Atomic8Test::addLong17));
+ assertEquals(35L, a.get(0));
+ }
+
+ /**
+ * AtomicLongArray updateAndGet updates with supplied function and
+ * returns result.
+ */
+ public void testLongArrayUpdateAndGet() {
+ AtomicLongArray a = new AtomicLongArray(1);
+ a.set(0, 1);
+ assertEquals(18L, a.updateAndGet(0, Atomic8Test::addLong17));
+ assertEquals(35L, a.updateAndGet(0, Atomic8Test::addLong17));
+ assertEquals(35L, a.get(0));
+ }
+
+ /**
+ * AtomicLongArray getAndAccumulate returns previous value and updates
+ * with supplied function.
+ */
+ public void testLongArrayGetAndAccumulate() {
+ AtomicLongArray a = new AtomicLongArray(1);
+ a.set(0, 1);
+ assertEquals(1L, a.getAndAccumulate(0, 2L, Long::sum));
+ assertEquals(3L, a.getAndAccumulate(0, 3L, Long::sum));
+ assertEquals(6L, a.get(0));
+ }
+
+ /**
+ * AtomicLongArray accumulateAndGet updates with supplied function and
+ * returns result.
+ */
+ public void testLongArrayAccumulateAndGet() {
+ AtomicLongArray a = new AtomicLongArray(1);
+ a.set(0, 1);
+ assertEquals(7L, a.accumulateAndGet(0, 6L, Long::sum));
+ assertEquals(10L, a.accumulateAndGet(0, 3L, Long::sum));
+ assertEquals(10L, a.get(0));
+ }
+
+ /**
+ * AtomicIntegerArray getAndUpdate returns previous value and updates
+ * result of supplied function
+ */
+ public void testIntArrayGetAndUpdate() {
+ AtomicIntegerArray a = new AtomicIntegerArray(1);
+ a.set(0, 1);
+ assertEquals(1, a.getAndUpdate(0, Atomic8Test::addInt17));
+ assertEquals(18, a.getAndUpdate(0, Atomic8Test::addInt17));
+ assertEquals(35, a.get(0));
+ }
+
+ /**
+ * AtomicIntegerArray updateAndGet updates with supplied function and
+ * returns result.
+ */
+ public void testIntArrayUpdateAndGet() {
+ AtomicIntegerArray a = new AtomicIntegerArray(1);
+ a.set(0, 1);
+ assertEquals(18, a.updateAndGet(0, Atomic8Test::addInt17));
+ assertEquals(35, a.updateAndGet(0, Atomic8Test::addInt17));
+ assertEquals(35, a.get(0));
+ }
+
+ /**
+ * AtomicIntegerArray getAndAccumulate returns previous value and updates
+ * with supplied function.
+ */
+ public void testIntArrayGetAndAccumulate() {
+ AtomicIntegerArray a = new AtomicIntegerArray(1);
+ a.set(0, 1);
+ assertEquals(1, a.getAndAccumulate(0, 2, Integer::sum));
+ assertEquals(3, a.getAndAccumulate(0, 3, Integer::sum));
+ assertEquals(6, a.get(0));
+ }
+
+ /**
+ * AtomicIntegerArray accumulateAndGet updates with supplied function and
+ * returns result.
+ */
+ public void testIntArrayAccumulateAndGet() {
+ AtomicIntegerArray a = new AtomicIntegerArray(1);
+ a.set(0, 1);
+ assertEquals(7, a.accumulateAndGet(0, 6, Integer::sum));
+ assertEquals(10, a.accumulateAndGet(0, 3, Integer::sum));
+ }
+
+ /**
+ * AtomicReferenceArray getAndUpdate returns previous value and updates
+ * result of supplied function
+ */
+ public void testReferenceArrayGetAndUpdate() {
+ AtomicReferenceArray<Integer> a = new AtomicReferenceArray<Integer>(1);
+ a.set(0, one);
+ assertEquals(new Integer(1), a.getAndUpdate(0, Atomic8Test::addInteger17));
+ assertEquals(new Integer(18), a.getAndUpdate(0, Atomic8Test::addInteger17));
+ assertEquals(new Integer(35), a.get(0));
+ }
+
+ /**
+ * AtomicReferenceArray updateAndGet updates with supplied function and
+ * returns result.
+ */
+ public void testReferenceArrayUpdateAndGet() {
+ AtomicReferenceArray<Integer> a = new AtomicReferenceArray<Integer>(1);
+ a.set(0, one);
+ assertEquals(new Integer(18), a.updateAndGet(0, Atomic8Test::addInteger17));
+ assertEquals(new Integer(35), a.updateAndGet(0, Atomic8Test::addInteger17));
+ }
+
+ /**
+ * AtomicReferenceArray getAndAccumulate returns previous value and updates
+ * with supplied function.
+ */
+ public void testReferenceArrayGetAndAccumulate() {
+ AtomicReferenceArray<Integer> a = new AtomicReferenceArray<Integer>(1);
+ a.set(0, one);
+ assertEquals(new Integer(1), a.getAndAccumulate(0, 2, Atomic8Test::sumInteger));
+ assertEquals(new Integer(3), a.getAndAccumulate(0, 3, Atomic8Test::sumInteger));
+ assertEquals(new Integer(6), a.get(0));
+ }
+
+ /**
+ * AtomicReferenceArray accumulateAndGet updates with supplied function and
+ * returns result.
+ */
+ public void testReferenceArrayAccumulateAndGet() {
+ AtomicReferenceArray<Integer> a = new AtomicReferenceArray<Integer>(1);
+ a.set(0, one);
+ assertEquals(new Integer(7), a.accumulateAndGet(0, 6, Atomic8Test::sumInteger));
+ assertEquals(new Integer(10), a.accumulateAndGet(0, 3, Atomic8Test::sumInteger));
+ }
+
+ /**
+ * AtomicLongFieldUpdater getAndUpdate returns previous value and updates
+ * result of supplied function
+ */
+ public void testLongFieldUpdaterGetAndUpdate() {
+ AtomicLongFieldUpdater a = aLongFieldUpdater();
+ a.set(this, 1);
+ assertEquals(1L, a.getAndUpdate(this, Atomic8Test::addLong17));
+ assertEquals(18L, a.getAndUpdate(this, Atomic8Test::addLong17));
+ assertEquals(35L, a.get(this));
+ assertEquals(35L, aLongField);
+ }
+
+ /**
+ * AtomicLongFieldUpdater updateAndGet updates with supplied function and
+ * returns result.
+ */
+ public void testLongFieldUpdaterUpdateAndGet() {
+ AtomicLongFieldUpdater a = aLongFieldUpdater();
+ a.set(this, 1);
+ assertEquals(18L, a.updateAndGet(this, Atomic8Test::addLong17));
+ assertEquals(35L, a.updateAndGet(this, Atomic8Test::addLong17));
+ assertEquals(35L, a.get(this));
+ assertEquals(35L, aLongField);
+ }
+
+ /**
+ * AtomicLongFieldUpdater getAndAccumulate returns previous value
+ * and updates with supplied function.
+ */
+ public void testLongFieldUpdaterGetAndAccumulate() {
+ AtomicLongFieldUpdater a = aLongFieldUpdater();
+ a.set(this, 1);
+ assertEquals(1L, a.getAndAccumulate(this, 2L, Long::sum));
+ assertEquals(3L, a.getAndAccumulate(this, 3L, Long::sum));
+ assertEquals(6L, a.get(this));
+ assertEquals(6L, aLongField);
+ }
+
+ /**
+ * AtomicLongFieldUpdater accumulateAndGet updates with supplied
+ * function and returns result.
+ */
+ public void testLongFieldUpdaterAccumulateAndGet() {
+ AtomicLongFieldUpdater a = aLongFieldUpdater();
+ a.set(this, 1);
+ assertEquals(7L, a.accumulateAndGet(this, 6L, Long::sum));
+ assertEquals(10L, a.accumulateAndGet(this, 3L, Long::sum));
+ assertEquals(10L, a.get(this));
+ assertEquals(10L, aLongField);
+ }
+
+ /**
+ * AtomicIntegerFieldUpdater getAndUpdate returns previous value and updates
+ * result of supplied function
+ */
+ public void testIntegerFieldUpdaterGetAndUpdate() {
+ AtomicIntegerFieldUpdater a = anIntFieldUpdater();
+ a.set(this, 1);
+ assertEquals(1, a.getAndUpdate(this, Atomic8Test::addInt17));
+ assertEquals(18, a.getAndUpdate(this, Atomic8Test::addInt17));
+ assertEquals(35, a.get(this));
+ assertEquals(35, anIntField);
+ }
+
+ /**
+ * AtomicIntegerFieldUpdater updateAndGet updates with supplied function and
+ * returns result.
+ */
+ public void testIntegerFieldUpdaterUpdateAndGet() {
+ AtomicIntegerFieldUpdater a = anIntFieldUpdater();
+ a.set(this, 1);
+ assertEquals(18, a.updateAndGet(this, Atomic8Test::addInt17));
+ assertEquals(35, a.updateAndGet(this, Atomic8Test::addInt17));
+ assertEquals(35, a.get(this));
+ assertEquals(35, anIntField);
+ }
+
+ /**
+ * AtomicIntegerFieldUpdater getAndAccumulate returns previous value
+ * and updates with supplied function.
+ */
+ public void testIntegerFieldUpdaterGetAndAccumulate() {
+ AtomicIntegerFieldUpdater a = anIntFieldUpdater();
+ a.set(this, 1);
+ assertEquals(1, a.getAndAccumulate(this, 2, Integer::sum));
+ assertEquals(3, a.getAndAccumulate(this, 3, Integer::sum));
+ assertEquals(6, a.get(this));
+ assertEquals(6, anIntField);
+ }
+
+ /**
+ * AtomicIntegerFieldUpdater accumulateAndGet updates with supplied
+ * function and returns result.
+ */
+ public void testIntegerFieldUpdaterAccumulateAndGet() {
+ AtomicIntegerFieldUpdater a = anIntFieldUpdater();
+ a.set(this, 1);
+ assertEquals(7, a.accumulateAndGet(this, 6, Integer::sum));
+ assertEquals(10, a.accumulateAndGet(this, 3, Integer::sum));
+ assertEquals(10, a.get(this));
+ assertEquals(10, anIntField);
+ }
+
+ /**
+ * AtomicReferenceFieldUpdater getAndUpdate returns previous value
+ * and updates result of supplied function
+ */
+ public void testReferenceFieldUpdaterGetAndUpdate() {
+ AtomicReferenceFieldUpdater<Atomic8Test,Integer> a = anIntegerFieldUpdater();
+ a.set(this, one);
+ assertEquals(new Integer(1), a.getAndUpdate(this, Atomic8Test::addInteger17));
+ assertEquals(new Integer(18), a.getAndUpdate(this, Atomic8Test::addInteger17));
+ assertEquals(new Integer(35), a.get(this));
+ assertEquals(new Integer(35), anIntegerField);
+ }
+
+ /**
+ * AtomicReferenceFieldUpdater updateAndGet updates with supplied
+ * function and returns result.
+ */
+ public void testReferenceFieldUpdaterUpdateAndGet() {
+ AtomicReferenceFieldUpdater<Atomic8Test,Integer> a = anIntegerFieldUpdater();
+ a.set(this, one);
+ assertEquals(new Integer(18), a.updateAndGet(this, Atomic8Test::addInteger17));
+ assertEquals(new Integer(35), a.updateAndGet(this, Atomic8Test::addInteger17));
+ assertEquals(new Integer(35), a.get(this));
+ assertEquals(new Integer(35), anIntegerField);
+ }
+
+ /**
+ * AtomicReferenceFieldUpdater returns previous value and updates
+ * with supplied function.
+ */
+ public void testReferenceFieldUpdaterGetAndAccumulate() {
+ AtomicReferenceFieldUpdater<Atomic8Test,Integer> a = anIntegerFieldUpdater();
+ a.set(this, one);
+ assertEquals(new Integer(1), a.getAndAccumulate(this, 2, Atomic8Test::sumInteger));
+ assertEquals(new Integer(3), a.getAndAccumulate(this, 3, Atomic8Test::sumInteger));
+ assertEquals(new Integer(6), a.get(this));
+ assertEquals(new Integer(6), anIntegerField);
+ }
+
+ /**
+ * AtomicReferenceFieldUpdater accumulateAndGet updates with
+ * supplied function and returns result.
+ */
+ public void testReferenceFieldUpdaterAccumulateAndGet() {
+ AtomicReferenceFieldUpdater<Atomic8Test,Integer> a = anIntegerFieldUpdater();
+ a.set(this, one);
+ assertEquals(new Integer(7), a.accumulateAndGet(this, 6, Atomic8Test::sumInteger));
+ assertEquals(new Integer(10), a.accumulateAndGet(this, 3, Atomic8Test::sumInteger));
+ assertEquals(new Integer(10), a.get(this));
+ assertEquals(new Integer(10), anIntegerField);
+ }
+
+ /**
+ * All Atomic getAndUpdate methods throw NullPointerException on
+ * null function argument
+ */
+ public void testGetAndUpdateNPE() {
+ Runnable[] throwingActions = {
+ () -> new AtomicLong().getAndUpdate(null),
+ () -> new AtomicInteger().getAndUpdate(null),
+ () -> new AtomicReference().getAndUpdate(null),
+ () -> new AtomicLongArray(1).getAndUpdate(0, null),
+ () -> new AtomicIntegerArray(1).getAndUpdate(0, null),
+ () -> new AtomicReferenceArray(1).getAndUpdate(0, null),
+ () -> aLongFieldUpdater().getAndUpdate(this, null),
+ () -> anIntFieldUpdater().getAndUpdate(this, null),
+ () -> anIntegerFieldUpdater().getAndUpdate(this, null),
+ ////() -> aLongFieldUpdater().getAndUpdate(null, Atomic8Test::addLong17),
+ ////() -> anIntFieldUpdater().getAndUpdate(null, Atomic8Test::addInt17),
+ ////() -> anIntegerFieldUpdater().getAndUpdate(null, Atomic8Test::addInteger17),
+ };
+ assertThrows(NullPointerException.class, throwingActions);
+ }
+
+ /**
+ * All Atomic updateAndGet methods throw NullPointerException on null function argument
+ */
+ public void testUpdateAndGetNPE() {
+ Runnable[] throwingActions = {
+ () -> new AtomicLong().updateAndGet(null),
+ () -> new AtomicInteger().updateAndGet(null),
+ () -> new AtomicReference().updateAndGet(null),
+ () -> new AtomicLongArray(1).updateAndGet(0, null),
+ () -> new AtomicIntegerArray(1).updateAndGet(0, null),
+ () -> new AtomicReferenceArray(1).updateAndGet(0, null),
+ () -> aLongFieldUpdater().updateAndGet(this, null),
+ () -> anIntFieldUpdater().updateAndGet(this, null),
+ () -> anIntegerFieldUpdater().updateAndGet(this, null),
+ };
+ assertThrows(NullPointerException.class, throwingActions);
+ }
+
+ /**
+ * All Atomic getAndAccumulate methods throw NullPointerException
+ * on null function argument
+ */
+ public void testGetAndAccumulateNPE() {
+ Runnable[] throwingActions = {
+ () -> new AtomicLong().getAndAccumulate(1L, null),
+ () -> new AtomicInteger().getAndAccumulate(1, null),
+ () -> new AtomicReference().getAndAccumulate(one, null),
+ () -> new AtomicLongArray(1).getAndAccumulate(0, 1L, null),
+ () -> new AtomicIntegerArray(1).getAndAccumulate(0, 1, null),
+ () -> new AtomicReferenceArray(1).getAndAccumulate(0, one, null),
+ () -> aLongFieldUpdater().getAndAccumulate(this, 1L, null),
+ () -> anIntFieldUpdater().getAndAccumulate(this, 1, null),
+ () -> anIntegerFieldUpdater().getAndAccumulate(this, one, null),
+ };
+ assertThrows(NullPointerException.class, throwingActions);
+ }
+
+ /**
+ * All Atomic accumulateAndGet methods throw NullPointerException
+ * on null function argument
+ */
+ public void testAccumulateAndGetNPE() {
+ Runnable[] throwingActions = {
+ () -> new AtomicLong().accumulateAndGet(1L, null),
+ () -> new AtomicInteger().accumulateAndGet(1, null),
+ () -> new AtomicReference().accumulateAndGet(one, null),
+ () -> new AtomicLongArray(1).accumulateAndGet(0, 1L, null),
+ () -> new AtomicIntegerArray(1).accumulateAndGet(0, 1, null),
+ () -> new AtomicReferenceArray(1).accumulateAndGet(0, one, null),
+ () -> aLongFieldUpdater().accumulateAndGet(this, 1L, null),
+ () -> anIntFieldUpdater().accumulateAndGet(this, 1, null),
+ () -> anIntegerFieldUpdater().accumulateAndGet(this, one, null),
+ };
+ assertThrows(NullPointerException.class, throwingActions);
+ }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/AtomicBooleanTest.java b/jsr166-tests/src/test/java/jsr166/AtomicBooleanTest.java
index bfe3fc6..6f5decf 100644
--- a/jsr166-tests/src/test/java/jsr166/AtomicBooleanTest.java
+++ b/jsr166-tests/src/test/java/jsr166/AtomicBooleanTest.java
@@ -21,7 +21,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(AtomicBooleanTest.class);
// }
/**
diff --git a/jsr166-tests/src/test/java/jsr166/AtomicIntegerArrayTest.java b/jsr166-tests/src/test/java/jsr166/AtomicIntegerArrayTest.java
index 670b9ce..c9e32aa 100644
--- a/jsr166-tests/src/test/java/jsr166/AtomicIntegerArrayTest.java
+++ b/jsr166-tests/src/test/java/jsr166/AtomicIntegerArrayTest.java
@@ -15,6 +15,7 @@
import junit.framework.TestSuite;
public class AtomicIntegerArrayTest extends JSR166TestCase {
+
// android-note: Removed because the CTS runner does a bad job of
// retrying tests that have suite() declarations.
//
@@ -22,7 +23,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(AtomicIntegerArrayTest.class);
// }
/**
@@ -290,7 +291,7 @@
assertTrue(v >= 0);
if (v != 0) {
done = false;
- if (aa.compareAndSet(i, v, v-1))
+ if (aa.compareAndSet(i, v, v - 1))
++counts;
}
}
diff --git a/jsr166-tests/src/test/java/jsr166/AtomicIntegerFieldUpdaterTest.java b/jsr166-tests/src/test/java/jsr166/AtomicIntegerFieldUpdaterTest.java
index ef75b46..c8d3856 100644
--- a/jsr166-tests/src/test/java/jsr166/AtomicIntegerFieldUpdaterTest.java
+++ b/jsr166-tests/src/test/java/jsr166/AtomicIntegerFieldUpdaterTest.java
@@ -15,8 +15,10 @@
public class AtomicIntegerFieldUpdaterTest extends JSR166TestCase {
volatile int x = 0;
+ protected volatile int protectedField;
+ private volatile int privateField;
int w;
- long z;
+ float z;
// android-note: Removed because the CTS runner does a bad job of
// retrying tests that have suite() declarations.
//
@@ -24,7 +26,59 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(AtomicIntegerFieldUpdaterTest.class);
+ // }
+
+ // for testing subclass access
+ // android-note: Removed because android doesn't restrict reflection access
+ // static class AtomicIntegerFieldUpdaterTestSubclass extends AtomicIntegerFieldUpdaterTest {
+ // public void checkPrivateAccess() {
+ // try {
+ // AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a =
+ // AtomicIntegerFieldUpdater.newUpdater
+ // (AtomicIntegerFieldUpdaterTest.class, "privateField");
+ // shouldThrow();
+ // } catch (RuntimeException success) {
+ // assertNotNull(success.getCause());
+ // }
+ // }
+
+ // public void checkCompareAndSetProtectedSub() {
+ // AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a =
+ // AtomicIntegerFieldUpdater.newUpdater
+ // (AtomicIntegerFieldUpdaterTest.class, "protectedField");
+ // this.protectedField = 1;
+ // assertTrue(a.compareAndSet(this, 1, 2));
+ // assertTrue(a.compareAndSet(this, 2, -4));
+ // assertEquals(-4, a.get(this));
+ // assertFalse(a.compareAndSet(this, -5, 7));
+ // assertEquals(-4, a.get(this));
+ // assertTrue(a.compareAndSet(this, -4, 7));
+ // assertEquals(7, a.get(this));
+ // }
+ // }
+
+ // static class UnrelatedClass {
+ // public void checkPackageAccess(AtomicIntegerFieldUpdaterTest obj) {
+ // obj.x = 72;
+ // AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a =
+ // AtomicIntegerFieldUpdater.newUpdater
+ // (AtomicIntegerFieldUpdaterTest.class, "x");
+ // assertEquals(72, a.get(obj));
+ // assertTrue(a.compareAndSet(obj, 72, 73));
+ // assertEquals(73, a.get(obj));
+ // }
+
+ // public void checkPrivateAccess(AtomicIntegerFieldUpdaterTest obj) {
+ // try {
+ // AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a =
+ // AtomicIntegerFieldUpdater.newUpdater
+ // (AtomicIntegerFieldUpdaterTest.class, "privateField");
+ // throw new AssertionError("should throw");
+ // } catch (RuntimeException success) {
+ // assertNotNull(success.getCause());
+ // }
+ // }
// }
AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> updaterFor(String fieldName) {
@@ -65,6 +119,26 @@
}
/**
+ * construction using private field from subclass throws RuntimeException
+ */
+ // android-note: Removed because android doesn't restrict reflection access
+ // public void testPrivateFieldInSubclass() {
+ // AtomicIntegerFieldUpdaterTestSubclass s =
+ // new AtomicIntegerFieldUpdaterTestSubclass();
+ // s.checkPrivateAccess();
+ // }
+
+ /**
+ * construction from unrelated class; package access is allowed,
+ * private access is not
+ */
+ // android-note: Removed because android doesn't restrict reflection access
+ // public void testUnrelatedClassAccess() {
+ // new UnrelatedClass().checkPackageAccess(this);
+ // new UnrelatedClass().checkPrivateAccess(this);
+ // }
+
+ /**
* get returns the last value set or assigned
*/
public void testGetSet() {
@@ -109,6 +183,34 @@
}
/**
+ * compareAndSet succeeds in changing protected field value if
+ * equal to expected else fails
+ */
+ public void testCompareAndSetProtected() {
+ AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+ a = updaterFor("protectedField");
+ protectedField = 1;
+ assertTrue(a.compareAndSet(this, 1, 2));
+ assertTrue(a.compareAndSet(this, 2, -4));
+ assertEquals(-4, a.get(this));
+ assertFalse(a.compareAndSet(this, -5, 7));
+ assertEquals(-4, a.get(this));
+ assertTrue(a.compareAndSet(this, -4, 7));
+ assertEquals(7, a.get(this));
+ }
+
+ /**
+ * compareAndSet succeeds in changing protected field value if
+ * equal to expected else fails
+ */
+ // android-note: Removed because android doesn't restrict reflection access
+ // public void testCompareAndSetProtectedInSubclass() {
+ // AtomicIntegerFieldUpdaterTestSubclass s =
+ // new AtomicIntegerFieldUpdaterTestSubclass();
+ // s.checkCompareAndSetProtectedSub();
+ // }
+
+ /**
* compareAndSet in one thread enables another waiting for value
* to succeed
*/
diff --git a/jsr166-tests/src/test/java/jsr166/AtomicIntegerTest.java b/jsr166-tests/src/test/java/jsr166/AtomicIntegerTest.java
index cf73810..6392c54 100644
--- a/jsr166-tests/src/test/java/jsr166/AtomicIntegerTest.java
+++ b/jsr166-tests/src/test/java/jsr166/AtomicIntegerTest.java
@@ -21,7 +21,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(AtomicIntegerTest.class);
// }
final int[] VALUES = {
diff --git a/jsr166-tests/src/test/java/jsr166/AtomicLongArrayTest.java b/jsr166-tests/src/test/java/jsr166/AtomicLongArrayTest.java
index 08df01e..a60ecfd 100644
--- a/jsr166-tests/src/test/java/jsr166/AtomicLongArrayTest.java
+++ b/jsr166-tests/src/test/java/jsr166/AtomicLongArrayTest.java
@@ -22,7 +22,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(AtomicLongArrayTest.class);
// }
/**
@@ -290,7 +290,7 @@
assertTrue(v >= 0);
if (v != 0) {
done = false;
- if (aa.compareAndSet(i, v, v-1))
+ if (aa.compareAndSet(i, v, v - 1))
++counts;
}
}
diff --git a/jsr166-tests/src/test/java/jsr166/AtomicLongFieldUpdaterTest.java b/jsr166-tests/src/test/java/jsr166/AtomicLongFieldUpdaterTest.java
index 204f814..d46280b 100644
--- a/jsr166-tests/src/test/java/jsr166/AtomicLongFieldUpdaterTest.java
+++ b/jsr166-tests/src/test/java/jsr166/AtomicLongFieldUpdaterTest.java
@@ -15,9 +15,10 @@
public class AtomicLongFieldUpdaterTest extends JSR166TestCase {
volatile long x = 0;
- int z;
+ protected volatile long protectedField;
+ private volatile long privateField;
long w;
-
+ float z;
// android-note: Removed because the CTS runner does a bad job of
// retrying tests that have suite() declarations.
//
@@ -25,7 +26,59 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(AtomicLongFieldUpdaterTest.class);
+ // }
+
+ // for testing subclass access
+ // android-note: Removed because android doesn't restrict reflection access
+ // static class AtomicLongFieldUpdaterTestSubclass extends AtomicLongFieldUpdaterTest {
+ // public void checkPrivateAccess() {
+ // try {
+ // AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a =
+ // AtomicLongFieldUpdater.newUpdater
+ // (AtomicLongFieldUpdaterTest.class, "privateField");
+ // shouldThrow();
+ // } catch (RuntimeException success) {
+ // assertNotNull(success.getCause());
+ // }
+ // }
+
+ // public void checkCompareAndSetProtectedSub() {
+ // AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a =
+ // AtomicLongFieldUpdater.newUpdater
+ // (AtomicLongFieldUpdaterTest.class, "protectedField");
+ // this.protectedField = 1;
+ // assertTrue(a.compareAndSet(this, 1, 2));
+ // assertTrue(a.compareAndSet(this, 2, -4));
+ // assertEquals(-4, a.get(this));
+ // assertFalse(a.compareAndSet(this, -5, 7));
+ // assertEquals(-4, a.get(this));
+ // assertTrue(a.compareAndSet(this, -4, 7));
+ // assertEquals(7, a.get(this));
+ // }
+ // }
+
+ // static class UnrelatedClass {
+ // public void checkPackageAccess(AtomicLongFieldUpdaterTest obj) {
+ // obj.x = 72L;
+ // AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a =
+ // AtomicLongFieldUpdater.newUpdater
+ // (AtomicLongFieldUpdaterTest.class, "x");
+ // assertEquals(72L, a.get(obj));
+ // assertTrue(a.compareAndSet(obj, 72L, 73L));
+ // assertEquals(73L, a.get(obj));
+ // }
+
+ // public void checkPrivateAccess(AtomicLongFieldUpdaterTest obj) {
+ // try {
+ // AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a =
+ // AtomicLongFieldUpdater.newUpdater
+ // (AtomicLongFieldUpdaterTest.class, "privateField");
+ // throw new AssertionError("should throw");
+ // } catch (RuntimeException success) {
+ // assertNotNull(success.getCause());
+ // }
+ // }
// }
AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> updaterFor(String fieldName) {
@@ -66,6 +119,26 @@
}
/**
+ * construction using private field from subclass throws RuntimeException
+ */
+ // android-note: Removed because android doesn't restrict reflection access
+ // public void testPrivateFieldInSubclass() {
+ // AtomicLongFieldUpdaterTestSubclass s =
+ // new AtomicLongFieldUpdaterTestSubclass();
+ // s.checkPrivateAccess();
+ // }
+
+ /**
+ * construction from unrelated class; package access is allowed,
+ * private access is not
+ */
+ // android-note: Removed because android doesn't restrict reflection access
+ // public void testUnrelatedClassAccess() {
+ // new UnrelatedClass().checkPackageAccess(this);
+ // new UnrelatedClass().checkPrivateAccess(this);
+ // }
+
+ /**
* get returns the last value set or assigned
*/
public void testGetSet() {
@@ -110,6 +183,34 @@
}
/**
+ * compareAndSet succeeds in changing protected field value if
+ * equal to expected else fails
+ */
+ public void testCompareAndSetProtected() {
+ AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+ a = updaterFor("protectedField");
+ protectedField = 1;
+ assertTrue(a.compareAndSet(this, 1, 2));
+ assertTrue(a.compareAndSet(this, 2, -4));
+ assertEquals(-4, a.get(this));
+ assertFalse(a.compareAndSet(this, -5, 7));
+ assertEquals(-4, a.get(this));
+ assertTrue(a.compareAndSet(this, -4, 7));
+ assertEquals(7, a.get(this));
+ }
+
+ /**
+ * compareAndSet succeeds in changing protected field value if
+ * equal to expected else fails
+ */
+ // android-note: Removed because android doesn't restrict reflection access
+ // public void testCompareAndSetProtectedInSubclass() {
+ // AtomicLongFieldUpdaterTestSubclass s =
+ // new AtomicLongFieldUpdaterTestSubclass();
+ // s.checkCompareAndSetProtectedSub();
+ // }
+
+ /**
* compareAndSet in one thread enables another waiting for value
* to succeed
*/
diff --git a/jsr166-tests/src/test/java/jsr166/AtomicLongTest.java b/jsr166-tests/src/test/java/jsr166/AtomicLongTest.java
index b9c1722..a8ee7c6 100644
--- a/jsr166-tests/src/test/java/jsr166/AtomicLongTest.java
+++ b/jsr166-tests/src/test/java/jsr166/AtomicLongTest.java
@@ -21,7 +21,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(AtomicLongTest.class);
// }
final long[] VALUES = {
diff --git a/jsr166-tests/src/test/java/jsr166/AtomicMarkableReferenceTest.java b/jsr166-tests/src/test/java/jsr166/AtomicMarkableReferenceTest.java
index 61b6b1b..bd4e8cb 100644
--- a/jsr166-tests/src/test/java/jsr166/AtomicMarkableReferenceTest.java
+++ b/jsr166-tests/src/test/java/jsr166/AtomicMarkableReferenceTest.java
@@ -21,7 +21,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(AtomicMarkableReferenceTest.class);
// }
/**
diff --git a/jsr166-tests/src/test/java/jsr166/AtomicReferenceArrayTest.java b/jsr166-tests/src/test/java/jsr166/AtomicReferenceArrayTest.java
index 1df2f9f..f3aab44 100644
--- a/jsr166-tests/src/test/java/jsr166/AtomicReferenceArrayTest.java
+++ b/jsr166-tests/src/test/java/jsr166/AtomicReferenceArrayTest.java
@@ -22,7 +22,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(AtomicReferenceArrayTest.class);
// }
/**
diff --git a/jsr166-tests/src/test/java/jsr166/AtomicReferenceFieldUpdaterTest.java b/jsr166-tests/src/test/java/jsr166/AtomicReferenceFieldUpdaterTest.java
index 4b0d946..9b2e9a9 100644
--- a/jsr166-tests/src/test/java/jsr166/AtomicReferenceFieldUpdaterTest.java
+++ b/jsr166-tests/src/test/java/jsr166/AtomicReferenceFieldUpdaterTest.java
@@ -15,6 +15,8 @@
public class AtomicReferenceFieldUpdaterTest extends JSR166TestCase {
volatile Integer x = null;
+ protected volatile Integer protectedField;
+ private volatile Integer privateField;
Object z;
Integer w;
volatile int i;
@@ -26,10 +28,62 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(AtomicReferenceFieldUpdaterTest.class);
// }
- AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer> updaterFor(String fieldName) {
+ // for testing subclass access
+ // android-note: Removed because android doesn't restrict reflection access
+ // static class AtomicReferenceFieldUpdaterTestSubclass extends AtomicReferenceFieldUpdaterTest {
+ // public void checkPrivateAccess() {
+ // try {
+ // AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest,Integer> a =
+ // AtomicReferenceFieldUpdater.newUpdater
+ // (AtomicReferenceFieldUpdaterTest.class, Integer.class, "privateField");
+ // shouldThrow();
+ // } catch (RuntimeException success) {
+ // assertNotNull(success.getCause());
+ // }
+ // }
+
+ // public void checkCompareAndSetProtectedSub() {
+ // AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest,Integer> a =
+ // AtomicReferenceFieldUpdater.newUpdater
+ // (AtomicReferenceFieldUpdaterTest.class, Integer.class, "protectedField");
+ // this.protectedField = one;
+ // assertTrue(a.compareAndSet(this, one, two));
+ // assertTrue(a.compareAndSet(this, two, m4));
+ // assertSame(m4, a.get(this));
+ // assertFalse(a.compareAndSet(this, m5, seven));
+ // assertFalse(seven == a.get(this));
+ // assertTrue(a.compareAndSet(this, m4, seven));
+ // assertSame(seven, a.get(this));
+ // }
+ // }
+
+ // static class UnrelatedClass {
+ // public void checkPackageAccess(AtomicReferenceFieldUpdaterTest obj) {
+ // obj.x = one;
+ // AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest,Integer> a =
+ // AtomicReferenceFieldUpdater.newUpdater
+ // (AtomicReferenceFieldUpdaterTest.class, Integer.class, "x");
+ // assertSame(one, a.get(obj));
+ // assertTrue(a.compareAndSet(obj, one, two));
+ // assertSame(two, a.get(obj));
+ // }
+
+ // public void checkPrivateAccess(AtomicReferenceFieldUpdaterTest obj) {
+ // try {
+ // AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest,Integer> a =
+ // AtomicReferenceFieldUpdater.newUpdater
+ // (AtomicReferenceFieldUpdaterTest.class, Integer.class, "privateField");
+ // throw new AssertionError("should throw");
+ // } catch (RuntimeException success) {
+ // assertNotNull(success.getCause());
+ // }
+ // }
+ // }
+
+ static AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer> updaterFor(String fieldName) {
return AtomicReferenceFieldUpdater.newUpdater
(AtomicReferenceFieldUpdaterTest.class, Integer.class, fieldName);
}
@@ -77,6 +131,26 @@
}
/**
+ * construction using private field from subclass throws RuntimeException
+ */
+ // android-note: Removed because android doesn't restrict reflection access
+ // public void testPrivateFieldInSubclass() {
+ // AtomicReferenceFieldUpdaterTestSubclass s =
+ // new AtomicReferenceFieldUpdaterTestSubclass();
+ // s.checkPrivateAccess();
+ // }
+
+ /**
+ * construction from unrelated class; package access is allowed,
+ * private access is not
+ */
+ // android-note: Removed because android doesn't restrict reflection access
+ // public void testUnrelatedClassAccess() {
+ // new UnrelatedClass().checkPackageAccess(this);
+ // new UnrelatedClass().checkPrivateAccess(this);
+ // }
+
+ /**
* get returns the last value set or assigned
*/
public void testGetSet() {
diff --git a/jsr166-tests/src/test/java/jsr166/AtomicReferenceTest.java b/jsr166-tests/src/test/java/jsr166/AtomicReferenceTest.java
index 457182f..4728970 100644
--- a/jsr166-tests/src/test/java/jsr166/AtomicReferenceTest.java
+++ b/jsr166-tests/src/test/java/jsr166/AtomicReferenceTest.java
@@ -21,7 +21,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(AtomicReferenceTest.class);
// }
/**
diff --git a/jsr166-tests/src/test/java/jsr166/AtomicStampedReferenceTest.java b/jsr166-tests/src/test/java/jsr166/AtomicStampedReferenceTest.java
index b3ff06a..a2e8c7f 100644
--- a/jsr166-tests/src/test/java/jsr166/AtomicStampedReferenceTest.java
+++ b/jsr166-tests/src/test/java/jsr166/AtomicStampedReferenceTest.java
@@ -21,7 +21,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(AtomicStampedReferenceTest.class);
// }
/**
diff --git a/jsr166-tests/src/test/java/jsr166/BlockingQueueTest.java b/jsr166-tests/src/test/java/jsr166/BlockingQueueTest.java
index db0f03d..1a188e1 100644
--- a/jsr166-tests/src/test/java/jsr166/BlockingQueueTest.java
+++ b/jsr166-tests/src/test/java/jsr166/BlockingQueueTest.java
@@ -39,9 +39,9 @@
// android-note: Explicitly instantiated.
//
// public Test testSuite() {
- // // TODO: filter the returned tests using the configuration
- // // information provided by the subclass via protected methods.
- // return new TestSuite(this.getClass());
+ // // TODO: filter the returned tests using the configuration
+ // // information provided by the subclass via protected methods.
+ // return new TestSuite(this.getClass());
// }
//----------------------------------------------------------------
@@ -239,6 +239,8 @@
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
+
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}});
barrier.await();
@@ -352,20 +354,20 @@
assertEquals((pass == 0), q.contains(elts[i]));
assertEquals((pass == 0), q.remove(elts[i]));
assertFalse(q.contains(elts[i]));
- assertTrue(q.contains(elts[i-1]));
+ assertTrue(q.contains(elts[i - 1]));
if (i < size - 1)
- assertTrue(q.contains(elts[i+1]));
+ assertTrue(q.contains(elts[i + 1]));
}
}
if (size > 0)
assertTrue(q.contains(elts[0]));
- for (int i = size-2; i >= 0; i -= 2) {
+ for (int i = size - 2; i >= 0; i -= 2) {
assertTrue(q.contains(elts[i]));
- assertFalse(q.contains(elts[i+1]));
+ assertFalse(q.contains(elts[i + 1]));
assertTrue(q.remove(elts[i]));
assertFalse(q.contains(elts[i]));
- assertFalse(q.remove(elts[i+1]));
- assertFalse(q.contains(elts[i+1]));
+ assertFalse(q.remove(elts[i + 1]));
+ assertFalse(q.contains(elts[i + 1]));
}
checkEmpty(q);
}
diff --git a/jsr166-tests/src/test/java/jsr166/Collection8Test.java b/jsr166-tests/src/test/java/jsr166/Collection8Test.java
new file mode 100644
index 0000000..634182b
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/Collection8Test.java
@@ -0,0 +1,104 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Consumer;
+
+import junit.framework.Test;
+
+/**
+ * Contains tests applicable to all jdk8+ Collection implementations.
+ * An extension of CollectionTest.
+ */
+public class Collection8Test extends JSR166TestCase {
+ final CollectionImplementation impl;
+
+ /** Tests are parameterized by a Collection implementation. */
+ Collection8Test(CollectionImplementation impl, String methodName) {
+ super(methodName);
+ this.impl = impl;
+ }
+
+ public static Test testSuite(CollectionImplementation impl) {
+ return parameterizedTestSuite(Collection8Test.class,
+ CollectionImplementation.class,
+ impl);
+ }
+
+ /**
+ * stream().forEach returns elements in the collection
+ */
+ // TODO(streams):
+ // public void testForEach() throws Throwable {
+ // final Collection c = impl.emptyCollection();
+ // final AtomicLong count = new AtomicLong(0L);
+ // final Object x = impl.makeElement(1);
+ // final Object y = impl.makeElement(2);
+ // final ArrayList found = new ArrayList();
+ // Consumer<Object> spy = (o) -> { found.add(o); };
+ // c.stream().forEach(spy);
+ // assertTrue(found.isEmpty());
+
+ // assertTrue(c.add(x));
+ // c.stream().forEach(spy);
+ // assertEquals(Collections.singletonList(x), found);
+ // found.clear();
+
+ // assertTrue(c.add(y));
+ // c.stream().forEach(spy);
+ // assertEquals(2, found.size());
+ // assertTrue(found.contains(x));
+ // assertTrue(found.contains(y));
+ // found.clear();
+
+ // c.clear();
+ // c.stream().forEach(spy);
+ // assertTrue(found.isEmpty());
+ // }
+
+ // public void testForEachConcurrentStressTest() throws Throwable {
+ // if (!impl.isConcurrent()) return;
+ // final Collection c = impl.emptyCollection();
+ // final long testDurationMillis = timeoutMillis();
+ // final AtomicBoolean done = new AtomicBoolean(false);
+ // final Object elt = impl.makeElement(1);
+ // final Future<?> f1, f2;
+ // final ExecutorService pool = Executors.newCachedThreadPool();
+ // try (PoolCleaner cleaner = cleaner(pool, done)) {
+ // final CountDownLatch threadsStarted = new CountDownLatch(2);
+ // Runnable checkElt = () -> {
+ // threadsStarted.countDown();
+ // while (!done.get())
+ // c.stream().forEach((x) -> { assertSame(x, elt); }); };
+ // Runnable addRemove = () -> {
+ // threadsStarted.countDown();
+ // while (!done.get()) {
+ // assertTrue(c.add(elt));
+ // assertTrue(c.remove(elt));
+ // }};
+ // f1 = pool.submit(checkElt);
+ // f2 = pool.submit(addRemove);
+ // Thread.sleep(testDurationMillis);
+ // }
+ // assertNull(f1.get(0L, MILLISECONDS));
+ // assertNull(f2.get(0L, MILLISECONDS));
+ // }
+
+ // public void testCollection8DebugFail() { fail(); }
+}
diff --git a/jsr166-tests/src/test/java/jsr166/CollectionImplementation.java b/jsr166-tests/src/test/java/jsr166/CollectionImplementation.java
new file mode 100644
index 0000000..4ba5bda
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/CollectionImplementation.java
@@ -0,0 +1,21 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import java.util.Collection;
+
+/** Allows tests to work with different Collection implementations. */
+public interface CollectionImplementation {
+ /** Returns the Collection class. */
+ public Class<?> klazz();
+ /** Returns an empty collection. */
+ public Collection emptyCollection();
+ public Object makeElement(int i);
+ public boolean isConcurrent();
+ public boolean permitsNulls();
+}
diff --git a/jsr166-tests/src/test/java/jsr166/CollectionTest.java b/jsr166-tests/src/test/java/jsr166/CollectionTest.java
new file mode 100644
index 0000000..44ef66d
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/CollectionTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import java.util.Collection;
+
+import junit.framework.Test;
+
+/**
+ * Contains tests applicable to all Collection implementations.
+ */
+public class CollectionTest extends JSR166TestCase {
+ final CollectionImplementation impl;
+
+ /** Tests are parameterized by a Collection implementation. */
+ CollectionTest(CollectionImplementation impl, String methodName) {
+ super(methodName);
+ this.impl = impl;
+ }
+
+ public static Test testSuite(CollectionImplementation impl) {
+ return newTestSuite
+ (parameterizedTestSuite(CollectionTest.class,
+ CollectionImplementation.class,
+ impl),
+ jdk8ParameterizedTestSuite(CollectionTest.class,
+ CollectionImplementation.class,
+ impl));
+ }
+
+ /** A test of the CollectionImplementation implementation ! */
+ public void testEmptyMeansEmpty() {
+ assertTrue(impl.emptyCollection().isEmpty());
+ assertEquals(0, impl.emptyCollection().size());
+ }
+
+ // public void testCollectionDebugFail() { fail(); }
+}
diff --git a/jsr166-tests/src/test/java/jsr166/CompletableFutureTest.java b/jsr166-tests/src/test/java/jsr166/CompletableFutureTest.java
new file mode 100644
index 0000000..1372cc4
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/CompletableFutureTest.java
@@ -0,0 +1,3959 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static java.util.concurrent.CompletableFuture.completedFuture;
+import static java.util.concurrent.CompletableFuture.failedFuture;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+// TODO(streams):
+//import java.util.stream.Collectors;
+//import java.util.stream.Stream;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class CompletableFutureTest extends JSR166TestCase {
+
+ // android-note: Removed because the CTS runner does a bad job of
+ // retrying tests that have suite() declarations.
+ //
+ // public static void main(String[] args) {
+ // main(suite(), args);
+ // }
+ // public static Test suite() {
+ // return new TestSuite(CompletableFutureTest.class);
+ // }
+
+ static class CFException extends RuntimeException {}
+
+ void checkIncomplete(CompletableFuture<?> f) {
+ assertFalse(f.isDone());
+ assertFalse(f.isCancelled());
+ assertTrue(f.toString().contains("Not completed"));
+ try {
+ assertNull(f.getNow(null));
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+ try {
+ f.get(0L, SECONDS);
+ shouldThrow();
+ }
+ catch (TimeoutException success) {}
+ catch (Throwable fail) { threadUnexpectedException(fail); }
+ }
+
+ <T> void checkCompletedNormally(CompletableFuture<T> f, T value) {
+ checkTimedGet(f, value);
+
+ try {
+ assertEquals(value, f.join());
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+ try {
+ assertEquals(value, f.getNow(null));
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+ try {
+ assertEquals(value, f.get());
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+ assertTrue(f.isDone());
+ assertFalse(f.isCancelled());
+ assertFalse(f.isCompletedExceptionally());
+ assertTrue(f.toString().contains("[Completed normally]"));
+ }
+
+ /**
+ * Returns the "raw" internal exceptional completion of f,
+ * without any additional wrapping with CompletionException.
+ */
+ <U> Throwable exceptionalCompletion(CompletableFuture<U> f) {
+ // handle (and whenComplete) can distinguish between "direct"
+ // and "wrapped" exceptional completion
+ return f.handle((U u, Throwable t) -> t).join();
+ }
+
+ void checkCompletedExceptionally(CompletableFuture<?> f,
+ boolean wrapped,
+ Consumer<Throwable> checker) {
+ Throwable cause = exceptionalCompletion(f);
+ if (wrapped) {
+ assertTrue(cause instanceof CompletionException);
+ cause = cause.getCause();
+ }
+ checker.accept(cause);
+
+ long startTime = System.nanoTime();
+ try {
+ f.get(LONG_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertSame(cause, success.getCause());
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2);
+
+ try {
+ f.join();
+ shouldThrow();
+ } catch (CompletionException success) {
+ assertSame(cause, success.getCause());
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+ try {
+ f.getNow(null);
+ shouldThrow();
+ } catch (CompletionException success) {
+ assertSame(cause, success.getCause());
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+ try {
+ f.get();
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertSame(cause, success.getCause());
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+ assertFalse(f.isCancelled());
+ assertTrue(f.isDone());
+ assertTrue(f.isCompletedExceptionally());
+ assertTrue(f.toString().contains("[Completed exceptionally]"));
+ }
+
+ void checkCompletedWithWrappedCFException(CompletableFuture<?> f) {
+ checkCompletedExceptionally(f, true,
+ (t) -> assertTrue(t instanceof CFException));
+ }
+
+ void checkCompletedWithWrappedCancellationException(CompletableFuture<?> f) {
+ checkCompletedExceptionally(f, true,
+ (t) -> assertTrue(t instanceof CancellationException));
+ }
+
+ void checkCompletedWithTimeoutException(CompletableFuture<?> f) {
+ checkCompletedExceptionally(f, false,
+ (t) -> assertTrue(t instanceof TimeoutException));
+ }
+
+ void checkCompletedWithWrappedException(CompletableFuture<?> f,
+ Throwable ex) {
+ checkCompletedExceptionally(f, true, (t) -> assertSame(t, ex));
+ }
+
+ void checkCompletedExceptionally(CompletableFuture<?> f, Throwable ex) {
+ checkCompletedExceptionally(f, false, (t) -> assertSame(t, ex));
+ }
+
+ void checkCancelled(CompletableFuture<?> f) {
+ long startTime = System.nanoTime();
+ try {
+ f.get(LONG_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (CancellationException success) {
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2);
+
+ try {
+ f.join();
+ shouldThrow();
+ } catch (CancellationException success) {}
+ try {
+ f.getNow(null);
+ shouldThrow();
+ } catch (CancellationException success) {}
+ try {
+ f.get();
+ shouldThrow();
+ } catch (CancellationException success) {
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+ assertTrue(exceptionalCompletion(f) instanceof CancellationException);
+
+ assertTrue(f.isDone());
+ assertTrue(f.isCompletedExceptionally());
+ assertTrue(f.isCancelled());
+ assertTrue(f.toString().contains("[Completed exceptionally]"));
+ }
+
+ /**
+ * A newly constructed CompletableFuture is incomplete, as indicated
+ * by methods isDone, isCancelled, and getNow
+ */
+ public void testConstructor() {
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ checkIncomplete(f);
+ }
+
+ /**
+ * complete completes normally, as indicated by methods isDone,
+ * isCancelled, join, get, and getNow
+ */
+ public void testComplete() {
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ checkIncomplete(f);
+ assertTrue(f.complete(v1));
+ assertFalse(f.complete(v1));
+ checkCompletedNormally(f, v1);
+ }}
+
+ /**
+ * completeExceptionally completes exceptionally, as indicated by
+ * methods isDone, isCancelled, join, get, and getNow
+ */
+ public void testCompleteExceptionally() {
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ CFException ex = new CFException();
+ checkIncomplete(f);
+ f.completeExceptionally(ex);
+ checkCompletedExceptionally(f, ex);
+ }
+
+ /**
+ * cancel completes exceptionally and reports cancelled, as indicated by
+ * methods isDone, isCancelled, join, get, and getNow
+ */
+ public void testCancel() {
+ for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+ {
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ checkIncomplete(f);
+ assertTrue(f.cancel(mayInterruptIfRunning));
+ assertTrue(f.cancel(mayInterruptIfRunning));
+ assertTrue(f.cancel(!mayInterruptIfRunning));
+ checkCancelled(f);
+ }}
+
+ /**
+ * obtrudeValue forces completion with given value
+ */
+ public void testObtrudeValue() {
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ checkIncomplete(f);
+ assertTrue(f.complete(one));
+ checkCompletedNormally(f, one);
+ f.obtrudeValue(three);
+ checkCompletedNormally(f, three);
+ f.obtrudeValue(two);
+ checkCompletedNormally(f, two);
+ f = new CompletableFuture<>();
+ f.obtrudeValue(three);
+ checkCompletedNormally(f, three);
+ f.obtrudeValue(null);
+ checkCompletedNormally(f, null);
+ f = new CompletableFuture<>();
+ f.completeExceptionally(new CFException());
+ f.obtrudeValue(four);
+ checkCompletedNormally(f, four);
+ }
+
+ /**
+ * obtrudeException forces completion with given exception
+ */
+ public void testObtrudeException() {
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ CFException ex;
+ CompletableFuture<Integer> f;
+
+ f = new CompletableFuture<>();
+ assertTrue(f.complete(v1));
+ for (int i = 0; i < 2; i++) {
+ f.obtrudeException(ex = new CFException());
+ checkCompletedExceptionally(f, ex);
+ }
+
+ f = new CompletableFuture<>();
+ for (int i = 0; i < 2; i++) {
+ f.obtrudeException(ex = new CFException());
+ checkCompletedExceptionally(f, ex);
+ }
+
+ f = new CompletableFuture<>();
+ f.completeExceptionally(ex = new CFException());
+ f.obtrudeValue(v1);
+ checkCompletedNormally(f, v1);
+ f.obtrudeException(ex = new CFException());
+ checkCompletedExceptionally(f, ex);
+ f.completeExceptionally(new CFException());
+ checkCompletedExceptionally(f, ex);
+ assertFalse(f.complete(v1));
+ checkCompletedExceptionally(f, ex);
+ }}
+
+ /**
+ * getNumberOfDependents returns number of dependent tasks
+ */
+ public void testGetNumberOfDependents() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ assertEquals(0, f.getNumberOfDependents());
+ final CompletableFuture<Void> g = m.thenRun(f, new Noop(m));
+ assertEquals(1, f.getNumberOfDependents());
+ assertEquals(0, g.getNumberOfDependents());
+ final CompletableFuture<Void> h = m.thenRun(f, new Noop(m));
+ assertEquals(2, f.getNumberOfDependents());
+ assertEquals(0, h.getNumberOfDependents());
+ assertTrue(f.complete(v1));
+ checkCompletedNormally(g, null);
+ checkCompletedNormally(h, null);
+ assertEquals(0, f.getNumberOfDependents());
+ assertEquals(0, g.getNumberOfDependents());
+ assertEquals(0, h.getNumberOfDependents());
+ }}
+
+ /**
+ * toString indicates current completion state
+ */
+ public void testToString() {
+ CompletableFuture<String> f;
+
+ f = new CompletableFuture<String>();
+ assertTrue(f.toString().contains("[Not completed]"));
+
+ assertTrue(f.complete("foo"));
+ assertTrue(f.toString().contains("[Completed normally]"));
+
+ f = new CompletableFuture<String>();
+ assertTrue(f.completeExceptionally(new IndexOutOfBoundsException()));
+ assertTrue(f.toString().contains("[Completed exceptionally]"));
+
+ for (boolean mayInterruptIfRunning : new boolean[] { true, false }) {
+ f = new CompletableFuture<String>();
+ assertTrue(f.cancel(mayInterruptIfRunning));
+ assertTrue(f.toString().contains("[Completed exceptionally]"));
+ }
+ }
+
+ /**
+ * completedFuture returns a completed CompletableFuture with given value
+ */
+ public void testCompletedFuture() {
+ CompletableFuture<String> f = CompletableFuture.completedFuture("test");
+ checkCompletedNormally(f, "test");
+ }
+
+ abstract class CheckedAction {
+ int invocationCount = 0;
+ final ExecutionMode m;
+ CheckedAction(ExecutionMode m) { this.m = m; }
+ void invoked() {
+ m.checkExecutionMode();
+ assertEquals(0, invocationCount++);
+ }
+ void assertNotInvoked() { assertEquals(0, invocationCount); }
+ void assertInvoked() { assertEquals(1, invocationCount); }
+ }
+
+ abstract class CheckedIntegerAction extends CheckedAction {
+ Integer value;
+ CheckedIntegerAction(ExecutionMode m) { super(m); }
+ void assertValue(Integer expected) {
+ assertInvoked();
+ assertEquals(expected, value);
+ }
+ }
+
+ class IntegerSupplier extends CheckedAction
+ implements Supplier<Integer>
+ {
+ final Integer value;
+ IntegerSupplier(ExecutionMode m, Integer value) {
+ super(m);
+ this.value = value;
+ }
+ public Integer get() {
+ invoked();
+ return value;
+ }
+ }
+
+ // A function that handles and produces null values as well.
+ static Integer inc(Integer x) {
+ return (x == null) ? null : x + 1;
+ }
+
+ class NoopConsumer extends CheckedIntegerAction
+ implements Consumer<Integer>
+ {
+ NoopConsumer(ExecutionMode m) { super(m); }
+ public void accept(Integer x) {
+ invoked();
+ value = x;
+ }
+ }
+
+ class IncFunction extends CheckedIntegerAction
+ implements Function<Integer,Integer>
+ {
+ IncFunction(ExecutionMode m) { super(m); }
+ public Integer apply(Integer x) {
+ invoked();
+ return value = inc(x);
+ }
+ }
+
+ // Choose non-commutative actions for better coverage
+ // A non-commutative function that handles and produces null values as well.
+ static Integer subtract(Integer x, Integer y) {
+ return (x == null && y == null) ? null :
+ ((x == null) ? 42 : x.intValue())
+ - ((y == null) ? 99 : y.intValue());
+ }
+
+ class SubtractAction extends CheckedIntegerAction
+ implements BiConsumer<Integer, Integer>
+ {
+ SubtractAction(ExecutionMode m) { super(m); }
+ public void accept(Integer x, Integer y) {
+ invoked();
+ value = subtract(x, y);
+ }
+ }
+
+ class SubtractFunction extends CheckedIntegerAction
+ implements BiFunction<Integer, Integer, Integer>
+ {
+ SubtractFunction(ExecutionMode m) { super(m); }
+ public Integer apply(Integer x, Integer y) {
+ invoked();
+ return value = subtract(x, y);
+ }
+ }
+
+ class Noop extends CheckedAction implements Runnable {
+ Noop(ExecutionMode m) { super(m); }
+ public void run() {
+ invoked();
+ }
+ }
+
+ class FailingSupplier extends CheckedAction
+ implements Supplier<Integer>
+ {
+ FailingSupplier(ExecutionMode m) { super(m); }
+ public Integer get() {
+ invoked();
+ throw new CFException();
+ }
+ }
+
+ class FailingConsumer extends CheckedIntegerAction
+ implements Consumer<Integer>
+ {
+ FailingConsumer(ExecutionMode m) { super(m); }
+ public void accept(Integer x) {
+ invoked();
+ value = x;
+ throw new CFException();
+ }
+ }
+
+ class FailingBiConsumer extends CheckedIntegerAction
+ implements BiConsumer<Integer, Integer>
+ {
+ FailingBiConsumer(ExecutionMode m) { super(m); }
+ public void accept(Integer x, Integer y) {
+ invoked();
+ value = subtract(x, y);
+ throw new CFException();
+ }
+ }
+
+ class FailingFunction extends CheckedIntegerAction
+ implements Function<Integer, Integer>
+ {
+ FailingFunction(ExecutionMode m) { super(m); }
+ public Integer apply(Integer x) {
+ invoked();
+ value = x;
+ throw new CFException();
+ }
+ }
+
+ class FailingBiFunction extends CheckedIntegerAction
+ implements BiFunction<Integer, Integer, Integer>
+ {
+ FailingBiFunction(ExecutionMode m) { super(m); }
+ public Integer apply(Integer x, Integer y) {
+ invoked();
+ value = subtract(x, y);
+ throw new CFException();
+ }
+ }
+
+ class FailingRunnable extends CheckedAction implements Runnable {
+ FailingRunnable(ExecutionMode m) { super(m); }
+ public void run() {
+ invoked();
+ throw new CFException();
+ }
+ }
+
+ class CompletableFutureInc extends CheckedIntegerAction
+ implements Function<Integer, CompletableFuture<Integer>>
+ {
+ CompletableFutureInc(ExecutionMode m) { super(m); }
+ public CompletableFuture<Integer> apply(Integer x) {
+ invoked();
+ value = x;
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ assertTrue(f.complete(inc(x)));
+ return f;
+ }
+ }
+
+ class FailingCompletableFutureFunction extends CheckedIntegerAction
+ implements Function<Integer, CompletableFuture<Integer>>
+ {
+ FailingCompletableFutureFunction(ExecutionMode m) { super(m); }
+ public CompletableFuture<Integer> apply(Integer x) {
+ invoked();
+ value = x;
+ throw new CFException();
+ }
+ }
+
+ // Used for explicit executor tests
+ static final class ThreadExecutor implements Executor {
+ final AtomicInteger count = new AtomicInteger(0);
+ static final ThreadGroup tg = new ThreadGroup("ThreadExecutor");
+ static boolean startedCurrentThread() {
+ return Thread.currentThread().getThreadGroup() == tg;
+ }
+
+ public void execute(Runnable r) {
+ count.getAndIncrement();
+ new Thread(tg, r).start();
+ }
+ }
+
+ static final boolean defaultExecutorIsCommonPool
+ = ForkJoinPool.getCommonPoolParallelism() > 1;
+
+ /**
+ * Permits the testing of parallel code for the 3 different
+ * execution modes without copy/pasting all the test methods.
+ */
+ enum ExecutionMode {
+ SYNC {
+ public void checkExecutionMode() {
+ assertFalse(ThreadExecutor.startedCurrentThread());
+ assertNull(ForkJoinTask.getPool());
+ }
+ public CompletableFuture<Void> runAsync(Runnable a) {
+ throw new UnsupportedOperationException();
+ }
+ public <U> CompletableFuture<U> supplyAsync(Supplier<U> a) {
+ throw new UnsupportedOperationException();
+ }
+ public <T> CompletableFuture<Void> thenRun
+ (CompletableFuture<T> f, Runnable a) {
+ return f.thenRun(a);
+ }
+ public <T> CompletableFuture<Void> thenAccept
+ (CompletableFuture<T> f, Consumer<? super T> a) {
+ return f.thenAccept(a);
+ }
+ public <T,U> CompletableFuture<U> thenApply
+ (CompletableFuture<T> f, Function<? super T,U> a) {
+ return f.thenApply(a);
+ }
+ public <T,U> CompletableFuture<U> thenCompose
+ (CompletableFuture<T> f,
+ Function<? super T,? extends CompletionStage<U>> a) {
+ return f.thenCompose(a);
+ }
+ public <T,U> CompletableFuture<U> handle
+ (CompletableFuture<T> f,
+ BiFunction<? super T,Throwable,? extends U> a) {
+ return f.handle(a);
+ }
+ public <T> CompletableFuture<T> whenComplete
+ (CompletableFuture<T> f,
+ BiConsumer<? super T,? super Throwable> a) {
+ return f.whenComplete(a);
+ }
+ public <T,U> CompletableFuture<Void> runAfterBoth
+ (CompletableFuture<T> f, CompletableFuture<U> g, Runnable a) {
+ return f.runAfterBoth(g, a);
+ }
+ public <T,U> CompletableFuture<Void> thenAcceptBoth
+ (CompletableFuture<T> f,
+ CompletionStage<? extends U> g,
+ BiConsumer<? super T,? super U> a) {
+ return f.thenAcceptBoth(g, a);
+ }
+ public <T,U,V> CompletableFuture<V> thenCombine
+ (CompletableFuture<T> f,
+ CompletionStage<? extends U> g,
+ BiFunction<? super T,? super U,? extends V> a) {
+ return f.thenCombine(g, a);
+ }
+ public <T> CompletableFuture<Void> runAfterEither
+ (CompletableFuture<T> f,
+ CompletionStage<?> g,
+ java.lang.Runnable a) {
+ return f.runAfterEither(g, a);
+ }
+ public <T> CompletableFuture<Void> acceptEither
+ (CompletableFuture<T> f,
+ CompletionStage<? extends T> g,
+ Consumer<? super T> a) {
+ return f.acceptEither(g, a);
+ }
+ public <T,U> CompletableFuture<U> applyToEither
+ (CompletableFuture<T> f,
+ CompletionStage<? extends T> g,
+ Function<? super T,U> a) {
+ return f.applyToEither(g, a);
+ }
+ },
+
+ ASYNC {
+ public void checkExecutionMode() {
+ assertEquals(defaultExecutorIsCommonPool,
+ (ForkJoinPool.commonPool() == ForkJoinTask.getPool()));
+ }
+ public CompletableFuture<Void> runAsync(Runnable a) {
+ return CompletableFuture.runAsync(a);
+ }
+ public <U> CompletableFuture<U> supplyAsync(Supplier<U> a) {
+ return CompletableFuture.supplyAsync(a);
+ }
+ public <T> CompletableFuture<Void> thenRun
+ (CompletableFuture<T> f, Runnable a) {
+ return f.thenRunAsync(a);
+ }
+ public <T> CompletableFuture<Void> thenAccept
+ (CompletableFuture<T> f, Consumer<? super T> a) {
+ return f.thenAcceptAsync(a);
+ }
+ public <T,U> CompletableFuture<U> thenApply
+ (CompletableFuture<T> f, Function<? super T,U> a) {
+ return f.thenApplyAsync(a);
+ }
+ public <T,U> CompletableFuture<U> thenCompose
+ (CompletableFuture<T> f,
+ Function<? super T,? extends CompletionStage<U>> a) {
+ return f.thenComposeAsync(a);
+ }
+ public <T,U> CompletableFuture<U> handle
+ (CompletableFuture<T> f,
+ BiFunction<? super T,Throwable,? extends U> a) {
+ return f.handleAsync(a);
+ }
+ public <T> CompletableFuture<T> whenComplete
+ (CompletableFuture<T> f,
+ BiConsumer<? super T,? super Throwable> a) {
+ return f.whenCompleteAsync(a);
+ }
+ public <T,U> CompletableFuture<Void> runAfterBoth
+ (CompletableFuture<T> f, CompletableFuture<U> g, Runnable a) {
+ return f.runAfterBothAsync(g, a);
+ }
+ public <T,U> CompletableFuture<Void> thenAcceptBoth
+ (CompletableFuture<T> f,
+ CompletionStage<? extends U> g,
+ BiConsumer<? super T,? super U> a) {
+ return f.thenAcceptBothAsync(g, a);
+ }
+ public <T,U,V> CompletableFuture<V> thenCombine
+ (CompletableFuture<T> f,
+ CompletionStage<? extends U> g,
+ BiFunction<? super T,? super U,? extends V> a) {
+ return f.thenCombineAsync(g, a);
+ }
+ public <T> CompletableFuture<Void> runAfterEither
+ (CompletableFuture<T> f,
+ CompletionStage<?> g,
+ java.lang.Runnable a) {
+ return f.runAfterEitherAsync(g, a);
+ }
+ public <T> CompletableFuture<Void> acceptEither
+ (CompletableFuture<T> f,
+ CompletionStage<? extends T> g,
+ Consumer<? super T> a) {
+ return f.acceptEitherAsync(g, a);
+ }
+ public <T,U> CompletableFuture<U> applyToEither
+ (CompletableFuture<T> f,
+ CompletionStage<? extends T> g,
+ Function<? super T,U> a) {
+ return f.applyToEitherAsync(g, a);
+ }
+ },
+
+ EXECUTOR {
+ public void checkExecutionMode() {
+ assertTrue(ThreadExecutor.startedCurrentThread());
+ }
+ public CompletableFuture<Void> runAsync(Runnable a) {
+ return CompletableFuture.runAsync(a, new ThreadExecutor());
+ }
+ public <U> CompletableFuture<U> supplyAsync(Supplier<U> a) {
+ return CompletableFuture.supplyAsync(a, new ThreadExecutor());
+ }
+ public <T> CompletableFuture<Void> thenRun
+ (CompletableFuture<T> f, Runnable a) {
+ return f.thenRunAsync(a, new ThreadExecutor());
+ }
+ public <T> CompletableFuture<Void> thenAccept
+ (CompletableFuture<T> f, Consumer<? super T> a) {
+ return f.thenAcceptAsync(a, new ThreadExecutor());
+ }
+ public <T,U> CompletableFuture<U> thenApply
+ (CompletableFuture<T> f, Function<? super T,U> a) {
+ return f.thenApplyAsync(a, new ThreadExecutor());
+ }
+ public <T,U> CompletableFuture<U> thenCompose
+ (CompletableFuture<T> f,
+ Function<? super T,? extends CompletionStage<U>> a) {
+ return f.thenComposeAsync(a, new ThreadExecutor());
+ }
+ public <T,U> CompletableFuture<U> handle
+ (CompletableFuture<T> f,
+ BiFunction<? super T,Throwable,? extends U> a) {
+ return f.handleAsync(a, new ThreadExecutor());
+ }
+ public <T> CompletableFuture<T> whenComplete
+ (CompletableFuture<T> f,
+ BiConsumer<? super T,? super Throwable> a) {
+ return f.whenCompleteAsync(a, new ThreadExecutor());
+ }
+ public <T,U> CompletableFuture<Void> runAfterBoth
+ (CompletableFuture<T> f, CompletableFuture<U> g, Runnable a) {
+ return f.runAfterBothAsync(g, a, new ThreadExecutor());
+ }
+ public <T,U> CompletableFuture<Void> thenAcceptBoth
+ (CompletableFuture<T> f,
+ CompletionStage<? extends U> g,
+ BiConsumer<? super T,? super U> a) {
+ return f.thenAcceptBothAsync(g, a, new ThreadExecutor());
+ }
+ public <T,U,V> CompletableFuture<V> thenCombine
+ (CompletableFuture<T> f,
+ CompletionStage<? extends U> g,
+ BiFunction<? super T,? super U,? extends V> a) {
+ return f.thenCombineAsync(g, a, new ThreadExecutor());
+ }
+ public <T> CompletableFuture<Void> runAfterEither
+ (CompletableFuture<T> f,
+ CompletionStage<?> g,
+ java.lang.Runnable a) {
+ return f.runAfterEitherAsync(g, a, new ThreadExecutor());
+ }
+ public <T> CompletableFuture<Void> acceptEither
+ (CompletableFuture<T> f,
+ CompletionStage<? extends T> g,
+ Consumer<? super T> a) {
+ return f.acceptEitherAsync(g, a, new ThreadExecutor());
+ }
+ public <T,U> CompletableFuture<U> applyToEither
+ (CompletableFuture<T> f,
+ CompletionStage<? extends T> g,
+ Function<? super T,U> a) {
+ return f.applyToEitherAsync(g, a, new ThreadExecutor());
+ }
+ };
+
+ public abstract void checkExecutionMode();
+ public abstract CompletableFuture<Void> runAsync(Runnable a);
+ public abstract <U> CompletableFuture<U> supplyAsync(Supplier<U> a);
+ public abstract <T> CompletableFuture<Void> thenRun
+ (CompletableFuture<T> f, Runnable a);
+ public abstract <T> CompletableFuture<Void> thenAccept
+ (CompletableFuture<T> f, Consumer<? super T> a);
+ public abstract <T,U> CompletableFuture<U> thenApply
+ (CompletableFuture<T> f, Function<? super T,U> a);
+ public abstract <T,U> CompletableFuture<U> thenCompose
+ (CompletableFuture<T> f,
+ Function<? super T,? extends CompletionStage<U>> a);
+ public abstract <T,U> CompletableFuture<U> handle
+ (CompletableFuture<T> f,
+ BiFunction<? super T,Throwable,? extends U> a);
+ public abstract <T> CompletableFuture<T> whenComplete
+ (CompletableFuture<T> f,
+ BiConsumer<? super T,? super Throwable> a);
+ public abstract <T,U> CompletableFuture<Void> runAfterBoth
+ (CompletableFuture<T> f, CompletableFuture<U> g, Runnable a);
+ public abstract <T,U> CompletableFuture<Void> thenAcceptBoth
+ (CompletableFuture<T> f,
+ CompletionStage<? extends U> g,
+ BiConsumer<? super T,? super U> a);
+ public abstract <T,U,V> CompletableFuture<V> thenCombine
+ (CompletableFuture<T> f,
+ CompletionStage<? extends U> g,
+ BiFunction<? super T,? super U,? extends V> a);
+ public abstract <T> CompletableFuture<Void> runAfterEither
+ (CompletableFuture<T> f,
+ CompletionStage<?> g,
+ java.lang.Runnable a);
+ public abstract <T> CompletableFuture<Void> acceptEither
+ (CompletableFuture<T> f,
+ CompletionStage<? extends T> g,
+ Consumer<? super T> a);
+ public abstract <T,U> CompletableFuture<U> applyToEither
+ (CompletableFuture<T> f,
+ CompletionStage<? extends T> g,
+ Function<? super T,U> a);
+ }
+
+ /**
+ * exceptionally action is not invoked when source completes
+ * normally, and source result is propagated
+ */
+ public void testExceptionally_normalCompletion() {
+ for (boolean createIncomplete : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final AtomicInteger a = new AtomicInteger(0);
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ if (!createIncomplete) assertTrue(f.complete(v1));
+ final CompletableFuture<Integer> g = f.exceptionally
+ ((Throwable t) -> {
+ a.getAndIncrement();
+ threadFail("should not be called");
+ return null; // unreached
+ });
+ if (createIncomplete) assertTrue(f.complete(v1));
+
+ checkCompletedNormally(g, v1);
+ checkCompletedNormally(f, v1);
+ assertEquals(0, a.get());
+ }}
+
+ /**
+ * exceptionally action completes with function value on source
+ * exception
+ */
+ public void testExceptionally_exceptionalCompletion() {
+ for (boolean createIncomplete : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final AtomicInteger a = new AtomicInteger(0);
+ final CFException ex = new CFException();
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ if (!createIncomplete) f.completeExceptionally(ex);
+ final CompletableFuture<Integer> g = f.exceptionally
+ ((Throwable t) -> {
+ ExecutionMode.SYNC.checkExecutionMode();
+ threadAssertSame(t, ex);
+ a.getAndIncrement();
+ return v1;
+ });
+ if (createIncomplete) f.completeExceptionally(ex);
+
+ checkCompletedNormally(g, v1);
+ assertEquals(1, a.get());
+ }}
+
+ /**
+ * If an "exceptionally action" throws an exception, it completes
+ * exceptionally with that exception
+ */
+ public void testExceptionally_exceptionalCompletionActionFailed() {
+ for (boolean createIncomplete : new boolean[] { true, false })
+ {
+ final AtomicInteger a = new AtomicInteger(0);
+ final CFException ex1 = new CFException();
+ final CFException ex2 = new CFException();
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ if (!createIncomplete) f.completeExceptionally(ex1);
+ final CompletableFuture<Integer> g = f.exceptionally
+ ((Throwable t) -> {
+ ExecutionMode.SYNC.checkExecutionMode();
+ threadAssertSame(t, ex1);
+ a.getAndIncrement();
+ throw ex2;
+ });
+ if (createIncomplete) f.completeExceptionally(ex1);
+
+ checkCompletedWithWrappedException(g, ex2);
+ checkCompletedExceptionally(f, ex1);
+ assertEquals(1, a.get());
+ }}
+
+ /**
+ * whenComplete action executes on normal completion, propagating
+ * source result.
+ */
+ public void testWhenComplete_normalCompletion() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean createIncomplete : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final AtomicInteger a = new AtomicInteger(0);
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ if (!createIncomplete) assertTrue(f.complete(v1));
+ final CompletableFuture<Integer> g = m.whenComplete
+ (f,
+ (Integer result, Throwable t) -> {
+ m.checkExecutionMode();
+ threadAssertSame(result, v1);
+ threadAssertNull(t);
+ a.getAndIncrement();
+ });
+ if (createIncomplete) assertTrue(f.complete(v1));
+
+ checkCompletedNormally(g, v1);
+ checkCompletedNormally(f, v1);
+ assertEquals(1, a.get());
+ }}
+
+ /**
+ * whenComplete action executes on exceptional completion, propagating
+ * source result.
+ */
+ public void testWhenComplete_exceptionalCompletion() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean createIncomplete : new boolean[] { true, false })
+ {
+ final AtomicInteger a = new AtomicInteger(0);
+ final CFException ex = new CFException();
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ if (!createIncomplete) f.completeExceptionally(ex);
+ final CompletableFuture<Integer> g = m.whenComplete
+ (f,
+ (Integer result, Throwable t) -> {
+ m.checkExecutionMode();
+ threadAssertNull(result);
+ threadAssertSame(t, ex);
+ a.getAndIncrement();
+ });
+ if (createIncomplete) f.completeExceptionally(ex);
+
+ checkCompletedWithWrappedException(g, ex);
+ checkCompletedExceptionally(f, ex);
+ assertEquals(1, a.get());
+ }}
+
+ /**
+ * whenComplete action executes on cancelled source, propagating
+ * CancellationException.
+ */
+ public void testWhenComplete_sourceCancelled() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+ for (boolean createIncomplete : new boolean[] { true, false })
+ {
+ final AtomicInteger a = new AtomicInteger(0);
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ if (!createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning));
+ final CompletableFuture<Integer> g = m.whenComplete
+ (f,
+ (Integer result, Throwable t) -> {
+ m.checkExecutionMode();
+ threadAssertNull(result);
+ threadAssertTrue(t instanceof CancellationException);
+ a.getAndIncrement();
+ });
+ if (createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning));
+
+ checkCompletedWithWrappedCancellationException(g);
+ checkCancelled(f);
+ assertEquals(1, a.get());
+ }}
+
+ /**
+ * If a whenComplete action throws an exception when triggered by
+ * a normal completion, it completes exceptionally
+ */
+ public void testWhenComplete_sourceCompletedNormallyActionFailed() {
+ for (boolean createIncomplete : new boolean[] { true, false })
+ for (ExecutionMode m : ExecutionMode.values())
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final AtomicInteger a = new AtomicInteger(0);
+ final CFException ex = new CFException();
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ if (!createIncomplete) assertTrue(f.complete(v1));
+ final CompletableFuture<Integer> g = m.whenComplete
+ (f,
+ (Integer result, Throwable t) -> {
+ m.checkExecutionMode();
+ threadAssertSame(result, v1);
+ threadAssertNull(t);
+ a.getAndIncrement();
+ throw ex;
+ });
+ if (createIncomplete) assertTrue(f.complete(v1));
+
+ checkCompletedWithWrappedException(g, ex);
+ checkCompletedNormally(f, v1);
+ assertEquals(1, a.get());
+ }}
+
+ /**
+ * If a whenComplete action throws an exception when triggered by
+ * a source completion that also throws an exception, the source
+ * exception takes precedence (unlike handle)
+ */
+ public void testWhenComplete_sourceFailedActionFailed() {
+ for (boolean createIncomplete : new boolean[] { true, false })
+ for (ExecutionMode m : ExecutionMode.values())
+ {
+ final AtomicInteger a = new AtomicInteger(0);
+ final CFException ex1 = new CFException();
+ final CFException ex2 = new CFException();
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+
+ if (!createIncomplete) f.completeExceptionally(ex1);
+ final CompletableFuture<Integer> g = m.whenComplete
+ (f,
+ (Integer result, Throwable t) -> {
+ m.checkExecutionMode();
+ threadAssertSame(t, ex1);
+ threadAssertNull(result);
+ a.getAndIncrement();
+ throw ex2;
+ });
+ if (createIncomplete) f.completeExceptionally(ex1);
+
+ checkCompletedWithWrappedException(g, ex1);
+ checkCompletedExceptionally(f, ex1);
+ if (testImplementationDetails) {
+ assertEquals(1, ex1.getSuppressed().length);
+ assertSame(ex2, ex1.getSuppressed()[0]);
+ }
+ assertEquals(1, a.get());
+ }}
+
+ /**
+ * handle action completes normally with function value on normal
+ * completion of source
+ */
+ public void testHandle_normalCompletion() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean createIncomplete : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final AtomicInteger a = new AtomicInteger(0);
+ if (!createIncomplete) assertTrue(f.complete(v1));
+ final CompletableFuture<Integer> g = m.handle
+ (f,
+ (Integer result, Throwable t) -> {
+ m.checkExecutionMode();
+ threadAssertSame(result, v1);
+ threadAssertNull(t);
+ a.getAndIncrement();
+ return inc(v1);
+ });
+ if (createIncomplete) assertTrue(f.complete(v1));
+
+ checkCompletedNormally(g, inc(v1));
+ checkCompletedNormally(f, v1);
+ assertEquals(1, a.get());
+ }}
+
+ /**
+ * handle action completes normally with function value on
+ * exceptional completion of source
+ */
+ public void testHandle_exceptionalCompletion() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean createIncomplete : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final AtomicInteger a = new AtomicInteger(0);
+ final CFException ex = new CFException();
+ if (!createIncomplete) f.completeExceptionally(ex);
+ final CompletableFuture<Integer> g = m.handle
+ (f,
+ (Integer result, Throwable t) -> {
+ m.checkExecutionMode();
+ threadAssertNull(result);
+ threadAssertSame(t, ex);
+ a.getAndIncrement();
+ return v1;
+ });
+ if (createIncomplete) f.completeExceptionally(ex);
+
+ checkCompletedNormally(g, v1);
+ checkCompletedExceptionally(f, ex);
+ assertEquals(1, a.get());
+ }}
+
+ /**
+ * handle action completes normally with function value on
+ * cancelled source
+ */
+ public void testHandle_sourceCancelled() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+ for (boolean createIncomplete : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final AtomicInteger a = new AtomicInteger(0);
+ if (!createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning));
+ final CompletableFuture<Integer> g = m.handle
+ (f,
+ (Integer result, Throwable t) -> {
+ m.checkExecutionMode();
+ threadAssertNull(result);
+ threadAssertTrue(t instanceof CancellationException);
+ a.getAndIncrement();
+ return v1;
+ });
+ if (createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning));
+
+ checkCompletedNormally(g, v1);
+ checkCancelled(f);
+ assertEquals(1, a.get());
+ }}
+
+ /**
+ * If a "handle action" throws an exception when triggered by
+ * a normal completion, it completes exceptionally
+ */
+ public void testHandle_sourceCompletedNormallyActionFailed() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean createIncomplete : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final AtomicInteger a = new AtomicInteger(0);
+ final CFException ex = new CFException();
+ if (!createIncomplete) assertTrue(f.complete(v1));
+ final CompletableFuture<Integer> g = m.handle
+ (f,
+ (Integer result, Throwable t) -> {
+ m.checkExecutionMode();
+ threadAssertSame(result, v1);
+ threadAssertNull(t);
+ a.getAndIncrement();
+ throw ex;
+ });
+ if (createIncomplete) assertTrue(f.complete(v1));
+
+ checkCompletedWithWrappedException(g, ex);
+ checkCompletedNormally(f, v1);
+ assertEquals(1, a.get());
+ }}
+
+ /**
+ * If a "handle action" throws an exception when triggered by
+ * a source completion that also throws an exception, the action
+ * exception takes precedence (unlike whenComplete)
+ */
+ public void testHandle_sourceFailedActionFailed() {
+ for (boolean createIncomplete : new boolean[] { true, false })
+ for (ExecutionMode m : ExecutionMode.values())
+ {
+ final AtomicInteger a = new AtomicInteger(0);
+ final CFException ex1 = new CFException();
+ final CFException ex2 = new CFException();
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+
+ if (!createIncomplete) f.completeExceptionally(ex1);
+ final CompletableFuture<Integer> g = m.handle
+ (f,
+ (Integer result, Throwable t) -> {
+ m.checkExecutionMode();
+ threadAssertNull(result);
+ threadAssertSame(ex1, t);
+ a.getAndIncrement();
+ throw ex2;
+ });
+ if (createIncomplete) f.completeExceptionally(ex1);
+
+ checkCompletedWithWrappedException(g, ex2);
+ checkCompletedExceptionally(f, ex1);
+ assertEquals(1, a.get());
+ }}
+
+ /**
+ * runAsync completes after running Runnable
+ */
+ public void testRunAsync_normalCompletion() {
+ ExecutionMode[] executionModes = {
+ ExecutionMode.ASYNC,
+ ExecutionMode.EXECUTOR,
+ };
+ for (ExecutionMode m : executionModes)
+ {
+ final Noop r = new Noop(m);
+ final CompletableFuture<Void> f = m.runAsync(r);
+ assertNull(f.join());
+ checkCompletedNormally(f, null);
+ r.assertInvoked();
+ }}
+
+ /**
+ * failing runAsync completes exceptionally after running Runnable
+ */
+ public void testRunAsync_exceptionalCompletion() {
+ ExecutionMode[] executionModes = {
+ ExecutionMode.ASYNC,
+ ExecutionMode.EXECUTOR,
+ };
+ for (ExecutionMode m : executionModes)
+ {
+ final FailingRunnable r = new FailingRunnable(m);
+ final CompletableFuture<Void> f = m.runAsync(r);
+ checkCompletedWithWrappedCFException(f);
+ r.assertInvoked();
+ }}
+
+ /**
+ * supplyAsync completes with result of supplier
+ */
+ public void testSupplyAsync_normalCompletion() {
+ ExecutionMode[] executionModes = {
+ ExecutionMode.ASYNC,
+ ExecutionMode.EXECUTOR,
+ };
+ for (ExecutionMode m : executionModes)
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final IntegerSupplier r = new IntegerSupplier(m, v1);
+ final CompletableFuture<Integer> f = m.supplyAsync(r);
+ assertSame(v1, f.join());
+ checkCompletedNormally(f, v1);
+ r.assertInvoked();
+ }}
+
+ /**
+ * Failing supplyAsync completes exceptionally
+ */
+ public void testSupplyAsync_exceptionalCompletion() {
+ ExecutionMode[] executionModes = {
+ ExecutionMode.ASYNC,
+ ExecutionMode.EXECUTOR,
+ };
+ for (ExecutionMode m : executionModes)
+ {
+ FailingSupplier r = new FailingSupplier(m);
+ CompletableFuture<Integer> f = m.supplyAsync(r);
+ checkCompletedWithWrappedCFException(f);
+ r.assertInvoked();
+ }}
+
+ // seq completion methods
+
+ /**
+ * thenRun result completes normally after normal completion of source
+ */
+ public void testThenRun_normalCompletion() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final Noop[] rs = new Noop[6];
+ for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m);
+
+ final CompletableFuture<Void> h0 = m.thenRun(f, rs[0]);
+ final CompletableFuture<Void> h1 = m.runAfterBoth(f, f, rs[1]);
+ final CompletableFuture<Void> h2 = m.runAfterEither(f, f, rs[2]);
+ checkIncomplete(h0);
+ checkIncomplete(h1);
+ checkIncomplete(h2);
+ assertTrue(f.complete(v1));
+ final CompletableFuture<Void> h3 = m.thenRun(f, rs[3]);
+ final CompletableFuture<Void> h4 = m.runAfterBoth(f, f, rs[4]);
+ final CompletableFuture<Void> h5 = m.runAfterEither(f, f, rs[5]);
+
+ checkCompletedNormally(h0, null);
+ checkCompletedNormally(h1, null);
+ checkCompletedNormally(h2, null);
+ checkCompletedNormally(h3, null);
+ checkCompletedNormally(h4, null);
+ checkCompletedNormally(h5, null);
+ checkCompletedNormally(f, v1);
+ for (Noop r : rs) r.assertInvoked();
+ }}
+
+ /**
+ * thenRun result completes exceptionally after exceptional
+ * completion of source
+ */
+ public void testThenRun_exceptionalCompletion() {
+ for (ExecutionMode m : ExecutionMode.values())
+ {
+ final CFException ex = new CFException();
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final Noop[] rs = new Noop[6];
+ for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m);
+
+ final CompletableFuture<Void> h0 = m.thenRun(f, rs[0]);
+ final CompletableFuture<Void> h1 = m.runAfterBoth(f, f, rs[1]);
+ final CompletableFuture<Void> h2 = m.runAfterEither(f, f, rs[2]);
+ checkIncomplete(h0);
+ checkIncomplete(h1);
+ checkIncomplete(h2);
+ assertTrue(f.completeExceptionally(ex));
+ final CompletableFuture<Void> h3 = m.thenRun(f, rs[3]);
+ final CompletableFuture<Void> h4 = m.runAfterBoth(f, f, rs[4]);
+ final CompletableFuture<Void> h5 = m.runAfterEither(f, f, rs[5]);
+
+ checkCompletedWithWrappedException(h0, ex);
+ checkCompletedWithWrappedException(h1, ex);
+ checkCompletedWithWrappedException(h2, ex);
+ checkCompletedWithWrappedException(h3, ex);
+ checkCompletedWithWrappedException(h4, ex);
+ checkCompletedWithWrappedException(h5, ex);
+ checkCompletedExceptionally(f, ex);
+ for (Noop r : rs) r.assertNotInvoked();
+ }}
+
+ /**
+ * thenRun result completes exceptionally if source cancelled
+ */
+ public void testThenRun_sourceCancelled() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final Noop[] rs = new Noop[6];
+ for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m);
+
+ final CompletableFuture<Void> h0 = m.thenRun(f, rs[0]);
+ final CompletableFuture<Void> h1 = m.runAfterBoth(f, f, rs[1]);
+ final CompletableFuture<Void> h2 = m.runAfterEither(f, f, rs[2]);
+ checkIncomplete(h0);
+ checkIncomplete(h1);
+ checkIncomplete(h2);
+ assertTrue(f.cancel(mayInterruptIfRunning));
+ final CompletableFuture<Void> h3 = m.thenRun(f, rs[3]);
+ final CompletableFuture<Void> h4 = m.runAfterBoth(f, f, rs[4]);
+ final CompletableFuture<Void> h5 = m.runAfterEither(f, f, rs[5]);
+
+ checkCompletedWithWrappedCancellationException(h0);
+ checkCompletedWithWrappedCancellationException(h1);
+ checkCompletedWithWrappedCancellationException(h2);
+ checkCompletedWithWrappedCancellationException(h3);
+ checkCompletedWithWrappedCancellationException(h4);
+ checkCompletedWithWrappedCancellationException(h5);
+ checkCancelled(f);
+ for (Noop r : rs) r.assertNotInvoked();
+ }}
+
+ /**
+ * thenRun result completes exceptionally if action does
+ */
+ public void testThenRun_actionFailed() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final FailingRunnable[] rs = new FailingRunnable[6];
+ for (int i = 0; i < rs.length; i++) rs[i] = new FailingRunnable(m);
+
+ final CompletableFuture<Void> h0 = m.thenRun(f, rs[0]);
+ final CompletableFuture<Void> h1 = m.runAfterBoth(f, f, rs[1]);
+ final CompletableFuture<Void> h2 = m.runAfterEither(f, f, rs[2]);
+ assertTrue(f.complete(v1));
+ final CompletableFuture<Void> h3 = m.thenRun(f, rs[3]);
+ final CompletableFuture<Void> h4 = m.runAfterBoth(f, f, rs[4]);
+ final CompletableFuture<Void> h5 = m.runAfterEither(f, f, rs[5]);
+
+ checkCompletedWithWrappedCFException(h0);
+ checkCompletedWithWrappedCFException(h1);
+ checkCompletedWithWrappedCFException(h2);
+ checkCompletedWithWrappedCFException(h3);
+ checkCompletedWithWrappedCFException(h4);
+ checkCompletedWithWrappedCFException(h5);
+ checkCompletedNormally(f, v1);
+ }}
+
+ /**
+ * thenApply result completes normally after normal completion of source
+ */
+ public void testThenApply_normalCompletion() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final IncFunction[] rs = new IncFunction[4];
+ for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m);
+
+ final CompletableFuture<Integer> h0 = m.thenApply(f, rs[0]);
+ final CompletableFuture<Integer> h1 = m.applyToEither(f, f, rs[1]);
+ checkIncomplete(h0);
+ checkIncomplete(h1);
+ assertTrue(f.complete(v1));
+ final CompletableFuture<Integer> h2 = m.thenApply(f, rs[2]);
+ final CompletableFuture<Integer> h3 = m.applyToEither(f, f, rs[3]);
+
+ checkCompletedNormally(h0, inc(v1));
+ checkCompletedNormally(h1, inc(v1));
+ checkCompletedNormally(h2, inc(v1));
+ checkCompletedNormally(h3, inc(v1));
+ checkCompletedNormally(f, v1);
+ for (IncFunction r : rs) r.assertValue(inc(v1));
+ }}
+
+ /**
+ * thenApply result completes exceptionally after exceptional
+ * completion of source
+ */
+ public void testThenApply_exceptionalCompletion() {
+ for (ExecutionMode m : ExecutionMode.values())
+ {
+ final CFException ex = new CFException();
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final IncFunction[] rs = new IncFunction[4];
+ for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m);
+
+ final CompletableFuture<Integer> h0 = m.thenApply(f, rs[0]);
+ final CompletableFuture<Integer> h1 = m.applyToEither(f, f, rs[1]);
+ assertTrue(f.completeExceptionally(ex));
+ final CompletableFuture<Integer> h2 = m.thenApply(f, rs[2]);
+ final CompletableFuture<Integer> h3 = m.applyToEither(f, f, rs[3]);
+
+ checkCompletedWithWrappedException(h0, ex);
+ checkCompletedWithWrappedException(h1, ex);
+ checkCompletedWithWrappedException(h2, ex);
+ checkCompletedWithWrappedException(h3, ex);
+ checkCompletedExceptionally(f, ex);
+ for (IncFunction r : rs) r.assertNotInvoked();
+ }}
+
+ /**
+ * thenApply result completes exceptionally if source cancelled
+ */
+ public void testThenApply_sourceCancelled() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final IncFunction[] rs = new IncFunction[4];
+ for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m);
+
+ final CompletableFuture<Integer> h0 = m.thenApply(f, rs[0]);
+ final CompletableFuture<Integer> h1 = m.applyToEither(f, f, rs[1]);
+ assertTrue(f.cancel(mayInterruptIfRunning));
+ final CompletableFuture<Integer> h2 = m.thenApply(f, rs[2]);
+ final CompletableFuture<Integer> h3 = m.applyToEither(f, f, rs[3]);
+
+ checkCompletedWithWrappedCancellationException(h0);
+ checkCompletedWithWrappedCancellationException(h1);
+ checkCompletedWithWrappedCancellationException(h2);
+ checkCompletedWithWrappedCancellationException(h3);
+ checkCancelled(f);
+ for (IncFunction r : rs) r.assertNotInvoked();
+ }}
+
+ /**
+ * thenApply result completes exceptionally if action does
+ */
+ public void testThenApply_actionFailed() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final FailingFunction[] rs = new FailingFunction[4];
+ for (int i = 0; i < rs.length; i++) rs[i] = new FailingFunction(m);
+
+ final CompletableFuture<Integer> h0 = m.thenApply(f, rs[0]);
+ final CompletableFuture<Integer> h1 = m.applyToEither(f, f, rs[1]);
+ assertTrue(f.complete(v1));
+ final CompletableFuture<Integer> h2 = m.thenApply(f, rs[2]);
+ final CompletableFuture<Integer> h3 = m.applyToEither(f, f, rs[3]);
+
+ checkCompletedWithWrappedCFException(h0);
+ checkCompletedWithWrappedCFException(h1);
+ checkCompletedWithWrappedCFException(h2);
+ checkCompletedWithWrappedCFException(h3);
+ checkCompletedNormally(f, v1);
+ }}
+
+ /**
+ * thenAccept result completes normally after normal completion of source
+ */
+ public void testThenAccept_normalCompletion() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final NoopConsumer[] rs = new NoopConsumer[4];
+ for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m);
+
+ final CompletableFuture<Void> h0 = m.thenAccept(f, rs[0]);
+ final CompletableFuture<Void> h1 = m.acceptEither(f, f, rs[1]);
+ checkIncomplete(h0);
+ checkIncomplete(h1);
+ assertTrue(f.complete(v1));
+ final CompletableFuture<Void> h2 = m.thenAccept(f, rs[2]);
+ final CompletableFuture<Void> h3 = m.acceptEither(f, f, rs[3]);
+
+ checkCompletedNormally(h0, null);
+ checkCompletedNormally(h1, null);
+ checkCompletedNormally(h2, null);
+ checkCompletedNormally(h3, null);
+ checkCompletedNormally(f, v1);
+ for (NoopConsumer r : rs) r.assertValue(v1);
+ }}
+
+ /**
+ * thenAccept result completes exceptionally after exceptional
+ * completion of source
+ */
+ public void testThenAccept_exceptionalCompletion() {
+ for (ExecutionMode m : ExecutionMode.values())
+ {
+ final CFException ex = new CFException();
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final NoopConsumer[] rs = new NoopConsumer[4];
+ for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m);
+
+ final CompletableFuture<Void> h0 = m.thenAccept(f, rs[0]);
+ final CompletableFuture<Void> h1 = m.acceptEither(f, f, rs[1]);
+ assertTrue(f.completeExceptionally(ex));
+ final CompletableFuture<Void> h2 = m.thenAccept(f, rs[2]);
+ final CompletableFuture<Void> h3 = m.acceptEither(f, f, rs[3]);
+
+ checkCompletedWithWrappedException(h0, ex);
+ checkCompletedWithWrappedException(h1, ex);
+ checkCompletedWithWrappedException(h2, ex);
+ checkCompletedWithWrappedException(h3, ex);
+ checkCompletedExceptionally(f, ex);
+ for (NoopConsumer r : rs) r.assertNotInvoked();
+ }}
+
+ /**
+ * thenAccept result completes exceptionally if source cancelled
+ */
+ public void testThenAccept_sourceCancelled() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final NoopConsumer[] rs = new NoopConsumer[4];
+ for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m);
+
+ final CompletableFuture<Void> h0 = m.thenAccept(f, rs[0]);
+ final CompletableFuture<Void> h1 = m.acceptEither(f, f, rs[1]);
+ assertTrue(f.cancel(mayInterruptIfRunning));
+ final CompletableFuture<Void> h2 = m.thenAccept(f, rs[2]);
+ final CompletableFuture<Void> h3 = m.acceptEither(f, f, rs[3]);
+
+ checkCompletedWithWrappedCancellationException(h0);
+ checkCompletedWithWrappedCancellationException(h1);
+ checkCompletedWithWrappedCancellationException(h2);
+ checkCompletedWithWrappedCancellationException(h3);
+ checkCancelled(f);
+ for (NoopConsumer r : rs) r.assertNotInvoked();
+ }}
+
+ /**
+ * thenAccept result completes exceptionally if action does
+ */
+ public void testThenAccept_actionFailed() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final FailingConsumer[] rs = new FailingConsumer[4];
+ for (int i = 0; i < rs.length; i++) rs[i] = new FailingConsumer(m);
+
+ final CompletableFuture<Void> h0 = m.thenAccept(f, rs[0]);
+ final CompletableFuture<Void> h1 = m.acceptEither(f, f, rs[1]);
+ assertTrue(f.complete(v1));
+ final CompletableFuture<Void> h2 = m.thenAccept(f, rs[2]);
+ final CompletableFuture<Void> h3 = m.acceptEither(f, f, rs[3]);
+
+ checkCompletedWithWrappedCFException(h0);
+ checkCompletedWithWrappedCFException(h1);
+ checkCompletedWithWrappedCFException(h2);
+ checkCompletedWithWrappedCFException(h3);
+ checkCompletedNormally(f, v1);
+ }}
+
+ /**
+ * thenCombine result completes normally after normal completion
+ * of sources
+ */
+ public void testThenCombine_normalCompletion() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean fFirst : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ for (Integer v2 : new Integer[] { 2, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final SubtractFunction[] rs = new SubtractFunction[6];
+ for (int i = 0; i < rs.length; i++) rs[i] = new SubtractFunction(m);
+
+ final CompletableFuture<Integer> fst = fFirst ? f : g;
+ final CompletableFuture<Integer> snd = !fFirst ? f : g;
+ final Integer w1 = fFirst ? v1 : v2;
+ final Integer w2 = !fFirst ? v1 : v2;
+
+ final CompletableFuture<Integer> h0 = m.thenCombine(f, g, rs[0]);
+ final CompletableFuture<Integer> h1 = m.thenCombine(fst, fst, rs[1]);
+ assertTrue(fst.complete(w1));
+ final CompletableFuture<Integer> h2 = m.thenCombine(f, g, rs[2]);
+ final CompletableFuture<Integer> h3 = m.thenCombine(fst, fst, rs[3]);
+ checkIncomplete(h0); rs[0].assertNotInvoked();
+ checkIncomplete(h2); rs[2].assertNotInvoked();
+ checkCompletedNormally(h1, subtract(w1, w1));
+ checkCompletedNormally(h3, subtract(w1, w1));
+ rs[1].assertValue(subtract(w1, w1));
+ rs[3].assertValue(subtract(w1, w1));
+ assertTrue(snd.complete(w2));
+ final CompletableFuture<Integer> h4 = m.thenCombine(f, g, rs[4]);
+
+ checkCompletedNormally(h0, subtract(v1, v2));
+ checkCompletedNormally(h2, subtract(v1, v2));
+ checkCompletedNormally(h4, subtract(v1, v2));
+ rs[0].assertValue(subtract(v1, v2));
+ rs[2].assertValue(subtract(v1, v2));
+ rs[4].assertValue(subtract(v1, v2));
+
+ checkCompletedNormally(f, v1);
+ checkCompletedNormally(g, v2);
+ }}
+
+ /**
+ * thenCombine result completes exceptionally after exceptional
+ * completion of either source
+ */
+ public void testThenCombine_exceptionalCompletion() throws Throwable {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean fFirst : new boolean[] { true, false })
+ for (boolean failFirst : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final CFException ex = new CFException();
+ final SubtractFunction r1 = new SubtractFunction(m);
+ final SubtractFunction r2 = new SubtractFunction(m);
+ final SubtractFunction r3 = new SubtractFunction(m);
+
+ final CompletableFuture<Integer> fst = fFirst ? f : g;
+ final CompletableFuture<Integer> snd = !fFirst ? f : g;
+ final Callable<Boolean> complete1 = failFirst ?
+ () -> fst.completeExceptionally(ex) :
+ () -> fst.complete(v1);
+ final Callable<Boolean> complete2 = failFirst ?
+ () -> snd.complete(v1) :
+ () -> snd.completeExceptionally(ex);
+
+ final CompletableFuture<Integer> h1 = m.thenCombine(f, g, r1);
+ assertTrue(complete1.call());
+ final CompletableFuture<Integer> h2 = m.thenCombine(f, g, r2);
+ checkIncomplete(h1);
+ checkIncomplete(h2);
+ assertTrue(complete2.call());
+ final CompletableFuture<Integer> h3 = m.thenCombine(f, g, r3);
+
+ checkCompletedWithWrappedException(h1, ex);
+ checkCompletedWithWrappedException(h2, ex);
+ checkCompletedWithWrappedException(h3, ex);
+ r1.assertNotInvoked();
+ r2.assertNotInvoked();
+ r3.assertNotInvoked();
+ checkCompletedNormally(failFirst ? snd : fst, v1);
+ checkCompletedExceptionally(failFirst ? fst : snd, ex);
+ }}
+
+ /**
+ * thenCombine result completes exceptionally if either source cancelled
+ */
+ public void testThenCombine_sourceCancelled() throws Throwable {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+ for (boolean fFirst : new boolean[] { true, false })
+ for (boolean failFirst : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final SubtractFunction r1 = new SubtractFunction(m);
+ final SubtractFunction r2 = new SubtractFunction(m);
+ final SubtractFunction r3 = new SubtractFunction(m);
+
+ final CompletableFuture<Integer> fst = fFirst ? f : g;
+ final CompletableFuture<Integer> snd = !fFirst ? f : g;
+ final Callable<Boolean> complete1 = failFirst ?
+ () -> fst.cancel(mayInterruptIfRunning) :
+ () -> fst.complete(v1);
+ final Callable<Boolean> complete2 = failFirst ?
+ () -> snd.complete(v1) :
+ () -> snd.cancel(mayInterruptIfRunning);
+
+ final CompletableFuture<Integer> h1 = m.thenCombine(f, g, r1);
+ assertTrue(complete1.call());
+ final CompletableFuture<Integer> h2 = m.thenCombine(f, g, r2);
+ checkIncomplete(h1);
+ checkIncomplete(h2);
+ assertTrue(complete2.call());
+ final CompletableFuture<Integer> h3 = m.thenCombine(f, g, r3);
+
+ checkCompletedWithWrappedCancellationException(h1);
+ checkCompletedWithWrappedCancellationException(h2);
+ checkCompletedWithWrappedCancellationException(h3);
+ r1.assertNotInvoked();
+ r2.assertNotInvoked();
+ r3.assertNotInvoked();
+ checkCompletedNormally(failFirst ? snd : fst, v1);
+ checkCancelled(failFirst ? fst : snd);
+ }}
+
+ /**
+ * thenCombine result completes exceptionally if action does
+ */
+ public void testThenCombine_actionFailed() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean fFirst : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ for (Integer v2 : new Integer[] { 2, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final FailingBiFunction r1 = new FailingBiFunction(m);
+ final FailingBiFunction r2 = new FailingBiFunction(m);
+ final FailingBiFunction r3 = new FailingBiFunction(m);
+
+ final CompletableFuture<Integer> fst = fFirst ? f : g;
+ final CompletableFuture<Integer> snd = !fFirst ? f : g;
+ final Integer w1 = fFirst ? v1 : v2;
+ final Integer w2 = !fFirst ? v1 : v2;
+
+ final CompletableFuture<Integer> h1 = m.thenCombine(f, g, r1);
+ assertTrue(fst.complete(w1));
+ final CompletableFuture<Integer> h2 = m.thenCombine(f, g, r2);
+ assertTrue(snd.complete(w2));
+ final CompletableFuture<Integer> h3 = m.thenCombine(f, g, r3);
+
+ checkCompletedWithWrappedCFException(h1);
+ checkCompletedWithWrappedCFException(h2);
+ checkCompletedWithWrappedCFException(h3);
+ r1.assertInvoked();
+ r2.assertInvoked();
+ r3.assertInvoked();
+ checkCompletedNormally(f, v1);
+ checkCompletedNormally(g, v2);
+ }}
+
+ /**
+ * thenAcceptBoth result completes normally after normal
+ * completion of sources
+ */
+ public void testThenAcceptBoth_normalCompletion() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean fFirst : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ for (Integer v2 : new Integer[] { 2, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final SubtractAction r1 = new SubtractAction(m);
+ final SubtractAction r2 = new SubtractAction(m);
+ final SubtractAction r3 = new SubtractAction(m);
+
+ final CompletableFuture<Integer> fst = fFirst ? f : g;
+ final CompletableFuture<Integer> snd = !fFirst ? f : g;
+ final Integer w1 = fFirst ? v1 : v2;
+ final Integer w2 = !fFirst ? v1 : v2;
+
+ final CompletableFuture<Void> h1 = m.thenAcceptBoth(f, g, r1);
+ assertTrue(fst.complete(w1));
+ final CompletableFuture<Void> h2 = m.thenAcceptBoth(f, g, r2);
+ checkIncomplete(h1);
+ checkIncomplete(h2);
+ r1.assertNotInvoked();
+ r2.assertNotInvoked();
+ assertTrue(snd.complete(w2));
+ final CompletableFuture<Void> h3 = m.thenAcceptBoth(f, g, r3);
+
+ checkCompletedNormally(h1, null);
+ checkCompletedNormally(h2, null);
+ checkCompletedNormally(h3, null);
+ r1.assertValue(subtract(v1, v2));
+ r2.assertValue(subtract(v1, v2));
+ r3.assertValue(subtract(v1, v2));
+ checkCompletedNormally(f, v1);
+ checkCompletedNormally(g, v2);
+ }}
+
+ /**
+ * thenAcceptBoth result completes exceptionally after exceptional
+ * completion of either source
+ */
+ public void testThenAcceptBoth_exceptionalCompletion() throws Throwable {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean fFirst : new boolean[] { true, false })
+ for (boolean failFirst : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final CFException ex = new CFException();
+ final SubtractAction r1 = new SubtractAction(m);
+ final SubtractAction r2 = new SubtractAction(m);
+ final SubtractAction r3 = new SubtractAction(m);
+
+ final CompletableFuture<Integer> fst = fFirst ? f : g;
+ final CompletableFuture<Integer> snd = !fFirst ? f : g;
+ final Callable<Boolean> complete1 = failFirst ?
+ () -> fst.completeExceptionally(ex) :
+ () -> fst.complete(v1);
+ final Callable<Boolean> complete2 = failFirst ?
+ () -> snd.complete(v1) :
+ () -> snd.completeExceptionally(ex);
+
+ final CompletableFuture<Void> h1 = m.thenAcceptBoth(f, g, r1);
+ assertTrue(complete1.call());
+ final CompletableFuture<Void> h2 = m.thenAcceptBoth(f, g, r2);
+ checkIncomplete(h1);
+ checkIncomplete(h2);
+ assertTrue(complete2.call());
+ final CompletableFuture<Void> h3 = m.thenAcceptBoth(f, g, r3);
+
+ checkCompletedWithWrappedException(h1, ex);
+ checkCompletedWithWrappedException(h2, ex);
+ checkCompletedWithWrappedException(h3, ex);
+ r1.assertNotInvoked();
+ r2.assertNotInvoked();
+ r3.assertNotInvoked();
+ checkCompletedNormally(failFirst ? snd : fst, v1);
+ checkCompletedExceptionally(failFirst ? fst : snd, ex);
+ }}
+
+ /**
+ * thenAcceptBoth result completes exceptionally if either source cancelled
+ */
+ public void testThenAcceptBoth_sourceCancelled() throws Throwable {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+ for (boolean fFirst : new boolean[] { true, false })
+ for (boolean failFirst : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final SubtractAction r1 = new SubtractAction(m);
+ final SubtractAction r2 = new SubtractAction(m);
+ final SubtractAction r3 = new SubtractAction(m);
+
+ final CompletableFuture<Integer> fst = fFirst ? f : g;
+ final CompletableFuture<Integer> snd = !fFirst ? f : g;
+ final Callable<Boolean> complete1 = failFirst ?
+ () -> fst.cancel(mayInterruptIfRunning) :
+ () -> fst.complete(v1);
+ final Callable<Boolean> complete2 = failFirst ?
+ () -> snd.complete(v1) :
+ () -> snd.cancel(mayInterruptIfRunning);
+
+ final CompletableFuture<Void> h1 = m.thenAcceptBoth(f, g, r1);
+ assertTrue(complete1.call());
+ final CompletableFuture<Void> h2 = m.thenAcceptBoth(f, g, r2);
+ checkIncomplete(h1);
+ checkIncomplete(h2);
+ assertTrue(complete2.call());
+ final CompletableFuture<Void> h3 = m.thenAcceptBoth(f, g, r3);
+
+ checkCompletedWithWrappedCancellationException(h1);
+ checkCompletedWithWrappedCancellationException(h2);
+ checkCompletedWithWrappedCancellationException(h3);
+ r1.assertNotInvoked();
+ r2.assertNotInvoked();
+ r3.assertNotInvoked();
+ checkCompletedNormally(failFirst ? snd : fst, v1);
+ checkCancelled(failFirst ? fst : snd);
+ }}
+
+ /**
+ * thenAcceptBoth result completes exceptionally if action does
+ */
+ public void testThenAcceptBoth_actionFailed() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean fFirst : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ for (Integer v2 : new Integer[] { 2, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final FailingBiConsumer r1 = new FailingBiConsumer(m);
+ final FailingBiConsumer r2 = new FailingBiConsumer(m);
+ final FailingBiConsumer r3 = new FailingBiConsumer(m);
+
+ final CompletableFuture<Integer> fst = fFirst ? f : g;
+ final CompletableFuture<Integer> snd = !fFirst ? f : g;
+ final Integer w1 = fFirst ? v1 : v2;
+ final Integer w2 = !fFirst ? v1 : v2;
+
+ final CompletableFuture<Void> h1 = m.thenAcceptBoth(f, g, r1);
+ assertTrue(fst.complete(w1));
+ final CompletableFuture<Void> h2 = m.thenAcceptBoth(f, g, r2);
+ assertTrue(snd.complete(w2));
+ final CompletableFuture<Void> h3 = m.thenAcceptBoth(f, g, r3);
+
+ checkCompletedWithWrappedCFException(h1);
+ checkCompletedWithWrappedCFException(h2);
+ checkCompletedWithWrappedCFException(h3);
+ r1.assertInvoked();
+ r2.assertInvoked();
+ r3.assertInvoked();
+ checkCompletedNormally(f, v1);
+ checkCompletedNormally(g, v2);
+ }}
+
+ /**
+ * runAfterBoth result completes normally after normal
+ * completion of sources
+ */
+ public void testRunAfterBoth_normalCompletion() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean fFirst : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ for (Integer v2 : new Integer[] { 2, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final Noop r1 = new Noop(m);
+ final Noop r2 = new Noop(m);
+ final Noop r3 = new Noop(m);
+
+ final CompletableFuture<Integer> fst = fFirst ? f : g;
+ final CompletableFuture<Integer> snd = !fFirst ? f : g;
+ final Integer w1 = fFirst ? v1 : v2;
+ final Integer w2 = !fFirst ? v1 : v2;
+
+ final CompletableFuture<Void> h1 = m.runAfterBoth(f, g, r1);
+ assertTrue(fst.complete(w1));
+ final CompletableFuture<Void> h2 = m.runAfterBoth(f, g, r2);
+ checkIncomplete(h1);
+ checkIncomplete(h2);
+ r1.assertNotInvoked();
+ r2.assertNotInvoked();
+ assertTrue(snd.complete(w2));
+ final CompletableFuture<Void> h3 = m.runAfterBoth(f, g, r3);
+
+ checkCompletedNormally(h1, null);
+ checkCompletedNormally(h2, null);
+ checkCompletedNormally(h3, null);
+ r1.assertInvoked();
+ r2.assertInvoked();
+ r3.assertInvoked();
+ checkCompletedNormally(f, v1);
+ checkCompletedNormally(g, v2);
+ }}
+
+ /**
+ * runAfterBoth result completes exceptionally after exceptional
+ * completion of either source
+ */
+ public void testRunAfterBoth_exceptionalCompletion() throws Throwable {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean fFirst : new boolean[] { true, false })
+ for (boolean failFirst : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final CFException ex = new CFException();
+ final Noop r1 = new Noop(m);
+ final Noop r2 = new Noop(m);
+ final Noop r3 = new Noop(m);
+
+ final CompletableFuture<Integer> fst = fFirst ? f : g;
+ final CompletableFuture<Integer> snd = !fFirst ? f : g;
+ final Callable<Boolean> complete1 = failFirst ?
+ () -> fst.completeExceptionally(ex) :
+ () -> fst.complete(v1);
+ final Callable<Boolean> complete2 = failFirst ?
+ () -> snd.complete(v1) :
+ () -> snd.completeExceptionally(ex);
+
+ final CompletableFuture<Void> h1 = m.runAfterBoth(f, g, r1);
+ assertTrue(complete1.call());
+ final CompletableFuture<Void> h2 = m.runAfterBoth(f, g, r2);
+ checkIncomplete(h1);
+ checkIncomplete(h2);
+ assertTrue(complete2.call());
+ final CompletableFuture<Void> h3 = m.runAfterBoth(f, g, r3);
+
+ checkCompletedWithWrappedException(h1, ex);
+ checkCompletedWithWrappedException(h2, ex);
+ checkCompletedWithWrappedException(h3, ex);
+ r1.assertNotInvoked();
+ r2.assertNotInvoked();
+ r3.assertNotInvoked();
+ checkCompletedNormally(failFirst ? snd : fst, v1);
+ checkCompletedExceptionally(failFirst ? fst : snd, ex);
+ }}
+
+ /**
+ * runAfterBoth result completes exceptionally if either source cancelled
+ */
+ public void testRunAfterBoth_sourceCancelled() throws Throwable {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+ for (boolean fFirst : new boolean[] { true, false })
+ for (boolean failFirst : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final Noop r1 = new Noop(m);
+ final Noop r2 = new Noop(m);
+ final Noop r3 = new Noop(m);
+
+ final CompletableFuture<Integer> fst = fFirst ? f : g;
+ final CompletableFuture<Integer> snd = !fFirst ? f : g;
+ final Callable<Boolean> complete1 = failFirst ?
+ () -> fst.cancel(mayInterruptIfRunning) :
+ () -> fst.complete(v1);
+ final Callable<Boolean> complete2 = failFirst ?
+ () -> snd.complete(v1) :
+ () -> snd.cancel(mayInterruptIfRunning);
+
+ final CompletableFuture<Void> h1 = m.runAfterBoth(f, g, r1);
+ assertTrue(complete1.call());
+ final CompletableFuture<Void> h2 = m.runAfterBoth(f, g, r2);
+ checkIncomplete(h1);
+ checkIncomplete(h2);
+ assertTrue(complete2.call());
+ final CompletableFuture<Void> h3 = m.runAfterBoth(f, g, r3);
+
+ checkCompletedWithWrappedCancellationException(h1);
+ checkCompletedWithWrappedCancellationException(h2);
+ checkCompletedWithWrappedCancellationException(h3);
+ r1.assertNotInvoked();
+ r2.assertNotInvoked();
+ r3.assertNotInvoked();
+ checkCompletedNormally(failFirst ? snd : fst, v1);
+ checkCancelled(failFirst ? fst : snd);
+ }}
+
+ /**
+ * runAfterBoth result completes exceptionally if action does
+ */
+ public void testRunAfterBoth_actionFailed() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean fFirst : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ for (Integer v2 : new Integer[] { 2, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final FailingRunnable r1 = new FailingRunnable(m);
+ final FailingRunnable r2 = new FailingRunnable(m);
+ final FailingRunnable r3 = new FailingRunnable(m);
+
+ final CompletableFuture<Integer> fst = fFirst ? f : g;
+ final CompletableFuture<Integer> snd = !fFirst ? f : g;
+ final Integer w1 = fFirst ? v1 : v2;
+ final Integer w2 = !fFirst ? v1 : v2;
+
+ final CompletableFuture<Void> h1 = m.runAfterBoth(f, g, r1);
+ assertTrue(fst.complete(w1));
+ final CompletableFuture<Void> h2 = m.runAfterBoth(f, g, r2);
+ assertTrue(snd.complete(w2));
+ final CompletableFuture<Void> h3 = m.runAfterBoth(f, g, r3);
+
+ checkCompletedWithWrappedCFException(h1);
+ checkCompletedWithWrappedCFException(h2);
+ checkCompletedWithWrappedCFException(h3);
+ r1.assertInvoked();
+ r2.assertInvoked();
+ r3.assertInvoked();
+ checkCompletedNormally(f, v1);
+ checkCompletedNormally(g, v2);
+ }}
+
+ /**
+ * applyToEither result completes normally after normal completion
+ * of either source
+ */
+ public void testApplyToEither_normalCompletion() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (Integer v1 : new Integer[] { 1, null })
+ for (Integer v2 : new Integer[] { 2, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final IncFunction[] rs = new IncFunction[6];
+ for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m);
+
+ final CompletableFuture<Integer> h0 = m.applyToEither(f, g, rs[0]);
+ final CompletableFuture<Integer> h1 = m.applyToEither(g, f, rs[1]);
+ checkIncomplete(h0);
+ checkIncomplete(h1);
+ rs[0].assertNotInvoked();
+ rs[1].assertNotInvoked();
+ f.complete(v1);
+ checkCompletedNormally(h0, inc(v1));
+ checkCompletedNormally(h1, inc(v1));
+ final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]);
+ final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]);
+ checkCompletedNormally(h2, inc(v1));
+ checkCompletedNormally(h3, inc(v1));
+ g.complete(v2);
+
+ // unspecified behavior - both source completions available
+ final CompletableFuture<Integer> h4 = m.applyToEither(f, g, rs[4]);
+ final CompletableFuture<Integer> h5 = m.applyToEither(g, f, rs[5]);
+ rs[4].assertValue(h4.join());
+ rs[5].assertValue(h5.join());
+ assertTrue(Objects.equals(inc(v1), h4.join()) ||
+ Objects.equals(inc(v2), h4.join()));
+ assertTrue(Objects.equals(inc(v1), h5.join()) ||
+ Objects.equals(inc(v2), h5.join()));
+
+ checkCompletedNormally(f, v1);
+ checkCompletedNormally(g, v2);
+ checkCompletedNormally(h0, inc(v1));
+ checkCompletedNormally(h1, inc(v1));
+ checkCompletedNormally(h2, inc(v1));
+ checkCompletedNormally(h3, inc(v1));
+ for (int i = 0; i < 4; i++) rs[i].assertValue(inc(v1));
+ }}
+
+ /**
+ * applyToEither result completes exceptionally after exceptional
+ * completion of either source
+ */
+ public void testApplyToEither_exceptionalCompletion() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final CFException ex = new CFException();
+ final IncFunction[] rs = new IncFunction[6];
+ for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m);
+
+ final CompletableFuture<Integer> h0 = m.applyToEither(f, g, rs[0]);
+ final CompletableFuture<Integer> h1 = m.applyToEither(g, f, rs[1]);
+ checkIncomplete(h0);
+ checkIncomplete(h1);
+ rs[0].assertNotInvoked();
+ rs[1].assertNotInvoked();
+ f.completeExceptionally(ex);
+ checkCompletedWithWrappedException(h0, ex);
+ checkCompletedWithWrappedException(h1, ex);
+ final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]);
+ final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]);
+ checkCompletedWithWrappedException(h2, ex);
+ checkCompletedWithWrappedException(h3, ex);
+ g.complete(v1);
+
+ // unspecified behavior - both source completions available
+ final CompletableFuture<Integer> h4 = m.applyToEither(f, g, rs[4]);
+ final CompletableFuture<Integer> h5 = m.applyToEither(g, f, rs[5]);
+ try {
+ assertEquals(inc(v1), h4.join());
+ rs[4].assertValue(inc(v1));
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedException(h4, ex);
+ rs[4].assertNotInvoked();
+ }
+ try {
+ assertEquals(inc(v1), h5.join());
+ rs[5].assertValue(inc(v1));
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedException(h5, ex);
+ rs[5].assertNotInvoked();
+ }
+
+ checkCompletedExceptionally(f, ex);
+ checkCompletedNormally(g, v1);
+ checkCompletedWithWrappedException(h0, ex);
+ checkCompletedWithWrappedException(h1, ex);
+ checkCompletedWithWrappedException(h2, ex);
+ checkCompletedWithWrappedException(h3, ex);
+ checkCompletedWithWrappedException(h4, ex);
+ for (int i = 0; i < 4; i++) rs[i].assertNotInvoked();
+ }}
+
+ public void testApplyToEither_exceptionalCompletion2() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean fFirst : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final CFException ex = new CFException();
+ final IncFunction[] rs = new IncFunction[6];
+ for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m);
+
+ final CompletableFuture<Integer> h0 = m.applyToEither(f, g, rs[0]);
+ final CompletableFuture<Integer> h1 = m.applyToEither(g, f, rs[1]);
+ assertTrue(fFirst ? f.complete(v1) : g.completeExceptionally(ex));
+ assertTrue(!fFirst ? f.complete(v1) : g.completeExceptionally(ex));
+ final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]);
+ final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]);
+
+ // unspecified behavior - both source completions available
+ try {
+ assertEquals(inc(v1), h0.join());
+ rs[0].assertValue(inc(v1));
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedException(h0, ex);
+ rs[0].assertNotInvoked();
+ }
+ try {
+ assertEquals(inc(v1), h1.join());
+ rs[1].assertValue(inc(v1));
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedException(h1, ex);
+ rs[1].assertNotInvoked();
+ }
+ try {
+ assertEquals(inc(v1), h2.join());
+ rs[2].assertValue(inc(v1));
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedException(h2, ex);
+ rs[2].assertNotInvoked();
+ }
+ try {
+ assertEquals(inc(v1), h3.join());
+ rs[3].assertValue(inc(v1));
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedException(h3, ex);
+ rs[3].assertNotInvoked();
+ }
+
+ checkCompletedNormally(f, v1);
+ checkCompletedExceptionally(g, ex);
+ }}
+
+ /**
+ * applyToEither result completes exceptionally if either source cancelled
+ */
+ public void testApplyToEither_sourceCancelled() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final IncFunction[] rs = new IncFunction[6];
+ for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m);
+
+ final CompletableFuture<Integer> h0 = m.applyToEither(f, g, rs[0]);
+ final CompletableFuture<Integer> h1 = m.applyToEither(g, f, rs[1]);
+ checkIncomplete(h0);
+ checkIncomplete(h1);
+ rs[0].assertNotInvoked();
+ rs[1].assertNotInvoked();
+ f.cancel(mayInterruptIfRunning);
+ checkCompletedWithWrappedCancellationException(h0);
+ checkCompletedWithWrappedCancellationException(h1);
+ final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]);
+ final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]);
+ checkCompletedWithWrappedCancellationException(h2);
+ checkCompletedWithWrappedCancellationException(h3);
+ g.complete(v1);
+
+ // unspecified behavior - both source completions available
+ final CompletableFuture<Integer> h4 = m.applyToEither(f, g, rs[4]);
+ final CompletableFuture<Integer> h5 = m.applyToEither(g, f, rs[5]);
+ try {
+ assertEquals(inc(v1), h4.join());
+ rs[4].assertValue(inc(v1));
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedCancellationException(h4);
+ rs[4].assertNotInvoked();
+ }
+ try {
+ assertEquals(inc(v1), h5.join());
+ rs[5].assertValue(inc(v1));
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedCancellationException(h5);
+ rs[5].assertNotInvoked();
+ }
+
+ checkCancelled(f);
+ checkCompletedNormally(g, v1);
+ checkCompletedWithWrappedCancellationException(h0);
+ checkCompletedWithWrappedCancellationException(h1);
+ checkCompletedWithWrappedCancellationException(h2);
+ checkCompletedWithWrappedCancellationException(h3);
+ for (int i = 0; i < 4; i++) rs[i].assertNotInvoked();
+ }}
+
+ public void testApplyToEither_sourceCancelled2() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+ for (boolean fFirst : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final IncFunction[] rs = new IncFunction[6];
+ for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m);
+
+ final CompletableFuture<Integer> h0 = m.applyToEither(f, g, rs[0]);
+ final CompletableFuture<Integer> h1 = m.applyToEither(g, f, rs[1]);
+ assertTrue(fFirst ? f.complete(v1) : g.cancel(mayInterruptIfRunning));
+ assertTrue(!fFirst ? f.complete(v1) : g.cancel(mayInterruptIfRunning));
+ final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]);
+ final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]);
+
+ // unspecified behavior - both source completions available
+ try {
+ assertEquals(inc(v1), h0.join());
+ rs[0].assertValue(inc(v1));
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedCancellationException(h0);
+ rs[0].assertNotInvoked();
+ }
+ try {
+ assertEquals(inc(v1), h1.join());
+ rs[1].assertValue(inc(v1));
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedCancellationException(h1);
+ rs[1].assertNotInvoked();
+ }
+ try {
+ assertEquals(inc(v1), h2.join());
+ rs[2].assertValue(inc(v1));
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedCancellationException(h2);
+ rs[2].assertNotInvoked();
+ }
+ try {
+ assertEquals(inc(v1), h3.join());
+ rs[3].assertValue(inc(v1));
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedCancellationException(h3);
+ rs[3].assertNotInvoked();
+ }
+
+ checkCompletedNormally(f, v1);
+ checkCancelled(g);
+ }}
+
+ /**
+ * applyToEither result completes exceptionally if action does
+ */
+ public void testApplyToEither_actionFailed() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (Integer v1 : new Integer[] { 1, null })
+ for (Integer v2 : new Integer[] { 2, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final FailingFunction[] rs = new FailingFunction[6];
+ for (int i = 0; i < rs.length; i++) rs[i] = new FailingFunction(m);
+
+ final CompletableFuture<Integer> h0 = m.applyToEither(f, g, rs[0]);
+ final CompletableFuture<Integer> h1 = m.applyToEither(g, f, rs[1]);
+ f.complete(v1);
+ final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]);
+ final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]);
+ checkCompletedWithWrappedCFException(h0);
+ checkCompletedWithWrappedCFException(h1);
+ checkCompletedWithWrappedCFException(h2);
+ checkCompletedWithWrappedCFException(h3);
+ for (int i = 0; i < 4; i++) rs[i].assertValue(v1);
+
+ g.complete(v2);
+
+ // unspecified behavior - both source completions available
+ final CompletableFuture<Integer> h4 = m.applyToEither(f, g, rs[4]);
+ final CompletableFuture<Integer> h5 = m.applyToEither(g, f, rs[5]);
+
+ checkCompletedWithWrappedCFException(h4);
+ assertTrue(Objects.equals(v1, rs[4].value) ||
+ Objects.equals(v2, rs[4].value));
+ checkCompletedWithWrappedCFException(h5);
+ assertTrue(Objects.equals(v1, rs[5].value) ||
+ Objects.equals(v2, rs[5].value));
+
+ checkCompletedNormally(f, v1);
+ checkCompletedNormally(g, v2);
+ }}
+
+ /**
+ * acceptEither result completes normally after normal completion
+ * of either source
+ */
+ public void testAcceptEither_normalCompletion() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (Integer v1 : new Integer[] { 1, null })
+ for (Integer v2 : new Integer[] { 2, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final NoopConsumer[] rs = new NoopConsumer[6];
+ for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m);
+
+ final CompletableFuture<Void> h0 = m.acceptEither(f, g, rs[0]);
+ final CompletableFuture<Void> h1 = m.acceptEither(g, f, rs[1]);
+ checkIncomplete(h0);
+ checkIncomplete(h1);
+ rs[0].assertNotInvoked();
+ rs[1].assertNotInvoked();
+ f.complete(v1);
+ checkCompletedNormally(h0, null);
+ checkCompletedNormally(h1, null);
+ rs[0].assertValue(v1);
+ rs[1].assertValue(v1);
+ final CompletableFuture<Void> h2 = m.acceptEither(f, g, rs[2]);
+ final CompletableFuture<Void> h3 = m.acceptEither(g, f, rs[3]);
+ checkCompletedNormally(h2, null);
+ checkCompletedNormally(h3, null);
+ rs[2].assertValue(v1);
+ rs[3].assertValue(v1);
+ g.complete(v2);
+
+ // unspecified behavior - both source completions available
+ final CompletableFuture<Void> h4 = m.acceptEither(f, g, rs[4]);
+ final CompletableFuture<Void> h5 = m.acceptEither(g, f, rs[5]);
+ checkCompletedNormally(h4, null);
+ checkCompletedNormally(h5, null);
+ assertTrue(Objects.equals(v1, rs[4].value) ||
+ Objects.equals(v2, rs[4].value));
+ assertTrue(Objects.equals(v1, rs[5].value) ||
+ Objects.equals(v2, rs[5].value));
+
+ checkCompletedNormally(f, v1);
+ checkCompletedNormally(g, v2);
+ checkCompletedNormally(h0, null);
+ checkCompletedNormally(h1, null);
+ checkCompletedNormally(h2, null);
+ checkCompletedNormally(h3, null);
+ for (int i = 0; i < 4; i++) rs[i].assertValue(v1);
+ }}
+
+ /**
+ * acceptEither result completes exceptionally after exceptional
+ * completion of either source
+ */
+ public void testAcceptEither_exceptionalCompletion() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final CFException ex = new CFException();
+ final NoopConsumer[] rs = new NoopConsumer[6];
+ for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m);
+
+ final CompletableFuture<Void> h0 = m.acceptEither(f, g, rs[0]);
+ final CompletableFuture<Void> h1 = m.acceptEither(g, f, rs[1]);
+ checkIncomplete(h0);
+ checkIncomplete(h1);
+ rs[0].assertNotInvoked();
+ rs[1].assertNotInvoked();
+ f.completeExceptionally(ex);
+ checkCompletedWithWrappedException(h0, ex);
+ checkCompletedWithWrappedException(h1, ex);
+ final CompletableFuture<Void> h2 = m.acceptEither(f, g, rs[2]);
+ final CompletableFuture<Void> h3 = m.acceptEither(g, f, rs[3]);
+ checkCompletedWithWrappedException(h2, ex);
+ checkCompletedWithWrappedException(h3, ex);
+
+ g.complete(v1);
+
+ // unspecified behavior - both source completions available
+ final CompletableFuture<Void> h4 = m.acceptEither(f, g, rs[4]);
+ final CompletableFuture<Void> h5 = m.acceptEither(g, f, rs[5]);
+ try {
+ assertNull(h4.join());
+ rs[4].assertValue(v1);
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedException(h4, ex);
+ rs[4].assertNotInvoked();
+ }
+ try {
+ assertNull(h5.join());
+ rs[5].assertValue(v1);
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedException(h5, ex);
+ rs[5].assertNotInvoked();
+ }
+
+ checkCompletedExceptionally(f, ex);
+ checkCompletedNormally(g, v1);
+ checkCompletedWithWrappedException(h0, ex);
+ checkCompletedWithWrappedException(h1, ex);
+ checkCompletedWithWrappedException(h2, ex);
+ checkCompletedWithWrappedException(h3, ex);
+ checkCompletedWithWrappedException(h4, ex);
+ for (int i = 0; i < 4; i++) rs[i].assertNotInvoked();
+ }}
+
+ public void testAcceptEither_exceptionalCompletion2() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean fFirst : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final CFException ex = new CFException();
+ final NoopConsumer[] rs = new NoopConsumer[6];
+ for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m);
+
+ final CompletableFuture<Void> h0 = m.acceptEither(f, g, rs[0]);
+ final CompletableFuture<Void> h1 = m.acceptEither(g, f, rs[1]);
+ assertTrue(fFirst ? f.complete(v1) : g.completeExceptionally(ex));
+ assertTrue(!fFirst ? f.complete(v1) : g.completeExceptionally(ex));
+ final CompletableFuture<Void> h2 = m.acceptEither(f, g, rs[2]);
+ final CompletableFuture<Void> h3 = m.acceptEither(g, f, rs[3]);
+
+ // unspecified behavior - both source completions available
+ try {
+ assertEquals(null, h0.join());
+ rs[0].assertValue(v1);
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedException(h0, ex);
+ rs[0].assertNotInvoked();
+ }
+ try {
+ assertEquals(null, h1.join());
+ rs[1].assertValue(v1);
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedException(h1, ex);
+ rs[1].assertNotInvoked();
+ }
+ try {
+ assertEquals(null, h2.join());
+ rs[2].assertValue(v1);
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedException(h2, ex);
+ rs[2].assertNotInvoked();
+ }
+ try {
+ assertEquals(null, h3.join());
+ rs[3].assertValue(v1);
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedException(h3, ex);
+ rs[3].assertNotInvoked();
+ }
+
+ checkCompletedNormally(f, v1);
+ checkCompletedExceptionally(g, ex);
+ }}
+
+ /**
+ * acceptEither result completes exceptionally if either source cancelled
+ */
+ public void testAcceptEither_sourceCancelled() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final NoopConsumer[] rs = new NoopConsumer[6];
+ for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m);
+
+ final CompletableFuture<Void> h0 = m.acceptEither(f, g, rs[0]);
+ final CompletableFuture<Void> h1 = m.acceptEither(g, f, rs[1]);
+ checkIncomplete(h0);
+ checkIncomplete(h1);
+ rs[0].assertNotInvoked();
+ rs[1].assertNotInvoked();
+ f.cancel(mayInterruptIfRunning);
+ checkCompletedWithWrappedCancellationException(h0);
+ checkCompletedWithWrappedCancellationException(h1);
+ final CompletableFuture<Void> h2 = m.acceptEither(f, g, rs[2]);
+ final CompletableFuture<Void> h3 = m.acceptEither(g, f, rs[3]);
+ checkCompletedWithWrappedCancellationException(h2);
+ checkCompletedWithWrappedCancellationException(h3);
+
+ g.complete(v1);
+
+ // unspecified behavior - both source completions available
+ final CompletableFuture<Void> h4 = m.acceptEither(f, g, rs[4]);
+ final CompletableFuture<Void> h5 = m.acceptEither(g, f, rs[5]);
+ try {
+ assertNull(h4.join());
+ rs[4].assertValue(v1);
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedCancellationException(h4);
+ rs[4].assertNotInvoked();
+ }
+ try {
+ assertNull(h5.join());
+ rs[5].assertValue(v1);
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedCancellationException(h5);
+ rs[5].assertNotInvoked();
+ }
+
+ checkCancelled(f);
+ checkCompletedNormally(g, v1);
+ checkCompletedWithWrappedCancellationException(h0);
+ checkCompletedWithWrappedCancellationException(h1);
+ checkCompletedWithWrappedCancellationException(h2);
+ checkCompletedWithWrappedCancellationException(h3);
+ for (int i = 0; i < 4; i++) rs[i].assertNotInvoked();
+ }}
+
+ /**
+ * acceptEither result completes exceptionally if action does
+ */
+ public void testAcceptEither_actionFailed() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (Integer v1 : new Integer[] { 1, null })
+ for (Integer v2 : new Integer[] { 2, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final FailingConsumer[] rs = new FailingConsumer[6];
+ for (int i = 0; i < rs.length; i++) rs[i] = new FailingConsumer(m);
+
+ final CompletableFuture<Void> h0 = m.acceptEither(f, g, rs[0]);
+ final CompletableFuture<Void> h1 = m.acceptEither(g, f, rs[1]);
+ f.complete(v1);
+ final CompletableFuture<Void> h2 = m.acceptEither(f, g, rs[2]);
+ final CompletableFuture<Void> h3 = m.acceptEither(g, f, rs[3]);
+ checkCompletedWithWrappedCFException(h0);
+ checkCompletedWithWrappedCFException(h1);
+ checkCompletedWithWrappedCFException(h2);
+ checkCompletedWithWrappedCFException(h3);
+ for (int i = 0; i < 4; i++) rs[i].assertValue(v1);
+
+ g.complete(v2);
+
+ // unspecified behavior - both source completions available
+ final CompletableFuture<Void> h4 = m.acceptEither(f, g, rs[4]);
+ final CompletableFuture<Void> h5 = m.acceptEither(g, f, rs[5]);
+
+ checkCompletedWithWrappedCFException(h4);
+ assertTrue(Objects.equals(v1, rs[4].value) ||
+ Objects.equals(v2, rs[4].value));
+ checkCompletedWithWrappedCFException(h5);
+ assertTrue(Objects.equals(v1, rs[5].value) ||
+ Objects.equals(v2, rs[5].value));
+
+ checkCompletedNormally(f, v1);
+ checkCompletedNormally(g, v2);
+ }}
+
+ /**
+ * runAfterEither result completes normally after normal completion
+ * of either source
+ */
+ public void testRunAfterEither_normalCompletion() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (Integer v1 : new Integer[] { 1, null })
+ for (Integer v2 : new Integer[] { 2, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final Noop[] rs = new Noop[6];
+ for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m);
+
+ final CompletableFuture<Void> h0 = m.runAfterEither(f, g, rs[0]);
+ final CompletableFuture<Void> h1 = m.runAfterEither(g, f, rs[1]);
+ checkIncomplete(h0);
+ checkIncomplete(h1);
+ rs[0].assertNotInvoked();
+ rs[1].assertNotInvoked();
+ f.complete(v1);
+ checkCompletedNormally(h0, null);
+ checkCompletedNormally(h1, null);
+ rs[0].assertInvoked();
+ rs[1].assertInvoked();
+ final CompletableFuture<Void> h2 = m.runAfterEither(f, g, rs[2]);
+ final CompletableFuture<Void> h3 = m.runAfterEither(g, f, rs[3]);
+ checkCompletedNormally(h2, null);
+ checkCompletedNormally(h3, null);
+ rs[2].assertInvoked();
+ rs[3].assertInvoked();
+
+ g.complete(v2);
+
+ final CompletableFuture<Void> h4 = m.runAfterEither(f, g, rs[4]);
+ final CompletableFuture<Void> h5 = m.runAfterEither(g, f, rs[5]);
+
+ checkCompletedNormally(f, v1);
+ checkCompletedNormally(g, v2);
+ checkCompletedNormally(h0, null);
+ checkCompletedNormally(h1, null);
+ checkCompletedNormally(h2, null);
+ checkCompletedNormally(h3, null);
+ checkCompletedNormally(h4, null);
+ checkCompletedNormally(h5, null);
+ for (int i = 0; i < 6; i++) rs[i].assertInvoked();
+ }}
+
+ /**
+ * runAfterEither result completes exceptionally after exceptional
+ * completion of either source
+ */
+ public void testRunAfterEither_exceptionalCompletion() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final CFException ex = new CFException();
+ final Noop[] rs = new Noop[6];
+ for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m);
+
+ final CompletableFuture<Void> h0 = m.runAfterEither(f, g, rs[0]);
+ final CompletableFuture<Void> h1 = m.runAfterEither(g, f, rs[1]);
+ checkIncomplete(h0);
+ checkIncomplete(h1);
+ rs[0].assertNotInvoked();
+ rs[1].assertNotInvoked();
+ assertTrue(f.completeExceptionally(ex));
+ checkCompletedWithWrappedException(h0, ex);
+ checkCompletedWithWrappedException(h1, ex);
+ final CompletableFuture<Void> h2 = m.runAfterEither(f, g, rs[2]);
+ final CompletableFuture<Void> h3 = m.runAfterEither(g, f, rs[3]);
+ checkCompletedWithWrappedException(h2, ex);
+ checkCompletedWithWrappedException(h3, ex);
+
+ assertTrue(g.complete(v1));
+
+ // unspecified behavior - both source completions available
+ final CompletableFuture<Void> h4 = m.runAfterEither(f, g, rs[4]);
+ final CompletableFuture<Void> h5 = m.runAfterEither(g, f, rs[5]);
+ try {
+ assertNull(h4.join());
+ rs[4].assertInvoked();
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedException(h4, ex);
+ rs[4].assertNotInvoked();
+ }
+ try {
+ assertNull(h5.join());
+ rs[5].assertInvoked();
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedException(h5, ex);
+ rs[5].assertNotInvoked();
+ }
+
+ checkCompletedExceptionally(f, ex);
+ checkCompletedNormally(g, v1);
+ checkCompletedWithWrappedException(h0, ex);
+ checkCompletedWithWrappedException(h1, ex);
+ checkCompletedWithWrappedException(h2, ex);
+ checkCompletedWithWrappedException(h3, ex);
+ checkCompletedWithWrappedException(h4, ex);
+ for (int i = 0; i < 4; i++) rs[i].assertNotInvoked();
+ }}
+
+ public void testRunAfterEither_exceptionalCompletion2() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean fFirst : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final CFException ex = new CFException();
+ final Noop[] rs = new Noop[6];
+ for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m);
+
+ final CompletableFuture<Void> h0 = m.runAfterEither(f, g, rs[0]);
+ final CompletableFuture<Void> h1 = m.runAfterEither(g, f, rs[1]);
+ assertTrue( fFirst ? f.complete(v1) : g.completeExceptionally(ex));
+ assertTrue(!fFirst ? f.complete(v1) : g.completeExceptionally(ex));
+ final CompletableFuture<Void> h2 = m.runAfterEither(f, g, rs[2]);
+ final CompletableFuture<Void> h3 = m.runAfterEither(g, f, rs[3]);
+
+ // unspecified behavior - both source completions available
+ try {
+ assertEquals(null, h0.join());
+ rs[0].assertInvoked();
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedException(h0, ex);
+ rs[0].assertNotInvoked();
+ }
+ try {
+ assertEquals(null, h1.join());
+ rs[1].assertInvoked();
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedException(h1, ex);
+ rs[1].assertNotInvoked();
+ }
+ try {
+ assertEquals(null, h2.join());
+ rs[2].assertInvoked();
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedException(h2, ex);
+ rs[2].assertNotInvoked();
+ }
+ try {
+ assertEquals(null, h3.join());
+ rs[3].assertInvoked();
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedException(h3, ex);
+ rs[3].assertNotInvoked();
+ }
+
+ checkCompletedNormally(f, v1);
+ checkCompletedExceptionally(g, ex);
+ }}
+
+ /**
+ * runAfterEither result completes exceptionally if either source cancelled
+ */
+ public void testRunAfterEither_sourceCancelled() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final Noop[] rs = new Noop[6];
+ for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m);
+
+ final CompletableFuture<Void> h0 = m.runAfterEither(f, g, rs[0]);
+ final CompletableFuture<Void> h1 = m.runAfterEither(g, f, rs[1]);
+ checkIncomplete(h0);
+ checkIncomplete(h1);
+ rs[0].assertNotInvoked();
+ rs[1].assertNotInvoked();
+ f.cancel(mayInterruptIfRunning);
+ checkCompletedWithWrappedCancellationException(h0);
+ checkCompletedWithWrappedCancellationException(h1);
+ final CompletableFuture<Void> h2 = m.runAfterEither(f, g, rs[2]);
+ final CompletableFuture<Void> h3 = m.runAfterEither(g, f, rs[3]);
+ checkCompletedWithWrappedCancellationException(h2);
+ checkCompletedWithWrappedCancellationException(h3);
+
+ assertTrue(g.complete(v1));
+
+ // unspecified behavior - both source completions available
+ final CompletableFuture<Void> h4 = m.runAfterEither(f, g, rs[4]);
+ final CompletableFuture<Void> h5 = m.runAfterEither(g, f, rs[5]);
+ try {
+ assertNull(h4.join());
+ rs[4].assertInvoked();
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedCancellationException(h4);
+ rs[4].assertNotInvoked();
+ }
+ try {
+ assertNull(h5.join());
+ rs[5].assertInvoked();
+ } catch (CompletionException ok) {
+ checkCompletedWithWrappedCancellationException(h5);
+ rs[5].assertNotInvoked();
+ }
+
+ checkCancelled(f);
+ checkCompletedNormally(g, v1);
+ checkCompletedWithWrappedCancellationException(h0);
+ checkCompletedWithWrappedCancellationException(h1);
+ checkCompletedWithWrappedCancellationException(h2);
+ checkCompletedWithWrappedCancellationException(h3);
+ for (int i = 0; i < 4; i++) rs[i].assertNotInvoked();
+ }}
+
+ /**
+ * runAfterEither result completes exceptionally if action does
+ */
+ public void testRunAfterEither_actionFailed() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (Integer v1 : new Integer[] { 1, null })
+ for (Integer v2 : new Integer[] { 2, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final FailingRunnable[] rs = new FailingRunnable[6];
+ for (int i = 0; i < rs.length; i++) rs[i] = new FailingRunnable(m);
+
+ final CompletableFuture<Void> h0 = m.runAfterEither(f, g, rs[0]);
+ final CompletableFuture<Void> h1 = m.runAfterEither(g, f, rs[1]);
+ assertTrue(f.complete(v1));
+ final CompletableFuture<Void> h2 = m.runAfterEither(f, g, rs[2]);
+ final CompletableFuture<Void> h3 = m.runAfterEither(g, f, rs[3]);
+ checkCompletedWithWrappedCFException(h0);
+ checkCompletedWithWrappedCFException(h1);
+ checkCompletedWithWrappedCFException(h2);
+ checkCompletedWithWrappedCFException(h3);
+ for (int i = 0; i < 4; i++) rs[i].assertInvoked();
+ assertTrue(g.complete(v2));
+ final CompletableFuture<Void> h4 = m.runAfterEither(f, g, rs[4]);
+ final CompletableFuture<Void> h5 = m.runAfterEither(g, f, rs[5]);
+ checkCompletedWithWrappedCFException(h4);
+ checkCompletedWithWrappedCFException(h5);
+
+ checkCompletedNormally(f, v1);
+ checkCompletedNormally(g, v2);
+ for (int i = 0; i < 6; i++) rs[i].assertInvoked();
+ }}
+
+ /**
+ * thenCompose result completes normally after normal completion of source
+ */
+ public void testThenCompose_normalCompletion() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean createIncomplete : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFutureInc r = new CompletableFutureInc(m);
+ if (!createIncomplete) assertTrue(f.complete(v1));
+ final CompletableFuture<Integer> g = m.thenCompose(f, r);
+ if (createIncomplete) assertTrue(f.complete(v1));
+
+ checkCompletedNormally(g, inc(v1));
+ checkCompletedNormally(f, v1);
+ r.assertValue(v1);
+ }}
+
+ /**
+ * thenCompose result completes exceptionally after exceptional
+ * completion of source
+ */
+ public void testThenCompose_exceptionalCompletion() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean createIncomplete : new boolean[] { true, false })
+ {
+ final CFException ex = new CFException();
+ final CompletableFutureInc r = new CompletableFutureInc(m);
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ if (!createIncomplete) f.completeExceptionally(ex);
+ final CompletableFuture<Integer> g = m.thenCompose(f, r);
+ if (createIncomplete) f.completeExceptionally(ex);
+
+ checkCompletedWithWrappedException(g, ex);
+ checkCompletedExceptionally(f, ex);
+ r.assertNotInvoked();
+ }}
+
+ /**
+ * thenCompose result completes exceptionally if action does
+ */
+ public void testThenCompose_actionFailed() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean createIncomplete : new boolean[] { true, false })
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final FailingCompletableFutureFunction r
+ = new FailingCompletableFutureFunction(m);
+ if (!createIncomplete) assertTrue(f.complete(v1));
+ final CompletableFuture<Integer> g = m.thenCompose(f, r);
+ if (createIncomplete) assertTrue(f.complete(v1));
+
+ checkCompletedWithWrappedCFException(g);
+ checkCompletedNormally(f, v1);
+ }}
+
+ /**
+ * thenCompose result completes exceptionally if source cancelled
+ */
+ public void testThenCompose_sourceCancelled() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean createIncomplete : new boolean[] { true, false })
+ for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+ {
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFutureInc r = new CompletableFutureInc(m);
+ if (!createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning));
+ final CompletableFuture<Integer> g = m.thenCompose(f, r);
+ if (createIncomplete) {
+ checkIncomplete(g);
+ assertTrue(f.cancel(mayInterruptIfRunning));
+ }
+
+ checkCompletedWithWrappedCancellationException(g);
+ checkCancelled(f);
+ }}
+
+ /**
+ * thenCompose result completes exceptionally if the result of the action does
+ */
+ public void testThenCompose_actionReturnsFailingFuture() {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (int order = 0; order < 6; order++)
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ final CFException ex = new CFException();
+ final CompletableFuture<Integer> f = new CompletableFuture<>();
+ final CompletableFuture<Integer> g = new CompletableFuture<>();
+ final CompletableFuture<Integer> h;
+ // Test all permutations of orders
+ switch (order) {
+ case 0:
+ assertTrue(f.complete(v1));
+ assertTrue(g.completeExceptionally(ex));
+ h = m.thenCompose(f, (x -> g));
+ break;
+ case 1:
+ assertTrue(f.complete(v1));
+ h = m.thenCompose(f, (x -> g));
+ assertTrue(g.completeExceptionally(ex));
+ break;
+ case 2:
+ assertTrue(g.completeExceptionally(ex));
+ assertTrue(f.complete(v1));
+ h = m.thenCompose(f, (x -> g));
+ break;
+ case 3:
+ assertTrue(g.completeExceptionally(ex));
+ h = m.thenCompose(f, (x -> g));
+ assertTrue(f.complete(v1));
+ break;
+ case 4:
+ h = m.thenCompose(f, (x -> g));
+ assertTrue(f.complete(v1));
+ assertTrue(g.completeExceptionally(ex));
+ break;
+ case 5:
+ h = m.thenCompose(f, (x -> g));
+ assertTrue(f.complete(v1));
+ assertTrue(g.completeExceptionally(ex));
+ break;
+ default: throw new AssertionError();
+ }
+
+ checkCompletedExceptionally(g, ex);
+ checkCompletedWithWrappedException(h, ex);
+ checkCompletedNormally(f, v1);
+ }}
+
+ // other static methods
+
+ /**
+ * allOf(no component futures) returns a future completed normally
+ * with the value null
+ */
+ public void testAllOf_empty() throws Exception {
+ CompletableFuture<Void> f = CompletableFuture.allOf();
+ checkCompletedNormally(f, null);
+ }
+
+ /**
+ * allOf returns a future completed normally with the value null
+ * when all components complete normally
+ */
+ public void testAllOf_normal() throws Exception {
+ for (int k = 1; k < 10; k++) {
+ CompletableFuture<Integer>[] fs
+ = (CompletableFuture<Integer>[]) new CompletableFuture[k];
+ for (int i = 0; i < k; i++)
+ fs[i] = new CompletableFuture<>();
+ CompletableFuture<Void> f = CompletableFuture.allOf(fs);
+ for (int i = 0; i < k; i++) {
+ checkIncomplete(f);
+ checkIncomplete(CompletableFuture.allOf(fs));
+ fs[i].complete(one);
+ }
+ checkCompletedNormally(f, null);
+ checkCompletedNormally(CompletableFuture.allOf(fs), null);
+ }
+ }
+
+ public void testAllOf_backwards() throws Exception {
+ for (int k = 1; k < 10; k++) {
+ CompletableFuture<Integer>[] fs
+ = (CompletableFuture<Integer>[]) new CompletableFuture[k];
+ for (int i = 0; i < k; i++)
+ fs[i] = new CompletableFuture<>();
+ CompletableFuture<Void> f = CompletableFuture.allOf(fs);
+ for (int i = k - 1; i >= 0; i--) {
+ checkIncomplete(f);
+ checkIncomplete(CompletableFuture.allOf(fs));
+ fs[i].complete(one);
+ }
+ checkCompletedNormally(f, null);
+ checkCompletedNormally(CompletableFuture.allOf(fs), null);
+ }
+ }
+
+ public void testAllOf_exceptional() throws Exception {
+ for (int k = 1; k < 10; k++) {
+ CompletableFuture<Integer>[] fs
+ = (CompletableFuture<Integer>[]) new CompletableFuture[k];
+ CFException ex = new CFException();
+ for (int i = 0; i < k; i++)
+ fs[i] = new CompletableFuture<>();
+ CompletableFuture<Void> f = CompletableFuture.allOf(fs);
+ for (int i = 0; i < k; i++) {
+ checkIncomplete(f);
+ checkIncomplete(CompletableFuture.allOf(fs));
+ if (i != k / 2) {
+ fs[i].complete(i);
+ checkCompletedNormally(fs[i], i);
+ } else {
+ fs[i].completeExceptionally(ex);
+ checkCompletedExceptionally(fs[i], ex);
+ }
+ }
+ checkCompletedWithWrappedException(f, ex);
+ checkCompletedWithWrappedException(CompletableFuture.allOf(fs), ex);
+ }
+ }
+
+ /**
+ * anyOf(no component futures) returns an incomplete future
+ */
+ public void testAnyOf_empty() throws Exception {
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ CompletableFuture<Object> f = CompletableFuture.anyOf();
+ checkIncomplete(f);
+
+ f.complete(v1);
+ checkCompletedNormally(f, v1);
+ }}
+
+ /**
+ * anyOf returns a future completed normally with a value when
+ * a component future does
+ */
+ public void testAnyOf_normal() throws Exception {
+ for (int k = 0; k < 10; k++) {
+ CompletableFuture[] fs = new CompletableFuture[k];
+ for (int i = 0; i < k; i++)
+ fs[i] = new CompletableFuture<>();
+ CompletableFuture<Object> f = CompletableFuture.anyOf(fs);
+ checkIncomplete(f);
+ for (int i = 0; i < k; i++) {
+ fs[i].complete(i);
+ checkCompletedNormally(f, 0);
+ int x = (int) CompletableFuture.anyOf(fs).join();
+ assertTrue(0 <= x && x <= i);
+ }
+ }
+ }
+ public void testAnyOf_normal_backwards() throws Exception {
+ for (int k = 0; k < 10; k++) {
+ CompletableFuture[] fs = new CompletableFuture[k];
+ for (int i = 0; i < k; i++)
+ fs[i] = new CompletableFuture<>();
+ CompletableFuture<Object> f = CompletableFuture.anyOf(fs);
+ checkIncomplete(f);
+ for (int i = k - 1; i >= 0; i--) {
+ fs[i].complete(i);
+ checkCompletedNormally(f, k - 1);
+ int x = (int) CompletableFuture.anyOf(fs).join();
+ assertTrue(i <= x && x <= k - 1);
+ }
+ }
+ }
+
+ /**
+ * anyOf result completes exceptionally when any component does.
+ */
+ public void testAnyOf_exceptional() throws Exception {
+ for (int k = 0; k < 10; k++) {
+ CompletableFuture[] fs = new CompletableFuture[k];
+ CFException[] exs = new CFException[k];
+ for (int i = 0; i < k; i++) {
+ fs[i] = new CompletableFuture<>();
+ exs[i] = new CFException();
+ }
+ CompletableFuture<Object> f = CompletableFuture.anyOf(fs);
+ checkIncomplete(f);
+ for (int i = 0; i < k; i++) {
+ fs[i].completeExceptionally(exs[i]);
+ checkCompletedWithWrappedException(f, exs[0]);
+ checkCompletedWithWrappedCFException(CompletableFuture.anyOf(fs));
+ }
+ }
+ }
+
+ public void testAnyOf_exceptional_backwards() throws Exception {
+ for (int k = 0; k < 10; k++) {
+ CompletableFuture[] fs = new CompletableFuture[k];
+ CFException[] exs = new CFException[k];
+ for (int i = 0; i < k; i++) {
+ fs[i] = new CompletableFuture<>();
+ exs[i] = new CFException();
+ }
+ CompletableFuture<Object> f = CompletableFuture.anyOf(fs);
+ checkIncomplete(f);
+ for (int i = k - 1; i >= 0; i--) {
+ fs[i].completeExceptionally(exs[i]);
+ checkCompletedWithWrappedException(f, exs[k - 1]);
+ checkCompletedWithWrappedCFException(CompletableFuture.anyOf(fs));
+ }
+ }
+ }
+
+ /**
+ * Completion methods throw NullPointerException with null arguments
+ */
+ public void testNPE() {
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ CompletableFuture<Integer> g = new CompletableFuture<>();
+ CompletableFuture<Integer> nullFuture = (CompletableFuture<Integer>)null;
+ ThreadExecutor exec = new ThreadExecutor();
+
+ Runnable[] throwingActions = {
+ () -> CompletableFuture.supplyAsync(null),
+ () -> CompletableFuture.supplyAsync(null, exec),
+ () -> CompletableFuture.supplyAsync(new IntegerSupplier(ExecutionMode.SYNC, 42), null),
+
+ () -> CompletableFuture.runAsync(null),
+ () -> CompletableFuture.runAsync(null, exec),
+ () -> CompletableFuture.runAsync(() -> {}, null),
+
+ () -> f.completeExceptionally(null),
+
+ () -> f.thenApply(null),
+ () -> f.thenApplyAsync(null),
+ () -> f.thenApplyAsync((x) -> x, null),
+ () -> f.thenApplyAsync(null, exec),
+
+ () -> f.thenAccept(null),
+ () -> f.thenAcceptAsync(null),
+ () -> f.thenAcceptAsync((x) -> {} , null),
+ () -> f.thenAcceptAsync(null, exec),
+
+ () -> f.thenRun(null),
+ () -> f.thenRunAsync(null),
+ () -> f.thenRunAsync(() -> {} , null),
+ () -> f.thenRunAsync(null, exec),
+
+ () -> f.thenCombine(g, null),
+ () -> f.thenCombineAsync(g, null),
+ () -> f.thenCombineAsync(g, null, exec),
+ () -> f.thenCombine(nullFuture, (x, y) -> x),
+ () -> f.thenCombineAsync(nullFuture, (x, y) -> x),
+ () -> f.thenCombineAsync(nullFuture, (x, y) -> x, exec),
+ () -> f.thenCombineAsync(g, (x, y) -> x, null),
+
+ () -> f.thenAcceptBoth(g, null),
+ () -> f.thenAcceptBothAsync(g, null),
+ () -> f.thenAcceptBothAsync(g, null, exec),
+ () -> f.thenAcceptBoth(nullFuture, (x, y) -> {}),
+ () -> f.thenAcceptBothAsync(nullFuture, (x, y) -> {}),
+ () -> f.thenAcceptBothAsync(nullFuture, (x, y) -> {}, exec),
+ () -> f.thenAcceptBothAsync(g, (x, y) -> {}, null),
+
+ () -> f.runAfterBoth(g, null),
+ () -> f.runAfterBothAsync(g, null),
+ () -> f.runAfterBothAsync(g, null, exec),
+ () -> f.runAfterBoth(nullFuture, () -> {}),
+ () -> f.runAfterBothAsync(nullFuture, () -> {}),
+ () -> f.runAfterBothAsync(nullFuture, () -> {}, exec),
+ () -> f.runAfterBothAsync(g, () -> {}, null),
+
+ () -> f.applyToEither(g, null),
+ () -> f.applyToEitherAsync(g, null),
+ () -> f.applyToEitherAsync(g, null, exec),
+ () -> f.applyToEither(nullFuture, (x) -> x),
+ () -> f.applyToEitherAsync(nullFuture, (x) -> x),
+ () -> f.applyToEitherAsync(nullFuture, (x) -> x, exec),
+ () -> f.applyToEitherAsync(g, (x) -> x, null),
+
+ () -> f.acceptEither(g, null),
+ () -> f.acceptEitherAsync(g, null),
+ () -> f.acceptEitherAsync(g, null, exec),
+ () -> f.acceptEither(nullFuture, (x) -> {}),
+ () -> f.acceptEitherAsync(nullFuture, (x) -> {}),
+ () -> f.acceptEitherAsync(nullFuture, (x) -> {}, exec),
+ () -> f.acceptEitherAsync(g, (x) -> {}, null),
+
+ () -> f.runAfterEither(g, null),
+ () -> f.runAfterEitherAsync(g, null),
+ () -> f.runAfterEitherAsync(g, null, exec),
+ () -> f.runAfterEither(nullFuture, () -> {}),
+ () -> f.runAfterEitherAsync(nullFuture, () -> {}),
+ () -> f.runAfterEitherAsync(nullFuture, () -> {}, exec),
+ () -> f.runAfterEitherAsync(g, () -> {}, null),
+
+ () -> f.thenCompose(null),
+ () -> f.thenComposeAsync(null),
+ () -> f.thenComposeAsync(new CompletableFutureInc(ExecutionMode.EXECUTOR), null),
+ () -> f.thenComposeAsync(null, exec),
+
+ () -> f.exceptionally(null),
+
+ () -> f.handle(null),
+
+ () -> CompletableFuture.allOf((CompletableFuture<?>)null),
+ () -> CompletableFuture.allOf((CompletableFuture<?>[])null),
+ () -> CompletableFuture.allOf(f, null),
+ () -> CompletableFuture.allOf(null, f),
+
+ () -> CompletableFuture.anyOf((CompletableFuture<?>)null),
+ () -> CompletableFuture.anyOf((CompletableFuture<?>[])null),
+ () -> CompletableFuture.anyOf(f, null),
+ () -> CompletableFuture.anyOf(null, f),
+
+ () -> f.obtrudeException(null),
+
+ () -> CompletableFuture.delayedExecutor(1L, SECONDS, null),
+ () -> CompletableFuture.delayedExecutor(1L, null, new ThreadExecutor()),
+ () -> CompletableFuture.delayedExecutor(1L, null),
+
+ () -> f.orTimeout(1L, null),
+ () -> f.completeOnTimeout(42, 1L, null),
+
+ () -> CompletableFuture.failedFuture(null),
+ () -> CompletableFuture.failedStage(null),
+ };
+
+ assertThrows(NullPointerException.class, throwingActions);
+ assertEquals(0, exec.count.get());
+ }
+
+ /**
+ * toCompletableFuture returns this CompletableFuture.
+ */
+ public void testToCompletableFuture() {
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ assertSame(f, f.toCompletableFuture());
+ }
+
+ // jdk9
+
+ /**
+ * newIncompleteFuture returns an incomplete CompletableFuture
+ */
+ public void testNewIncompleteFuture() {
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ CompletableFuture<Integer> g = f.newIncompleteFuture();
+ checkIncomplete(f);
+ checkIncomplete(g);
+ f.complete(v1);
+ checkCompletedNormally(f, v1);
+ checkIncomplete(g);
+ g.complete(v1);
+ checkCompletedNormally(g, v1);
+ assertSame(g.getClass(), CompletableFuture.class);
+ }}
+
+ /**
+ * completedStage returns a completed CompletionStage
+ */
+ public void testCompletedStage() {
+ AtomicInteger x = new AtomicInteger(0);
+ AtomicReference<Throwable> r = new AtomicReference<Throwable>();
+ CompletionStage<Integer> f = CompletableFuture.completedStage(1);
+ f.whenComplete((v, e) -> {if (e != null) r.set(e); else x.set(v);});
+ assertEquals(x.get(), 1);
+ assertNull(r.get());
+ }
+
+ /**
+ * defaultExecutor by default returns the commonPool if
+ * it supports more than one thread.
+ */
+ public void testDefaultExecutor() {
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ Executor e = f.defaultExecutor();
+ Executor c = ForkJoinPool.commonPool();
+ if (ForkJoinPool.getCommonPoolParallelism() > 1)
+ assertSame(e, c);
+ else
+ assertNotSame(e, c);
+ }
+
+ /**
+ * failedFuture returns a CompletableFuture completed
+ * exceptionally with the given Exception
+ */
+ public void testFailedFuture() {
+ CFException ex = new CFException();
+ CompletableFuture<Integer> f = CompletableFuture.failedFuture(ex);
+ checkCompletedExceptionally(f, ex);
+ }
+
+ /**
+ * failedFuture(null) throws NPE
+ */
+ public void testFailedFuture_null() {
+ try {
+ CompletableFuture<Integer> f = CompletableFuture.failedFuture(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
+ }
+
+ /**
+ * copy returns a CompletableFuture that is completed normally,
+ * with the same value, when source is.
+ */
+ public void testCopy() {
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ CompletableFuture<Integer> g = f.copy();
+ checkIncomplete(f);
+ checkIncomplete(g);
+ f.complete(1);
+ checkCompletedNormally(f, 1);
+ checkCompletedNormally(g, 1);
+ }
+
+ /**
+ * copy returns a CompletableFuture that is completed exceptionally
+ * when source is.
+ */
+ public void testCopy2() {
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ CompletableFuture<Integer> g = f.copy();
+ checkIncomplete(f);
+ checkIncomplete(g);
+ CFException ex = new CFException();
+ f.completeExceptionally(ex);
+ checkCompletedExceptionally(f, ex);
+ checkCompletedWithWrappedException(g, ex);
+ }
+
+ /**
+ * minimalCompletionStage returns a CompletableFuture that is
+ * completed normally, with the same value, when source is.
+ */
+ public void testMinimalCompletionStage() {
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ CompletionStage<Integer> g = f.minimalCompletionStage();
+ AtomicInteger x = new AtomicInteger(0);
+ AtomicReference<Throwable> r = new AtomicReference<Throwable>();
+ checkIncomplete(f);
+ g.whenComplete((v, e) -> {if (e != null) r.set(e); else x.set(v);});
+ f.complete(1);
+ checkCompletedNormally(f, 1);
+ assertEquals(x.get(), 1);
+ assertNull(r.get());
+ }
+
+ /**
+ * minimalCompletionStage returns a CompletableFuture that is
+ * completed exceptionally when source is.
+ */
+ public void testMinimalCompletionStage2() {
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ CompletionStage<Integer> g = f.minimalCompletionStage();
+ AtomicInteger x = new AtomicInteger(0);
+ AtomicReference<Throwable> r = new AtomicReference<Throwable>();
+ g.whenComplete((v, e) -> {if (e != null) r.set(e); else x.set(v);});
+ checkIncomplete(f);
+ CFException ex = new CFException();
+ f.completeExceptionally(ex);
+ checkCompletedExceptionally(f, ex);
+ assertEquals(x.get(), 0);
+ assertEquals(r.get().getCause(), ex);
+ }
+
+ /**
+ * failedStage returns a CompletionStage completed
+ * exceptionally with the given Exception
+ */
+ public void testFailedStage() {
+ CFException ex = new CFException();
+ CompletionStage<Integer> f = CompletableFuture.failedStage(ex);
+ AtomicInteger x = new AtomicInteger(0);
+ AtomicReference<Throwable> r = new AtomicReference<Throwable>();
+ f.whenComplete((v, e) -> {if (e != null) r.set(e); else x.set(v);});
+ assertEquals(x.get(), 0);
+ assertEquals(r.get(), ex);
+ }
+
+ /**
+ * completeAsync completes with value of given supplier
+ */
+ public void testCompleteAsync() {
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ f.completeAsync(() -> v1);
+ f.join();
+ checkCompletedNormally(f, v1);
+ }}
+
+ /**
+ * completeAsync completes exceptionally if given supplier throws
+ */
+ public void testCompleteAsync2() {
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ CFException ex = new CFException();
+ f.completeAsync(() -> {if (true) throw ex; return 1;});
+ try {
+ f.join();
+ shouldThrow();
+ } catch (CompletionException success) {}
+ checkCompletedWithWrappedException(f, ex);
+ }
+
+ /**
+ * completeAsync with given executor completes with value of given supplier
+ */
+ public void testCompleteAsync3() {
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ ThreadExecutor executor = new ThreadExecutor();
+ f.completeAsync(() -> v1, executor);
+ assertSame(v1, f.join());
+ checkCompletedNormally(f, v1);
+ assertEquals(1, executor.count.get());
+ }}
+
+ /**
+ * completeAsync with given executor completes exceptionally if
+ * given supplier throws
+ */
+ public void testCompleteAsync4() {
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ CFException ex = new CFException();
+ ThreadExecutor executor = new ThreadExecutor();
+ f.completeAsync(() -> {if (true) throw ex; return 1;}, executor);
+ try {
+ f.join();
+ shouldThrow();
+ } catch (CompletionException success) {}
+ checkCompletedWithWrappedException(f, ex);
+ assertEquals(1, executor.count.get());
+ }
+
+ /**
+ * orTimeout completes with TimeoutException if not complete
+ */
+ public void testOrTimeout_timesOut() {
+ long timeoutMillis = timeoutMillis();
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ long startTime = System.nanoTime();
+ f.orTimeout(timeoutMillis, MILLISECONDS);
+ checkCompletedWithTimeoutException(f);
+ assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+ }
+
+ /**
+ * orTimeout completes normally if completed before timeout
+ */
+ public void testOrTimeout_completed() {
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ CompletableFuture<Integer> g = new CompletableFuture<>();
+ long startTime = System.nanoTime();
+ f.complete(v1);
+ f.orTimeout(LONG_DELAY_MS, MILLISECONDS);
+ g.orTimeout(LONG_DELAY_MS, MILLISECONDS);
+ g.complete(v1);
+ checkCompletedNormally(f, v1);
+ checkCompletedNormally(g, v1);
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2);
+ }}
+
+ /**
+ * completeOnTimeout completes with given value if not complete
+ */
+ public void testCompleteOnTimeout_timesOut() {
+ testInParallel(() -> testCompleteOnTimeout_timesOut(42),
+ () -> testCompleteOnTimeout_timesOut(null));
+ }
+
+ public void testCompleteOnTimeout_timesOut(Integer v) {
+ long timeoutMillis = timeoutMillis();
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ long startTime = System.nanoTime();
+ f.completeOnTimeout(v, timeoutMillis, MILLISECONDS);
+ assertSame(v, f.join());
+ assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+ f.complete(99); // should have no effect
+ checkCompletedNormally(f, v);
+ }
+
+ /**
+ * completeOnTimeout has no effect if completed within timeout
+ */
+ public void testCompleteOnTimeout_completed() {
+ for (Integer v1 : new Integer[] { 1, null })
+ {
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ CompletableFuture<Integer> g = new CompletableFuture<>();
+ long startTime = System.nanoTime();
+ f.complete(v1);
+ f.completeOnTimeout(-1, LONG_DELAY_MS, MILLISECONDS);
+ g.completeOnTimeout(-1, LONG_DELAY_MS, MILLISECONDS);
+ g.complete(v1);
+ checkCompletedNormally(f, v1);
+ checkCompletedNormally(g, v1);
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2);
+ }}
+
+ /**
+ * delayedExecutor returns an executor that delays submission
+ */
+ public void testDelayedExecutor() {
+ testInParallel(() -> testDelayedExecutor(null, null),
+ () -> testDelayedExecutor(null, 1),
+ () -> testDelayedExecutor(new ThreadExecutor(), 1),
+ () -> testDelayedExecutor(new ThreadExecutor(), 1));
+ }
+
+ public void testDelayedExecutor(Executor executor, Integer v) throws Exception {
+ long timeoutMillis = timeoutMillis();
+ // Use an "unreasonably long" long timeout to catch lingering threads
+ long longTimeoutMillis = 1000 * 60 * 60 * 24;
+ final Executor delayer, longDelayer;
+ if (executor == null) {
+ delayer = CompletableFuture.delayedExecutor(timeoutMillis, MILLISECONDS);
+ longDelayer = CompletableFuture.delayedExecutor(longTimeoutMillis, MILLISECONDS);
+ } else {
+ delayer = CompletableFuture.delayedExecutor(timeoutMillis, MILLISECONDS, executor);
+ longDelayer = CompletableFuture.delayedExecutor(longTimeoutMillis, MILLISECONDS, executor);
+ }
+ long startTime = System.nanoTime();
+ CompletableFuture<Integer> f =
+ CompletableFuture.supplyAsync(() -> v, delayer);
+ CompletableFuture<Integer> g =
+ CompletableFuture.supplyAsync(() -> v, longDelayer);
+
+ assertNull(g.getNow(null));
+
+ assertSame(v, f.get(LONG_DELAY_MS, MILLISECONDS));
+ long millisElapsed = millisElapsedSince(startTime);
+ assertTrue(millisElapsed >= timeoutMillis);
+ assertTrue(millisElapsed < LONG_DELAY_MS / 2);
+
+ checkCompletedNormally(f, v);
+
+ checkIncomplete(g);
+ assertTrue(g.cancel(true));
+ }
+
+ //--- tests of implementation details; not part of official tck ---
+
+ Object resultOf(CompletableFuture<?> f) {
+ try {
+ java.lang.reflect.Field resultField
+ = CompletableFuture.class.getDeclaredField("result");
+ resultField.setAccessible(true);
+ return resultField.get(f);
+ } catch (Throwable t) { throw new AssertionError(t); }
+ }
+
+ public void testExceptionPropagationReusesResultObject() {
+ if (!testImplementationDetails) return;
+ for (ExecutionMode m : ExecutionMode.values())
+ {
+ final CFException ex = new CFException();
+ final CompletableFuture<Integer> v42 = CompletableFuture.completedFuture(42);
+ final CompletableFuture<Integer> incomplete = new CompletableFuture<>();
+
+ List<Function<CompletableFuture<Integer>, CompletableFuture<?>>> funs
+ = new ArrayList<>();
+
+ funs.add((y) -> m.thenRun(y, new Noop(m)));
+ funs.add((y) -> m.thenAccept(y, new NoopConsumer(m)));
+ funs.add((y) -> m.thenApply(y, new IncFunction(m)));
+
+ funs.add((y) -> m.runAfterEither(y, incomplete, new Noop(m)));
+ funs.add((y) -> m.acceptEither(y, incomplete, new NoopConsumer(m)));
+ funs.add((y) -> m.applyToEither(y, incomplete, new IncFunction(m)));
+
+ funs.add((y) -> m.runAfterBoth(y, v42, new Noop(m)));
+ funs.add((y) -> m.thenAcceptBoth(y, v42, new SubtractAction(m)));
+ funs.add((y) -> m.thenCombine(y, v42, new SubtractFunction(m)));
+
+ funs.add((y) -> m.whenComplete(y, (Integer r, Throwable t) -> {}));
+
+ funs.add((y) -> m.thenCompose(y, new CompletableFutureInc(m)));
+
+ funs.add((y) -> CompletableFuture.allOf(new CompletableFuture<?>[] {y, v42}));
+ funs.add((y) -> CompletableFuture.anyOf(new CompletableFuture<?>[] {y, incomplete}));
+
+ for (Function<CompletableFuture<Integer>, CompletableFuture<?>>
+ fun : funs) {
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ f.completeExceptionally(ex);
+ CompletableFuture<Integer> src = m.thenApply(f, new IncFunction(m));
+ checkCompletedWithWrappedException(src, ex);
+ CompletableFuture<?> dep = fun.apply(src);
+ checkCompletedWithWrappedException(dep, ex);
+ assertSame(resultOf(src), resultOf(dep));
+ }
+
+ for (Function<CompletableFuture<Integer>, CompletableFuture<?>>
+ fun : funs) {
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ CompletableFuture<Integer> src = m.thenApply(f, new IncFunction(m));
+ CompletableFuture<?> dep = fun.apply(src);
+ f.completeExceptionally(ex);
+ checkCompletedWithWrappedException(src, ex);
+ checkCompletedWithWrappedException(dep, ex);
+ assertSame(resultOf(src), resultOf(dep));
+ }
+
+ for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+ for (Function<CompletableFuture<Integer>, CompletableFuture<?>>
+ fun : funs) {
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ f.cancel(mayInterruptIfRunning);
+ checkCancelled(f);
+ CompletableFuture<Integer> src = m.thenApply(f, new IncFunction(m));
+ checkCompletedWithWrappedCancellationException(src);
+ CompletableFuture<?> dep = fun.apply(src);
+ checkCompletedWithWrappedCancellationException(dep);
+ assertSame(resultOf(src), resultOf(dep));
+ }
+
+ for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+ for (Function<CompletableFuture<Integer>, CompletableFuture<?>>
+ fun : funs) {
+ CompletableFuture<Integer> f = new CompletableFuture<>();
+ CompletableFuture<Integer> src = m.thenApply(f, new IncFunction(m));
+ CompletableFuture<?> dep = fun.apply(src);
+ f.cancel(mayInterruptIfRunning);
+ checkCancelled(f);
+ checkCompletedWithWrappedCancellationException(src);
+ checkCompletedWithWrappedCancellationException(dep);
+ assertSame(resultOf(src), resultOf(dep));
+ }
+ }}
+
+ /**
+ * Minimal completion stages throw UOE for all non-CompletionStage methods
+ */
+ // TODO(streams):
+ // public void testMinimalCompletionStage_minimality() {
+ // if (!testImplementationDetails) return;
+ // Function<Method, String> toSignature =
+ // (method) -> method.getName() + Arrays.toString(method.getParameterTypes());
+ // Predicate<Method> isNotStatic =
+ // (method) -> (method.getModifiers() & Modifier.STATIC) == 0;
+ // List<Method> minimalMethods =
+ // Stream.of(Object.class, CompletionStage.class)
+ // .flatMap((klazz) -> Stream.of(klazz.getMethods()))
+ // .filter(isNotStatic)
+ // .collect(Collectors.toList());
+ // // Methods from CompletableFuture permitted NOT to throw UOE
+ // String[] signatureWhitelist = {
+ // "newIncompleteFuture[]",
+ // "defaultExecutor[]",
+ // "minimalCompletionStage[]",
+ // "copy[]",
+ // };
+ // Set<String> permittedMethodSignatures =
+ // Stream.concat(minimalMethods.stream().map(toSignature),
+ // Stream.of(signatureWhitelist))
+ // .collect(Collectors.toSet());
+ // List<Method> allMethods = Stream.of(CompletableFuture.class.getMethods())
+ // .filter(isNotStatic)
+ // .filter((method) -> !permittedMethodSignatures.contains(toSignature.apply(method)))
+ // .collect(Collectors.toList());
+
+ // CompletionStage<Integer> minimalStage =
+ // new CompletableFuture<Integer>().minimalCompletionStage();
+
+ // List<Method> bugs = new ArrayList<>();
+ // for (Method method : allMethods) {
+ // Class<?>[] parameterTypes = method.getParameterTypes();
+ // Object[] args = new Object[parameterTypes.length];
+ // // Manufacture boxed primitives for primitive params
+ // for (int i = 0; i < args.length; i++) {
+ // Class<?> type = parameterTypes[i];
+ // if (parameterTypes[i] == boolean.class)
+ // args[i] = false;
+ // else if (parameterTypes[i] == int.class)
+ // args[i] = 0;
+ // else if (parameterTypes[i] == long.class)
+ // args[i] = 0L;
+ // }
+ // try {
+ // method.invoke(minimalStage, args);
+ // bugs.add(method);
+ // }
+ // catch (java.lang.reflect.InvocationTargetException expected) {
+ // if (! (expected.getCause() instanceof UnsupportedOperationException)) {
+ // bugs.add(method);
+ // // expected.getCause().printStackTrace();
+ // }
+ // }
+ // catch (ReflectiveOperationException bad) { throw new Error(bad); }
+ // }
+ // if (!bugs.isEmpty())
+ // throw new Error("Methods did not throw UOE: " + bugs.toString());
+ // }
+
+ static class Monad {
+ static class ZeroException extends RuntimeException {
+ public ZeroException() { super("monadic zero"); }
+ }
+ // "return", "unit"
+ static <T> CompletableFuture<T> unit(T value) {
+ return completedFuture(value);
+ }
+ // monadic zero ?
+ static <T> CompletableFuture<T> zero() {
+ return failedFuture(new ZeroException());
+ }
+ // >=>
+ static <T,U,V> Function<T, CompletableFuture<V>> compose
+ (Function<T, CompletableFuture<U>> f,
+ Function<U, CompletableFuture<V>> g) {
+ return (x) -> f.apply(x).thenCompose(g);
+ }
+
+ static void assertZero(CompletableFuture<?> f) {
+ try {
+ f.getNow(null);
+ throw new AssertionFailedError("should throw");
+ } catch (CompletionException success) {
+ assertTrue(success.getCause() instanceof ZeroException);
+ }
+ }
+
+ static <T> void assertFutureEquals(CompletableFuture<T> f,
+ CompletableFuture<T> g) {
+ T fval = null, gval = null;
+ Throwable fex = null, gex = null;
+
+ try { fval = f.get(); }
+ catch (ExecutionException ex) { fex = ex.getCause(); }
+ catch (Throwable ex) { fex = ex; }
+
+ try { gval = g.get(); }
+ catch (ExecutionException ex) { gex = ex.getCause(); }
+ catch (Throwable ex) { gex = ex; }
+
+ if (fex != null || gex != null)
+ assertSame(fex.getClass(), gex.getClass());
+ else
+ assertEquals(fval, gval);
+ }
+
+ static class PlusFuture<T> extends CompletableFuture<T> {
+ AtomicReference<Throwable> firstFailure = new AtomicReference<>(null);
+ }
+
+ /** Implements "monadic plus". */
+ static <T> CompletableFuture<T> plus(CompletableFuture<? extends T> f,
+ CompletableFuture<? extends T> g) {
+ PlusFuture<T> plus = new PlusFuture<T>();
+ BiConsumer<T, Throwable> action = (T result, Throwable ex) -> {
+ try {
+ if (ex == null) {
+ if (plus.complete(result))
+ if (plus.firstFailure.get() != null)
+ plus.firstFailure.set(null);
+ }
+ else if (plus.firstFailure.compareAndSet(null, ex)) {
+ if (plus.isDone())
+ plus.firstFailure.set(null);
+ }
+ else {
+ // first failure has precedence
+ Throwable first = plus.firstFailure.getAndSet(null);
+
+ // may fail with "Self-suppression not permitted"
+ try { first.addSuppressed(ex); }
+ catch (Exception ignored) {}
+
+ plus.completeExceptionally(first);
+ }
+ } catch (Throwable unexpected) {
+ plus.completeExceptionally(unexpected);
+ }
+ };
+ f.whenComplete(action);
+ g.whenComplete(action);
+ return plus;
+ }
+ }
+
+ /**
+ * CompletableFuture is an additive monad - sort of.
+ * https://en.wikipedia.org/wiki/Monad_(functional_programming)#Additive_monads
+ */
+ public void testAdditiveMonad() throws Throwable {
+ Function<Long, CompletableFuture<Long>> unit = Monad::unit;
+ CompletableFuture<Long> zero = Monad.zero();
+
+ // Some mutually non-commutative functions
+ Function<Long, CompletableFuture<Long>> triple
+ = (x) -> Monad.unit(3 * x);
+ Function<Long, CompletableFuture<Long>> inc
+ = (x) -> Monad.unit(x + 1);
+
+ // unit is a right identity: m >>= unit === m
+ Monad.assertFutureEquals(inc.apply(5L).thenCompose(unit),
+ inc.apply(5L));
+ // unit is a left identity: (unit x) >>= f === f x
+ Monad.assertFutureEquals(unit.apply(5L).thenCompose(inc),
+ inc.apply(5L));
+
+ // associativity: (m >>= f) >>= g === m >>= ( \x -> (f x >>= g) )
+ Monad.assertFutureEquals(
+ unit.apply(5L).thenCompose(inc).thenCompose(triple),
+ unit.apply(5L).thenCompose((x) -> inc.apply(x).thenCompose(triple)));
+
+ // The case for CompletableFuture as an additive monad is weaker...
+
+ // zero is a monadic zero
+ Monad.assertZero(zero);
+
+ // left zero: zero >>= f === zero
+ Monad.assertZero(zero.thenCompose(inc));
+ // right zero: f >>= (\x -> zero) === zero
+ Monad.assertZero(inc.apply(5L).thenCompose((x) -> zero));
+
+ // f plus zero === f
+ Monad.assertFutureEquals(Monad.unit(5L),
+ Monad.plus(Monad.unit(5L), zero));
+ // zero plus f === f
+ Monad.assertFutureEquals(Monad.unit(5L),
+ Monad.plus(zero, Monad.unit(5L)));
+ // zero plus zero === zero
+ Monad.assertZero(Monad.plus(zero, zero));
+ {
+ CompletableFuture<Long> f = Monad.plus(Monad.unit(5L),
+ Monad.unit(8L));
+ // non-determinism
+ assertTrue(f.get() == 5L || f.get() == 8L);
+ }
+
+ CompletableFuture<Long> godot = new CompletableFuture<>();
+ // f plus godot === f (doesn't wait for godot)
+ Monad.assertFutureEquals(Monad.unit(5L),
+ Monad.plus(Monad.unit(5L), godot));
+ // godot plus f === f (doesn't wait for godot)
+ Monad.assertFutureEquals(Monad.unit(5L),
+ Monad.plus(godot, Monad.unit(5L)));
+ }
+
+// static <U> U join(CompletionStage<U> stage) {
+// CompletableFuture<U> f = new CompletableFuture<>();
+// stage.whenComplete((v, ex) -> {
+// if (ex != null) f.completeExceptionally(ex); else f.complete(v);
+// });
+// return f.join();
+// }
+
+// static <U> boolean isDone(CompletionStage<U> stage) {
+// CompletableFuture<U> f = new CompletableFuture<>();
+// stage.whenComplete((v, ex) -> {
+// if (ex != null) f.completeExceptionally(ex); else f.complete(v);
+// });
+// return f.isDone();
+// }
+
+// static <U> U join2(CompletionStage<U> stage) {
+// return stage.toCompletableFuture().copy().join();
+// }
+
+// static <U> boolean isDone2(CompletionStage<U> stage) {
+// return stage.toCompletableFuture().copy().isDone();
+// }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ConcurrentHashMap8Test.java b/jsr166-tests/src/test/java/jsr166/ConcurrentHashMap8Test.java
new file mode 100644
index 0000000..60de8b9
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ConcurrentHashMap8Test.java
@@ -0,0 +1,1096 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import static java.util.Spliterator.CONCURRENT;
+import static java.util.Spliterator.DISTINCT;
+import static java.util.Spliterator.NONNULL;
+
+import java.util.AbstractMap;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.LongAdder;
+import java.util.function.BiFunction;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ConcurrentHashMap8Test extends JSR166TestCase {
+ // android-note: Removed because the CTS runner does a bad job of
+ // retrying tests that have suite() declarations.
+ //
+ // public static void main(String[] args) {
+ // main(suite(), args);
+ // }
+ // public static Test suite() {
+ // return new TestSuite(ConcurrentHashMap8Test.class);
+ // }
+
+ /**
+ * Returns a new map from Integers 1-5 to Strings "A"-"E".
+ */
+ private static ConcurrentHashMap map5() {
+ ConcurrentHashMap map = new ConcurrentHashMap(5);
+ assertTrue(map.isEmpty());
+ map.put(one, "A");
+ map.put(two, "B");
+ map.put(three, "C");
+ map.put(four, "D");
+ map.put(five, "E");
+ assertFalse(map.isEmpty());
+ assertEquals(5, map.size());
+ return map;
+ }
+
+ /**
+ * getOrDefault returns value if present, else default
+ */
+ public void testGetOrDefault() {
+ ConcurrentHashMap map = map5();
+ assertEquals(map.getOrDefault(one, "Z"), "A");
+ assertEquals(map.getOrDefault(six, "Z"), "Z");
+ }
+
+ /**
+ * computeIfAbsent adds when the given key is not present
+ */
+ public void testComputeIfAbsent() {
+ ConcurrentHashMap map = map5();
+ map.computeIfAbsent(six, (x) -> "Z");
+ assertTrue(map.containsKey(six));
+ }
+
+ /**
+ * computeIfAbsent does not replace if the key is already present
+ */
+ public void testComputeIfAbsent2() {
+ ConcurrentHashMap map = map5();
+ assertEquals("A", map.computeIfAbsent(one, (x) -> "Z"));
+ }
+
+ /**
+ * computeIfAbsent does not add if function returns null
+ */
+ public void testComputeIfAbsent3() {
+ ConcurrentHashMap map = map5();
+ map.computeIfAbsent(six, (x) -> null);
+ assertFalse(map.containsKey(six));
+ }
+
+ /**
+ * computeIfPresent does not replace if the key is already present
+ */
+ public void testComputeIfPresent() {
+ ConcurrentHashMap map = map5();
+ map.computeIfPresent(six, (x, y) -> "Z");
+ assertFalse(map.containsKey(six));
+ }
+
+ /**
+ * computeIfPresent adds when the given key is not present
+ */
+ public void testComputeIfPresent2() {
+ ConcurrentHashMap map = map5();
+ assertEquals("Z", map.computeIfPresent(one, (x, y) -> "Z"));
+ }
+
+ /**
+ * compute does not replace if the function returns null
+ */
+ public void testCompute() {
+ ConcurrentHashMap map = map5();
+ map.compute(six, (x, y) -> null);
+ assertFalse(map.containsKey(six));
+ }
+
+ /**
+ * compute adds when the given key is not present
+ */
+ public void testCompute2() {
+ ConcurrentHashMap map = map5();
+ assertEquals("Z", map.compute(six, (x, y) -> "Z"));
+ }
+
+ /**
+ * compute replaces when the given key is present
+ */
+ public void testCompute3() {
+ ConcurrentHashMap map = map5();
+ assertEquals("Z", map.compute(one, (x, y) -> "Z"));
+ }
+
+ /**
+ * compute removes when the given key is present and function returns null
+ */
+ public void testCompute4() {
+ ConcurrentHashMap map = map5();
+ map.compute(one, (x, y) -> null);
+ assertFalse(map.containsKey(one));
+ }
+
+ /**
+ * merge adds when the given key is not present
+ */
+ public void testMerge1() {
+ ConcurrentHashMap map = map5();
+ assertEquals("Y", map.merge(six, "Y", (x, y) -> "Z"));
+ }
+
+ /**
+ * merge replaces when the given key is present
+ */
+ public void testMerge2() {
+ ConcurrentHashMap map = map5();
+ assertEquals("Z", map.merge(one, "Y", (x, y) -> "Z"));
+ }
+
+ /**
+ * merge removes when the given key is present and function returns null
+ */
+ public void testMerge3() {
+ ConcurrentHashMap map = map5();
+ map.merge(one, "Y", (x, y) -> null);
+ assertFalse(map.containsKey(one));
+ }
+
+ static Set<Integer> populatedSet(int n) {
+ Set<Integer> a = ConcurrentHashMap.<Integer>newKeySet();
+ assertTrue(a.isEmpty());
+ for (int i = 0; i < n; i++)
+ assertTrue(a.add(i));
+ assertEquals(n == 0, a.isEmpty());
+ assertEquals(n, a.size());
+ return a;
+ }
+
+ static Set populatedSet(Integer[] elements) {
+ Set<Integer> a = ConcurrentHashMap.<Integer>newKeySet();
+ assertTrue(a.isEmpty());
+ for (int i = 0; i < elements.length; i++)
+ assertTrue(a.add(elements[i]));
+ assertFalse(a.isEmpty());
+ assertEquals(elements.length, a.size());
+ return a;
+ }
+
+ /**
+ * replaceAll replaces all matching values.
+ */
+ public void testReplaceAll() {
+ ConcurrentHashMap<Integer, String> map = map5();
+ map.replaceAll((x, y) -> { return x > 3 ? "Z" : y; });
+ assertEquals("A", map.get(one));
+ assertEquals("B", map.get(two));
+ assertEquals("C", map.get(three));
+ assertEquals("Z", map.get(four));
+ assertEquals("Z", map.get(five));
+ }
+
+ /**
+ * Default-constructed set is empty
+ */
+ public void testNewKeySet() {
+ Set a = ConcurrentHashMap.newKeySet();
+ assertTrue(a.isEmpty());
+ }
+
+ /**
+ * keySet.add adds the key with the established value to the map;
+ * remove removes it.
+ */
+ public void testKeySetAddRemove() {
+ ConcurrentHashMap map = map5();
+ Set set1 = map.keySet();
+ Set set2 = map.keySet(true);
+ set2.add(six);
+ assertTrue(((ConcurrentHashMap.KeySetView)set2).getMap() == map);
+ assertTrue(((ConcurrentHashMap.KeySetView)set1).getMap() == map);
+ assertEquals(set2.size(), map.size());
+ assertEquals(set1.size(), map.size());
+ assertTrue((Boolean)map.get(six));
+ assertTrue(set1.contains(six));
+ assertTrue(set2.contains(six));
+ set2.remove(six);
+ assertNull(map.get(six));
+ assertFalse(set1.contains(six));
+ assertFalse(set2.contains(six));
+ }
+
+ /**
+ * keySet.addAll adds each element from the given collection
+ */
+ public void testAddAll() {
+ Set full = populatedSet(3);
+ assertTrue(full.addAll(Arrays.asList(three, four, five)));
+ assertEquals(6, full.size());
+ assertFalse(full.addAll(Arrays.asList(three, four, five)));
+ assertEquals(6, full.size());
+ }
+
+ /**
+ * keySet.addAll adds each element from the given collection that did not
+ * already exist in the set
+ */
+ public void testAddAll2() {
+ Set full = populatedSet(3);
+ // "one" is duplicate and will not be added
+ assertTrue(full.addAll(Arrays.asList(three, four, one)));
+ assertEquals(5, full.size());
+ assertFalse(full.addAll(Arrays.asList(three, four, one)));
+ assertEquals(5, full.size());
+ }
+
+ /**
+ * keySet.add will not add the element if it already exists in the set
+ */
+ public void testAdd2() {
+ Set full = populatedSet(3);
+ assertFalse(full.add(one));
+ assertEquals(3, full.size());
+ }
+
+ /**
+ * keySet.add adds the element when it does not exist in the set
+ */
+ public void testAdd3() {
+ Set full = populatedSet(3);
+ assertTrue(full.add(three));
+ assertTrue(full.contains(three));
+ assertFalse(full.add(three));
+ assertTrue(full.contains(three));
+ }
+
+ /**
+ * keySet.add throws UnsupportedOperationException if no default
+ * mapped value
+ */
+ public void testAdd4() {
+ Set full = map5().keySet();
+ try {
+ full.add(three);
+ shouldThrow();
+ } catch (UnsupportedOperationException success) {}
+ }
+
+ /**
+ * keySet.add throws NullPointerException if the specified key is
+ * null
+ */
+ public void testAdd5() {
+ Set full = populatedSet(3);
+ try {
+ full.add(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
+ }
+
+ /**
+ * KeySetView.getMappedValue returns the map's mapped value
+ */
+ public void testGetMappedValue() {
+ ConcurrentHashMap map = map5();
+ assertNull(map.keySet().getMappedValue());
+ try {
+ map.keySet(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
+ ConcurrentHashMap.KeySetView set = map.keySet(one);
+ assertFalse(set.add(one));
+ assertTrue(set.add(six));
+ assertTrue(set.add(seven));
+ assertTrue(set.getMappedValue() == one);
+ assertTrue(map.get(one) != one);
+ assertTrue(map.get(six) == one);
+ assertTrue(map.get(seven) == one);
+ }
+
+ void checkSpliteratorCharacteristics(Spliterator<?> sp,
+ int requiredCharacteristics) {
+ assertEquals(requiredCharacteristics,
+ requiredCharacteristics & sp.characteristics());
+ }
+
+ /**
+ * KeySetView.spliterator returns spliterator over the elements in this set
+ */
+ public void testKeySetSpliterator() {
+ LongAdder adder = new LongAdder();
+ ConcurrentHashMap map = map5();
+ Set set = map.keySet();
+ Spliterator<Integer> sp = set.spliterator();
+ checkSpliteratorCharacteristics(sp, CONCURRENT | DISTINCT | NONNULL);
+ assertEquals(sp.estimateSize(), map.size());
+ Spliterator<Integer> sp2 = sp.trySplit();
+ sp.forEachRemaining((Integer x) -> adder.add(x.longValue()));
+ long v = adder.sumThenReset();
+ sp2.forEachRemaining((Integer x) -> adder.add(x.longValue()));
+ long v2 = adder.sum();
+ assertEquals(v + v2, 15);
+ }
+
+ /**
+ * keyset.clear removes all elements from the set
+ */
+ public void testClear() {
+ Set full = populatedSet(3);
+ full.clear();
+ assertEquals(0, full.size());
+ }
+
+ /**
+ * keyset.contains returns true for added elements
+ */
+ public void testContains() {
+ Set full = populatedSet(3);
+ assertTrue(full.contains(one));
+ assertFalse(full.contains(five));
+ }
+
+ /**
+ * KeySets with equal elements are equal
+ */
+ public void testEquals() {
+ Set a = populatedSet(3);
+ Set b = populatedSet(3);
+ assertTrue(a.equals(b));
+ assertTrue(b.equals(a));
+ assertEquals(a.hashCode(), b.hashCode());
+ a.add(m1);
+ assertFalse(a.equals(b));
+ assertFalse(b.equals(a));
+ b.add(m1);
+ assertTrue(a.equals(b));
+ assertTrue(b.equals(a));
+ assertEquals(a.hashCode(), b.hashCode());
+ }
+
+ /**
+ * KeySet.containsAll returns true for collections with subset of elements
+ */
+ public void testContainsAll() {
+ Collection full = populatedSet(3);
+ assertTrue(full.containsAll(Arrays.asList()));
+ assertTrue(full.containsAll(Arrays.asList(one)));
+ assertTrue(full.containsAll(Arrays.asList(one, two)));
+ assertFalse(full.containsAll(Arrays.asList(one, two, six)));
+ assertFalse(full.containsAll(Arrays.asList(six)));
+ }
+
+ /**
+ * KeySet.isEmpty is true when empty, else false
+ */
+ public void testIsEmpty() {
+ assertTrue(populatedSet(0).isEmpty());
+ assertFalse(populatedSet(3).isEmpty());
+ }
+
+ /**
+ * KeySet.iterator() returns an iterator containing the elements of the
+ * set
+ */
+ public void testIterator() {
+ Collection empty = ConcurrentHashMap.newKeySet();
+ int size = 20;
+ assertFalse(empty.iterator().hasNext());
+ try {
+ empty.iterator().next();
+ shouldThrow();
+ } catch (NoSuchElementException success) {}
+
+ Integer[] elements = new Integer[size];
+ for (int i = 0; i < size; i++)
+ elements[i] = i;
+ Collections.shuffle(Arrays.asList(elements));
+ Collection<Integer> full = populatedSet(elements);
+
+ Iterator it = full.iterator();
+ for (int j = 0; j < size; j++) {
+ assertTrue(it.hasNext());
+ it.next();
+ }
+ assertIteratorExhausted(it);
+ }
+
+ /**
+ * iterator of empty collections has no elements
+ */
+ public void testEmptyIterator() {
+ assertIteratorExhausted(ConcurrentHashMap.newKeySet().iterator());
+ assertIteratorExhausted(new ConcurrentHashMap().entrySet().iterator());
+ assertIteratorExhausted(new ConcurrentHashMap().values().iterator());
+ assertIteratorExhausted(new ConcurrentHashMap().keySet().iterator());
+ }
+
+ /**
+ * KeySet.iterator.remove removes current element
+ */
+ public void testIteratorRemove() {
+ Set q = populatedSet(3);
+ Iterator it = q.iterator();
+ Object removed = it.next();
+ it.remove();
+
+ it = q.iterator();
+ assertFalse(it.next().equals(removed));
+ assertFalse(it.next().equals(removed));
+ assertFalse(it.hasNext());
+ }
+
+ /**
+ * KeySet.toString holds toString of elements
+ */
+ public void testToString() {
+ assertEquals("[]", ConcurrentHashMap.newKeySet().toString());
+ Set full = populatedSet(3);
+ String s = full.toString();
+ for (int i = 0; i < 3; ++i)
+ assertTrue(s.contains(String.valueOf(i)));
+ }
+
+ /**
+ * KeySet.removeAll removes all elements from the given collection
+ */
+ public void testRemoveAll() {
+ Set full = populatedSet(3);
+ assertTrue(full.removeAll(Arrays.asList(one, two)));
+ assertEquals(1, full.size());
+ assertFalse(full.removeAll(Arrays.asList(one, two)));
+ assertEquals(1, full.size());
+ }
+
+ /**
+ * KeySet.remove removes an element
+ */
+ public void testRemove() {
+ Set full = populatedSet(3);
+ full.remove(one);
+ assertFalse(full.contains(one));
+ assertEquals(2, full.size());
+ }
+
+ /**
+ * keySet.size returns the number of elements
+ */
+ public void testSize() {
+ Set empty = ConcurrentHashMap.newKeySet();
+ Set full = populatedSet(3);
+ assertEquals(3, full.size());
+ assertEquals(0, empty.size());
+ }
+
+ /**
+ * KeySet.toArray() returns an Object array containing all elements from
+ * the set
+ */
+ public void testToArray() {
+ Object[] a = ConcurrentHashMap.newKeySet().toArray();
+ assertTrue(Arrays.equals(new Object[0], a));
+ assertSame(Object[].class, a.getClass());
+ int size = 20;
+ Integer[] elements = new Integer[size];
+ for (int i = 0; i < size; i++)
+ elements[i] = i;
+ Collections.shuffle(Arrays.asList(elements));
+ Collection<Integer> full = populatedSet(elements);
+
+ assertTrue(Arrays.asList(elements).containsAll(Arrays.asList(full.toArray())));
+ assertTrue(full.containsAll(Arrays.asList(full.toArray())));
+ assertSame(Object[].class, full.toArray().getClass());
+ }
+
+ /**
+ * toArray(Integer array) returns an Integer array containing all
+ * elements from the set
+ */
+ public void testToArray2() {
+ Collection empty = ConcurrentHashMap.newKeySet();
+ Integer[] a;
+ int size = 20;
+
+ a = new Integer[0];
+ assertSame(a, empty.toArray(a));
+
+ a = new Integer[size / 2];
+ Arrays.fill(a, 42);
+ assertSame(a, empty.toArray(a));
+ assertNull(a[0]);
+ for (int i = 1; i < a.length; i++)
+ assertEquals(42, (int) a[i]);
+
+ Integer[] elements = new Integer[size];
+ for (int i = 0; i < size; i++)
+ elements[i] = i;
+ Collections.shuffle(Arrays.asList(elements));
+ Collection<Integer> full = populatedSet(elements);
+
+ Arrays.fill(a, 42);
+ assertTrue(Arrays.asList(elements).containsAll(Arrays.asList(full.toArray(a))));
+ for (int i = 0; i < a.length; i++)
+ assertEquals(42, (int) a[i]);
+ assertSame(Integer[].class, full.toArray(a).getClass());
+
+ a = new Integer[size];
+ Arrays.fill(a, 42);
+ assertSame(a, full.toArray(a));
+ assertTrue(Arrays.asList(elements).containsAll(Arrays.asList(full.toArray(a))));
+ }
+
+ /**
+ * A deserialized serialized set is equal
+ */
+ public void testSerialization() throws Exception {
+ int size = 20;
+ Set x = populatedSet(size);
+ Set y = serialClone(x);
+
+ assertNotSame(x, y);
+ assertEquals(x.size(), y.size());
+ assertEquals(x, y);
+ assertEquals(y, x);
+ }
+
+ static final int SIZE = 10000;
+ static ConcurrentHashMap<Long, Long> longMap;
+
+ static ConcurrentHashMap<Long, Long> longMap() {
+ if (longMap == null) {
+ longMap = new ConcurrentHashMap<Long, Long>(SIZE);
+ for (int i = 0; i < SIZE; ++i)
+ longMap.put(Long.valueOf(i), Long.valueOf(2 *i));
+ }
+ return longMap;
+ }
+
+ // explicit function class to avoid type inference problems
+ static class AddKeys implements BiFunction<Map.Entry<Long,Long>, Map.Entry<Long,Long>, Map.Entry<Long,Long>> {
+ public Map.Entry<Long,Long> apply(Map.Entry<Long,Long> x, Map.Entry<Long,Long> y) {
+ return new AbstractMap.SimpleEntry<Long,Long>
+ (Long.valueOf(x.getKey().longValue() + y.getKey().longValue()),
+ Long.valueOf(1L));
+ }
+ }
+
+ /**
+ * forEachKeySequentially traverses all keys
+ */
+ public void testForEachKeySequentially() {
+ LongAdder adder = new LongAdder();
+ ConcurrentHashMap<Long, Long> m = longMap();
+ m.forEachKey(Long.MAX_VALUE, (Long x) -> adder.add(x.longValue()));
+ assertEquals(adder.sum(), SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * forEachValueSequentially traverses all values
+ */
+ public void testForEachValueSequentially() {
+ LongAdder adder = new LongAdder();
+ ConcurrentHashMap<Long, Long> m = longMap();
+ m.forEachValue(Long.MAX_VALUE, (Long x) -> adder.add(x.longValue()));
+ assertEquals(adder.sum(), SIZE * (SIZE - 1));
+ }
+
+ /**
+ * forEachSequentially traverses all mappings
+ */
+ public void testForEachSequentially() {
+ LongAdder adder = new LongAdder();
+ ConcurrentHashMap<Long, Long> m = longMap();
+ m.forEach(Long.MAX_VALUE, (Long x, Long y) -> adder.add(x.longValue() + y.longValue()));
+ assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * forEachEntrySequentially traverses all entries
+ */
+ public void testForEachEntrySequentially() {
+ LongAdder adder = new LongAdder();
+ ConcurrentHashMap<Long, Long> m = longMap();
+ m.forEachEntry(Long.MAX_VALUE, (Map.Entry<Long,Long> e) -> adder.add(e.getKey().longValue() + e.getValue().longValue()));
+ assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * forEachKeyInParallel traverses all keys
+ */
+ public void testForEachKeyInParallel() {
+ LongAdder adder = new LongAdder();
+ ConcurrentHashMap<Long, Long> m = longMap();
+ m.forEachKey(1L, (Long x) -> adder.add(x.longValue()));
+ assertEquals(adder.sum(), SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * forEachValueInParallel traverses all values
+ */
+ public void testForEachValueInParallel() {
+ LongAdder adder = new LongAdder();
+ ConcurrentHashMap<Long, Long> m = longMap();
+ m.forEachValue(1L, (Long x) -> adder.add(x.longValue()));
+ assertEquals(adder.sum(), SIZE * (SIZE - 1));
+ }
+
+ /**
+ * forEachInParallel traverses all mappings
+ */
+ public void testForEachInParallel() {
+ LongAdder adder = new LongAdder();
+ ConcurrentHashMap<Long, Long> m = longMap();
+ m.forEach(1L, (Long x, Long y) -> adder.add(x.longValue() + y.longValue()));
+ assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * forEachEntryInParallel traverses all entries
+ */
+ public void testForEachEntryInParallel() {
+ LongAdder adder = new LongAdder();
+ ConcurrentHashMap<Long, Long> m = longMap();
+ m.forEachEntry(1L, (Map.Entry<Long,Long> e) -> adder.add(e.getKey().longValue() + e.getValue().longValue()));
+ assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * Mapped forEachKeySequentially traverses the given
+ * transformations of all keys
+ */
+ public void testMappedForEachKeySequentially() {
+ LongAdder adder = new LongAdder();
+ ConcurrentHashMap<Long, Long> m = longMap();
+ m.forEachKey(Long.MAX_VALUE, (Long x) -> Long.valueOf(4 * x.longValue()),
+ (Long x) -> adder.add(x.longValue()));
+ assertEquals(adder.sum(), 4 * SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * Mapped forEachValueSequentially traverses the given
+ * transformations of all values
+ */
+ public void testMappedForEachValueSequentially() {
+ LongAdder adder = new LongAdder();
+ ConcurrentHashMap<Long, Long> m = longMap();
+ m.forEachValue(Long.MAX_VALUE, (Long x) -> Long.valueOf(4 * x.longValue()),
+ (Long x) -> adder.add(x.longValue()));
+ assertEquals(adder.sum(), 4 * SIZE * (SIZE - 1));
+ }
+
+ /**
+ * Mapped forEachSequentially traverses the given
+ * transformations of all mappings
+ */
+ public void testMappedForEachSequentially() {
+ LongAdder adder = new LongAdder();
+ ConcurrentHashMap<Long, Long> m = longMap();
+ m.forEach(Long.MAX_VALUE, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()),
+ (Long x) -> adder.add(x.longValue()));
+ assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * Mapped forEachEntrySequentially traverses the given
+ * transformations of all entries
+ */
+ public void testMappedForEachEntrySequentially() {
+ LongAdder adder = new LongAdder();
+ ConcurrentHashMap<Long, Long> m = longMap();
+ m.forEachEntry(Long.MAX_VALUE, (Map.Entry<Long,Long> e) -> Long.valueOf(e.getKey().longValue() + e.getValue().longValue()),
+ (Long x) -> adder.add(x.longValue()));
+ assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * Mapped forEachKeyInParallel traverses the given
+ * transformations of all keys
+ */
+ public void testMappedForEachKeyInParallel() {
+ LongAdder adder = new LongAdder();
+ ConcurrentHashMap<Long, Long> m = longMap();
+ m.forEachKey(1L, (Long x) -> Long.valueOf(4 * x.longValue()),
+ (Long x) -> adder.add(x.longValue()));
+ assertEquals(adder.sum(), 4 * SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * Mapped forEachValueInParallel traverses the given
+ * transformations of all values
+ */
+ public void testMappedForEachValueInParallel() {
+ LongAdder adder = new LongAdder();
+ ConcurrentHashMap<Long, Long> m = longMap();
+ m.forEachValue(1L, (Long x) -> Long.valueOf(4 * x.longValue()),
+ (Long x) -> adder.add(x.longValue()));
+ assertEquals(adder.sum(), 4 * SIZE * (SIZE - 1));
+ }
+
+ /**
+ * Mapped forEachInParallel traverses the given
+ * transformations of all mappings
+ */
+ public void testMappedForEachInParallel() {
+ LongAdder adder = new LongAdder();
+ ConcurrentHashMap<Long, Long> m = longMap();
+ m.forEach(1L, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()),
+ (Long x) -> adder.add(x.longValue()));
+ assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * Mapped forEachEntryInParallel traverses the given
+ * transformations of all entries
+ */
+ public void testMappedForEachEntryInParallel() {
+ LongAdder adder = new LongAdder();
+ ConcurrentHashMap<Long, Long> m = longMap();
+ m.forEachEntry(1L, (Map.Entry<Long,Long> e) -> Long.valueOf(e.getKey().longValue() + e.getValue().longValue()),
+ (Long x) -> adder.add(x.longValue()));
+ assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * reduceKeysSequentially accumulates across all keys,
+ */
+ public void testReduceKeysSequentially() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ Long r;
+ r = m.reduceKeys(Long.MAX_VALUE, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+ assertEquals((long)r, (long)SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * reduceValuesSequentially accumulates across all values
+ */
+ public void testReduceValuesSequentially() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ Long r;
+ r = m.reduceKeys(Long.MAX_VALUE, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+ assertEquals((long)r, (long)SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * reduceEntriesSequentially accumulates across all entries
+ */
+ public void testReduceEntriesSequentially() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ Map.Entry<Long,Long> r;
+ r = m.reduceEntries(Long.MAX_VALUE, new AddKeys());
+ assertEquals(r.getKey().longValue(), (long)SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * reduceKeysInParallel accumulates across all keys
+ */
+ public void testReduceKeysInParallel() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ Long r;
+ r = m.reduceKeys(1L, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+ assertEquals((long)r, (long)SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * reduceValuesInParallel accumulates across all values
+ */
+ public void testReduceValuesInParallel() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ Long r;
+ r = m.reduceValues(1L, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+ assertEquals((long)r, (long)SIZE * (SIZE - 1));
+ }
+
+ /**
+ * reduceEntriesInParallel accumulate across all entries
+ */
+ public void testReduceEntriesInParallel() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ Map.Entry<Long,Long> r;
+ r = m.reduceEntries(1L, new AddKeys());
+ assertEquals(r.getKey().longValue(), (long)SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * Mapped reduceKeysSequentially accumulates mapped keys
+ */
+ public void testMapReduceKeysSequentially() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ Long r = m.reduceKeys(Long.MAX_VALUE, (Long x) -> Long.valueOf(4 * x.longValue()),
+ (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+ assertEquals((long)r, (long)4 * SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * Mapped reduceValuesSequentially accumulates mapped values
+ */
+ public void testMapReduceValuesSequentially() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ Long r = m.reduceValues(Long.MAX_VALUE, (Long x) -> Long.valueOf(4 * x.longValue()),
+ (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+ assertEquals((long)r, (long)4 * SIZE * (SIZE - 1));
+ }
+
+ /**
+ * reduceSequentially accumulates across all transformed mappings
+ */
+ public void testMappedReduceSequentially() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ Long r = m.reduce(Long.MAX_VALUE, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()),
+ (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+
+ assertEquals((long)r, (long)3 * SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * Mapped reduceKeysInParallel, accumulates mapped keys
+ */
+ public void testMapReduceKeysInParallel() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ Long r = m.reduceKeys(1L, (Long x) -> Long.valueOf(4 * x.longValue()),
+ (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+ assertEquals((long)r, (long)4 * SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * Mapped reduceValuesInParallel accumulates mapped values
+ */
+ public void testMapReduceValuesInParallel() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ Long r = m.reduceValues(1L, (Long x) -> Long.valueOf(4 * x.longValue()),
+ (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+ assertEquals((long)r, (long)4 * SIZE * (SIZE - 1));
+ }
+
+ /**
+ * reduceInParallel accumulate across all transformed mappings
+ */
+ public void testMappedReduceInParallel() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ Long r;
+ r = m.reduce(1L, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()),
+ (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+ assertEquals((long)r, (long)3 * SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * reduceKeysToLongSequentially accumulates mapped keys
+ */
+ public void testReduceKeysToLongSequentially() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ long lr = m.reduceKeysToLong(Long.MAX_VALUE, (Long x) -> x.longValue(), 0L, Long::sum);
+ assertEquals(lr, (long)SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * reduceKeysToIntSequentially accumulates mapped keys
+ */
+ public void testReduceKeysToIntSequentially() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ int ir = m.reduceKeysToInt(Long.MAX_VALUE, (Long x) -> x.intValue(), 0, Integer::sum);
+ assertEquals(ir, SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * reduceKeysToDoubleSequentially accumulates mapped keys
+ */
+ public void testReduceKeysToDoubleSequentially() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ double dr = m.reduceKeysToDouble(Long.MAX_VALUE, (Long x) -> x.doubleValue(), 0.0, Double::sum);
+ assertEquals(dr, (double)SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * reduceValuesToLongSequentially accumulates mapped values
+ */
+ public void testReduceValuesToLongSequentially() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ long lr = m.reduceValuesToLong(Long.MAX_VALUE, (Long x) -> x.longValue(), 0L, Long::sum);
+ assertEquals(lr, (long)SIZE * (SIZE - 1));
+ }
+
+ /**
+ * reduceValuesToIntSequentially accumulates mapped values
+ */
+ public void testReduceValuesToIntSequentially() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ int ir = m.reduceValuesToInt(Long.MAX_VALUE, (Long x) -> x.intValue(), 0, Integer::sum);
+ assertEquals(ir, SIZE * (SIZE - 1));
+ }
+
+ /**
+ * reduceValuesToDoubleSequentially accumulates mapped values
+ */
+ public void testReduceValuesToDoubleSequentially() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ double dr = m.reduceValuesToDouble(Long.MAX_VALUE, (Long x) -> x.doubleValue(), 0.0, Double::sum);
+ assertEquals(dr, (double)SIZE * (SIZE - 1));
+ }
+
+ /**
+ * reduceKeysToLongInParallel accumulates mapped keys
+ */
+ public void testReduceKeysToLongInParallel() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ long lr = m.reduceKeysToLong(1L, (Long x) -> x.longValue(), 0L, Long::sum);
+ assertEquals(lr, (long)SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * reduceKeysToIntInParallel accumulates mapped keys
+ */
+ public void testReduceKeysToIntInParallel() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ int ir = m.reduceKeysToInt(1L, (Long x) -> x.intValue(), 0, Integer::sum);
+ assertEquals(ir, SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * reduceKeysToDoubleInParallel accumulates mapped values
+ */
+ public void testReduceKeysToDoubleInParallel() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ double dr = m.reduceKeysToDouble(1L, (Long x) -> x.doubleValue(), 0.0, Double::sum);
+ assertEquals(dr, (double)SIZE * (SIZE - 1) / 2);
+ }
+
+ /**
+ * reduceValuesToLongInParallel accumulates mapped values
+ */
+ public void testReduceValuesToLongInParallel() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ long lr = m.reduceValuesToLong(1L, (Long x) -> x.longValue(), 0L, Long::sum);
+ assertEquals(lr, (long)SIZE * (SIZE - 1));
+ }
+
+ /**
+ * reduceValuesToIntInParallel accumulates mapped values
+ */
+ public void testReduceValuesToIntInParallel() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ int ir = m.reduceValuesToInt(1L, (Long x) -> x.intValue(), 0, Integer::sum);
+ assertEquals(ir, SIZE * (SIZE - 1));
+ }
+
+ /**
+ * reduceValuesToDoubleInParallel accumulates mapped values
+ */
+ public void testReduceValuesToDoubleInParallel() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ double dr = m.reduceValuesToDouble(1L, (Long x) -> x.doubleValue(), 0.0, Double::sum);
+ assertEquals(dr, (double)SIZE * (SIZE - 1));
+ }
+
+ /**
+ * searchKeysSequentially returns a non-null result of search
+ * function, or null if none
+ */
+ public void testSearchKeysSequentially() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ Long r;
+ r = m.searchKeys(Long.MAX_VALUE, (Long x) -> x.longValue() == (long)(SIZE/2) ? x : null);
+ assertEquals((long)r, (long)(SIZE/2));
+ r = m.searchKeys(Long.MAX_VALUE, (Long x) -> x.longValue() < 0L ? x : null);
+ assertNull(r);
+ }
+
+ /**
+ * searchValuesSequentially returns a non-null result of search
+ * function, or null if none
+ */
+ public void testSearchValuesSequentially() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ Long r;
+ r = m.searchValues(Long.MAX_VALUE,
+ (Long x) -> (x.longValue() == (long)(SIZE/2)) ? x : null);
+ assertEquals((long)r, (long)(SIZE/2));
+ r = m.searchValues(Long.MAX_VALUE,
+ (Long x) -> (x.longValue() < 0L) ? x : null);
+ assertNull(r);
+ }
+
+ /**
+ * searchSequentially returns a non-null result of search
+ * function, or null if none
+ */
+ public void testSearchSequentially() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ Long r;
+ r = m.search(Long.MAX_VALUE, (Long x, Long y) -> x.longValue() == (long)(SIZE/2) ? x : null);
+ assertEquals((long)r, (long)(SIZE/2));
+ r = m.search(Long.MAX_VALUE, (Long x, Long y) -> x.longValue() < 0L ? x : null);
+ assertNull(r);
+ }
+
+ /**
+ * searchEntriesSequentially returns a non-null result of search
+ * function, or null if none
+ */
+ public void testSearchEntriesSequentially() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ Long r;
+ r = m.searchEntries(Long.MAX_VALUE, (Map.Entry<Long,Long> e) -> e.getKey().longValue() == (long)(SIZE/2) ? e.getKey() : null);
+ assertEquals((long)r, (long)(SIZE/2));
+ r = m.searchEntries(Long.MAX_VALUE, (Map.Entry<Long,Long> e) -> e.getKey().longValue() < 0L ? e.getKey() : null);
+ assertNull(r);
+ }
+
+ /**
+ * searchKeysInParallel returns a non-null result of search
+ * function, or null if none
+ */
+ public void testSearchKeysInParallel() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ Long r;
+ r = m.searchKeys(1L, (Long x) -> x.longValue() == (long)(SIZE/2) ? x : null);
+ assertEquals((long)r, (long)(SIZE/2));
+ r = m.searchKeys(1L, (Long x) -> x.longValue() < 0L ? x : null);
+ assertNull(r);
+ }
+
+ /**
+ * searchValuesInParallel returns a non-null result of search
+ * function, or null if none
+ */
+ public void testSearchValuesInParallel() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ Long r;
+ r = m.searchValues(1L, (Long x) -> x.longValue() == (long)(SIZE/2) ? x : null);
+ assertEquals((long)r, (long)(SIZE/2));
+ r = m.searchValues(1L, (Long x) -> x.longValue() < 0L ? x : null);
+ assertNull(r);
+ }
+
+ /**
+ * searchInParallel returns a non-null result of search function,
+ * or null if none
+ */
+ public void testSearchInParallel() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ Long r;
+ r = m.search(1L, (Long x, Long y) -> x.longValue() == (long)(SIZE/2) ? x : null);
+ assertEquals((long)r, (long)(SIZE/2));
+ r = m.search(1L, (Long x, Long y) -> x.longValue() < 0L ? x : null);
+ assertNull(r);
+ }
+
+ /**
+ * searchEntriesInParallel returns a non-null result of search
+ * function, or null if none
+ */
+ public void testSearchEntriesInParallel() {
+ ConcurrentHashMap<Long, Long> m = longMap();
+ Long r;
+ r = m.searchEntries(1L, (Map.Entry<Long,Long> e) -> e.getKey().longValue() == (long)(SIZE/2) ? e.getKey() : null);
+ assertEquals((long)r, (long)(SIZE/2));
+ r = m.searchEntries(1L, (Map.Entry<Long,Long> e) -> e.getKey().longValue() < 0L ? e.getKey() : null);
+ assertNull(r);
+ }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ConcurrentHashMapTest.java b/jsr166-tests/src/test/java/jsr166/ConcurrentHashMapTest.java
index 4650f41..f427127 100644
--- a/jsr166-tests/src/test/java/jsr166/ConcurrentHashMapTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ConcurrentHashMapTest.java
@@ -30,7 +30,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(ConcurrentHashMapTest.class);
// }
/**
@@ -50,7 +50,9 @@
}
/** Re-implement Integer.compare for old java versions */
- static int compare(int x, int y) { return x < y ? -1 : x > y ? 1 : 0; }
+ static int compare(int x, int y) {
+ return (x < y) ? -1 : (x > y) ? 1 : 0;
+ }
// classes for testing Comparable fallbacks
static class BI implements Comparable<BI> {
@@ -538,7 +540,7 @@
/**
* Constructor (initialCapacity, loadFactor) throws
* IllegalArgumentException if either argument is negative
- */
+ */
public void testConstructor2() {
try {
new ConcurrentHashMap(-1, .75f);
diff --git a/jsr166-tests/src/test/java/jsr166/ConcurrentLinkedDequeTest.java b/jsr166-tests/src/test/java/jsr166/ConcurrentLinkedDequeTest.java
index c445957..6625e7e 100644
--- a/jsr166-tests/src/test/java/jsr166/ConcurrentLinkedDequeTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ConcurrentLinkedDequeTest.java
@@ -29,7 +29,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(ConcurrentLinkedDequeTest.class);
// }
/**
@@ -69,8 +69,7 @@
*/
public void testConstructor4() {
try {
- Integer[] ints = new Integer[SIZE];
- new ConcurrentLinkedDeque(Arrays.asList(ints));
+ new ConcurrentLinkedDeque(Arrays.asList(new Integer[SIZE]));
shouldThrow();
} catch (NullPointerException success) {}
}
@@ -79,10 +78,10 @@
* Initializing from Collection with some null elements throws NPE
*/
public void testConstructor5() {
+ Integer[] ints = new Integer[SIZE];
+ for (int i = 0; i < SIZE - 1; ++i)
+ ints[i] = new Integer(i);
try {
- Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
- ints[i] = new Integer(i);
new ConcurrentLinkedDeque(Arrays.asList(ints));
shouldThrow();
} catch (NullPointerException success) {}
@@ -120,7 +119,7 @@
public void testSize() {
ConcurrentLinkedDeque q = populatedDeque(SIZE);
for (int i = 0; i < SIZE; ++i) {
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
q.remove();
}
for (int i = 0; i < SIZE; ++i) {
@@ -133,8 +132,8 @@
* push(null) throws NPE
*/
public void testPushNull() {
+ ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
try {
- ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
q.push(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -168,8 +167,8 @@
* offer(null) throws NPE
*/
public void testOfferNull() {
+ ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
try {
- ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
q.offer(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -179,8 +178,8 @@
* offerFirst(null) throws NPE
*/
public void testOfferFirstNull() {
+ ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
try {
- ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
q.offerFirst(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -190,8 +189,8 @@
* offerLast(null) throws NPE
*/
public void testOfferLastNull() {
+ ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
try {
- ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
q.offerLast(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -234,8 +233,8 @@
* add(null) throws NPE
*/
public void testAddNull() {
+ ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
try {
- ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
q.add(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -245,8 +244,8 @@
* addFirst(null) throws NPE
*/
public void testAddFirstNull() {
+ ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
try {
- ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
q.addFirst(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -256,8 +255,8 @@
* addLast(null) throws NPE
*/
public void testAddLastNull() {
+ ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
try {
- ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
q.addLast(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -300,8 +299,8 @@
* addAll(null) throws NPE
*/
public void testAddAll1() {
+ ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
try {
- ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
q.addAll(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -311,8 +310,8 @@
* addAll(this) throws IAE
*/
public void testAddAllSelf() {
+ ConcurrentLinkedDeque q = populatedDeque(SIZE);
try {
- ConcurrentLinkedDeque q = populatedDeque(SIZE);
q.addAll(q);
shouldThrow();
} catch (IllegalArgumentException success) {}
@@ -322,10 +321,9 @@
* addAll of a collection with null elements throws NPE
*/
public void testAddAll2() {
+ ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
try {
- ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
- Integer[] ints = new Integer[SIZE];
- q.addAll(Arrays.asList(ints));
+ q.addAll(Arrays.asList(new Integer[SIZE]));
shouldThrow();
} catch (NullPointerException success) {}
}
@@ -335,11 +333,11 @@
* possibly adding some elements
*/
public void testAddAll3() {
+ ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+ Integer[] ints = new Integer[SIZE];
+ for (int i = 0; i < SIZE - 1; ++i)
+ ints[i] = new Integer(i);
try {
- ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
- Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
- ints[i] = new Integer(i);
q.addAll(Arrays.asList(ints));
shouldThrow();
} catch (NullPointerException success) {}
@@ -376,7 +374,7 @@
*/
public void testPollLast() {
ConcurrentLinkedDeque q = populatedDeque(SIZE);
- for (int i = SIZE-1; i >= 0; --i) {
+ for (int i = SIZE - 1; i >= 0; --i) {
assertEquals(i, q.pollLast());
}
assertNull(q.pollLast());
@@ -445,14 +443,14 @@
assertTrue(q.contains(i));
assertTrue(q.remove(i));
assertFalse(q.contains(i));
- assertTrue(q.contains(i-1));
+ assertTrue(q.contains(i - 1));
}
for (int i = 0; i < SIZE; i += 2) {
assertTrue(q.contains(i));
assertTrue(q.remove(i));
assertFalse(q.contains(i));
- assertFalse(q.remove(i+1));
- assertFalse(q.contains(i+1));
+ assertFalse(q.remove(i + 1));
+ assertFalse(q.contains(i + 1));
}
assertTrue(q.isEmpty());
}
@@ -476,7 +474,7 @@
*/
public void testPeekLast() {
ConcurrentLinkedDeque q = populatedDeque(SIZE);
- for (int i = SIZE-1; i >= 0; --i) {
+ for (int i = SIZE - 1; i >= 0; --i) {
assertEquals(i, q.peekLast());
assertEquals(i, q.pollLast());
assertTrue(q.peekLast() == null ||
@@ -505,7 +503,7 @@
*/
public void testLastElement() {
ConcurrentLinkedDeque q = populatedDeque(SIZE);
- for (int i = SIZE-1; i >= 0; --i) {
+ for (int i = SIZE - 1; i >= 0; --i) {
assertEquals(i, q.getLast());
assertEquals(i, q.pollLast());
}
@@ -556,7 +554,7 @@
}
for (int i = 0; i < SIZE; i += 2) {
assertTrue(q.removeFirstOccurrence(new Integer(i)));
- assertFalse(q.removeFirstOccurrence(new Integer(i+1)));
+ assertFalse(q.removeFirstOccurrence(new Integer(i + 1)));
}
assertTrue(q.isEmpty());
}
@@ -571,7 +569,7 @@
}
for (int i = 0; i < SIZE; i += 2) {
assertTrue(q.removeLastOccurrence(new Integer(i)));
- assertFalse(q.removeLastOccurrence(new Integer(i+1)));
+ assertFalse(q.removeLastOccurrence(new Integer(i + 1)));
}
assertTrue(q.isEmpty());
}
@@ -630,7 +628,7 @@
assertTrue(changed);
assertTrue(q.containsAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
p.remove();
}
}
@@ -643,7 +641,7 @@
ConcurrentLinkedDeque q = populatedDeque(SIZE);
ConcurrentLinkedDeque p = populatedDeque(i);
assertTrue(q.removeAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
for (int j = 0; j < i; ++j) {
Integer x = (Integer)(p.remove());
assertFalse(q.contains(x));
@@ -759,18 +757,18 @@
final Random rng = new Random();
for (int iters = 0; iters < 100; ++iters) {
int max = rng.nextInt(5) + 2;
- int split = rng.nextInt(max-1) + 1;
+ int split = rng.nextInt(max - 1) + 1;
for (int j = 1; j <= max; ++j)
q.add(new Integer(j));
Iterator it = q.iterator();
for (int j = 1; j <= split; ++j)
assertEquals(it.next(), new Integer(j));
it.remove();
- assertEquals(it.next(), new Integer(split+1));
+ assertEquals(it.next(), new Integer(split + 1));
for (int j = 1; j <= split; ++j)
q.remove(new Integer(j));
it = q.iterator();
- for (int j = split+1; j <= max; ++j) {
+ for (int j = split + 1; j <= max; ++j) {
assertEquals(it.next(), new Integer(j));
it.remove();
}
@@ -827,18 +825,18 @@
final Random rng = new Random();
for (int iters = 0; iters < 100; ++iters) {
int max = rng.nextInt(5) + 2;
- int split = rng.nextInt(max-1) + 1;
+ int split = rng.nextInt(max - 1) + 1;
for (int j = max; j >= 1; --j)
q.add(new Integer(j));
Iterator it = q.descendingIterator();
for (int j = 1; j <= split; ++j)
assertEquals(it.next(), new Integer(j));
it.remove();
- assertEquals(it.next(), new Integer(split+1));
+ assertEquals(it.next(), new Integer(split + 1));
for (int j = 1; j <= split; ++j)
q.remove(new Integer(j));
it = q.descendingIterator();
- for (int j = split+1; j <= max; ++j) {
+ for (int j = split + 1; j <= max; ++j) {
assertEquals(it.next(), new Integer(j));
it.remove();
}
diff --git a/jsr166-tests/src/test/java/jsr166/ConcurrentLinkedQueueTest.java b/jsr166-tests/src/test/java/jsr166/ConcurrentLinkedQueueTest.java
index d3f5b1f..70519a4 100644
--- a/jsr166-tests/src/test/java/jsr166/ConcurrentLinkedQueueTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ConcurrentLinkedQueueTest.java
@@ -27,7 +27,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(ConcurrentLinkedQueueTest.class);
// }
/**
@@ -66,8 +66,7 @@
*/
public void testConstructor4() {
try {
- Integer[] ints = new Integer[SIZE];
- new ConcurrentLinkedQueue(Arrays.asList(ints));
+ new ConcurrentLinkedQueue(Arrays.asList(new Integer[SIZE]));
shouldThrow();
} catch (NullPointerException success) {}
}
@@ -76,10 +75,10 @@
* Initializing from Collection with some null elements throws NPE
*/
public void testConstructor5() {
+ Integer[] ints = new Integer[SIZE];
+ for (int i = 0; i < SIZE - 1; ++i)
+ ints[i] = new Integer(i);
try {
- Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
- ints[i] = new Integer(i);
new ConcurrentLinkedQueue(Arrays.asList(ints));
shouldThrow();
} catch (NullPointerException success) {}
@@ -117,7 +116,7 @@
public void testSize() {
ConcurrentLinkedQueue q = populatedQueue(SIZE);
for (int i = 0; i < SIZE; ++i) {
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
q.remove();
}
for (int i = 0; i < SIZE; ++i) {
@@ -130,8 +129,8 @@
* offer(null) throws NPE
*/
public void testOfferNull() {
+ ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
try {
- ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
q.offer(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -141,8 +140,8 @@
* add(null) throws NPE
*/
public void testAddNull() {
+ ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
try {
- ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
q.add(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -172,8 +171,8 @@
* addAll(null) throws NPE
*/
public void testAddAll1() {
+ ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
try {
- ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
q.addAll(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -183,8 +182,8 @@
* addAll(this) throws IAE
*/
public void testAddAllSelf() {
+ ConcurrentLinkedQueue q = populatedQueue(SIZE);
try {
- ConcurrentLinkedQueue q = populatedQueue(SIZE);
q.addAll(q);
shouldThrow();
} catch (IllegalArgumentException success) {}
@@ -194,10 +193,9 @@
* addAll of a collection with null elements throws NPE
*/
public void testAddAll2() {
+ ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
try {
- ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
- Integer[] ints = new Integer[SIZE];
- q.addAll(Arrays.asList(ints));
+ q.addAll(Arrays.asList(new Integer[SIZE]));
shouldThrow();
} catch (NullPointerException success) {}
}
@@ -207,11 +205,11 @@
* possibly adding some elements
*/
public void testAddAll3() {
+ ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+ Integer[] ints = new Integer[SIZE];
+ for (int i = 0; i < SIZE - 1; ++i)
+ ints[i] = new Integer(i);
try {
- ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
- Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
- ints[i] = new Integer(i);
q.addAll(Arrays.asList(ints));
shouldThrow();
} catch (NullPointerException success) {}
@@ -295,14 +293,14 @@
assertTrue(q.contains(i));
assertTrue(q.remove(i));
assertFalse(q.contains(i));
- assertTrue(q.contains(i-1));
+ assertTrue(q.contains(i - 1));
}
for (int i = 0; i < SIZE; i += 2) {
assertTrue(q.contains(i));
assertTrue(q.remove(i));
assertFalse(q.contains(i));
- assertFalse(q.remove(i+1));
- assertFalse(q.contains(i+1));
+ assertFalse(q.remove(i + 1));
+ assertFalse(q.contains(i + 1));
}
assertTrue(q.isEmpty());
}
@@ -361,7 +359,7 @@
assertTrue(changed);
assertTrue(q.containsAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
p.remove();
}
}
@@ -374,7 +372,7 @@
ConcurrentLinkedQueue q = populatedQueue(SIZE);
ConcurrentLinkedQueue p = populatedQueue(i);
assertTrue(q.removeAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
for (int j = 0; j < i; ++j) {
Integer x = (Integer)(p.remove());
assertFalse(q.contains(x));
diff --git a/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListMapTest.java b/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListMapTest.java
index 0aadd23..f53a446 100644
--- a/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListMapTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListMapTest.java
@@ -30,7 +30,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(ConcurrentSkipListMapTest.class);
// }
/**
@@ -1278,7 +1278,7 @@
}
static boolean eq(Integer i, int j) {
- return i == null ? j == -1 : i == j;
+ return (i == null) ? j == -1 : i == j;
}
}
diff --git a/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListSetTest.java b/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListSetTest.java
index 41f8835..959d703 100644
--- a/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListSetTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListSetTest.java
@@ -29,7 +29,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(ConcurrentSkipListSetTest.class);
// }
static class MyReverseComparator implements Comparator {
@@ -46,7 +46,7 @@
ConcurrentSkipListSet<Integer> q =
new ConcurrentSkipListSet<Integer>();
assertTrue(q.isEmpty());
- for (int i = n-1; i >= 0; i -= 2)
+ for (int i = n - 1; i >= 0; i -= 2)
assertTrue(q.add(new Integer(i)));
for (int i = (n & 1); i < n; i += 2)
assertTrue(q.add(new Integer(i)));
@@ -92,8 +92,7 @@
*/
public void testConstructor4() {
try {
- Integer[] ints = new Integer[SIZE];
- new ConcurrentSkipListSet(Arrays.asList(ints));
+ new ConcurrentSkipListSet(Arrays.asList(new Integer[SIZE]));
shouldThrow();
} catch (NullPointerException success) {}
}
@@ -102,10 +101,10 @@
* Initializing from Collection with some null elements throws NPE
*/
public void testConstructor5() {
+ Integer[] ints = new Integer[SIZE];
+ for (int i = 0; i < SIZE - 1; ++i)
+ ints[i] = new Integer(i);
try {
- Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
- ints[i] = new Integer(i);
new ConcurrentSkipListSet(Arrays.asList(ints));
shouldThrow();
} catch (NullPointerException success) {}
@@ -134,7 +133,7 @@
for (int i = 0; i < SIZE; ++i)
ints[i] = new Integer(i);
q.addAll(Arrays.asList(ints));
- for (int i = SIZE-1; i >= 0; --i)
+ for (int i = SIZE - 1; i >= 0; --i)
assertEquals(ints[i], q.pollFirst());
}
@@ -158,7 +157,7 @@
public void testSize() {
ConcurrentSkipListSet q = populatedSet(SIZE);
for (int i = 0; i < SIZE; ++i) {
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
q.pollFirst();
}
for (int i = 0; i < SIZE; ++i) {
@@ -238,7 +237,7 @@
public void testAddAll3() {
ConcurrentSkipListSet q = new ConcurrentSkipListSet();
Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
+ for (int i = 0; i < SIZE - 1; ++i)
ints[i] = new Integer(i);
try {
q.addAll(Arrays.asList(ints));
@@ -253,7 +252,7 @@
Integer[] empty = new Integer[0];
Integer[] ints = new Integer[SIZE];
for (int i = 0; i < SIZE; ++i)
- ints[i] = new Integer(SIZE-1-i);
+ ints[i] = new Integer(SIZE - 1 - i);
ConcurrentSkipListSet q = new ConcurrentSkipListSet();
assertFalse(q.addAll(Arrays.asList(empty)));
assertTrue(q.addAll(Arrays.asList(ints)));
@@ -277,7 +276,7 @@
*/
public void testPollLast() {
ConcurrentSkipListSet q = populatedSet(SIZE);
- for (int i = SIZE-1; i >= 0; --i) {
+ for (int i = SIZE - 1; i >= 0; --i) {
assertEquals(i, q.pollLast());
}
assertNull(q.pollFirst());
@@ -292,14 +291,14 @@
assertTrue(q.contains(i));
assertTrue(q.remove(i));
assertFalse(q.contains(i));
- assertTrue(q.contains(i-1));
+ assertTrue(q.contains(i - 1));
}
for (int i = 0; i < SIZE; i += 2) {
assertTrue(q.contains(i));
assertTrue(q.remove(i));
assertFalse(q.contains(i));
- assertFalse(q.remove(i+1));
- assertFalse(q.contains(i+1));
+ assertFalse(q.remove(i + 1));
+ assertFalse(q.contains(i + 1));
}
assertTrue(q.isEmpty());
}
@@ -358,7 +357,7 @@
assertTrue(changed);
assertTrue(q.containsAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
p.pollFirst();
}
}
@@ -371,7 +370,7 @@
ConcurrentSkipListSet q = populatedSet(SIZE);
ConcurrentSkipListSet p = populatedSet(i);
assertTrue(q.removeAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
for (int j = 0; j < i; ++j) {
Integer x = (Integer)(p.pollFirst());
assertFalse(q.contains(x));
@@ -980,7 +979,7 @@
}
static boolean eq(Integer i, int j) {
- return i == null ? j == -1 : i == j;
+ return (i == null) ? j == -1 : i == j;
}
}
diff --git a/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListSubMapTest.java b/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListSubMapTest.java
index 5315bcb..a6510f5 100644
--- a/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListSubMapTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListSubMapTest.java
@@ -28,7 +28,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(ConcurrentSkipListSubMapTest.class);
// }
/**
diff --git a/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListSubSetTest.java b/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListSubSetTest.java
index f1c4aae..220a092 100644
--- a/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListSubSetTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ConcurrentSkipListSubSetTest.java
@@ -24,7 +24,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(ConcurrentSkipListSubSetTest.class);
// }
static class MyReverseComparator implements Comparator {
@@ -42,7 +42,7 @@
new ConcurrentSkipListSet<Integer>();
assertTrue(q.isEmpty());
- for (int i = n-1; i >= 0; i -= 2)
+ for (int i = n - 1; i >= 0; i -= 2)
assertTrue(q.add(new Integer(i)));
for (int i = (n & 1); i < n; i += 2)
assertTrue(q.add(new Integer(i)));
@@ -127,7 +127,7 @@
public void testSize() {
NavigableSet q = populatedSet(SIZE);
for (int i = 0; i < SIZE; ++i) {
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
q.pollFirst();
}
for (int i = 0; i < SIZE; ++i) {
@@ -206,8 +206,8 @@
public void testAddAll3() {
NavigableSet q = set0();
Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
- ints[i] = new Integer(i+SIZE);
+ for (int i = 0; i < SIZE - 1; ++i)
+ ints[i] = new Integer(i + SIZE);
try {
q.addAll(Arrays.asList(ints));
shouldThrow();
@@ -221,7 +221,7 @@
Integer[] empty = new Integer[0];
Integer[] ints = new Integer[SIZE];
for (int i = 0; i < SIZE; ++i)
- ints[i] = new Integer(SIZE-1- i);
+ ints[i] = new Integer(SIZE - 1 - i);
NavigableSet q = set0();
assertFalse(q.addAll(Arrays.asList(empty)));
assertTrue(q.addAll(Arrays.asList(ints)));
@@ -249,14 +249,14 @@
assertTrue(q.contains(i));
assertTrue(q.remove(i));
assertFalse(q.contains(i));
- assertTrue(q.contains(i-1));
+ assertTrue(q.contains(i - 1));
}
for (int i = 0; i < SIZE; i += 2) {
assertTrue(q.contains(i));
assertTrue(q.remove(i));
assertFalse(q.contains(i));
- assertFalse(q.remove(i+1));
- assertFalse(q.contains(i+1));
+ assertFalse(q.remove(i + 1));
+ assertFalse(q.contains(i + 1));
}
assertTrue(q.isEmpty());
}
@@ -315,7 +315,7 @@
assertTrue(changed);
assertTrue(q.containsAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
p.pollFirst();
}
}
@@ -328,7 +328,7 @@
NavigableSet q = populatedSet(SIZE);
NavigableSet p = populatedSet(i);
assertTrue(q.removeAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
for (int j = 0; j < i; ++j) {
Integer x = (Integer)(p.pollFirst());
assertFalse(q.contains(x));
@@ -623,7 +623,7 @@
public void testDescendingSize() {
NavigableSet q = populatedSet(SIZE);
for (int i = 0; i < SIZE; ++i) {
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
q.pollFirst();
}
for (int i = 0; i < SIZE; ++i) {
@@ -702,8 +702,8 @@
public void testDescendingAddAll3() {
NavigableSet q = dset0();
Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
- ints[i] = new Integer(i+SIZE);
+ for (int i = 0; i < SIZE - 1; ++i)
+ ints[i] = new Integer(i + SIZE);
try {
q.addAll(Arrays.asList(ints));
shouldThrow();
@@ -717,7 +717,7 @@
Integer[] empty = new Integer[0];
Integer[] ints = new Integer[SIZE];
for (int i = 0; i < SIZE; ++i)
- ints[i] = new Integer(SIZE-1- i);
+ ints[i] = new Integer(SIZE - 1 - i);
NavigableSet q = dset0();
assertFalse(q.addAll(Arrays.asList(empty)));
assertTrue(q.addAll(Arrays.asList(ints)));
@@ -746,7 +746,7 @@
}
for (int i = 0; i < SIZE; i += 2 ) {
assertTrue(q.remove(new Integer(i)));
- assertFalse(q.remove(new Integer(i+1)));
+ assertFalse(q.remove(new Integer(i + 1)));
}
assertTrue(q.isEmpty());
}
@@ -805,7 +805,7 @@
assertTrue(changed);
assertTrue(q.containsAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
p.pollFirst();
}
}
@@ -818,7 +818,7 @@
NavigableSet q = populatedSet(SIZE);
NavigableSet p = populatedSet(i);
assertTrue(q.removeAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
for (int j = 0; j < i; ++j) {
Integer x = (Integer)(p.pollFirst());
assertFalse(q.contains(x));
diff --git a/jsr166-tests/src/test/java/jsr166/CopyOnWriteArrayListTest.java b/jsr166-tests/src/test/java/jsr166/CopyOnWriteArrayListTest.java
index 658268a..8a37d03 100644
--- a/jsr166-tests/src/test/java/jsr166/CopyOnWriteArrayListTest.java
+++ b/jsr166-tests/src/test/java/jsr166/CopyOnWriteArrayListTest.java
@@ -31,7 +31,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(CopyOnWriteArrayListTest.class);
// }
static CopyOnWriteArrayList<Integer> populatedArray(int n) {
@@ -67,7 +67,7 @@
*/
public void testConstructor2() {
Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
+ for (int i = 0; i < SIZE - 1; ++i)
ints[i] = new Integer(i);
CopyOnWriteArrayList a = new CopyOnWriteArrayList(ints);
for (int i = 0; i < SIZE; ++i)
@@ -79,7 +79,7 @@
*/
public void testConstructor3() {
Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
+ for (int i = 0; i < SIZE - 1; ++i)
ints[i] = new Integer(i);
CopyOnWriteArrayList a = new CopyOnWriteArrayList(Arrays.asList(ints));
for (int i = 0; i < SIZE; ++i)
@@ -181,18 +181,26 @@
CopyOnWriteArrayList b = populatedArray(3);
assertTrue(a.equals(b));
assertTrue(b.equals(a));
+ assertTrue(a.containsAll(b));
+ assertTrue(b.containsAll(a));
assertEquals(a.hashCode(), b.hashCode());
a.add(m1);
assertFalse(a.equals(b));
assertFalse(b.equals(a));
+ assertTrue(a.containsAll(b));
+ assertFalse(b.containsAll(a));
b.add(m1);
assertTrue(a.equals(b));
assertTrue(b.equals(a));
+ assertTrue(a.containsAll(b));
+ assertTrue(b.containsAll(a));
assertEquals(a.hashCode(), b.hashCode());
+
+ assertFalse(a.equals(null));
}
/**
- * containsAll returns true for collection with subset of elements
+ * containsAll returns true for collections with subset of elements
*/
public void testContainsAll() {
CopyOnWriteArrayList full = populatedArray(3);
@@ -201,6 +209,11 @@
assertTrue(full.containsAll(Arrays.asList(one, two)));
assertFalse(full.containsAll(Arrays.asList(one, two, six)));
assertFalse(full.containsAll(Arrays.asList(six)));
+
+ try {
+ full.containsAll(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
/**
@@ -342,7 +355,7 @@
ListIterator i = full.listIterator(1);
int j;
for (j = 0; i.hasNext(); j++)
- assertEquals(j+1, i.next());
+ assertEquals(j + 1, i.next());
assertEquals(2, j);
}
@@ -441,7 +454,7 @@
a = new Integer[0];
assertSame(a, empty.toArray(a));
- a = new Integer[SIZE/2];
+ a = new Integer[SIZE / 2];
Arrays.fill(a, 42);
assertSame(a, empty.toArray(a));
assertNull(a[0]);
@@ -465,7 +478,7 @@
assertSame(a, full.toArray(a));
assertTrue(Arrays.equals(elements, a));
- a = new Integer[2*SIZE];
+ a = new Integer[2 * SIZE];
Arrays.fill(a, 42);
assertSame(a, full.toArray(a));
assertTrue(Arrays.equals(elements, Arrays.copyOf(a, SIZE)));
diff --git a/jsr166-tests/src/test/java/jsr166/CopyOnWriteArraySetTest.java b/jsr166-tests/src/test/java/jsr166/CopyOnWriteArraySetTest.java
index 2810802..a486c6a 100644
--- a/jsr166-tests/src/test/java/jsr166/CopyOnWriteArraySetTest.java
+++ b/jsr166-tests/src/test/java/jsr166/CopyOnWriteArraySetTest.java
@@ -28,7 +28,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(CopyOnWriteArraySetTest.class);
// }
static CopyOnWriteArraySet<Integer> populatedSet(int n) {
@@ -64,7 +64,7 @@
*/
public void testConstructor3() {
Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
+ for (int i = 0; i < SIZE - 1; ++i)
ints[i] = new Integer(i);
CopyOnWriteArraySet a = new CopyOnWriteArraySet(Arrays.asList(ints));
for (int i = 0; i < SIZE; ++i)
@@ -139,14 +139,46 @@
CopyOnWriteArraySet b = populatedSet(3);
assertTrue(a.equals(b));
assertTrue(b.equals(a));
+ assertTrue(a.containsAll(b));
+ assertTrue(b.containsAll(a));
assertEquals(a.hashCode(), b.hashCode());
+ assertEquals(a.size(), b.size());
+
a.add(m1);
assertFalse(a.equals(b));
assertFalse(b.equals(a));
+ assertTrue(a.containsAll(b));
+ assertFalse(b.containsAll(a));
b.add(m1);
assertTrue(a.equals(b));
assertTrue(b.equals(a));
+ assertTrue(a.containsAll(b));
+ assertTrue(b.containsAll(a));
assertEquals(a.hashCode(), b.hashCode());
+
+ Object x = a.iterator().next();
+ a.remove(x);
+ assertFalse(a.equals(b));
+ assertFalse(b.equals(a));
+ assertFalse(a.containsAll(b));
+ assertTrue(b.containsAll(a));
+ a.add(x);
+ assertTrue(a.equals(b));
+ assertTrue(b.equals(a));
+ assertTrue(a.containsAll(b));
+ assertTrue(b.containsAll(a));
+ assertEquals(a.hashCode(), b.hashCode());
+ assertEquals(a.size(), b.size());
+
+ CopyOnWriteArraySet empty1 = new CopyOnWriteArraySet(Arrays.asList());
+ CopyOnWriteArraySet empty2 = new CopyOnWriteArraySet(Arrays.asList());
+ assertTrue(empty1.equals(empty1));
+ assertTrue(empty1.equals(empty2));
+
+ assertFalse(empty1.equals(a));
+ assertFalse(a.equals(empty1));
+
+ assertFalse(a.equals(null));
}
/**
@@ -154,11 +186,24 @@
*/
public void testContainsAll() {
Collection full = populatedSet(3);
+ assertTrue(full.containsAll(full));
assertTrue(full.containsAll(Arrays.asList()));
assertTrue(full.containsAll(Arrays.asList(one)));
assertTrue(full.containsAll(Arrays.asList(one, two)));
assertFalse(full.containsAll(Arrays.asList(one, two, six)));
assertFalse(full.containsAll(Arrays.asList(six)));
+
+ CopyOnWriteArraySet empty1 = new CopyOnWriteArraySet(Arrays.asList());
+ CopyOnWriteArraySet empty2 = new CopyOnWriteArraySet(Arrays.asList());
+ assertTrue(empty1.containsAll(empty2));
+ assertTrue(empty1.containsAll(empty1));
+ assertFalse(empty1.containsAll(full));
+ assertTrue(full.containsAll(empty1));
+
+ try {
+ full.containsAll(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
/**
@@ -289,7 +334,7 @@
a = new Integer[0];
assertSame(a, empty.toArray(a));
- a = new Integer[SIZE/2];
+ a = new Integer[SIZE / 2];
Arrays.fill(a, 42);
assertSame(a, empty.toArray(a));
assertNull(a[0]);
@@ -313,7 +358,7 @@
assertSame(a, full.toArray(a));
assertTrue(Arrays.equals(elements, a));
- a = new Integer[2*SIZE];
+ a = new Integer[2 * SIZE];
Arrays.fill(a, 42);
assertSame(a, full.toArray(a));
assertTrue(Arrays.equals(elements, Arrays.copyOf(a, SIZE)));
@@ -327,10 +372,10 @@
* not store the objects inside the set
*/
public void testToArray_ArrayStoreException() {
+ CopyOnWriteArraySet c = new CopyOnWriteArraySet();
+ c.add("zfasdfsdf");
+ c.add("asdadasd");
try {
- CopyOnWriteArraySet c = new CopyOnWriteArraySet();
- c.add("zfasdfsdf");
- c.add("asdadasd");
c.toArray(new Long[5]);
shouldThrow();
} catch (ArrayStoreException success) {}
diff --git a/jsr166-tests/src/test/java/jsr166/CountDownLatchTest.java b/jsr166-tests/src/test/java/jsr166/CountDownLatchTest.java
index da1ebb4..e764c9e 100644
--- a/jsr166-tests/src/test/java/jsr166/CountDownLatchTest.java
+++ b/jsr166-tests/src/test/java/jsr166/CountDownLatchTest.java
@@ -23,7 +23,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(CountDownLatchTest.class);
// }
/**
diff --git a/jsr166-tests/src/test/java/jsr166/CountedCompleterTest.java b/jsr166-tests/src/test/java/jsr166/CountedCompleterTest.java
index 80d7b3b..8a38eff 100644
--- a/jsr166-tests/src/test/java/jsr166/CountedCompleterTest.java
+++ b/jsr166-tests/src/test/java/jsr166/CountedCompleterTest.java
@@ -31,7 +31,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(CountedCompleterTest.class);
// }
// Runs with "mainPool" use > 1 thread. singletonPool tests use 1
@@ -53,7 +53,7 @@
}
private void testInvokeOnPool(ForkJoinPool pool, ForkJoinTask a) {
- try {
+ try (PoolCleaner cleaner = cleaner(pool)) {
assertFalse(a.isDone());
assertFalse(a.isCompletedNormally());
assertFalse(a.isCompletedAbnormally());
@@ -69,8 +69,6 @@
assertFalse(a.isCancelled());
assertNull(a.getException());
assertNull(a.getRawResult());
- } finally {
- joinPool(pool);
}
}
@@ -99,17 +97,17 @@
{
Thread.currentThread().interrupt();
- long t0 = System.nanoTime();
+ long startTime = System.nanoTime();
assertNull(a.join());
- assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
+ assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
Thread.interrupted();
}
{
Thread.currentThread().interrupt();
- long t0 = System.nanoTime();
+ long startTime = System.nanoTime();
a.quietlyJoin(); // should be no-op
- assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
+ assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
Thread.interrupted();
}
@@ -142,9 +140,9 @@
Thread.interrupted();
{
- long t0 = System.nanoTime();
+ long startTime = System.nanoTime();
a.quietlyJoin(); // should be no-op
- assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
+ assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
}
try {
@@ -180,9 +178,9 @@
Thread.interrupted();
{
- long t0 = System.nanoTime();
+ long startTime = System.nanoTime();
a.quietlyJoin(); // should be no-op
- assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
+ assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
}
try {
@@ -284,6 +282,9 @@
final class NoopCC extends CheckedCC {
NoopCC() { super(); }
NoopCC(CountedCompleter p) { super(p); }
+ NoopCC(CountedCompleter p, int initialPendingCount) {
+ super(p, initialPendingCount);
+ }
protected void realCompute() {}
}
@@ -302,6 +303,7 @@
void testComplete(NoopCC cc, Object x, int pendingCount) {
cc.setPendingCount(pendingCount);
cc.checkCompletes(x);
+ assertEquals(pendingCount, cc.getPendingCount());
}
/**
@@ -315,14 +317,20 @@
}
/**
- * completeExceptionally(null) throws NullPointerException
+ * completeExceptionally(null) surprisingly has the same effect as
+ * completeExceptionally(new RuntimeException())
*/
public void testCompleteExceptionally_null() {
+ NoopCC a = new NoopCC();
+ a.completeExceptionally(null);
try {
- new NoopCC()
- .checkCompletesExceptionally(null);
+ a.invoke();
shouldThrow();
- } catch (NullPointerException success) {}
+ } catch (RuntimeException success) {
+ assertSame(success.getClass(), RuntimeException.class);
+ assertNull(success.getCause());
+ a.checkCompletedExceptionally(success);
+ }
}
/**
@@ -331,10 +339,15 @@
public void testSetPendingCount() {
NoopCC a = new NoopCC();
assertEquals(0, a.getPendingCount());
- a.setPendingCount(1);
- assertEquals(1, a.getPendingCount());
- a.setPendingCount(27);
- assertEquals(27, a.getPendingCount());
+ int[] vals = {
+ -1, 0, 1,
+ Integer.MIN_VALUE,
+ Integer.MAX_VALUE,
+ };
+ for (int val : vals) {
+ a.setPendingCount(val);
+ assertEquals(val, a.getPendingCount());
+ }
}
/**
@@ -347,21 +360,26 @@
assertEquals(1, a.getPendingCount());
a.addToPendingCount(27);
assertEquals(28, a.getPendingCount());
+ a.addToPendingCount(-28);
+ assertEquals(0, a.getPendingCount());
}
/**
* decrementPendingCountUnlessZero decrements reported pending
* count unless zero
*/
- public void testDecrementPendingCount() {
- NoopCC a = new NoopCC();
- assertEquals(0, a.getPendingCount());
- a.addToPendingCount(1);
+ public void testDecrementPendingCountUnlessZero() {
+ NoopCC a = new NoopCC(null, 2);
+ assertEquals(2, a.getPendingCount());
+ assertEquals(2, a.decrementPendingCountUnlessZero());
assertEquals(1, a.getPendingCount());
- a.decrementPendingCountUnlessZero();
+ assertEquals(1, a.decrementPendingCountUnlessZero());
assertEquals(0, a.getPendingCount());
- a.decrementPendingCountUnlessZero();
+ assertEquals(0, a.decrementPendingCountUnlessZero());
assertEquals(0, a.getPendingCount());
+ a.setPendingCount(-1);
+ assertEquals(-1, a.decrementPendingCountUnlessZero());
+ assertEquals(-2, a.getPendingCount());
}
/**
@@ -485,7 +503,7 @@
}
/**
- * quietlyCompleteRoot completes root task
+ * quietlyCompleteRoot completes root task and only root task
*/
public void testQuietlyCompleteRoot() {
NoopCC a = new NoopCC();
diff --git a/jsr166-tests/src/test/java/jsr166/CyclicBarrierTest.java b/jsr166-tests/src/test/java/jsr166/CyclicBarrierTest.java
index a9d8c54..37adcb1 100644
--- a/jsr166-tests/src/test/java/jsr166/CyclicBarrierTest.java
+++ b/jsr166-tests/src/test/java/jsr166/CyclicBarrierTest.java
@@ -27,7 +27,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(CyclicBarrierTest.class);
// }
private volatile int countAction;
diff --git a/jsr166-tests/src/test/java/jsr166/DelayQueueTest.java b/jsr166-tests/src/test/java/jsr166/DelayQueueTest.java
index 7619b48..e42ac2d 100644
--- a/jsr166-tests/src/test/java/jsr166/DelayQueueTest.java
+++ b/jsr166-tests/src/test/java/jsr166/DelayQueueTest.java
@@ -25,25 +25,26 @@
import junit.framework.Test;
-// android-changed: Extend BlockingQueueTest directly.
-public class DelayQueueTest extends BlockingQueueTest {
+public class DelayQueueTest extends JSR166TestCase {
// android-changed: Extend BlockingQueueTest directly instead of creating
// an inner class and its associated suite.
//
// public static class Generic extends BlockingQueueTest {
- // protected BlockingQueue emptyCollection() {
+ // protected BlockingQueue emptyCollection() {
// return new DelayQueue();
// }
// protected PDelay makeElement(int i) {
// return new PDelay(i);
// }
// }
+
+ // android-note: Removed because the CTS runner does a bad job of
+ // retrying tests that have suite() declarations.
//
// public static void main(String[] args) {
// main(suite(), args);
// }
- //
// public static Test suite() {
// return newTestSuite(DelayQueueTest.class,
// new Generic().testSuite());
@@ -138,7 +139,7 @@
private DelayQueue<PDelay> populatedQueue(int n) {
DelayQueue<PDelay> q = new DelayQueue<PDelay>();
assertTrue(q.isEmpty());
- for (int i = n-1; i >= 0; i -= 2)
+ for (int i = n - 1; i >= 0; i -= 2)
assertTrue(q.offer(new PDelay(i)));
for (int i = (n & 1); i < n; i += 2)
assertTrue(q.offer(new PDelay(i)));
@@ -170,8 +171,7 @@
*/
public void testConstructor4() {
try {
- PDelay[] ints = new PDelay[SIZE];
- new DelayQueue(Arrays.asList(ints));
+ new DelayQueue(Arrays.asList(new PDelay[SIZE]));
shouldThrow();
} catch (NullPointerException success) {}
}
@@ -180,11 +180,11 @@
* Initializing from Collection with some null elements throws NPE
*/
public void testConstructor5() {
+ PDelay[] a = new PDelay[SIZE];
+ for (int i = 0; i < SIZE - 1; ++i)
+ a[i] = new PDelay(i);
try {
- PDelay[] ints = new PDelay[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
- ints[i] = new PDelay(i);
- new DelayQueue(Arrays.asList(ints));
+ new DelayQueue(Arrays.asList(a));
shouldThrow();
} catch (NullPointerException success) {}
}
@@ -223,7 +223,7 @@
BlockingQueue q = populatedQueue(SIZE);
for (int i = 0; i < SIZE; ++i) {
assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
assertTrue(q.remove() instanceof PDelay);
}
for (int i = 0; i < SIZE; ++i) {
@@ -257,8 +257,8 @@
* addAll(this) throws IAE
*/
public void testAddAllSelf() {
+ DelayQueue q = populatedQueue(SIZE);
try {
- DelayQueue q = populatedQueue(SIZE);
q.addAll(q);
shouldThrow();
} catch (IllegalArgumentException success) {}
@@ -269,12 +269,12 @@
* possibly adding some elements
*/
public void testAddAll3() {
+ DelayQueue q = new DelayQueue();
+ PDelay[] a = new PDelay[SIZE];
+ for (int i = 0; i < SIZE - 1; ++i)
+ a[i] = new PDelay(i);
try {
- DelayQueue q = new DelayQueue();
- PDelay[] ints = new PDelay[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
- ints[i] = new PDelay(i);
- q.addAll(Arrays.asList(ints));
+ q.addAll(Arrays.asList(a));
shouldThrow();
} catch (NullPointerException success) {}
}
@@ -285,7 +285,7 @@
public void testAddAll5() {
PDelay[] empty = new PDelay[0];
PDelay[] ints = new PDelay[SIZE];
- for (int i = SIZE-1; i >= 0; --i)
+ for (int i = SIZE - 1; i >= 0; --i)
ints[i] = new PDelay(i);
DelayQueue q = new DelayQueue();
assertFalse(q.addAll(Arrays.asList(empty)));
@@ -427,11 +427,13 @@
*/
public void testInterruptedTimedPoll() throws InterruptedException {
final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+ final DelayQueue q = populatedQueue(SIZE);
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
- DelayQueue q = populatedQueue(SIZE);
+ long startTime = System.nanoTime();
for (int i = 0; i < SIZE; ++i) {
- assertEquals(new PDelay(i), ((PDelay)q.poll(SHORT_DELAY_MS, MILLISECONDS)));
+ assertEquals(new PDelay(i),
+ ((PDelay)q.poll(LONG_DELAY_MS, MILLISECONDS)));
}
Thread.currentThread().interrupt();
@@ -447,12 +449,14 @@
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}});
await(pleaseInterrupt);
assertThreadStaysAlive(t);
t.interrupt();
awaitTermination(t);
+ checkEmpty(q);
}
/**
@@ -557,7 +561,7 @@
assertTrue(changed);
assertTrue(q.containsAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
p.remove();
}
}
@@ -570,7 +574,7 @@
DelayQueue q = populatedQueue(SIZE);
DelayQueue p = populatedQueue(i);
assertTrue(q.removeAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
for (int j = 0; j < i; ++j) {
PDelay x = (PDelay)(p.remove());
assertFalse(q.contains(x));
@@ -668,22 +672,22 @@
public void testPollInExecutor() {
final DelayQueue q = new DelayQueue();
final CheckedBarrier threadsStarted = new CheckedBarrier(2);
- ExecutorService executor = Executors.newFixedThreadPool(2);
- executor.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- assertNull(q.poll());
- threadsStarted.await();
- assertNotNull(q.poll(LONG_DELAY_MS, MILLISECONDS));
- checkEmpty(q);
- }});
+ final ExecutorService executor = Executors.newFixedThreadPool(2);
+ try (PoolCleaner cleaner = cleaner(executor)) {
+ executor.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ assertNull(q.poll());
+ threadsStarted.await();
+ assertNotNull(q.poll(LONG_DELAY_MS, MILLISECONDS));
+ checkEmpty(q);
+ }});
- executor.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- threadsStarted.await();
- q.put(new PDelay(1));
- }});
-
- joinPool(executor);
+ executor.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ threadsStarted.await();
+ q.put(new PDelay(1));
+ }});
+ }
}
/**
@@ -768,7 +772,7 @@
final DelayQueue q = populatedQueue(SIZE);
Thread t = new Thread(new CheckedRunnable() {
public void realRun() {
- q.put(new PDelay(SIZE+1));
+ q.put(new PDelay(SIZE + 1));
}});
t.start();
@@ -788,7 +792,7 @@
ArrayList l = new ArrayList();
q.drainTo(l, i);
int k = (i < SIZE) ? i : SIZE;
- assertEquals(SIZE-k, q.size());
+ assertEquals(SIZE - k, q.size());
assertEquals(k, l.size());
}
}
diff --git a/jsr166-tests/src/test/java/jsr166/DoubleAccumulatorTest.java b/jsr166-tests/src/test/java/jsr166/DoubleAccumulatorTest.java
new file mode 100644
index 0000000..e061f9a
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/DoubleAccumulatorTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Phaser;
+import java.util.concurrent.atomic.DoubleAccumulator;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class DoubleAccumulatorTest extends JSR166TestCase {
+ // android-note: Removed because the CTS runner does a bad job of
+ // retrying tests that have suite() declarations.
+ //
+ // public static void main(String[] args) {
+ // main(suite(), args);
+ // }
+ // public static Test suite() {
+ // return new TestSuite(DoubleAccumulatorTest.class);
+ // }
+
+ /**
+ * default constructed initializes to zero
+ */
+ public void testConstructor() {
+ DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+ assertEquals(0.0, ai.get());
+ }
+
+ /**
+ * accumulate accumulates given value to current, and get returns current value
+ */
+ public void testAccumulateAndGet() {
+ DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+ ai.accumulate(2.0);
+ assertEquals(2.0, ai.get());
+ ai.accumulate(-4.0);
+ assertEquals(2.0, ai.get());
+ ai.accumulate(4.0);
+ assertEquals(4.0, ai.get());
+ }
+
+ /**
+ * reset() causes subsequent get() to return zero
+ */
+ public void testReset() {
+ DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+ ai.accumulate(2.0);
+ assertEquals(2.0, ai.get());
+ ai.reset();
+ assertEquals(0.0, ai.get());
+ }
+
+ /**
+ * getThenReset() returns current value; subsequent get() returns zero
+ */
+ public void testGetThenReset() {
+ DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+ ai.accumulate(2.0);
+ assertEquals(2.0, ai.get());
+ assertEquals(2.0, ai.getThenReset());
+ assertEquals(0.0, ai.get());
+ }
+
+ /**
+ * toString returns current value.
+ */
+ public void testToString() {
+ DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+ assertEquals("0.0", ai.toString());
+ ai.accumulate(1.0);
+ assertEquals(Double.toString(1.0), ai.toString());
+ }
+
+ /**
+ * intValue returns current value.
+ */
+ public void testIntValue() {
+ DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+ assertEquals(0, ai.intValue());
+ ai.accumulate(1.0);
+ assertEquals(1, ai.intValue());
+ }
+
+ /**
+ * longValue returns current value.
+ */
+ public void testLongValue() {
+ DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+ assertEquals(0, ai.longValue());
+ ai.accumulate(1.0);
+ assertEquals(1, ai.longValue());
+ }
+
+ /**
+ * floatValue returns current value.
+ */
+ public void testFloatValue() {
+ DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+ assertEquals(0.0f, ai.floatValue());
+ ai.accumulate(1.0);
+ assertEquals(1.0f, ai.floatValue());
+ }
+
+ /**
+ * doubleValue returns current value.
+ */
+ public void testDoubleValue() {
+ DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+ assertEquals(0.0, ai.doubleValue());
+ ai.accumulate(1.0);
+ assertEquals(1.0, ai.doubleValue());
+ }
+
+ /**
+ * accumulates by multiple threads produce correct result
+ */
+ public void testAccumulateAndGetMT() {
+ final int incs = 1000000;
+ final int nthreads = 4;
+ final ExecutorService pool = Executors.newCachedThreadPool();
+ DoubleAccumulator a = new DoubleAccumulator(Double::max, 0.0);
+ Phaser phaser = new Phaser(nthreads + 1);
+ for (int i = 0; i < nthreads; ++i)
+ pool.execute(new AccTask(a, phaser, incs));
+ phaser.arriveAndAwaitAdvance();
+ phaser.arriveAndAwaitAdvance();
+ double expected = incs - 1;
+ double result = a.get();
+ assertEquals(expected, result);
+ pool.shutdown();
+ }
+
+ static final class AccTask implements Runnable {
+ final DoubleAccumulator acc;
+ final Phaser phaser;
+ final int incs;
+ volatile double result;
+ AccTask(DoubleAccumulator acc, Phaser phaser, int incs) {
+ this.acc = acc;
+ this.phaser = phaser;
+ this.incs = incs;
+ }
+
+ public void run() {
+ phaser.arriveAndAwaitAdvance();
+ DoubleAccumulator a = acc;
+ for (int i = 0; i < incs; ++i)
+ a.accumulate(i);
+ result = a.get();
+ phaser.arrive();
+ }
+ }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/DoubleAdderTest.java b/jsr166-tests/src/test/java/jsr166/DoubleAdderTest.java
new file mode 100644
index 0000000..d02e2a1
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/DoubleAdderTest.java
@@ -0,0 +1,175 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.atomic.DoubleAdder;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class DoubleAdderTest extends JSR166TestCase {
+ // android-note: Removed because the CTS runner does a bad job of
+ // retrying tests that have suite() declarations.
+ //
+ // public static void main(String[] args) {
+ // main(suite(), args);
+ // }
+ // public static Test suite() {
+ // return new TestSuite(DoubleAdderTest.class);
+ // }
+
+ /**
+ * default constructed initializes to zero
+ */
+ public void testConstructor() {
+ DoubleAdder ai = new DoubleAdder();
+ assertEquals(0.0, ai.sum());
+ }
+
+ /**
+ * add adds given value to current, and sum returns current value
+ */
+ public void testAddAndSum() {
+ DoubleAdder ai = new DoubleAdder();
+ ai.add(2.0);
+ assertEquals(2.0, ai.sum());
+ ai.add(-4.0);
+ assertEquals(-2.0, ai.sum());
+ }
+
+ /**
+ * reset() causes subsequent sum() to return zero
+ */
+ public void testReset() {
+ DoubleAdder ai = new DoubleAdder();
+ ai.add(2.0);
+ assertEquals(2.0, ai.sum());
+ ai.reset();
+ assertEquals(0.0, ai.sum());
+ }
+
+ /**
+ * sumThenReset() returns sum; subsequent sum() returns zero
+ */
+ public void testSumThenReset() {
+ DoubleAdder ai = new DoubleAdder();
+ ai.add(2.0);
+ assertEquals(2.0, ai.sum());
+ assertEquals(2.0, ai.sumThenReset());
+ assertEquals(0.0, ai.sum());
+ }
+
+ /**
+ * a deserialized serialized adder holds same value
+ */
+ public void testSerialization() throws Exception {
+ DoubleAdder x = new DoubleAdder();
+ DoubleAdder y = serialClone(x);
+ assertNotSame(x, y);
+ x.add(-22.0);
+ DoubleAdder z = serialClone(x);
+ assertEquals(-22.0, x.sum());
+ assertEquals(0.0, y.sum());
+ assertEquals(-22.0, z.sum());
+ }
+
+ /**
+ * toString returns current value.
+ */
+ public void testToString() {
+ DoubleAdder ai = new DoubleAdder();
+ assertEquals(Double.toString(0.0), ai.toString());
+ ai.add(1.0);
+ assertEquals(Double.toString(1.0), ai.toString());
+ }
+
+ /**
+ * intValue returns current value.
+ */
+ public void testIntValue() {
+ DoubleAdder ai = new DoubleAdder();
+ assertEquals(0, ai.intValue());
+ ai.add(1.0);
+ assertEquals(1, ai.intValue());
+ }
+
+ /**
+ * longValue returns current value.
+ */
+ public void testLongValue() {
+ DoubleAdder ai = new DoubleAdder();
+ assertEquals(0, ai.longValue());
+ ai.add(1.0);
+ assertEquals(1, ai.longValue());
+ }
+
+ /**
+ * floatValue returns current value.
+ */
+ public void testFloatValue() {
+ DoubleAdder ai = new DoubleAdder();
+ assertEquals(0.0f, ai.floatValue());
+ ai.add(1.0);
+ assertEquals(1.0f, ai.floatValue());
+ }
+
+ /**
+ * doubleValue returns current value.
+ */
+ public void testDoubleValue() {
+ DoubleAdder ai = new DoubleAdder();
+ assertEquals(0.0, ai.doubleValue());
+ ai.add(1.0);
+ assertEquals(1.0, ai.doubleValue());
+ }
+
+ /**
+ * adds by multiple threads produce correct sum
+ */
+ public void testAddAndSumMT() throws Throwable {
+ final int incs = 1000000;
+ final int nthreads = 4;
+ final ExecutorService pool = Executors.newCachedThreadPool();
+ DoubleAdder a = new DoubleAdder();
+ CyclicBarrier barrier = new CyclicBarrier(nthreads + 1);
+ for (int i = 0; i < nthreads; ++i)
+ pool.execute(new AdderTask(a, barrier, incs));
+ barrier.await();
+ barrier.await();
+ double total = (long)nthreads * incs;
+ double sum = a.sum();
+ assertEquals(sum, total);
+ pool.shutdown();
+ }
+
+ static final class AdderTask implements Runnable {
+ final DoubleAdder adder;
+ final CyclicBarrier barrier;
+ final int incs;
+ volatile double result;
+ AdderTask(DoubleAdder adder, CyclicBarrier barrier, int incs) {
+ this.adder = adder;
+ this.barrier = barrier;
+ this.incs = incs;
+ }
+
+ public void run() {
+ try {
+ barrier.await();
+ DoubleAdder a = adder;
+ for (int i = 0; i < incs; ++i)
+ a.add(1.0);
+ result = a.sum();
+ barrier.await();
+ } catch (Throwable t) { throw new Error(t); }
+ }
+ }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/EntryTest.java b/jsr166-tests/src/test/java/jsr166/EntryTest.java
index d141a84..72740e3 100644
--- a/jsr166-tests/src/test/java/jsr166/EntryTest.java
+++ b/jsr166-tests/src/test/java/jsr166/EntryTest.java
@@ -20,7 +20,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(EntryTest.class);
// }
static final String k1 = "1";
diff --git a/jsr166-tests/src/test/java/jsr166/ExchangerTest.java b/jsr166-tests/src/test/java/jsr166/ExchangerTest.java
index 172fccd..b111980 100644
--- a/jsr166-tests/src/test/java/jsr166/ExchangerTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ExchangerTest.java
@@ -26,7 +26,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(ExchangerTest.class);
// }
/**
diff --git a/jsr166-tests/src/test/java/jsr166/ExecutorCompletionServiceTest.java b/jsr166-tests/src/test/java/jsr166/ExecutorCompletionServiceTest.java
index e988cc6..0f58e78 100644
--- a/jsr166-tests/src/test/java/jsr166/ExecutorCompletionServiceTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ExecutorCompletionServiceTest.java
@@ -33,7 +33,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(ExecutorCompletionServiceTest.class);
// }
/**
@@ -61,15 +61,14 @@
* Submitting a null callable throws NPE
*/
public void testSubmitNPE() {
- ExecutorService e = Executors.newCachedThreadPool();
- ExecutorCompletionService ecs = new ExecutorCompletionService(e);
- try {
+ final ExecutorService e = Executors.newCachedThreadPool();
+ final ExecutorCompletionService ecs = new ExecutorCompletionService(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
Callable c = null;
- ecs.submit(c);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ try {
+ ecs.submit(c);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -77,15 +76,14 @@
* Submitting a null runnable throws NPE
*/
public void testSubmitNPE2() {
- ExecutorService e = Executors.newCachedThreadPool();
- ExecutorCompletionService ecs = new ExecutorCompletionService(e);
- try {
+ final ExecutorService e = Executors.newCachedThreadPool();
+ final ExecutorCompletionService ecs = new ExecutorCompletionService(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
Runnable r = null;
- ecs.submit(r, Boolean.TRUE);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ try {
+ ecs.submit(r, Boolean.TRUE);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -93,15 +91,13 @@
* A taken submitted task is completed
*/
public void testTake() throws InterruptedException {
- ExecutorService e = Executors.newCachedThreadPool();
- ExecutorCompletionService ecs = new ExecutorCompletionService(e);
- try {
+ final ExecutorService e = Executors.newCachedThreadPool();
+ final ExecutorCompletionService ecs = new ExecutorCompletionService(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
Callable c = new StringTask();
ecs.submit(c);
Future f = ecs.take();
assertTrue(f.isDone());
- } finally {
- joinPool(e);
}
}
@@ -109,15 +105,13 @@
* Take returns the same future object returned by submit
*/
public void testTake2() throws InterruptedException {
- ExecutorService e = Executors.newCachedThreadPool();
- ExecutorCompletionService ecs = new ExecutorCompletionService(e);
- try {
+ final ExecutorService e = Executors.newCachedThreadPool();
+ final ExecutorCompletionService ecs = new ExecutorCompletionService(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
Callable c = new StringTask();
Future f1 = ecs.submit(c);
Future f2 = ecs.take();
assertSame(f1, f2);
- } finally {
- joinPool(e);
}
}
@@ -125,9 +119,9 @@
* If poll returns non-null, the returned task is completed
*/
public void testPoll1() throws Exception {
- ExecutorService e = Executors.newCachedThreadPool();
- ExecutorCompletionService ecs = new ExecutorCompletionService(e);
- try {
+ final ExecutorService e = Executors.newCachedThreadPool();
+ final ExecutorCompletionService ecs = new ExecutorCompletionService(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
assertNull(ecs.poll());
Callable c = new StringTask();
ecs.submit(c);
@@ -141,8 +135,6 @@
}
assertTrue(f.isDone());
assertSame(TEST_STRING, f.get());
- } finally {
- joinPool(e);
}
}
@@ -150,17 +142,15 @@
* If timed poll returns non-null, the returned task is completed
*/
public void testPoll2() throws InterruptedException {
- ExecutorService e = Executors.newCachedThreadPool();
- ExecutorCompletionService ecs = new ExecutorCompletionService(e);
- try {
+ final ExecutorService e = Executors.newCachedThreadPool();
+ final ExecutorCompletionService ecs = new ExecutorCompletionService(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
assertNull(ecs.poll());
Callable c = new StringTask();
ecs.submit(c);
Future f = ecs.poll(SHORT_DELAY_MS, MILLISECONDS);
if (f != null)
assertTrue(f.isDone());
- } finally {
- joinPool(e);
}
}
@@ -174,15 +164,16 @@
MyCallableFuture(Callable<V> c) { super(c); }
protected void done() { done.set(true); }
}
- ExecutorService e = new ThreadPoolExecutor(
- 1, 1, 30L, TimeUnit.SECONDS,
- new ArrayBlockingQueue<Runnable>(1)) {
- protected <T> RunnableFuture<T> newTaskFor(Callable<T> c) {
- return new MyCallableFuture<T>(c);
- }};
+ final ExecutorService e =
+ new ThreadPoolExecutor(1, 1,
+ 30L, TimeUnit.SECONDS,
+ new ArrayBlockingQueue<Runnable>(1)) {
+ protected <T> RunnableFuture<T> newTaskFor(Callable<T> c) {
+ return new MyCallableFuture<T>(c);
+ }};
ExecutorCompletionService<String> ecs =
new ExecutorCompletionService<String>(e);
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
assertNull(ecs.poll());
Callable<String> c = new StringTask();
Future f1 = ecs.submit(c);
@@ -191,8 +182,6 @@
Future f2 = ecs.take();
assertSame("submit and take must return same objects", f1, f2);
assertTrue("completed task must have set done", done.get());
- } finally {
- joinPool(e);
}
}
@@ -206,15 +195,16 @@
MyRunnableFuture(Runnable t, V r) { super(t, r); }
protected void done() { done.set(true); }
}
- ExecutorService e = new ThreadPoolExecutor(
- 1, 1, 30L, TimeUnit.SECONDS,
- new ArrayBlockingQueue<Runnable>(1)) {
- protected <T> RunnableFuture<T> newTaskFor(Runnable t, T r) {
- return new MyRunnableFuture<T>(t, r);
- }};
- ExecutorCompletionService<String> ecs =
+ final ExecutorService e =
+ new ThreadPoolExecutor(1, 1,
+ 30L, TimeUnit.SECONDS,
+ new ArrayBlockingQueue<Runnable>(1)) {
+ protected <T> RunnableFuture<T> newTaskFor(Runnable t, T r) {
+ return new MyRunnableFuture<T>(t, r);
+ }};
+ final ExecutorCompletionService<String> ecs =
new ExecutorCompletionService<String>(e);
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
assertNull(ecs.poll());
Runnable r = new NoOpRunnable();
Future f1 = ecs.submit(r, null);
@@ -223,8 +213,6 @@
Future f2 = ecs.take();
assertSame("submit and take must return same objects", f1, f2);
assertTrue("completed task must have set done", done.get());
- } finally {
- joinPool(e);
}
}
diff --git a/jsr166-tests/src/test/java/jsr166/ExecutorsTest.java b/jsr166-tests/src/test/java/jsr166/ExecutorsTest.java
index ae475f1..d70ae6e 100644
--- a/jsr166-tests/src/test/java/jsr166/ExecutorsTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ExecutorsTest.java
@@ -36,29 +36,31 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(ExecutorsTest.class);
// }
/**
* A newCachedThreadPool can execute runnables
*/
public void testNewCachedThreadPool1() {
- ExecutorService e = Executors.newCachedThreadPool();
- e.execute(new NoOpRunnable());
- e.execute(new NoOpRunnable());
- e.execute(new NoOpRunnable());
- joinPool(e);
+ final ExecutorService e = Executors.newCachedThreadPool();
+ try (PoolCleaner cleaner = cleaner(e)) {
+ e.execute(new NoOpRunnable());
+ e.execute(new NoOpRunnable());
+ e.execute(new NoOpRunnable());
+ }
}
/**
* A newCachedThreadPool with given ThreadFactory can execute runnables
*/
public void testNewCachedThreadPool2() {
- ExecutorService e = Executors.newCachedThreadPool(new SimpleThreadFactory());
- e.execute(new NoOpRunnable());
- e.execute(new NoOpRunnable());
- e.execute(new NoOpRunnable());
- joinPool(e);
+ final ExecutorService e = Executors.newCachedThreadPool(new SimpleThreadFactory());
+ try (PoolCleaner cleaner = cleaner(e)) {
+ e.execute(new NoOpRunnable());
+ e.execute(new NoOpRunnable());
+ e.execute(new NoOpRunnable());
+ }
}
/**
@@ -75,22 +77,24 @@
* A new SingleThreadExecutor can execute runnables
*/
public void testNewSingleThreadExecutor1() {
- ExecutorService e = Executors.newSingleThreadExecutor();
- e.execute(new NoOpRunnable());
- e.execute(new NoOpRunnable());
- e.execute(new NoOpRunnable());
- joinPool(e);
+ final ExecutorService e = Executors.newSingleThreadExecutor();
+ try (PoolCleaner cleaner = cleaner(e)) {
+ e.execute(new NoOpRunnable());
+ e.execute(new NoOpRunnable());
+ e.execute(new NoOpRunnable());
+ }
}
/**
* A new SingleThreadExecutor with given ThreadFactory can execute runnables
*/
public void testNewSingleThreadExecutor2() {
- ExecutorService e = Executors.newSingleThreadExecutor(new SimpleThreadFactory());
- e.execute(new NoOpRunnable());
- e.execute(new NoOpRunnable());
- e.execute(new NoOpRunnable());
- joinPool(e);
+ final ExecutorService e = Executors.newSingleThreadExecutor(new SimpleThreadFactory());
+ try (PoolCleaner cleaner = cleaner(e)) {
+ e.execute(new NoOpRunnable());
+ e.execute(new NoOpRunnable());
+ e.execute(new NoOpRunnable());
+ }
}
/**
@@ -107,13 +111,12 @@
* A new SingleThreadExecutor cannot be casted to concrete implementation
*/
public void testCastNewSingleThreadExecutor() {
- ExecutorService e = Executors.newSingleThreadExecutor();
- try {
- ThreadPoolExecutor tpe = (ThreadPoolExecutor)e;
- shouldThrow();
- } catch (ClassCastException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = Executors.newSingleThreadExecutor();
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ ThreadPoolExecutor tpe = (ThreadPoolExecutor)e;
+ shouldThrow();
+ } catch (ClassCastException success) {}
}
}
@@ -121,22 +124,24 @@
* A new newFixedThreadPool can execute runnables
*/
public void testNewFixedThreadPool1() {
- ExecutorService e = Executors.newFixedThreadPool(2);
- e.execute(new NoOpRunnable());
- e.execute(new NoOpRunnable());
- e.execute(new NoOpRunnable());
- joinPool(e);
+ final ExecutorService e = Executors.newFixedThreadPool(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ e.execute(new NoOpRunnable());
+ e.execute(new NoOpRunnable());
+ e.execute(new NoOpRunnable());
+ }
}
/**
* A new newFixedThreadPool with given ThreadFactory can execute runnables
*/
public void testNewFixedThreadPool2() {
- ExecutorService e = Executors.newFixedThreadPool(2, new SimpleThreadFactory());
- e.execute(new NoOpRunnable());
- e.execute(new NoOpRunnable());
- e.execute(new NoOpRunnable());
- joinPool(e);
+ final ExecutorService e = Executors.newFixedThreadPool(2, new SimpleThreadFactory());
+ try (PoolCleaner cleaner = cleaner(e)) {
+ e.execute(new NoOpRunnable());
+ e.execute(new NoOpRunnable());
+ e.execute(new NoOpRunnable());
+ }
}
/**
@@ -163,11 +168,12 @@
* An unconfigurable newFixedThreadPool can execute runnables
*/
public void testUnconfigurableExecutorService() {
- ExecutorService e = Executors.unconfigurableExecutorService(Executors.newFixedThreadPool(2));
- e.execute(new NoOpRunnable());
- e.execute(new NoOpRunnable());
- e.execute(new NoOpRunnable());
- joinPool(e);
+ final ExecutorService e = Executors.unconfigurableExecutorService(Executors.newFixedThreadPool(2));
+ try (PoolCleaner cleaner = cleaner(e)) {
+ e.execute(new NoOpRunnable());
+ e.execute(new NoOpRunnable());
+ e.execute(new NoOpRunnable());
+ }
}
/**
@@ -194,8 +200,8 @@
* a newSingleThreadScheduledExecutor successfully runs delayed task
*/
public void testNewSingleThreadScheduledExecutor() throws Exception {
- ScheduledExecutorService p = Executors.newSingleThreadScheduledExecutor();
- try {
+ final ScheduledExecutorService p = Executors.newSingleThreadScheduledExecutor();
+ try (PoolCleaner cleaner = cleaner(p)) {
final CountDownLatch proceed = new CountDownLatch(1);
final Runnable task = new CheckedRunnable() {
public void realRun() {
@@ -211,8 +217,6 @@
assertTrue(f.isDone());
assertFalse(f.isCancelled());
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
- } finally {
- joinPool(p);
}
}
@@ -220,8 +224,8 @@
* a newScheduledThreadPool successfully runs delayed task
*/
public void testNewScheduledThreadPool() throws Exception {
- ScheduledExecutorService p = Executors.newScheduledThreadPool(2);
- try {
+ final ScheduledExecutorService p = Executors.newScheduledThreadPool(2);
+ try (PoolCleaner cleaner = cleaner(p)) {
final CountDownLatch proceed = new CountDownLatch(1);
final Runnable task = new CheckedRunnable() {
public void realRun() {
@@ -237,8 +241,6 @@
assertTrue(f.isDone());
assertFalse(f.isCancelled());
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
- } finally {
- joinPool(p);
}
}
@@ -246,10 +248,10 @@
* an unconfigurable newScheduledThreadPool successfully runs delayed task
*/
public void testUnconfigurableScheduledExecutorService() throws Exception {
- ScheduledExecutorService p =
+ final ScheduledExecutorService p =
Executors.unconfigurableScheduledExecutorService
(Executors.newScheduledThreadPool(2));
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
final CountDownLatch proceed = new CountDownLatch(1);
final Runnable task = new CheckedRunnable() {
public void realRun() {
@@ -265,8 +267,6 @@
assertTrue(f.isDone());
assertFalse(f.isCancelled());
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
- } finally {
- joinPool(p);
}
}
@@ -327,16 +327,10 @@
done.countDown();
}};
ExecutorService e = Executors.newSingleThreadExecutor(Executors.defaultThreadFactory());
-
- e.execute(r);
- await(done);
-
- try {
- e.shutdown();
- } catch (SecurityException ok) {
+ try (PoolCleaner cleaner = cleaner(e)) {
+ e.execute(r);
+ await(done);
}
-
- joinPool(e);
}
/**
@@ -366,14 +360,14 @@
String name = current.getName();
assertTrue(name.endsWith("thread-1"));
assertSame(thisccl, current.getContextClassLoader());
- // assertEquals(thisacc, AccessController.getContext());
+ //assertEquals(thisacc, AccessController.getContext());
done.countDown();
}};
ExecutorService e = Executors.newSingleThreadExecutor(Executors.privilegedThreadFactory());
- e.execute(r);
- await(done);
- e.shutdown();
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ e.execute(r);
+ await(done);
+ }
}};
runWithPermissions(r,
diff --git a/jsr166-tests/src/test/java/jsr166/ForkJoinPool8Test.java b/jsr166-tests/src/test/java/jsr166/ForkJoinPool8Test.java
new file mode 100644
index 0000000..f9f9239
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ForkJoinPool8Test.java
@@ -0,0 +1,1592 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.HashSet;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountedCompleter;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.RecursiveAction;
+import java.util.concurrent.TimeoutException;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ForkJoinPool8Test extends JSR166TestCase {
+ // android-note: Removed because the CTS runner does a bad job of
+ // retrying tests that have suite() declarations.
+ //
+ // public static void main(String[] args) {
+ // main(suite(), args);
+ // }
+ // public static Test suite() {
+ // return new TestSuite(ForkJoinPool8Test.class);
+ // }
+
+ /**
+ * Common pool exists and has expected parallelism.
+ */
+ public void testCommonPoolParallelism() {
+ assertEquals(ForkJoinPool.getCommonPoolParallelism(),
+ ForkJoinPool.commonPool().getParallelism());
+ }
+
+ /**
+ * Common pool cannot be shut down
+ */
+ public void testCommonPoolShutDown() {
+ assertFalse(ForkJoinPool.commonPool().isShutdown());
+ assertFalse(ForkJoinPool.commonPool().isTerminating());
+ assertFalse(ForkJoinPool.commonPool().isTerminated());
+ ForkJoinPool.commonPool().shutdown();
+ assertFalse(ForkJoinPool.commonPool().isShutdown());
+ assertFalse(ForkJoinPool.commonPool().isTerminating());
+ assertFalse(ForkJoinPool.commonPool().isTerminated());
+ ForkJoinPool.commonPool().shutdownNow();
+ assertFalse(ForkJoinPool.commonPool().isShutdown());
+ assertFalse(ForkJoinPool.commonPool().isTerminating());
+ assertFalse(ForkJoinPool.commonPool().isTerminated());
+ }
+
+ /*
+ * All of the following test methods are adaptations of those for
+ * RecursiveAction and CountedCompleter, but with all actions
+ * executed in the common pool, generally implicitly via
+ * checkInvoke.
+ */
+
+ private void checkInvoke(ForkJoinTask a) {
+ checkNotDone(a);
+ assertNull(a.invoke());
+ checkCompletedNormally(a);
+ }
+
+ void checkNotDone(ForkJoinTask a) {
+ assertFalse(a.isDone());
+ assertFalse(a.isCompletedNormally());
+ assertFalse(a.isCompletedAbnormally());
+ assertFalse(a.isCancelled());
+ assertNull(a.getException());
+ assertNull(a.getRawResult());
+
+ if (! ForkJoinTask.inForkJoinPool()) {
+ Thread.currentThread().interrupt();
+ try {
+ a.get();
+ shouldThrow();
+ } catch (InterruptedException success) {
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+ Thread.currentThread().interrupt();
+ try {
+ a.get(5L, SECONDS);
+ shouldThrow();
+ } catch (InterruptedException success) {
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+ }
+
+ try {
+ a.get(0L, SECONDS);
+ shouldThrow();
+ } catch (TimeoutException success) {
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+ }
+
+ void checkCompletedNormally(ForkJoinTask a) {
+ assertTrue(a.isDone());
+ assertFalse(a.isCancelled());
+ assertTrue(a.isCompletedNormally());
+ assertFalse(a.isCompletedAbnormally());
+ assertNull(a.getException());
+ assertNull(a.getRawResult());
+ assertNull(a.join());
+ assertFalse(a.cancel(false));
+ assertFalse(a.cancel(true));
+ try {
+ assertNull(a.get());
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+ try {
+ assertNull(a.get(5L, SECONDS));
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+ }
+
+ void checkCancelled(ForkJoinTask a) {
+ assertTrue(a.isDone());
+ assertTrue(a.isCancelled());
+ assertFalse(a.isCompletedNormally());
+ assertTrue(a.isCompletedAbnormally());
+ assertTrue(a.getException() instanceof CancellationException);
+ assertNull(a.getRawResult());
+
+ try {
+ a.join();
+ shouldThrow();
+ } catch (CancellationException success) {
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+ try {
+ a.get();
+ shouldThrow();
+ } catch (CancellationException success) {
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+ try {
+ a.get(5L, SECONDS);
+ shouldThrow();
+ } catch (CancellationException success) {
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+ }
+
+ void checkCompletedAbnormally(ForkJoinTask a, Throwable t) {
+ assertTrue(a.isDone());
+ assertFalse(a.isCancelled());
+ assertFalse(a.isCompletedNormally());
+ assertTrue(a.isCompletedAbnormally());
+ assertSame(t.getClass(), a.getException().getClass());
+ assertNull(a.getRawResult());
+ assertFalse(a.cancel(false));
+ assertFalse(a.cancel(true));
+
+ try {
+ a.join();
+ shouldThrow();
+ } catch (Throwable expected) {
+ assertSame(expected.getClass(), t.getClass());
+ }
+
+ try {
+ a.get();
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertSame(t.getClass(), success.getCause().getClass());
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+ try {
+ a.get(5L, SECONDS);
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertSame(t.getClass(), success.getCause().getClass());
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+ }
+
+ public static final class FJException extends RuntimeException {
+ public FJException() { super(); }
+ public FJException(Throwable cause) { super(cause); }
+ }
+
+ // A simple recursive action for testing
+ final class FibAction extends CheckedRecursiveAction {
+ final int number;
+ int result;
+ FibAction(int n) { number = n; }
+ protected void realCompute() {
+ int n = number;
+ if (n <= 1)
+ result = n;
+ else {
+ FibAction f1 = new FibAction(n - 1);
+ FibAction f2 = new FibAction(n - 2);
+ invokeAll(f1, f2);
+ result = f1.result + f2.result;
+ }
+ }
+ }
+
+ // A recursive action failing in base case
+ static final class FailingFibAction extends RecursiveAction {
+ final int number;
+ int result;
+ FailingFibAction(int n) { number = n; }
+ public void compute() {
+ int n = number;
+ if (n <= 1)
+ throw new FJException();
+ else {
+ FailingFibAction f1 = new FailingFibAction(n - 1);
+ FailingFibAction f2 = new FailingFibAction(n - 2);
+ invokeAll(f1, f2);
+ result = f1.result + f2.result;
+ }
+ }
+ }
+
+ /**
+ * invoke returns when task completes normally.
+ * isCompletedAbnormally and isCancelled return false for normally
+ * completed tasks. getRawResult of a RecursiveAction returns null;
+ */
+ public void testInvoke() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FibAction f = new FibAction(8);
+ assertNull(f.invoke());
+ assertEquals(21, f.result);
+ checkCompletedNormally(f);
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * quietlyInvoke task returns when task completes normally.
+ * isCompletedAbnormally and isCancelled return false for normally
+ * completed tasks
+ */
+ public void testQuietlyInvoke() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FibAction f = new FibAction(8);
+ f.quietlyInvoke();
+ assertEquals(21, f.result);
+ checkCompletedNormally(f);
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * join of a forked task returns when task completes
+ */
+ public void testForkJoin() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FibAction f = new FibAction(8);
+ assertSame(f, f.fork());
+ assertNull(f.join());
+ assertEquals(21, f.result);
+ checkCompletedNormally(f);
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * join/quietlyJoin of a forked task succeeds in the presence of interrupts
+ */
+ public void testJoinIgnoresInterrupts() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FibAction f = new FibAction(8);
+ final Thread myself = Thread.currentThread();
+
+ // test join()
+ assertSame(f, f.fork());
+ myself.interrupt();
+ assertTrue(myself.isInterrupted());
+ assertNull(f.join());
+ Thread.interrupted();
+ assertEquals(21, f.result);
+ checkCompletedNormally(f);
+
+ f = new FibAction(8);
+ f.cancel(true);
+ assertSame(f, f.fork());
+ myself.interrupt();
+ assertTrue(myself.isInterrupted());
+ try {
+ f.join();
+ shouldThrow();
+ } catch (CancellationException success) {
+ Thread.interrupted();
+ checkCancelled(f);
+ }
+
+ f = new FibAction(8);
+ f.completeExceptionally(new FJException());
+ assertSame(f, f.fork());
+ myself.interrupt();
+ assertTrue(myself.isInterrupted());
+ try {
+ f.join();
+ shouldThrow();
+ } catch (FJException success) {
+ Thread.interrupted();
+ checkCompletedAbnormally(f, success);
+ }
+
+ // test quietlyJoin()
+ f = new FibAction(8);
+ assertSame(f, f.fork());
+ myself.interrupt();
+ assertTrue(myself.isInterrupted());
+ f.quietlyJoin();
+ Thread.interrupted();
+ assertEquals(21, f.result);
+ checkCompletedNormally(f);
+
+ f = new FibAction(8);
+ f.cancel(true);
+ assertSame(f, f.fork());
+ myself.interrupt();
+ assertTrue(myself.isInterrupted());
+ f.quietlyJoin();
+ Thread.interrupted();
+ checkCancelled(f);
+
+ f = new FibAction(8);
+ f.completeExceptionally(new FJException());
+ assertSame(f, f.fork());
+ myself.interrupt();
+ assertTrue(myself.isInterrupted());
+ f.quietlyJoin();
+ Thread.interrupted();
+ checkCompletedAbnormally(f, f.getException());
+ }};
+ checkInvoke(a);
+ a.reinitialize();
+ checkInvoke(a);
+ }
+
+ /**
+ * get of a forked task returns when task completes
+ */
+ public void testForkGet() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() throws Exception {
+ FibAction f = new FibAction(8);
+ assertSame(f, f.fork());
+ assertNull(f.get());
+ assertEquals(21, f.result);
+ checkCompletedNormally(f);
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * timed get of a forked task returns when task completes
+ */
+ public void testForkTimedGet() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() throws Exception {
+ FibAction f = new FibAction(8);
+ assertSame(f, f.fork());
+ assertNull(f.get(5L, SECONDS));
+ assertEquals(21, f.result);
+ checkCompletedNormally(f);
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * timed get with null time unit throws NPE
+ */
+ public void testForkTimedGetNPE() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() throws Exception {
+ FibAction f = new FibAction(8);
+ assertSame(f, f.fork());
+ try {
+ f.get(5L, null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * quietlyJoin of a forked task returns when task completes
+ */
+ public void testForkQuietlyJoin() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FibAction f = new FibAction(8);
+ assertSame(f, f.fork());
+ f.quietlyJoin();
+ assertEquals(21, f.result);
+ checkCompletedNormally(f);
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * invoke task throws exception when task completes abnormally
+ */
+ public void testAbnormalInvoke() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FailingFibAction f = new FailingFibAction(8);
+ try {
+ f.invoke();
+ shouldThrow();
+ } catch (FJException success) {
+ checkCompletedAbnormally(f, success);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * quietlyInvoke task returns when task completes abnormally
+ */
+ public void testAbnormalQuietlyInvoke() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FailingFibAction f = new FailingFibAction(8);
+ f.quietlyInvoke();
+ assertTrue(f.getException() instanceof FJException);
+ checkCompletedAbnormally(f, f.getException());
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * join of a forked task throws exception when task completes abnormally
+ */
+ public void testAbnormalForkJoin() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FailingFibAction f = new FailingFibAction(8);
+ assertSame(f, f.fork());
+ try {
+ f.join();
+ shouldThrow();
+ } catch (FJException success) {
+ checkCompletedAbnormally(f, success);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * get of a forked task throws exception when task completes abnormally
+ */
+ public void testAbnormalForkGet() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() throws Exception {
+ FailingFibAction f = new FailingFibAction(8);
+ assertSame(f, f.fork());
+ try {
+ f.get();
+ shouldThrow();
+ } catch (ExecutionException success) {
+ Throwable cause = success.getCause();
+ assertTrue(cause instanceof FJException);
+ checkCompletedAbnormally(f, cause);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * timed get of a forked task throws exception when task completes abnormally
+ */
+ public void testAbnormalForkTimedGet() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() throws Exception {
+ FailingFibAction f = new FailingFibAction(8);
+ assertSame(f, f.fork());
+ try {
+ f.get(5L, SECONDS);
+ shouldThrow();
+ } catch (ExecutionException success) {
+ Throwable cause = success.getCause();
+ assertTrue(cause instanceof FJException);
+ checkCompletedAbnormally(f, cause);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * quietlyJoin of a forked task returns when task completes abnormally
+ */
+ public void testAbnormalForkQuietlyJoin() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FailingFibAction f = new FailingFibAction(8);
+ assertSame(f, f.fork());
+ f.quietlyJoin();
+ assertTrue(f.getException() instanceof FJException);
+ checkCompletedAbnormally(f, f.getException());
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * invoke task throws exception when task cancelled
+ */
+ public void testCancelledInvoke() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FibAction f = new FibAction(8);
+ assertTrue(f.cancel(true));
+ try {
+ f.invoke();
+ shouldThrow();
+ } catch (CancellationException success) {
+ checkCancelled(f);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * join of a forked task throws exception when task cancelled
+ */
+ public void testCancelledForkJoin() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FibAction f = new FibAction(8);
+ assertTrue(f.cancel(true));
+ assertSame(f, f.fork());
+ try {
+ f.join();
+ shouldThrow();
+ } catch (CancellationException success) {
+ checkCancelled(f);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * get of a forked task throws exception when task cancelled
+ */
+ public void testCancelledForkGet() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() throws Exception {
+ FibAction f = new FibAction(8);
+ assertTrue(f.cancel(true));
+ assertSame(f, f.fork());
+ try {
+ f.get();
+ shouldThrow();
+ } catch (CancellationException success) {
+ checkCancelled(f);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * timed get of a forked task throws exception when task cancelled
+ */
+ public void testCancelledForkTimedGet() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() throws Exception {
+ FibAction f = new FibAction(8);
+ assertTrue(f.cancel(true));
+ assertSame(f, f.fork());
+ try {
+ f.get(5L, SECONDS);
+ shouldThrow();
+ } catch (CancellationException success) {
+ checkCancelled(f);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * quietlyJoin of a forked task returns when task cancelled
+ */
+ public void testCancelledForkQuietlyJoin() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FibAction f = new FibAction(8);
+ assertTrue(f.cancel(true));
+ assertSame(f, f.fork());
+ f.quietlyJoin();
+ checkCancelled(f);
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * inForkJoinPool of non-FJ task returns false
+ */
+ public void testInForkJoinPool2() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ assertFalse(inForkJoinPool());
+ }};
+ assertNull(a.invoke());
+ }
+
+ /**
+ * A reinitialized normally completed task may be re-invoked
+ */
+ public void testReinitialize() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FibAction f = new FibAction(8);
+ checkNotDone(f);
+
+ for (int i = 0; i < 3; i++) {
+ assertNull(f.invoke());
+ assertEquals(21, f.result);
+ checkCompletedNormally(f);
+ f.reinitialize();
+ checkNotDone(f);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * A reinitialized abnormally completed task may be re-invoked
+ */
+ public void testReinitializeAbnormal() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FailingFibAction f = new FailingFibAction(8);
+ checkNotDone(f);
+
+ for (int i = 0; i < 3; i++) {
+ try {
+ f.invoke();
+ shouldThrow();
+ } catch (FJException success) {
+ checkCompletedAbnormally(f, success);
+ }
+ f.reinitialize();
+ checkNotDone(f);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * invoke task throws exception after invoking completeExceptionally
+ */
+ public void testCompleteExceptionally() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FibAction f = new FibAction(8);
+ f.completeExceptionally(new FJException());
+ try {
+ f.invoke();
+ shouldThrow();
+ } catch (FJException success) {
+ checkCompletedAbnormally(f, success);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * invoke task suppresses execution invoking complete
+ */
+ public void testComplete() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FibAction f = new FibAction(8);
+ f.complete(null);
+ assertNull(f.invoke());
+ assertEquals(0, f.result);
+ checkCompletedNormally(f);
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * invokeAll(t1, t2) invokes all task arguments
+ */
+ public void testInvokeAll2() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FibAction f = new FibAction(8);
+ FibAction g = new FibAction(9);
+ invokeAll(f, g);
+ checkCompletedNormally(f);
+ assertEquals(21, f.result);
+ checkCompletedNormally(g);
+ assertEquals(34, g.result);
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * invokeAll(tasks) with 1 argument invokes task
+ */
+ public void testInvokeAll1() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FibAction f = new FibAction(8);
+ invokeAll(f);
+ checkCompletedNormally(f);
+ assertEquals(21, f.result);
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * invokeAll(tasks) with > 2 argument invokes tasks
+ */
+ public void testInvokeAll3() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FibAction f = new FibAction(8);
+ FibAction g = new FibAction(9);
+ FibAction h = new FibAction(7);
+ invokeAll(f, g, h);
+ assertTrue(f.isDone());
+ assertTrue(g.isDone());
+ assertTrue(h.isDone());
+ checkCompletedNormally(f);
+ assertEquals(21, f.result);
+ checkCompletedNormally(g);
+ assertEquals(34, g.result);
+ checkCompletedNormally(g);
+ assertEquals(13, h.result);
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * invokeAll(collection) invokes all tasks in the collection
+ */
+ public void testInvokeAllCollection() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FibAction f = new FibAction(8);
+ FibAction g = new FibAction(9);
+ FibAction h = new FibAction(7);
+ HashSet set = new HashSet();
+ set.add(f);
+ set.add(g);
+ set.add(h);
+ invokeAll(set);
+ assertTrue(f.isDone());
+ assertTrue(g.isDone());
+ assertTrue(h.isDone());
+ checkCompletedNormally(f);
+ assertEquals(21, f.result);
+ checkCompletedNormally(g);
+ assertEquals(34, g.result);
+ checkCompletedNormally(g);
+ assertEquals(13, h.result);
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * invokeAll(tasks) with any null task throws NPE
+ */
+ public void testInvokeAllNPE() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FibAction f = new FibAction(8);
+ FibAction g = new FibAction(9);
+ FibAction h = null;
+ try {
+ invokeAll(f, g, h);
+ shouldThrow();
+ } catch (NullPointerException success) {}
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * invokeAll(t1, t2) throw exception if any task does
+ */
+ public void testAbnormalInvokeAll2() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FibAction f = new FibAction(8);
+ FailingFibAction g = new FailingFibAction(9);
+ try {
+ invokeAll(f, g);
+ shouldThrow();
+ } catch (FJException success) {
+ checkCompletedAbnormally(g, success);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * invokeAll(tasks) with 1 argument throws exception if task does
+ */
+ public void testAbnormalInvokeAll1() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FailingFibAction g = new FailingFibAction(9);
+ try {
+ invokeAll(g);
+ shouldThrow();
+ } catch (FJException success) {
+ checkCompletedAbnormally(g, success);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * invokeAll(tasks) with > 2 argument throws exception if any task does
+ */
+ public void testAbnormalInvokeAll3() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FibAction f = new FibAction(8);
+ FailingFibAction g = new FailingFibAction(9);
+ FibAction h = new FibAction(7);
+ try {
+ invokeAll(f, g, h);
+ shouldThrow();
+ } catch (FJException success) {
+ checkCompletedAbnormally(g, success);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * invokeAll(collection) throws exception if any task does
+ */
+ public void testAbnormalInvokeAllCollection() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FailingFibAction f = new FailingFibAction(8);
+ FibAction g = new FibAction(9);
+ FibAction h = new FibAction(7);
+ HashSet set = new HashSet();
+ set.add(f);
+ set.add(g);
+ set.add(h);
+ try {
+ invokeAll(set);
+ shouldThrow();
+ } catch (FJException success) {
+ checkCompletedAbnormally(f, success);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ // CountedCompleter versions
+
+ abstract static class CCF extends CountedCompleter {
+ int number;
+ int rnumber;
+
+ public CCF(CountedCompleter parent, int n) {
+ super(parent, 1);
+ this.number = n;
+ }
+
+ public final void compute() {
+ CountedCompleter p;
+ CCF f = this;
+ int n = number;
+ while (n >= 2) {
+ new RCCF(f, n - 2).fork();
+ f = new LCCF(f, --n);
+ }
+ f.number = n;
+ f.onCompletion(f);
+ if ((p = f.getCompleter()) != null)
+ p.tryComplete();
+ else
+ f.quietlyComplete();
+ }
+ }
+
+ static final class LCCF extends CCF {
+ public LCCF(CountedCompleter parent, int n) {
+ super(parent, n);
+ }
+ public final void onCompletion(CountedCompleter caller) {
+ CCF p = (CCF)getCompleter();
+ int n = number + rnumber;
+ if (p != null)
+ p.number = n;
+ else
+ number = n;
+ }
+ }
+ static final class RCCF extends CCF {
+ public RCCF(CountedCompleter parent, int n) {
+ super(parent, n);
+ }
+ public final void onCompletion(CountedCompleter caller) {
+ CCF p = (CCF)getCompleter();
+ int n = number + rnumber;
+ if (p != null)
+ p.rnumber = n;
+ else
+ number = n;
+ }
+ }
+
+ // Version of CCF with forced failure in left completions
+ abstract static class FailingCCF extends CountedCompleter {
+ int number;
+ int rnumber;
+
+ public FailingCCF(CountedCompleter parent, int n) {
+ super(parent, 1);
+ this.number = n;
+ }
+
+ public final void compute() {
+ CountedCompleter p;
+ FailingCCF f = this;
+ int n = number;
+ while (n >= 2) {
+ new RFCCF(f, n - 2).fork();
+ f = new LFCCF(f, --n);
+ }
+ f.number = n;
+ f.onCompletion(f);
+ if ((p = f.getCompleter()) != null)
+ p.tryComplete();
+ else
+ f.quietlyComplete();
+ }
+ }
+
+ static final class LFCCF extends FailingCCF {
+ public LFCCF(CountedCompleter parent, int n) {
+ super(parent, n);
+ }
+ public final void onCompletion(CountedCompleter caller) {
+ FailingCCF p = (FailingCCF)getCompleter();
+ int n = number + rnumber;
+ if (p != null)
+ p.number = n;
+ else
+ number = n;
+ }
+ }
+ static final class RFCCF extends FailingCCF {
+ public RFCCF(CountedCompleter parent, int n) {
+ super(parent, n);
+ }
+ public final void onCompletion(CountedCompleter caller) {
+ completeExceptionally(new FJException());
+ }
+ }
+
+ /**
+ * invoke returns when task completes normally.
+ * isCompletedAbnormally and isCancelled return false for normally
+ * completed tasks; getRawResult returns null.
+ */
+ public void testInvokeCC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ CCF f = new LCCF(null, 8);
+ assertNull(f.invoke());
+ assertEquals(21, f.number);
+ checkCompletedNormally(f);
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * quietlyInvoke task returns when task completes normally.
+ * isCompletedAbnormally and isCancelled return false for normally
+ * completed tasks
+ */
+ public void testQuietlyInvokeCC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ CCF f = new LCCF(null, 8);
+ f.quietlyInvoke();
+ assertEquals(21, f.number);
+ checkCompletedNormally(f);
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * join of a forked task returns when task completes
+ */
+ public void testForkJoinCC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ CCF f = new LCCF(null, 8);
+ assertSame(f, f.fork());
+ assertNull(f.join());
+ assertEquals(21, f.number);
+ checkCompletedNormally(f);
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * get of a forked task returns when task completes
+ */
+ public void testForkGetCC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() throws Exception {
+ CCF f = new LCCF(null, 8);
+ assertSame(f, f.fork());
+ assertNull(f.get());
+ assertEquals(21, f.number);
+ checkCompletedNormally(f);
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * timed get of a forked task returns when task completes
+ */
+ public void testForkTimedGetCC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() throws Exception {
+ CCF f = new LCCF(null, 8);
+ assertSame(f, f.fork());
+ assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
+ assertEquals(21, f.number);
+ checkCompletedNormally(f);
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * timed get with null time unit throws NPE
+ */
+ public void testForkTimedGetNPECC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() throws Exception {
+ CCF f = new LCCF(null, 8);
+ assertSame(f, f.fork());
+ try {
+ f.get(5L, null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * quietlyJoin of a forked task returns when task completes
+ */
+ public void testForkQuietlyJoinCC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ CCF f = new LCCF(null, 8);
+ assertSame(f, f.fork());
+ f.quietlyJoin();
+ assertEquals(21, f.number);
+ checkCompletedNormally(f);
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * invoke task throws exception when task completes abnormally
+ */
+ public void testAbnormalInvokeCC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FailingCCF f = new LFCCF(null, 8);
+ try {
+ f.invoke();
+ shouldThrow();
+ } catch (FJException success) {
+ checkCompletedAbnormally(f, success);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * quietlyInvoke task returns when task completes abnormally
+ */
+ public void testAbnormalQuietlyInvokeCC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FailingCCF f = new LFCCF(null, 8);
+ f.quietlyInvoke();
+ assertTrue(f.getException() instanceof FJException);
+ checkCompletedAbnormally(f, f.getException());
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * join of a forked task throws exception when task completes abnormally
+ */
+ public void testAbnormalForkJoinCC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FailingCCF f = new LFCCF(null, 8);
+ assertSame(f, f.fork());
+ try {
+ f.join();
+ shouldThrow();
+ } catch (FJException success) {
+ checkCompletedAbnormally(f, success);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * get of a forked task throws exception when task completes abnormally
+ */
+ public void testAbnormalForkGetCC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() throws Exception {
+ FailingCCF f = new LFCCF(null, 8);
+ assertSame(f, f.fork());
+ try {
+ f.get();
+ shouldThrow();
+ } catch (ExecutionException success) {
+ Throwable cause = success.getCause();
+ assertTrue(cause instanceof FJException);
+ checkCompletedAbnormally(f, cause);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * timed get of a forked task throws exception when task completes abnormally
+ */
+ public void testAbnormalForkTimedGetCC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() throws Exception {
+ FailingCCF f = new LFCCF(null, 8);
+ assertSame(f, f.fork());
+ try {
+ f.get(LONG_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (ExecutionException success) {
+ Throwable cause = success.getCause();
+ assertTrue(cause instanceof FJException);
+ checkCompletedAbnormally(f, cause);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * quietlyJoin of a forked task returns when task completes abnormally
+ */
+ public void testAbnormalForkQuietlyJoinCC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FailingCCF f = new LFCCF(null, 8);
+ assertSame(f, f.fork());
+ f.quietlyJoin();
+ assertTrue(f.getException() instanceof FJException);
+ checkCompletedAbnormally(f, f.getException());
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * invoke task throws exception when task cancelled
+ */
+ public void testCancelledInvokeCC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ CCF f = new LCCF(null, 8);
+ assertTrue(f.cancel(true));
+ try {
+ f.invoke();
+ shouldThrow();
+ } catch (CancellationException success) {
+ checkCancelled(f);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * join of a forked task throws exception when task cancelled
+ */
+ public void testCancelledForkJoinCC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ CCF f = new LCCF(null, 8);
+ assertTrue(f.cancel(true));
+ assertSame(f, f.fork());
+ try {
+ f.join();
+ shouldThrow();
+ } catch (CancellationException success) {
+ checkCancelled(f);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * get of a forked task throws exception when task cancelled
+ */
+ public void testCancelledForkGetCC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() throws Exception {
+ CCF f = new LCCF(null, 8);
+ assertTrue(f.cancel(true));
+ assertSame(f, f.fork());
+ try {
+ f.get();
+ shouldThrow();
+ } catch (CancellationException success) {
+ checkCancelled(f);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * timed get of a forked task throws exception when task cancelled
+ */
+ public void testCancelledForkTimedGetCC() throws Exception {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() throws Exception {
+ CCF f = new LCCF(null, 8);
+ assertTrue(f.cancel(true));
+ assertSame(f, f.fork());
+ try {
+ f.get(LONG_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (CancellationException success) {
+ checkCancelled(f);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * quietlyJoin of a forked task returns when task cancelled
+ */
+ public void testCancelledForkQuietlyJoinCC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ CCF f = new LCCF(null, 8);
+ assertTrue(f.cancel(true));
+ assertSame(f, f.fork());
+ f.quietlyJoin();
+ checkCancelled(f);
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * getPool of non-FJ task returns null
+ */
+ public void testGetPool2CC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ assertNull(getPool());
+ }};
+ assertNull(a.invoke());
+ }
+
+ /**
+ * inForkJoinPool of non-FJ task returns false
+ */
+ public void testInForkJoinPool2CC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ assertFalse(inForkJoinPool());
+ }};
+ assertNull(a.invoke());
+ }
+
+ /**
+ * setRawResult(null) succeeds
+ */
+ public void testSetRawResultCC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ setRawResult(null);
+ assertNull(getRawResult());
+ }};
+ assertNull(a.invoke());
+ }
+
+ /**
+ * invoke task throws exception after invoking completeExceptionally
+ */
+ public void testCompleteExceptionally2CC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ CCF f = new LCCF(null, 8);
+ f.completeExceptionally(new FJException());
+ try {
+ f.invoke();
+ shouldThrow();
+ } catch (FJException success) {
+ checkCompletedAbnormally(f, success);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * invokeAll(t1, t2) invokes all task arguments
+ */
+ public void testInvokeAll2CC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ CCF f = new LCCF(null, 8);
+ CCF g = new LCCF(null, 9);
+ invokeAll(f, g);
+ assertEquals(21, f.number);
+ assertEquals(34, g.number);
+ checkCompletedNormally(f);
+ checkCompletedNormally(g);
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * invokeAll(tasks) with 1 argument invokes task
+ */
+ public void testInvokeAll1CC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ CCF f = new LCCF(null, 8);
+ invokeAll(f);
+ checkCompletedNormally(f);
+ assertEquals(21, f.number);
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * invokeAll(tasks) with > 2 argument invokes tasks
+ */
+ public void testInvokeAll3CC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ CCF f = new LCCF(null, 8);
+ CCF g = new LCCF(null, 9);
+ CCF h = new LCCF(null, 7);
+ invokeAll(f, g, h);
+ assertEquals(21, f.number);
+ assertEquals(34, g.number);
+ assertEquals(13, h.number);
+ checkCompletedNormally(f);
+ checkCompletedNormally(g);
+ checkCompletedNormally(h);
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * invokeAll(collection) invokes all tasks in the collection
+ */
+ public void testInvokeAllCollectionCC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ CCF f = new LCCF(null, 8);
+ CCF g = new LCCF(null, 9);
+ CCF h = new LCCF(null, 7);
+ HashSet set = new HashSet();
+ set.add(f);
+ set.add(g);
+ set.add(h);
+ invokeAll(set);
+ assertEquals(21, f.number);
+ assertEquals(34, g.number);
+ assertEquals(13, h.number);
+ checkCompletedNormally(f);
+ checkCompletedNormally(g);
+ checkCompletedNormally(h);
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * invokeAll(tasks) with any null task throws NPE
+ */
+ public void testInvokeAllNPECC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ CCF f = new LCCF(null, 8);
+ CCF g = new LCCF(null, 9);
+ CCF h = null;
+ try {
+ invokeAll(f, g, h);
+ shouldThrow();
+ } catch (NullPointerException success) {}
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * invokeAll(t1, t2) throw exception if any task does
+ */
+ public void testAbnormalInvokeAll2CC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ CCF f = new LCCF(null, 8);
+ FailingCCF g = new LFCCF(null, 9);
+ try {
+ invokeAll(f, g);
+ shouldThrow();
+ } catch (FJException success) {
+ checkCompletedAbnormally(g, success);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * invokeAll(tasks) with 1 argument throws exception if task does
+ */
+ public void testAbnormalInvokeAll1CC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FailingCCF g = new LFCCF(null, 9);
+ try {
+ invokeAll(g);
+ shouldThrow();
+ } catch (FJException success) {
+ checkCompletedAbnormally(g, success);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * invokeAll(tasks) with > 2 argument throws exception if any task does
+ */
+ public void testAbnormalInvokeAll3CC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ CCF f = new LCCF(null, 8);
+ FailingCCF g = new LFCCF(null, 9);
+ CCF h = new LCCF(null, 7);
+ try {
+ invokeAll(f, g, h);
+ shouldThrow();
+ } catch (FJException success) {
+ checkCompletedAbnormally(g, success);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * invokeAll(collection) throws exception if any task does
+ */
+ public void testAbnormalInvokeAllCollectionCC() {
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FailingCCF f = new LFCCF(null, 8);
+ CCF g = new LCCF(null, 9);
+ CCF h = new LCCF(null, 7);
+ HashSet set = new HashSet();
+ set.add(f);
+ set.add(g);
+ set.add(h);
+ try {
+ invokeAll(set);
+ shouldThrow();
+ } catch (FJException success) {
+ checkCompletedAbnormally(f, success);
+ }
+ }};
+ checkInvoke(a);
+ }
+
+ /**
+ * awaitQuiescence by a worker is equivalent in effect to
+ * ForkJoinTask.helpQuiesce()
+ */
+ public void testAwaitQuiescence1() throws Exception {
+ final ForkJoinPool p = new ForkJoinPool();
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final long startTime = System.nanoTime();
+ assertTrue(p.isQuiescent());
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FibAction f = new FibAction(8);
+ assertSame(f, f.fork());
+ assertSame(p, ForkJoinTask.getPool());
+ boolean quiescent = p.awaitQuiescence(LONG_DELAY_MS, MILLISECONDS);
+ assertTrue(quiescent);
+ assertFalse(p.isQuiescent());
+ while (!f.isDone()) {
+ assertFalse(p.getAsyncMode());
+ assertFalse(p.isShutdown());
+ assertFalse(p.isTerminating());
+ assertFalse(p.isTerminated());
+ Thread.yield();
+ }
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+ assertFalse(p.isQuiescent());
+ assertEquals(0, ForkJoinTask.getQueuedTaskCount());
+ assertEquals(21, f.result);
+ }};
+ p.execute(a);
+ while (!a.isDone() || !p.isQuiescent()) {
+ assertFalse(p.getAsyncMode());
+ assertFalse(p.isShutdown());
+ assertFalse(p.isTerminating());
+ assertFalse(p.isTerminated());
+ Thread.yield();
+ }
+ assertEquals(0, p.getQueuedTaskCount());
+ assertFalse(p.getAsyncMode());
+ assertEquals(0, p.getQueuedSubmissionCount());
+ assertFalse(p.hasQueuedSubmissions());
+ while (p.getActiveThreadCount() != 0
+ && millisElapsedSince(startTime) < LONG_DELAY_MS)
+ Thread.yield();
+ assertFalse(p.isShutdown());
+ assertFalse(p.isTerminating());
+ assertFalse(p.isTerminated());
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+ }
+ }
+
+ /**
+ * awaitQuiescence returns when pool isQuiescent() or the indicated
+ * timeout elapsed
+ */
+ public void testAwaitQuiescence2() throws Exception {
+ /**
+ * """It is possible to disable or limit the use of threads in the
+ * common pool by setting the parallelism property to zero. However
+ * doing so may cause unjoined tasks to never be executed."""
+ */
+ if ("0".equals(System.getProperty(
+ "java.util.concurrent.ForkJoinPool.common.parallelism")))
+ return;
+ final ForkJoinPool p = new ForkJoinPool();
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertTrue(p.isQuiescent());
+ final long startTime = System.nanoTime();
+ ForkJoinTask a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FibAction f = new FibAction(8);
+ assertSame(f, f.fork());
+ while (!f.isDone()
+ && millisElapsedSince(startTime) < LONG_DELAY_MS) {
+ assertFalse(p.getAsyncMode());
+ assertFalse(p.isShutdown());
+ assertFalse(p.isTerminating());
+ assertFalse(p.isTerminated());
+ Thread.yield();
+ }
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+ assertEquals(0, ForkJoinTask.getQueuedTaskCount());
+ assertEquals(21, f.result);
+ }};
+ p.execute(a);
+ assertTrue(p.awaitQuiescence(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(p.isQuiescent());
+ assertTrue(a.isDone());
+ assertEquals(0, p.getQueuedTaskCount());
+ assertFalse(p.getAsyncMode());
+ assertEquals(0, p.getQueuedSubmissionCount());
+ assertFalse(p.hasQueuedSubmissions());
+ while (p.getActiveThreadCount() != 0
+ && millisElapsedSince(startTime) < LONG_DELAY_MS)
+ Thread.yield();
+ assertFalse(p.isShutdown());
+ assertFalse(p.isTerminating());
+ assertFalse(p.isTerminated());
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+ }
+ }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ForkJoinPoolTest.java b/jsr166-tests/src/test/java/jsr166/ForkJoinPoolTest.java
index 09a3511..e3bb428 100644
--- a/jsr166-tests/src/test/java/jsr166/ForkJoinPoolTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ForkJoinPoolTest.java
@@ -40,7 +40,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(ForkJoinPoolTest.class);
// }
/*
@@ -68,10 +68,12 @@
}
}
+ static class MyError extends Error {}
+
// to test handlers
static class FailingFJWSubclass extends ForkJoinWorkerThread {
public FailingFJWSubclass(ForkJoinPool p) { super(p) ; }
- protected void onStart() { super.onStart(); throw new Error(); }
+ protected void onStart() { super.onStart(); throw new MyError(); }
}
static class FailingThreadFactory
@@ -166,7 +168,7 @@
*/
public void testDefaultInitialState() {
ForkJoinPool p = new ForkJoinPool(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
assertSame(ForkJoinPool.defaultForkJoinWorkerThreadFactory,
p.getFactory());
assertFalse(p.getAsyncMode());
@@ -178,8 +180,6 @@
assertFalse(p.isShutdown());
assertFalse(p.isTerminating());
assertFalse(p.isTerminated());
- } finally {
- joinPool(p);
}
}
@@ -208,10 +208,8 @@
*/
public void testGetParallelism() {
ForkJoinPool p = new ForkJoinPool(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
assertEquals(1, p.getParallelism());
- } finally {
- joinPool(p);
}
}
@@ -219,14 +217,26 @@
* getPoolSize returns number of started workers.
*/
public void testGetPoolSize() {
- ForkJoinPool p = new ForkJoinPool(1);
- try {
+ final CountDownLatch taskStarted = new CountDownLatch(1);
+ final CountDownLatch done = new CountDownLatch(1);
+ final ForkJoinPool p = new ForkJoinPool(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
assertEquals(0, p.getActiveThreadCount());
- Future<String> future = p.submit(new StringTask());
+ final Runnable task = new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ taskStarted.countDown();
+ assertEquals(1, p.getPoolSize());
+ assertEquals(1, p.getActiveThreadCount());
+ done.await();
+ }};
+ Future<?> future = p.submit(task);
+ await(taskStarted);
assertEquals(1, p.getPoolSize());
- } finally {
- joinPool(p);
+ assertEquals(1, p.getActiveThreadCount());
+ done.countDown();
}
+ assertEquals(0, p.getPoolSize());
+ assertEquals(0, p.getActiveThreadCount());
}
/**
@@ -234,26 +244,28 @@
*/
public void testAwaitTermination_timesOut() throws InterruptedException {
ForkJoinPool p = new ForkJoinPool(1);
- assertFalse(p.isTerminated());
- assertFalse(p.awaitTermination(Long.MIN_VALUE, NANOSECONDS));
- assertFalse(p.awaitTermination(Long.MIN_VALUE, MILLISECONDS));
- assertFalse(p.awaitTermination(-1L, NANOSECONDS));
- assertFalse(p.awaitTermination(-1L, MILLISECONDS));
- assertFalse(p.awaitTermination(0L, NANOSECONDS));
- assertFalse(p.awaitTermination(0L, MILLISECONDS));
- long timeoutNanos = 999999L;
- long startTime = System.nanoTime();
- assertFalse(p.awaitTermination(timeoutNanos, NANOSECONDS));
- assertTrue(System.nanoTime() - startTime >= timeoutNanos);
- assertFalse(p.isTerminated());
- startTime = System.nanoTime();
- long timeoutMillis = timeoutMillis();
- assertFalse(p.awaitTermination(timeoutMillis, MILLISECONDS));
- assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
- assertFalse(p.isTerminated());
- p.shutdown();
- assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
- assertTrue(p.isTerminated());
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertFalse(p.isTerminated());
+ assertFalse(p.awaitTermination(Long.MIN_VALUE, NANOSECONDS));
+ assertFalse(p.awaitTermination(Long.MIN_VALUE, MILLISECONDS));
+ assertFalse(p.awaitTermination(-1L, NANOSECONDS));
+ assertFalse(p.awaitTermination(-1L, MILLISECONDS));
+ assertFalse(p.awaitTermination(0L, NANOSECONDS));
+ assertFalse(p.awaitTermination(0L, MILLISECONDS));
+ long timeoutNanos = 999999L;
+ long startTime = System.nanoTime();
+ assertFalse(p.awaitTermination(timeoutNanos, NANOSECONDS));
+ assertTrue(System.nanoTime() - startTime >= timeoutNanos);
+ assertFalse(p.isTerminated());
+ startTime = System.nanoTime();
+ long timeoutMillis = timeoutMillis();
+ assertFalse(p.awaitTermination(timeoutMillis, MILLISECONDS));
+ assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+ assertFalse(p.isTerminated());
+ p.shutdown();
+ assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(p.isTerminated());
+ }
}
/**
@@ -264,23 +276,23 @@
*/
public void testSetUncaughtExceptionHandler() throws InterruptedException {
final CountDownLatch uehInvoked = new CountDownLatch(1);
- final Thread.UncaughtExceptionHandler eh =
+ final Thread.UncaughtExceptionHandler ueh =
new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread t, Throwable e) {
+ threadAssertTrue(e instanceof MyError);
+ threadAssertTrue(t instanceof FailingFJWSubclass);
uehInvoked.countDown();
}};
ForkJoinPool p = new ForkJoinPool(1, new FailingThreadFactory(),
- eh, false);
- try {
- assertSame(eh, p.getUncaughtExceptionHandler());
+ ueh, false);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertSame(ueh, p.getUncaughtExceptionHandler());
try {
p.execute(new FibTask(8));
- assertTrue(uehInvoked.await(MEDIUM_DELAY_MS, MILLISECONDS));
- } catch (RejectedExecutionException ok) {
+ await(uehInvoked);
+ } finally {
+ p.shutdownNow(); // failure might have prevented processing task
}
- } finally {
- p.shutdownNow(); // failure might have prevented processing task
- joinPool(p);
}
}
@@ -292,7 +304,7 @@
*/
public void testIsQuiescent() throws Exception {
ForkJoinPool p = new ForkJoinPool(2);
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
assertTrue(p.isQuiescent());
long startTime = System.nanoTime();
FibTask f = new FibTask(20);
@@ -311,17 +323,18 @@
assertTrue(p.isQuiescent());
assertFalse(p.getAsyncMode());
- assertEquals(0, p.getActiveThreadCount());
assertEquals(0, p.getQueuedTaskCount());
assertEquals(0, p.getQueuedSubmissionCount());
assertFalse(p.hasQueuedSubmissions());
+ while (p.getActiveThreadCount() != 0
+ && millisElapsedSince(startTime) < LONG_DELAY_MS)
+ Thread.yield();
assertFalse(p.isShutdown());
assertFalse(p.isTerminating());
assertFalse(p.isTerminated());
assertTrue(f.isDone());
assertEquals(6765, (int) f.get());
- } finally {
- joinPool(p);
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}
}
@@ -330,11 +343,9 @@
*/
public void testSubmitForkJoinTask() throws Throwable {
ForkJoinPool p = new ForkJoinPool(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
ForkJoinTask<Integer> f = p.submit(new FibTask(8));
assertEquals(21, (int) f.get());
- } finally {
- joinPool(p);
}
}
@@ -343,15 +354,13 @@
*/
public void testSubmitAfterShutdown() {
ForkJoinPool p = new ForkJoinPool(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
p.shutdown();
assertTrue(p.isShutdown());
try {
ForkJoinTask<Integer> f = p.submit(new FibTask(8));
shouldThrow();
} catch (RejectedExecutionException success) {}
- } finally {
- joinPool(p);
}
}
@@ -377,16 +386,14 @@
public void testPollSubmission() {
final CountDownLatch done = new CountDownLatch(1);
SubFJP p = new SubFJP();
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
ForkJoinTask a = p.submit(awaiter(done));
ForkJoinTask b = p.submit(awaiter(done));
ForkJoinTask c = p.submit(awaiter(done));
ForkJoinTask r = p.pollSubmission();
assertTrue(r == a || r == b || r == c);
assertFalse(r.isDone());
- } finally {
done.countDown();
- joinPool(p);
}
}
@@ -396,7 +403,7 @@
public void testDrainTasksTo() {
final CountDownLatch done = new CountDownLatch(1);
SubFJP p = new SubFJP();
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
ForkJoinTask a = p.submit(awaiter(done));
ForkJoinTask b = p.submit(awaiter(done));
ForkJoinTask c = p.submit(awaiter(done));
@@ -407,9 +414,7 @@
assertTrue(r == a || r == b || r == c);
assertFalse(r.isDone());
}
- } finally {
done.countDown();
- joinPool(p);
}
}
@@ -420,7 +425,7 @@
*/
public void testExecuteRunnable() throws Throwable {
ExecutorService e = new ForkJoinPool(1);
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
final AtomicBoolean done = new AtomicBoolean(false);
Future<?> future = e.submit(new CheckedRunnable() {
public void realRun() {
@@ -431,8 +436,6 @@
assertTrue(done.get());
assertTrue(future.isDone());
assertFalse(future.isCancelled());
- } finally {
- joinPool(e);
}
}
@@ -441,13 +444,11 @@
*/
public void testSubmitCallable() throws Throwable {
ExecutorService e = new ForkJoinPool(1);
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
Future<String> future = e.submit(new StringTask());
assertSame(TEST_STRING, future.get());
assertTrue(future.isDone());
assertFalse(future.isCancelled());
- } finally {
- joinPool(e);
}
}
@@ -456,13 +457,11 @@
*/
public void testSubmitRunnable() throws Throwable {
ExecutorService e = new ForkJoinPool(1);
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
Future<?> future = e.submit(new NoOpRunnable());
assertNull(future.get());
assertTrue(future.isDone());
assertFalse(future.isCancelled());
- } finally {
- joinPool(e);
}
}
@@ -471,13 +470,11 @@
*/
public void testSubmitRunnable2() throws Throwable {
ExecutorService e = new ForkJoinPool(1);
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
assertSame(TEST_STRING, future.get());
assertTrue(future.isDone());
assertFalse(future.isCancelled());
- } finally {
- joinPool(e);
}
}
@@ -490,11 +487,9 @@
Runnable r = new CheckedRunnable() {
public void realRun() throws Exception {
ExecutorService e = new ForkJoinPool(1);
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
Future future = e.submit(callable);
assertSame(TEST_STRING, future.get());
- } finally {
- joinPool(e);
}
}};
@@ -511,11 +506,9 @@
Runnable r = new CheckedRunnable() {
public void realRun() throws Exception {
ExecutorService e = new ForkJoinPool(1);
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
Future future = e.submit(callable);
assertSame(TEST_STRING, future.get());
- } finally {
- joinPool(e);
}
}};
@@ -532,7 +525,7 @@
Runnable r = new CheckedRunnable() {
public void realRun() throws Exception {
ExecutorService e = new ForkJoinPool(1);
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
Future future = e.submit(callable);
try {
future.get();
@@ -540,8 +533,6 @@
} catch (ExecutionException success) {
assertTrue(success.getCause() instanceof IndexOutOfBoundsException);
}
- } finally {
- joinPool(e);
}
}};
@@ -553,12 +544,11 @@
*/
public void testExecuteNullRunnable() {
ExecutorService e = new ForkJoinPool(1);
- try {
- Future<?> future = e.submit((Runnable) null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ Future<?> future = e.submit((Runnable) null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -567,12 +557,11 @@
*/
public void testSubmitNullCallable() {
ExecutorService e = new ForkJoinPool(1);
- try {
- Future<String> future = e.submit((Callable) null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ Future<String> future = e.submit((Callable) null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -582,13 +571,13 @@
public void testInterruptedSubmit() throws InterruptedException {
final CountDownLatch submitted = new CountDownLatch(1);
final CountDownLatch quittingTime = new CountDownLatch(1);
- final ExecutorService p = new ForkJoinPool(1);
final Callable<Void> awaiter = new CheckedCallable<Void>() {
public Void realCall() throws InterruptedException {
- assertTrue(quittingTime.await(MEDIUM_DELAY_MS, MILLISECONDS));
+ assertTrue(quittingTime.await(2*LONG_DELAY_MS, MILLISECONDS));
return null;
}};
- try {
+ final ExecutorService p = new ForkJoinPool(1);
+ try (PoolCleaner cleaner = cleaner(p, quittingTime)) {
Thread t = new Thread(new CheckedInterruptedRunnable() {
public void realRun() throws Exception {
Future<Void> future = p.submit(awaiter);
@@ -596,12 +585,9 @@
future.get();
}});
t.start();
- assertTrue(submitted.await(MEDIUM_DELAY_MS, MILLISECONDS));
+ await(submitted);
t.interrupt();
- t.join();
- } finally {
- quittingTime.countDown();
- joinPool(p);
+ awaitTermination(t);
}
}
@@ -611,15 +597,15 @@
*/
public void testSubmitEE() throws Throwable {
ForkJoinPool p = new ForkJoinPool(1);
- try {
- p.submit(new Callable() {
- public Object call() { throw new ArithmeticException(); }})
- .get();
- shouldThrow();
- } catch (ExecutionException success) {
- assertTrue(success.getCause() instanceof ArithmeticException);
- } finally {
- joinPool(p);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.submit(new Callable() {
+ public Object call() { throw new ArithmeticException(); }})
+ .get();
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertTrue(success.getCause() instanceof ArithmeticException);
+ }
}
}
@@ -628,12 +614,11 @@
*/
public void testInvokeAny1() throws Throwable {
ExecutorService e = new ForkJoinPool(1);
- try {
- e.invokeAny(null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAny(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -642,12 +627,11 @@
*/
public void testInvokeAny2() throws Throwable {
ExecutorService e = new ForkJoinPool(1);
- try {
- e.invokeAny(new ArrayList<Callable<String>>());
- shouldThrow();
- } catch (IllegalArgumentException success) {
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAny(new ArrayList<Callable<String>>());
+ shouldThrow();
+ } catch (IllegalArgumentException success) {}
}
}
@@ -656,14 +640,13 @@
*/
public void testInvokeAny3() throws Throwable {
ExecutorService e = new ForkJoinPool(1);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(null);
- try {
- e.invokeAny(l);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(null);
+ try {
+ e.invokeAny(l);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -673,16 +656,15 @@
public void testInvokeAny4() throws Throwable {
CountDownLatch latch = new CountDownLatch(1);
ExecutorService e = new ForkJoinPool(1);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(latchAwaitingStringTask(latch));
- l.add(null);
- try {
- e.invokeAny(l);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(latchAwaitingStringTask(latch));
+ l.add(null);
+ try {
+ e.invokeAny(l);
+ shouldThrow();
+ } catch (NullPointerException success) {}
latch.countDown();
- joinPool(e);
}
}
@@ -691,15 +673,15 @@
*/
public void testInvokeAny5() throws Throwable {
ExecutorService e = new ForkJoinPool(1);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new NPETask());
- try {
- e.invokeAny(l);
- shouldThrow();
- } catch (ExecutionException success) {
- assertTrue(success.getCause() instanceof NullPointerException);
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new NPETask());
+ try {
+ e.invokeAny(l);
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertTrue(success.getCause() instanceof NullPointerException);
+ }
}
}
@@ -708,14 +690,12 @@
*/
public void testInvokeAny6() throws Throwable {
ExecutorService e = new ForkJoinPool(1);
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
String result = e.invokeAny(l);
assertSame(TEST_STRING, result);
- } finally {
- joinPool(e);
}
}
@@ -724,12 +704,11 @@
*/
public void testInvokeAll1() throws Throwable {
ExecutorService e = new ForkJoinPool(1);
- try {
- e.invokeAll(null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAll(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -738,12 +717,10 @@
*/
public void testInvokeAll2() throws InterruptedException {
ExecutorService e = new ForkJoinPool(1);
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Future<String>> r
= e.invokeAll(new ArrayList<Callable<String>>());
assertTrue(r.isEmpty());
- } finally {
- joinPool(e);
}
}
@@ -752,15 +729,14 @@
*/
public void testInvokeAll3() throws InterruptedException {
ExecutorService e = new ForkJoinPool(1);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- l.add(null);
- try {
- e.invokeAll(l);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new StringTask());
+ l.add(null);
+ try {
+ e.invokeAll(l);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -770,17 +746,17 @@
*/
public void testInvokeAll4() throws Throwable {
ExecutorService e = new ForkJoinPool(1);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new NPETask());
- List<Future<String>> futures = e.invokeAll(l);
- assertEquals(1, futures.size());
- try {
- futures.get(0).get();
- shouldThrow();
- } catch (ExecutionException success) {
- assertTrue(success.getCause() instanceof NullPointerException);
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new NPETask());
+ List<Future<String>> futures = e.invokeAll(l);
+ assertEquals(1, futures.size());
+ try {
+ futures.get(0).get();
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertTrue(success.getCause() instanceof NullPointerException);
+ }
}
}
@@ -789,7 +765,7 @@
*/
public void testInvokeAll5() throws Throwable {
ExecutorService e = new ForkJoinPool(1);
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
@@ -797,8 +773,6 @@
assertEquals(2, futures.size());
for (Future<String> future : futures)
assertSame(TEST_STRING, future.get());
- } finally {
- joinPool(e);
}
}
@@ -807,12 +781,11 @@
*/
public void testTimedInvokeAny1() throws Throwable {
ExecutorService e = new ForkJoinPool(1);
- try {
- e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -821,14 +794,13 @@
*/
public void testTimedInvokeAnyNullTimeUnit() throws Throwable {
ExecutorService e = new ForkJoinPool(1);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- try {
- e.invokeAny(l, MEDIUM_DELAY_MS, null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new StringTask());
+ try {
+ e.invokeAny(l, MEDIUM_DELAY_MS, null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -837,13 +809,12 @@
*/
public void testTimedInvokeAny2() throws Throwable {
ExecutorService e = new ForkJoinPool(1);
- try {
- e.invokeAny(new ArrayList<Callable<String>>(),
- MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (IllegalArgumentException success) {
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAny(new ArrayList<Callable<String>>(),
+ MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (IllegalArgumentException success) {}
}
}
@@ -853,16 +824,15 @@
public void testTimedInvokeAny3() throws Throwable {
CountDownLatch latch = new CountDownLatch(1);
ExecutorService e = new ForkJoinPool(1);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(latchAwaitingStringTask(latch));
- l.add(null);
- try {
- e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(latchAwaitingStringTask(latch));
+ l.add(null);
+ try {
+ e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
latch.countDown();
- joinPool(e);
}
}
@@ -871,15 +841,17 @@
*/
public void testTimedInvokeAny4() throws Throwable {
ExecutorService e = new ForkJoinPool(1);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new NPETask());
- try {
- e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (ExecutionException success) {
- assertTrue(success.getCause() instanceof NullPointerException);
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ long startTime = System.nanoTime();
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new NPETask());
+ try {
+ e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertTrue(success.getCause() instanceof NullPointerException);
+ }
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}
}
@@ -888,14 +860,14 @@
*/
public void testTimedInvokeAny5() throws Throwable {
ExecutorService e = new ForkJoinPool(1);
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
+ long startTime = System.nanoTime();
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
- String result = e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
assertSame(TEST_STRING, result);
- } finally {
- joinPool(e);
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}
}
@@ -904,12 +876,11 @@
*/
public void testTimedInvokeAll1() throws Throwable {
ExecutorService e = new ForkJoinPool(1);
- try {
- e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -918,14 +889,13 @@
*/
public void testTimedInvokeAllNullTimeUnit() throws Throwable {
ExecutorService e = new ForkJoinPool(1);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- try {
- e.invokeAll(l, MEDIUM_DELAY_MS, null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new StringTask());
+ try {
+ e.invokeAll(l, MEDIUM_DELAY_MS, null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -934,13 +904,11 @@
*/
public void testTimedInvokeAll2() throws InterruptedException {
ExecutorService e = new ForkJoinPool(1);
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Future<String>> r
= e.invokeAll(new ArrayList<Callable<String>>(),
MEDIUM_DELAY_MS, MILLISECONDS);
assertTrue(r.isEmpty());
- } finally {
- joinPool(e);
}
}
@@ -949,15 +917,14 @@
*/
public void testTimedInvokeAll3() throws InterruptedException {
ExecutorService e = new ForkJoinPool(1);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- l.add(null);
- try {
- e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new StringTask());
+ l.add(null);
+ try {
+ e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -966,18 +933,18 @@
*/
public void testTimedInvokeAll4() throws Throwable {
ExecutorService e = new ForkJoinPool(1);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new NPETask());
- List<Future<String>> futures
- = e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
- assertEquals(1, futures.size());
- try {
- futures.get(0).get();
- shouldThrow();
- } catch (ExecutionException success) {
- assertTrue(success.getCause() instanceof NullPointerException);
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new NPETask());
+ List<Future<String>> futures
+ = e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+ assertEquals(1, futures.size());
+ try {
+ futures.get(0).get();
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertTrue(success.getCause() instanceof NullPointerException);
+ }
}
}
@@ -985,18 +952,16 @@
* timed invokeAll(c) returns results of all completed tasks in c
*/
public void testTimedInvokeAll5() throws Throwable {
- ExecutorService e = new ForkJoinPool(1);
- try {
+ ForkJoinPool e = new ForkJoinPool(1);
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
List<Future<String>> futures
- = e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ = e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
assertEquals(2, futures.size());
for (Future<String> future : futures)
assertSame(TEST_STRING, future.get());
- } finally {
- joinPool(e);
}
}
diff --git a/jsr166-tests/src/test/java/jsr166/ForkJoinTask8Test.java b/jsr166-tests/src/test/java/jsr166/ForkJoinTask8Test.java
new file mode 100644
index 0000000..6c03348
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ForkJoinTask8Test.java
@@ -0,0 +1,1205 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.ForkJoinWorkerThread;
+import java.util.concurrent.RecursiveAction;
+import java.util.concurrent.TimeoutException;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ForkJoinTask8Test extends JSR166TestCase {
+
+ /*
+ * Testing notes: This differs from ForkJoinTaskTest mainly by
+ * defining a version of BinaryAsyncAction that uses JDK8 task
+ * tags for control state, thereby testing getForkJoinTaskTag,
+ * setForkJoinTaskTag, and compareAndSetForkJoinTaskTag across
+ * various contexts. Most of the test methods using it are
+ * otherwise identical, but omitting retest of those dealing with
+ * cancellation, which is not represented in this tag scheme.
+ */
+
+ static final short INITIAL_STATE = -1;
+ static final short COMPLETE_STATE = 0;
+ static final short EXCEPTION_STATE = 1;
+
+ // android-note: Removed because the CTS runner does a bad job of
+ // retrying tests that have suite() declarations.
+ //
+ // public static void main(String[] args) {
+ // main(suite(), args);
+ // }
+ // public static Test suite() {
+ // return new TestSuite(ForkJoinTask8Test.class);
+ // }
+
+ // Runs with "mainPool" use > 1 thread. singletonPool tests use 1
+ static final int mainPoolSize =
+ Math.max(2, Runtime.getRuntime().availableProcessors());
+
+ private static ForkJoinPool mainPool() {
+ return new ForkJoinPool(mainPoolSize);
+ }
+
+ private static ForkJoinPool singletonPool() {
+ return new ForkJoinPool(1);
+ }
+
+ private static ForkJoinPool asyncSingletonPool() {
+ return new ForkJoinPool(1,
+ ForkJoinPool.defaultForkJoinWorkerThreadFactory,
+ null, true);
+ }
+
+ // Compute fib naively and efficiently
+ final int[] fib;
+ {
+ int[] fib = new int[10];
+ fib[0] = 0;
+ fib[1] = 1;
+ for (int i = 2; i < fib.length; i++)
+ fib[i] = fib[i - 1] + fib[i - 2];
+ this.fib = fib;
+ }
+
+ private void testInvokeOnPool(ForkJoinPool pool, RecursiveAction a) {
+ try (PoolCleaner cleaner = cleaner(pool)) {
+ assertFalse(a.isDone());
+ assertFalse(a.isCompletedNormally());
+ assertFalse(a.isCompletedAbnormally());
+ assertFalse(a.isCancelled());
+ assertNull(a.getException());
+ assertNull(a.getRawResult());
+
+ assertNull(pool.invoke(a));
+
+ assertTrue(a.isDone());
+ assertTrue(a.isCompletedNormally());
+ assertFalse(a.isCompletedAbnormally());
+ assertFalse(a.isCancelled());
+ assertNull(a.getException());
+ assertNull(a.getRawResult());
+ }
+ }
+
+ void checkNotDone(ForkJoinTask a) {
+ assertFalse(a.isDone());
+ assertFalse(a.isCompletedNormally());
+ assertFalse(a.isCompletedAbnormally());
+ assertFalse(a.isCancelled());
+ assertNull(a.getException());
+ assertNull(a.getRawResult());
+ if (a instanceof BinaryAsyncAction)
+ assertTrue(((BinaryAsyncAction)a).getForkJoinTaskTag() == INITIAL_STATE);
+
+ try {
+ a.get(0L, SECONDS);
+ shouldThrow();
+ } catch (TimeoutException success) {
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+ }
+
+ <T> void checkCompletedNormally(ForkJoinTask<T> a) {
+ checkCompletedNormally(a, null);
+ }
+
+ <T> void checkCompletedNormally(ForkJoinTask<T> a, T expected) {
+ assertTrue(a.isDone());
+ assertFalse(a.isCancelled());
+ assertTrue(a.isCompletedNormally());
+ assertFalse(a.isCompletedAbnormally());
+ assertNull(a.getException());
+ assertSame(expected, a.getRawResult());
+ if (a instanceof BinaryAsyncAction)
+ assertTrue(((BinaryAsyncAction)a).getForkJoinTaskTag() == COMPLETE_STATE);
+
+ {
+ Thread.currentThread().interrupt();
+ long startTime = System.nanoTime();
+ assertSame(expected, a.join());
+ assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+ Thread.interrupted();
+ }
+
+ {
+ Thread.currentThread().interrupt();
+ long startTime = System.nanoTime();
+ a.quietlyJoin(); // should be no-op
+ assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+ Thread.interrupted();
+ }
+
+ assertFalse(a.cancel(false));
+ assertFalse(a.cancel(true));
+ try {
+ assertSame(expected, a.get());
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+ try {
+ assertSame(expected, a.get(5L, SECONDS));
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+ }
+
+ void checkCompletedAbnormally(ForkJoinTask a, Throwable t) {
+ assertTrue(a.isDone());
+ assertFalse(a.isCancelled());
+ assertFalse(a.isCompletedNormally());
+ assertTrue(a.isCompletedAbnormally());
+ assertSame(t.getClass(), a.getException().getClass());
+ assertNull(a.getRawResult());
+ assertFalse(a.cancel(false));
+ assertFalse(a.cancel(true));
+ if (a instanceof BinaryAsyncAction)
+ assertTrue(((BinaryAsyncAction)a).getForkJoinTaskTag() != INITIAL_STATE);
+
+ try {
+ Thread.currentThread().interrupt();
+ a.join();
+ shouldThrow();
+ } catch (Throwable expected) {
+ assertSame(t.getClass(), expected.getClass());
+ }
+ Thread.interrupted();
+
+ {
+ long startTime = System.nanoTime();
+ a.quietlyJoin(); // should be no-op
+ assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+ }
+
+ try {
+ a.get();
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertSame(t.getClass(), success.getCause().getClass());
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+ try {
+ a.get(5L, SECONDS);
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertSame(t.getClass(), success.getCause().getClass());
+ } catch (Throwable fail) { threadUnexpectedException(fail); }
+ }
+
+ public static final class FJException extends RuntimeException {
+ FJException() { super(); }
+ }
+
+ abstract static class BinaryAsyncAction extends ForkJoinTask<Void> {
+
+ private volatile BinaryAsyncAction parent;
+
+ private volatile BinaryAsyncAction sibling;
+
+ protected BinaryAsyncAction() {
+ setForkJoinTaskTag(INITIAL_STATE);
+ }
+
+ public final Void getRawResult() { return null; }
+ protected final void setRawResult(Void mustBeNull) { }
+
+ public final void linkSubtasks(BinaryAsyncAction x, BinaryAsyncAction y) {
+ x.parent = y.parent = this;
+ x.sibling = y;
+ y.sibling = x;
+ }
+
+ protected void onComplete(BinaryAsyncAction x, BinaryAsyncAction y) {
+ if (this.getForkJoinTaskTag() != COMPLETE_STATE ||
+ x.getForkJoinTaskTag() != COMPLETE_STATE ||
+ y.getForkJoinTaskTag() != COMPLETE_STATE) {
+ completeThisExceptionally(new FJException());
+ }
+ }
+
+ protected boolean onException() {
+ return true;
+ }
+
+ public void linkAndForkSubtasks(BinaryAsyncAction x, BinaryAsyncAction y) {
+ linkSubtasks(x, y);
+ y.fork();
+ x.fork();
+ }
+
+ private void completeThis() {
+ setForkJoinTaskTag(COMPLETE_STATE);
+ super.complete(null);
+ }
+
+ private void completeThisExceptionally(Throwable ex) {
+ setForkJoinTaskTag(EXCEPTION_STATE);
+ super.completeExceptionally(ex);
+ }
+
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ if (super.cancel(mayInterruptIfRunning)) {
+ completeExceptionally(new FJException());
+ return true;
+ }
+ return false;
+ }
+
+ public final void complete() {
+ BinaryAsyncAction a = this;
+ for (;;) {
+ BinaryAsyncAction s = a.sibling;
+ BinaryAsyncAction p = a.parent;
+ a.sibling = null;
+ a.parent = null;
+ a.completeThis();
+ if (p == null ||
+ p.compareAndSetForkJoinTaskTag(INITIAL_STATE, COMPLETE_STATE))
+ break;
+ try {
+ p.onComplete(a, s);
+ } catch (Throwable rex) {
+ p.completeExceptionally(rex);
+ return;
+ }
+ a = p;
+ }
+ }
+
+ public final void completeExceptionally(Throwable ex) {
+ for (BinaryAsyncAction a = this;;) {
+ a.completeThisExceptionally(ex);
+ BinaryAsyncAction s = a.sibling;
+ if (s != null && !s.isDone())
+ s.completeExceptionally(ex);
+ if ((a = a.parent) == null)
+ break;
+ }
+ }
+
+ public final BinaryAsyncAction getParent() {
+ return parent;
+ }
+
+ public BinaryAsyncAction getSibling() {
+ return sibling;
+ }
+
+ public void reinitialize() {
+ parent = sibling = null;
+ super.reinitialize();
+ }
+
+ }
+
+ final class AsyncFib extends BinaryAsyncAction {
+ int number;
+ int expectedResult;
+ public AsyncFib(int number) {
+ this.number = number;
+ this.expectedResult = fib[number];
+ }
+
+ public final boolean exec() {
+ try {
+ AsyncFib f = this;
+ int n = f.number;
+ while (n > 1) {
+ AsyncFib p = f;
+ AsyncFib r = new AsyncFib(n - 2);
+ f = new AsyncFib(--n);
+ p.linkSubtasks(r, f);
+ r.fork();
+ }
+ f.complete();
+ }
+ catch (Throwable ex) {
+ compareAndSetForkJoinTaskTag(INITIAL_STATE, EXCEPTION_STATE);
+ }
+ if (getForkJoinTaskTag() == EXCEPTION_STATE)
+ throw new FJException();
+ return false;
+ }
+
+ protected void onComplete(BinaryAsyncAction x, BinaryAsyncAction y) {
+ number = ((AsyncFib)x).number + ((AsyncFib)y).number;
+ super.onComplete(x, y);
+ }
+
+ public void checkCompletedNormally() {
+ assertEquals(expectedResult, number);
+ ForkJoinTask8Test.this.checkCompletedNormally(this);
+ }
+ }
+
+ static final class FailingAsyncFib extends BinaryAsyncAction {
+ int number;
+ public FailingAsyncFib(int n) {
+ this.number = n;
+ }
+
+ public final boolean exec() {
+ try {
+ FailingAsyncFib f = this;
+ int n = f.number;
+ while (n > 1) {
+ FailingAsyncFib p = f;
+ FailingAsyncFib r = new FailingAsyncFib(n - 2);
+ f = new FailingAsyncFib(--n);
+ p.linkSubtasks(r, f);
+ r.fork();
+ }
+ f.complete();
+ }
+ catch (Throwable ex) {
+ compareAndSetForkJoinTaskTag(INITIAL_STATE, EXCEPTION_STATE);
+ }
+ if (getForkJoinTaskTag() == EXCEPTION_STATE)
+ throw new FJException();
+ return false;
+ }
+
+ protected void onComplete(BinaryAsyncAction x, BinaryAsyncAction y) {
+ completeExceptionally(new FJException());
+ }
+ }
+
+ /**
+ * invoke returns when task completes normally.
+ * isCompletedAbnormally and isCancelled return false for normally
+ * completed tasks; getRawResult returns null.
+ */
+ public void testInvoke() {
+ testInvoke(mainPool());
+ }
+ public void testInvoke_Singleton() {
+ testInvoke(singletonPool());
+ }
+ public void testInvoke(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ AsyncFib f = new AsyncFib(8);
+ assertNull(f.invoke());
+ f.checkCompletedNormally();
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * quietlyInvoke task returns when task completes normally.
+ * isCompletedAbnormally and isCancelled return false for normally
+ * completed tasks
+ */
+ public void testQuietlyInvoke() {
+ testQuietlyInvoke(mainPool());
+ }
+ public void testQuietlyInvoke_Singleton() {
+ testQuietlyInvoke(singletonPool());
+ }
+ public void testQuietlyInvoke(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ AsyncFib f = new AsyncFib(8);
+ f.quietlyInvoke();
+ f.checkCompletedNormally();
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * join of a forked task returns when task completes
+ */
+ public void testForkJoin() {
+ testForkJoin(mainPool());
+ }
+ public void testForkJoin_Singleton() {
+ testForkJoin(singletonPool());
+ }
+ public void testForkJoin(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ AsyncFib f = new AsyncFib(8);
+ assertSame(f, f.fork());
+ assertNull(f.join());
+ f.checkCompletedNormally();
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * get of a forked task returns when task completes
+ */
+ public void testForkGet() {
+ testForkGet(mainPool());
+ }
+ public void testForkGet_Singleton() {
+ testForkGet(singletonPool());
+ }
+ public void testForkGet(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() throws Exception {
+ AsyncFib f = new AsyncFib(8);
+ assertSame(f, f.fork());
+ assertNull(f.get());
+ f.checkCompletedNormally();
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * timed get of a forked task returns when task completes
+ */
+ public void testForkTimedGet() {
+ testForkTimedGet(mainPool());
+ }
+ public void testForkTimedGet_Singleton() {
+ testForkTimedGet(singletonPool());
+ }
+ public void testForkTimedGet(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() throws Exception {
+ AsyncFib f = new AsyncFib(8);
+ assertSame(f, f.fork());
+ assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
+ f.checkCompletedNormally();
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * timed get with null time unit throws NullPointerException
+ */
+ public void testForkTimedGetNullTimeUnit() {
+ testForkTimedGetNullTimeUnit(mainPool());
+ }
+ public void testForkTimedGetNullTimeUnit_Singleton() {
+ testForkTimedGet(singletonPool());
+ }
+ public void testForkTimedGetNullTimeUnit(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() throws Exception {
+ AsyncFib f = new AsyncFib(8);
+ assertSame(f, f.fork());
+ try {
+ f.get(5L, null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * quietlyJoin of a forked task returns when task completes
+ */
+ public void testForkQuietlyJoin() {
+ testForkQuietlyJoin(mainPool());
+ }
+ public void testForkQuietlyJoin_Singleton() {
+ testForkQuietlyJoin(singletonPool());
+ }
+ public void testForkQuietlyJoin(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ AsyncFib f = new AsyncFib(8);
+ assertSame(f, f.fork());
+ f.quietlyJoin();
+ f.checkCompletedNormally();
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * helpQuiesce returns when tasks are complete.
+ * getQueuedTaskCount returns 0 when quiescent
+ */
+ public void testForkHelpQuiesce() {
+ testForkHelpQuiesce(mainPool());
+ }
+ public void testForkHelpQuiesce_Singleton() {
+ testForkHelpQuiesce(singletonPool());
+ }
+ public void testForkHelpQuiesce(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ AsyncFib f = new AsyncFib(8);
+ assertSame(f, f.fork());
+ helpQuiesce();
+ assertEquals(0, getQueuedTaskCount());
+ f.checkCompletedNormally();
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * invoke task throws exception when task completes abnormally
+ */
+ public void testAbnormalInvoke() {
+ testAbnormalInvoke(mainPool());
+ }
+ public void testAbnormalInvoke_Singleton() {
+ testAbnormalInvoke(singletonPool());
+ }
+ public void testAbnormalInvoke(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FailingAsyncFib f = new FailingAsyncFib(8);
+ try {
+ f.invoke();
+ shouldThrow();
+ } catch (FJException success) {
+ checkCompletedAbnormally(f, success);
+ }
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * quietlyInvoke task returns when task completes abnormally
+ */
+ public void testAbnormalQuietlyInvoke() {
+ testAbnormalQuietlyInvoke(mainPool());
+ }
+ public void testAbnormalQuietlyInvoke_Singleton() {
+ testAbnormalQuietlyInvoke(singletonPool());
+ }
+ public void testAbnormalQuietlyInvoke(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FailingAsyncFib f = new FailingAsyncFib(8);
+ f.quietlyInvoke();
+ assertTrue(f.getException() instanceof FJException);
+ checkCompletedAbnormally(f, f.getException());
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * join of a forked task throws exception when task completes abnormally
+ */
+ public void testAbnormalForkJoin() {
+ testAbnormalForkJoin(mainPool());
+ }
+ public void testAbnormalForkJoin_Singleton() {
+ testAbnormalForkJoin(singletonPool());
+ }
+ public void testAbnormalForkJoin(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FailingAsyncFib f = new FailingAsyncFib(8);
+ assertSame(f, f.fork());
+ try {
+ f.join();
+ shouldThrow();
+ } catch (FJException success) {
+ checkCompletedAbnormally(f, success);
+ }
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * get of a forked task throws exception when task completes abnormally
+ */
+ public void testAbnormalForkGet() {
+ testAbnormalForkGet(mainPool());
+ }
+ public void testAbnormalForkGet_Singleton() {
+ testAbnormalForkJoin(singletonPool());
+ }
+ public void testAbnormalForkGet(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() throws Exception {
+ FailingAsyncFib f = new FailingAsyncFib(8);
+ assertSame(f, f.fork());
+ try {
+ f.get();
+ shouldThrow();
+ } catch (ExecutionException success) {
+ Throwable cause = success.getCause();
+ assertTrue(cause instanceof FJException);
+ checkCompletedAbnormally(f, cause);
+ }
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * timed get of a forked task throws exception when task completes abnormally
+ */
+ public void testAbnormalForkTimedGet() {
+ testAbnormalForkTimedGet(mainPool());
+ }
+ public void testAbnormalForkTimedGet_Singleton() {
+ testAbnormalForkTimedGet(singletonPool());
+ }
+ public void testAbnormalForkTimedGet(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() throws Exception {
+ FailingAsyncFib f = new FailingAsyncFib(8);
+ assertSame(f, f.fork());
+ try {
+ f.get(LONG_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (ExecutionException success) {
+ Throwable cause = success.getCause();
+ assertTrue(cause instanceof FJException);
+ checkCompletedAbnormally(f, cause);
+ }
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * quietlyJoin of a forked task returns when task completes abnormally
+ */
+ public void testAbnormalForkQuietlyJoin() {
+ testAbnormalForkQuietlyJoin(mainPool());
+ }
+ public void testAbnormalForkQuietlyJoin_Singleton() {
+ testAbnormalForkQuietlyJoin(singletonPool());
+ }
+ public void testAbnormalForkQuietlyJoin(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FailingAsyncFib f = new FailingAsyncFib(8);
+ assertSame(f, f.fork());
+ f.quietlyJoin();
+ assertTrue(f.getException() instanceof FJException);
+ checkCompletedAbnormally(f, f.getException());
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * getPool of executing task returns its pool
+ */
+ public void testGetPool() {
+ testGetPool(mainPool());
+ }
+ public void testGetPool_Singleton() {
+ testGetPool(singletonPool());
+ }
+ public void testGetPool(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ assertSame(pool, getPool());
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * getPool of non-FJ task returns null
+ */
+ public void testGetPool2() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ assertNull(getPool());
+ }};
+ assertNull(a.invoke());
+ }
+
+ /**
+ * inForkJoinPool of executing task returns true
+ */
+ public void testInForkJoinPool() {
+ testInForkJoinPool(mainPool());
+ }
+ public void testInForkJoinPool_Singleton() {
+ testInForkJoinPool(singletonPool());
+ }
+ public void testInForkJoinPool(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ assertTrue(inForkJoinPool());
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * inForkJoinPool of non-FJ task returns false
+ */
+ public void testInForkJoinPool2() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ assertFalse(inForkJoinPool());
+ }};
+ assertNull(a.invoke());
+ }
+
+ /**
+ * setRawResult(null) succeeds
+ */
+ public void testSetRawResult() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ setRawResult(null);
+ assertNull(getRawResult());
+ }};
+ assertNull(a.invoke());
+ }
+
+ /**
+ * invoke task throws exception after invoking completeExceptionally
+ */
+ public void testCompleteExceptionally() {
+ testCompleteExceptionally(mainPool());
+ }
+ public void testCompleteExceptionally_Singleton() {
+ testCompleteExceptionally(singletonPool());
+ }
+ public void testCompleteExceptionally(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ AsyncFib f = new AsyncFib(8);
+ f.completeExceptionally(new FJException());
+ try {
+ f.invoke();
+ shouldThrow();
+ } catch (FJException success) {
+ checkCompletedAbnormally(f, success);
+ }
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * invokeAll(tasks) with 1 argument invokes task
+ */
+ public void testInvokeAll1() {
+ testInvokeAll1(mainPool());
+ }
+ public void testInvokeAll1_Singleton() {
+ testInvokeAll1(singletonPool());
+ }
+ public void testInvokeAll1(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ AsyncFib f = new AsyncFib(8);
+ invokeAll(f);
+ f.checkCompletedNormally();
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * invokeAll(t1, t2) invokes all task arguments
+ */
+ public void testInvokeAll2() {
+ testInvokeAll2(mainPool());
+ }
+ public void testInvokeAll2_Singleton() {
+ testInvokeAll2(singletonPool());
+ }
+ public void testInvokeAll2(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ AsyncFib[] tasks = {
+ new AsyncFib(8),
+ new AsyncFib(9),
+ };
+ invokeAll(tasks[0], tasks[1]);
+ for (AsyncFib task : tasks) assertTrue(task.isDone());
+ for (AsyncFib task : tasks) task.checkCompletedNormally();
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * invokeAll(tasks) with > 2 argument invokes tasks
+ */
+ public void testInvokeAll3() {
+ testInvokeAll3(mainPool());
+ }
+ public void testInvokeAll3_Singleton() {
+ testInvokeAll3(singletonPool());
+ }
+ public void testInvokeAll3(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ AsyncFib[] tasks = {
+ new AsyncFib(8),
+ new AsyncFib(9),
+ new AsyncFib(7),
+ };
+ invokeAll(tasks[0], tasks[1], tasks[2]);
+ for (AsyncFib task : tasks) assertTrue(task.isDone());
+ for (AsyncFib task : tasks) task.checkCompletedNormally();
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * invokeAll(collection) invokes all tasks in the collection
+ */
+ public void testInvokeAllCollection() {
+ testInvokeAllCollection(mainPool());
+ }
+ public void testInvokeAllCollection_Singleton() {
+ testInvokeAllCollection(singletonPool());
+ }
+ public void testInvokeAllCollection(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ AsyncFib[] tasks = {
+ new AsyncFib(8),
+ new AsyncFib(9),
+ new AsyncFib(7),
+ };
+ invokeAll(Arrays.asList(tasks));
+ for (AsyncFib task : tasks) assertTrue(task.isDone());
+ for (AsyncFib task : tasks) task.checkCompletedNormally();
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * invokeAll(tasks) with any null task throws NullPointerException
+ */
+ public void testInvokeAllNullTask() {
+ testInvokeAllNullTask(mainPool());
+ }
+ public void testInvokeAllNullTask_Singleton() {
+ testInvokeAllNullTask(singletonPool());
+ }
+ public void testInvokeAllNullTask(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ AsyncFib nul = null;
+ Runnable[] throwingActions = {
+ () -> invokeAll(nul),
+ () -> invokeAll(nul, nul),
+ () -> invokeAll(new AsyncFib(8), new AsyncFib(9), nul),
+ () -> invokeAll(new AsyncFib(8), nul, new AsyncFib(9)),
+ () -> invokeAll(nul, new AsyncFib(8), new AsyncFib(9)),
+ };
+ assertThrows(NullPointerException.class, throwingActions);
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * invokeAll(tasks) with 1 argument throws exception if task does
+ */
+ public void testAbnormalInvokeAll1() {
+ testAbnormalInvokeAll1(mainPool());
+ }
+ public void testAbnormalInvokeAll1_Singleton() {
+ testAbnormalInvokeAll1(singletonPool());
+ }
+ public void testAbnormalInvokeAll1(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FailingAsyncFib g = new FailingAsyncFib(9);
+ try {
+ invokeAll(g);
+ shouldThrow();
+ } catch (FJException success) {
+ checkCompletedAbnormally(g, success);
+ }
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * invokeAll(t1, t2) throw exception if any task does
+ */
+ public void testAbnormalInvokeAll2() {
+ testAbnormalInvokeAll2(mainPool());
+ }
+ public void testAbnormalInvokeAll2_Singleton() {
+ testAbnormalInvokeAll2(singletonPool());
+ }
+ public void testAbnormalInvokeAll2(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ AsyncFib f = new AsyncFib(8);
+ FailingAsyncFib g = new FailingAsyncFib(9);
+ ForkJoinTask[] tasks = { f, g };
+ Collections.shuffle(Arrays.asList(tasks));
+ try {
+ invokeAll(tasks[0], tasks[1]);
+ shouldThrow();
+ } catch (FJException success) {
+ checkCompletedAbnormally(g, success);
+ }
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * invokeAll(tasks) with > 2 argument throws exception if any task does
+ */
+ public void testAbnormalInvokeAll3() {
+ testAbnormalInvokeAll3(mainPool());
+ }
+ public void testAbnormalInvokeAll3_Singleton() {
+ testAbnormalInvokeAll3(singletonPool());
+ }
+ public void testAbnormalInvokeAll3(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ AsyncFib f = new AsyncFib(8);
+ FailingAsyncFib g = new FailingAsyncFib(9);
+ AsyncFib h = new AsyncFib(7);
+ ForkJoinTask[] tasks = { f, g, h };
+ Collections.shuffle(Arrays.asList(tasks));
+ try {
+ invokeAll(tasks[0], tasks[1], tasks[2]);
+ shouldThrow();
+ } catch (FJException success) {
+ checkCompletedAbnormally(g, success);
+ }
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * invokeAll(collection) throws exception if any task does
+ */
+ public void testAbnormalInvokeAllCollection() {
+ testAbnormalInvokeAllCollection(mainPool());
+ }
+ public void testAbnormalInvokeAllCollection_Singleton() {
+ testAbnormalInvokeAllCollection(singletonPool());
+ }
+ public void testAbnormalInvokeAllCollection(ForkJoinPool pool) {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ FailingAsyncFib f = new FailingAsyncFib(8);
+ AsyncFib g = new AsyncFib(9);
+ AsyncFib h = new AsyncFib(7);
+ ForkJoinTask[] tasks = { f, g, h };
+ Collections.shuffle(Arrays.asList(tasks));
+ try {
+ invokeAll(Arrays.asList(tasks));
+ shouldThrow();
+ } catch (FJException success) {
+ checkCompletedAbnormally(f, success);
+ }
+ }};
+ testInvokeOnPool(pool, a);
+ }
+
+ /**
+ * tryUnfork returns true for most recent unexecuted task,
+ * and suppresses execution
+ */
+ public void testTryUnfork() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ AsyncFib g = new AsyncFib(9);
+ assertSame(g, g.fork());
+ AsyncFib f = new AsyncFib(8);
+ assertSame(f, f.fork());
+ assertTrue(f.tryUnfork());
+ helpQuiesce();
+ checkNotDone(f);
+ g.checkCompletedNormally();
+ }};
+ testInvokeOnPool(singletonPool(), a);
+ }
+
+ /**
+ * getSurplusQueuedTaskCount returns > 0 when
+ * there are more tasks than threads
+ */
+ public void testGetSurplusQueuedTaskCount() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ AsyncFib h = new AsyncFib(7);
+ assertSame(h, h.fork());
+ AsyncFib g = new AsyncFib(9);
+ assertSame(g, g.fork());
+ AsyncFib f = new AsyncFib(8);
+ assertSame(f, f.fork());
+ assertTrue(getSurplusQueuedTaskCount() > 0);
+ helpQuiesce();
+ assertEquals(0, getSurplusQueuedTaskCount());
+ f.checkCompletedNormally();
+ g.checkCompletedNormally();
+ h.checkCompletedNormally();
+ }};
+ testInvokeOnPool(singletonPool(), a);
+ }
+
+ /**
+ * peekNextLocalTask returns most recent unexecuted task.
+ */
+ public void testPeekNextLocalTask() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ AsyncFib g = new AsyncFib(9);
+ assertSame(g, g.fork());
+ AsyncFib f = new AsyncFib(8);
+ assertSame(f, f.fork());
+ assertSame(f, peekNextLocalTask());
+ assertNull(f.join());
+ f.checkCompletedNormally();
+ helpQuiesce();
+ g.checkCompletedNormally();
+ }};
+ testInvokeOnPool(singletonPool(), a);
+ }
+
+ /**
+ * pollNextLocalTask returns most recent unexecuted task without
+ * executing it
+ */
+ public void testPollNextLocalTask() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ AsyncFib g = new AsyncFib(9);
+ assertSame(g, g.fork());
+ AsyncFib f = new AsyncFib(8);
+ assertSame(f, f.fork());
+ assertSame(f, pollNextLocalTask());
+ helpQuiesce();
+ checkNotDone(f);
+ g.checkCompletedNormally();
+ }};
+ testInvokeOnPool(singletonPool(), a);
+ }
+
+ /**
+ * pollTask returns an unexecuted task without executing it
+ */
+ public void testPollTask() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ AsyncFib g = new AsyncFib(9);
+ assertSame(g, g.fork());
+ AsyncFib f = new AsyncFib(8);
+ assertSame(f, f.fork());
+ assertSame(f, pollTask());
+ helpQuiesce();
+ checkNotDone(f);
+ g.checkCompletedNormally();
+ }};
+ testInvokeOnPool(singletonPool(), a);
+ }
+
+ /**
+ * peekNextLocalTask returns least recent unexecuted task in async mode
+ */
+ public void testPeekNextLocalTaskAsync() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ AsyncFib g = new AsyncFib(9);
+ assertSame(g, g.fork());
+ AsyncFib f = new AsyncFib(8);
+ assertSame(f, f.fork());
+ assertSame(g, peekNextLocalTask());
+ assertNull(f.join());
+ helpQuiesce();
+ f.checkCompletedNormally();
+ g.checkCompletedNormally();
+ }};
+ testInvokeOnPool(asyncSingletonPool(), a);
+ }
+
+ /**
+ * pollNextLocalTask returns least recent unexecuted task without
+ * executing it, in async mode
+ */
+ public void testPollNextLocalTaskAsync() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ AsyncFib g = new AsyncFib(9);
+ assertSame(g, g.fork());
+ AsyncFib f = new AsyncFib(8);
+ assertSame(f, f.fork());
+ assertSame(g, pollNextLocalTask());
+ helpQuiesce();
+ f.checkCompletedNormally();
+ checkNotDone(g);
+ }};
+ testInvokeOnPool(asyncSingletonPool(), a);
+ }
+
+ /**
+ * pollTask returns an unexecuted task without executing it, in
+ * async mode
+ */
+ public void testPollTaskAsync() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ AsyncFib g = new AsyncFib(9);
+ assertSame(g, g.fork());
+ AsyncFib f = new AsyncFib(8);
+ assertSame(f, f.fork());
+ assertSame(g, pollTask());
+ helpQuiesce();
+ f.checkCompletedNormally();
+ checkNotDone(g);
+ }};
+ testInvokeOnPool(asyncSingletonPool(), a);
+ }
+
+ /**
+ * ForkJoinTask.quietlyComplete returns when task completes
+ * normally without setting a value. The most recent value
+ * established by setRawResult(V) (or null by default) is returned
+ * from invoke.
+ */
+ public void testQuietlyComplete() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ AsyncFib f = new AsyncFib(8);
+ f.quietlyComplete();
+ assertEquals(8, f.number);
+ assertTrue(f.isDone());
+ assertFalse(f.isCancelled());
+ assertTrue(f.isCompletedNormally());
+ assertFalse(f.isCompletedAbnormally());
+ assertNull(f.getException());
+ }};
+ testInvokeOnPool(mainPool(), a);
+ }
+
+ // jdk9
+
+ /**
+ * pollSubmission returns unexecuted submitted task, if present
+ */
+ public void testPollSubmission() {
+ final CountDownLatch done = new CountDownLatch(1);
+ final ForkJoinTask a = ForkJoinTask.adapt(awaiter(done));
+ final ForkJoinTask b = ForkJoinTask.adapt(awaiter(done));
+ final ForkJoinTask c = ForkJoinTask.adapt(awaiter(done));
+ final ForkJoinPool p = singletonPool();
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ Thread external = new Thread(new CheckedRunnable() {
+ public void realRun() {
+ p.execute(a);
+ p.execute(b);
+ p.execute(c);
+ }});
+ RecursiveAction s = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ external.start();
+ try {
+ external.join();
+ } catch (Exception ex) {
+ threadUnexpectedException(ex);
+ }
+ assertTrue(p.hasQueuedSubmissions());
+ assertTrue(Thread.currentThread() instanceof ForkJoinWorkerThread);
+ ForkJoinTask r = ForkJoinTask.pollSubmission();
+ assertTrue(r == a || r == b || r == c);
+ assertFalse(r.isDone());
+ }};
+ p.invoke(s);
+ }
+ }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ForkJoinTaskTest.java b/jsr166-tests/src/test/java/jsr166/ForkJoinTaskTest.java
index 3c1fcb7..1616d4f 100644
--- a/jsr166-tests/src/test/java/jsr166/ForkJoinTaskTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ForkJoinTaskTest.java
@@ -9,7 +9,10 @@
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
@@ -30,7 +33,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(ForkJoinTaskTest.class);
// }
// Runs with "mainPool" use > 1 thread. singletonPool tests use 1
@@ -52,7 +55,7 @@
}
private void testInvokeOnPool(ForkJoinPool pool, RecursiveAction a) {
- try {
+ try (PoolCleaner cleaner = cleaner(pool)) {
assertFalse(a.isDone());
assertFalse(a.isCompletedNormally());
assertFalse(a.isCompletedAbnormally());
@@ -68,8 +71,6 @@
assertFalse(a.isCancelled());
assertNull(a.getException());
assertNull(a.getRawResult());
- } finally {
- joinPool(pool);
}
}
@@ -102,17 +103,17 @@
{
Thread.currentThread().interrupt();
- long t0 = System.nanoTime();
+ long startTime = System.nanoTime();
assertSame(expected, a.join());
- assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
+ assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
Thread.interrupted();
}
{
Thread.currentThread().interrupt();
- long t0 = System.nanoTime();
+ long startTime = System.nanoTime();
a.quietlyJoin(); // should be no-op
- assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
+ assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
Thread.interrupted();
}
@@ -145,9 +146,9 @@
Thread.interrupted();
{
- long t0 = System.nanoTime();
+ long startTime = System.nanoTime();
a.quietlyJoin(); // should be no-op
- assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
+ assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
}
try {
@@ -183,9 +184,9 @@
Thread.interrupted();
{
- long t0 = System.nanoTime();
+ long startTime = System.nanoTime();
a.quietlyJoin(); // should be no-op
- assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
+ assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
}
try {
@@ -222,9 +223,9 @@
AtomicIntegerFieldUpdater.newUpdater(BinaryAsyncAction.class,
"controlState");
- private BinaryAsyncAction parent;
+ private volatile BinaryAsyncAction parent;
- private BinaryAsyncAction sibling;
+ private volatile BinaryAsyncAction sibling;
protected BinaryAsyncAction() {
}
@@ -259,6 +260,14 @@
super.completeExceptionally(ex);
}
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ if (super.cancel(mayInterruptIfRunning)) {
+ completeExceptionally(new FJException());
+ return true;
+ }
+ return false;
+ }
+
public final void complete() {
BinaryAsyncAction a = this;
for (;;) {
@@ -280,13 +289,12 @@
}
public final void completeExceptionally(Throwable ex) {
- BinaryAsyncAction a = this;
- while (!a.isCompletedAbnormally()) {
+ for (BinaryAsyncAction a = this;;) {
a.completeThisExceptionally(ex);
BinaryAsyncAction s = a.sibling;
- if (s != null)
- s.cancel(false);
- if (!a.onException() || (a = a.parent) == null)
+ if (s != null && !s.isDone())
+ s.completeExceptionally(ex);
+ if ((a = a.parent) == null)
break;
}
}
@@ -336,15 +344,12 @@
public final boolean exec() {
AsyncFib f = this;
int n = f.number;
- if (n > 1) {
- while (n > 1) {
- AsyncFib p = f;
- AsyncFib r = new AsyncFib(n - 2);
- f = new AsyncFib(--n);
- p.linkSubtasks(r, f);
- r.fork();
- }
- f.number = n;
+ while (n > 1) {
+ AsyncFib p = f;
+ AsyncFib r = new AsyncFib(n - 2);
+ f = new AsyncFib(--n);
+ p.linkSubtasks(r, f);
+ r.fork();
}
f.complete();
return false;
@@ -364,15 +369,12 @@
public final boolean exec() {
FailingAsyncFib f = this;
int n = f.number;
- if (n > 1) {
- while (n > 1) {
- FailingAsyncFib p = f;
- FailingAsyncFib r = new FailingAsyncFib(n - 2);
- f = new FailingAsyncFib(--n);
- p.linkSubtasks(r, f);
- r.fork();
- }
- f.number = n;
+ while (n > 1) {
+ FailingAsyncFib p = f;
+ FailingAsyncFib r = new FailingAsyncFib(n - 2);
+ f = new FailingAsyncFib(--n);
+ p.linkSubtasks(r, f);
+ r.fork();
}
f.complete();
return false;
@@ -778,6 +780,27 @@
}
/**
+ * completeExceptionally(null) surprisingly has the same effect as
+ * completeExceptionally(new RuntimeException())
+ */
+ public void testCompleteExceptionally_null() {
+ RecursiveAction a = new CheckedRecursiveAction() {
+ protected void realCompute() {
+ AsyncFib f = new AsyncFib(8);
+ f.completeExceptionally(null);
+ try {
+ f.invoke();
+ shouldThrow();
+ } catch (RuntimeException success) {
+ assertSame(success.getClass(), RuntimeException.class);
+ assertNull(success.getCause());
+ checkCompletedAbnormally(f, success);
+ }
+ }};
+ testInvokeOnPool(mainPool(), a);
+ }
+
+ /**
* invokeAll(t1, t2) invokes all task arguments
*/
public void testInvokeAll2() {
@@ -877,8 +900,10 @@
protected void realCompute() {
AsyncFib f = new AsyncFib(8);
FailingAsyncFib g = new FailingAsyncFib(9);
+ ForkJoinTask[] tasks = { f, g };
+ Collections.shuffle(Arrays.asList(tasks));
try {
- invokeAll(f, g);
+ invokeAll(tasks);
shouldThrow();
} catch (FJException success) {
checkCompletedAbnormally(g, success);
@@ -913,8 +938,10 @@
AsyncFib f = new AsyncFib(8);
FailingAsyncFib g = new FailingAsyncFib(9);
AsyncFib h = new AsyncFib(7);
+ ForkJoinTask[] tasks = { f, g, h };
+ Collections.shuffle(Arrays.asList(tasks));
try {
- invokeAll(f, g, h);
+ invokeAll(tasks);
shouldThrow();
} catch (FJException success) {
checkCompletedAbnormally(g, success);
@@ -932,12 +959,11 @@
FailingAsyncFib f = new FailingAsyncFib(8);
AsyncFib g = new AsyncFib(9);
AsyncFib h = new AsyncFib(7);
- HashSet set = new HashSet();
- set.add(f);
- set.add(g);
- set.add(h);
+ ForkJoinTask[] tasks = { f, g, h };
+ List taskList = Arrays.asList(tasks);
+ Collections.shuffle(taskList);
try {
- invokeAll(set);
+ invokeAll(taskList);
shouldThrow();
} catch (FJException success) {
checkCompletedAbnormally(f, success);
@@ -1544,8 +1570,10 @@
protected void realCompute() {
AsyncFib f = new AsyncFib(8);
FailingAsyncFib g = new FailingAsyncFib(9);
+ ForkJoinTask[] tasks = { f, g };
+ Collections.shuffle(Arrays.asList(tasks));
try {
- invokeAll(f, g);
+ invokeAll(tasks);
shouldThrow();
} catch (FJException success) {
checkCompletedAbnormally(g, success);
@@ -1580,8 +1608,10 @@
AsyncFib f = new AsyncFib(8);
FailingAsyncFib g = new FailingAsyncFib(9);
AsyncFib h = new AsyncFib(7);
+ ForkJoinTask[] tasks = { f, g, h };
+ Collections.shuffle(Arrays.asList(tasks));
try {
- invokeAll(f, g, h);
+ invokeAll(tasks);
shouldThrow();
} catch (FJException success) {
checkCompletedAbnormally(g, success);
@@ -1599,12 +1629,11 @@
FailingAsyncFib f = new FailingAsyncFib(8);
AsyncFib g = new AsyncFib(9);
AsyncFib h = new AsyncFib(7);
- HashSet set = new HashSet();
- set.add(f);
- set.add(g);
- set.add(h);
+ ForkJoinTask[] tasks = { f, g, h };
+ List taskList = Arrays.asList(tasks);
+ Collections.shuffle(taskList);
try {
- invokeAll(set);
+ invokeAll(taskList);
shouldThrow();
} catch (FJException success) {
checkCompletedAbnormally(f, success);
diff --git a/jsr166-tests/src/test/java/jsr166/FutureTaskTest.java b/jsr166-tests/src/test/java/jsr166/FutureTaskTest.java
index a5d8c46..44d12b3 100644
--- a/jsr166-tests/src/test/java/jsr166/FutureTaskTest.java
+++ b/jsr166-tests/src/test/java/jsr166/FutureTaskTest.java
@@ -38,7 +38,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(FutureTaskTest.class);
// }
void checkIsDone(Future<?> f) {
@@ -272,8 +272,8 @@
for (int i = 0; i < 3; i++) {
assertTrue(task.runAndReset());
checkNotDone(task);
- assertEquals(i+1, task.runCount());
- assertEquals(i+1, task.runAndResetCount());
+ assertEquals(i + 1, task.runCount());
+ assertEquals(i + 1, task.runAndResetCount());
assertEquals(0, task.setCount());
assertEquals(0, task.setExceptionCount());
}
@@ -289,7 +289,7 @@
for (int i = 0; i < 3; i++) {
assertFalse(task.runAndReset());
assertEquals(0, task.runCount());
- assertEquals(i+1, task.runAndResetCount());
+ assertEquals(i + 1, task.runAndResetCount());
assertEquals(0, task.setCount());
assertEquals(0, task.setExceptionCount());
}
diff --git a/jsr166-tests/src/test/java/jsr166/JSR166TestCase.java b/jsr166-tests/src/test/java/jsr166/JSR166TestCase.java
index 46be906..fc1632c 100644
--- a/jsr166-tests/src/test/java/jsr166/JSR166TestCase.java
+++ b/jsr166-tests/src/test/java/jsr166/JSR166TestCase.java
@@ -6,17 +6,21 @@
* Pat Fisher, Mike Judd.
*/
+
package jsr166;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
+import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
-import java.security.CodeSource;
+import java.lang.reflect.Modifier;
+ import java.security.CodeSource;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Permissions;
@@ -35,7 +39,10 @@
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.RecursiveTask;
@@ -44,12 +51,15 @@
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.regex.Matcher;
import java.util.regex.Pattern;
import junit.framework.AssertionFailedError;
import junit.framework.Test;
import junit.framework.TestCase;
+import junit.framework.TestResult;
import junit.framework.TestSuite;
/**
@@ -62,18 +72,18 @@
*
* <ol>
*
- * <li> All assertions in code running in generated threads must use
+ * <li>All assertions in code running in generated threads must use
* the forms {@link #threadFail}, {@link #threadAssertTrue}, {@link
* #threadAssertEquals}, or {@link #threadAssertNull}, (not
* {@code fail}, {@code assertTrue}, etc.) It is OK (but not
* particularly recommended) for other code to use these forms too.
* Only the most typically used JUnit assertion methods are defined
- * this way, but enough to live with.</li>
+ * this way, but enough to live with.
*
- * <li> If you override {@link #setUp} or {@link #tearDown}, make sure
+ * <li>If you override {@link #setUp} or {@link #tearDown}, make sure
* to invoke {@code super.setUp} and {@code super.tearDown} within
* them. These methods are used to clear and check for thread
- * assertion failures.</li>
+ * assertion failures.
*
* <li>All delays and timeouts must use one of the constants {@code
* SHORT_DELAY_MS}, {@code SMALL_DELAY_MS}, {@code MEDIUM_DELAY_MS},
@@ -84,51 +94,480 @@
* is always discriminable as larger than SHORT and smaller than
* MEDIUM. And so on. These constants are set to conservative values,
* but even so, if there is ever any doubt, they can all be increased
- * in one spot to rerun tests on slower platforms.</li>
+ * in one spot to rerun tests on slower platforms.
*
- * <li> All threads generated must be joined inside each test case
+ * <li>All threads generated must be joined inside each test case
* method (or {@code fail} to do so) before returning from the
* method. The {@code joinPool} method can be used to do this when
- * using Executors.</li>
+ * using Executors.
*
* </ol>
*
* <p><b>Other notes</b>
* <ul>
*
- * <li> Usually, there is one testcase method per JSR166 method
+ * <li>Usually, there is one testcase method per JSR166 method
* covering "normal" operation, and then as many exception-testing
* methods as there are exceptions the method can throw. Sometimes
* there are multiple tests per JSR166 method when the different
* "normal" behaviors differ significantly. And sometimes testcases
- * cover multiple methods when they cannot be tested in
- * isolation.</li>
+ * cover multiple methods when they cannot be tested in isolation.
*
- * <li> The documentation style for testcases is to provide as javadoc
+ * <li>The documentation style for testcases is to provide as javadoc
* a simple sentence or two describing the property that the testcase
* method purports to test. The javadocs do not say anything about how
- * the property is tested. To find out, read the code.</li>
+ * the property is tested. To find out, read the code.
*
- * <li> These tests are "conformance tests", and do not attempt to
+ * <li>These tests are "conformance tests", and do not attempt to
* test throughput, latency, scalability or other performance factors
* (see the separate "jtreg" tests for a set intended to check these
* for the most central aspects of functionality.) So, most tests use
* the smallest sensible numbers of threads, collection sizes, etc
- * needed to check basic conformance.</li>
+ * needed to check basic conformance.
*
* <li>The test classes currently do not declare inclusion in
* any particular package to simplify things for people integrating
- * them in TCK test suites.</li>
+ * them in TCK test suites.
*
- * <li> As a convenience, the {@code main} of this class (JSR166TestCase)
- * runs all JSR166 unit tests.</li>
+ * <li>As a convenience, the {@code main} of this class (JSR166TestCase)
+ * runs all JSR166 unit tests.
*
* </ul>
*/
public class JSR166TestCase extends TestCase {
- // Delays for timing-dependent tests, in milliseconds.
+ private static final boolean useSecurityManager =
+ Boolean.getBoolean("jsr166.useSecurityManager");
- protected static final boolean expensiveTests = false;
+ protected static final boolean expensiveTests =
+ Boolean.getBoolean("jsr166.expensiveTests");
+
+ /**
+ * If true, also run tests that are not part of the official tck
+ * because they test unspecified implementation details.
+ */
+ protected static final boolean testImplementationDetails =
+ Boolean.getBoolean("jsr166.testImplementationDetails");
+
+ /**
+ * If true, report on stdout all "slow" tests, that is, ones that
+ * take more than profileThreshold milliseconds to execute.
+ */
+ private static final boolean profileTests =
+ Boolean.getBoolean("jsr166.profileTests");
+
+ /**
+ * The number of milliseconds that tests are permitted for
+ * execution without being reported, when profileTests is set.
+ */
+ private static final long profileThreshold =
+ Long.getLong("jsr166.profileThreshold", 100);
+
+ /**
+ * The number of repetitions per test (for tickling rare bugs).
+ */
+ private static final int runsPerTest =
+ Integer.getInteger("jsr166.runsPerTest", 1);
+
+ /**
+ * The number of repetitions of the test suite (for finding leaks?).
+ */
+ private static final int suiteRuns =
+ Integer.getInteger("jsr166.suiteRuns", 1);
+
+ private static float systemPropertyValue(String name, float defaultValue) {
+ String floatString = System.getProperty(name);
+ if (floatString == null)
+ return defaultValue;
+ try {
+ return Float.parseFloat(floatString);
+ } catch (NumberFormatException ex) {
+ throw new IllegalArgumentException(
+ String.format("Bad float value in system property %s=%s",
+ name, floatString));
+ }
+ }
+
+ /**
+ * The scaling factor to apply to standard delays used in tests.
+ */
+ private static final float delayFactor =
+ systemPropertyValue("jsr166.delay.factor", 1.0f);
+
+ /**
+ * The timeout factor as used in the jtreg test harness.
+ * See: http://openjdk.java.net/jtreg/tag-spec.html
+ */
+ private static final float jtregTestTimeoutFactor
+ = systemPropertyValue("test.timeout.factor", 1.0f);
+
+ public JSR166TestCase() { super(); }
+ public JSR166TestCase(String name) { super(name); }
+
+ /**
+ * A filter for tests to run, matching strings of the form
+ * methodName(className), e.g. "testInvokeAll5(ForkJoinPoolTest)"
+ * Usefully combined with jsr166.runsPerTest.
+ */
+ private static final Pattern methodFilter = methodFilter();
+
+ private static Pattern methodFilter() {
+ String regex = System.getProperty("jsr166.methodFilter");
+ return (regex == null) ? null : Pattern.compile(regex);
+ }
+
+ // Instrumentation to debug very rare, but very annoying hung test runs.
+ static volatile TestCase currentTestCase;
+ // static volatile int currentRun = 0;
+ static {
+ Runnable checkForWedgedTest = new Runnable() { public void run() {
+ // Avoid spurious reports with enormous runsPerTest.
+ // A single test case run should never take more than 1 second.
+ // But let's cap it at the high end too ...
+ final int timeoutMinutes =
+ Math.min(15, Math.max(runsPerTest / 60, 1));
+ for (TestCase lastTestCase = currentTestCase;;) {
+ try { MINUTES.sleep(timeoutMinutes); }
+ catch (InterruptedException unexpected) { break; }
+ if (lastTestCase == currentTestCase) {
+ System.err.printf(
+ "Looks like we're stuck running test: %s%n",
+ lastTestCase);
+// System.err.printf(
+// "Looks like we're stuck running test: %s (%d/%d)%n",
+// lastTestCase, currentRun, runsPerTest);
+// System.err.println("availableProcessors=" +
+// Runtime.getRuntime().availableProcessors());
+// System.err.printf("cpu model = %s%n", cpuModel());
+ dumpTestThreads();
+ // one stack dump is probably enough; more would be spam
+ break;
+ }
+ lastTestCase = currentTestCase;
+ }}};
+ Thread thread = new Thread(checkForWedgedTest, "checkForWedgedTest");
+ thread.setDaemon(true);
+ thread.start();
+ }
+
+// public static String cpuModel() {
+// try {
+// Matcher matcher = Pattern.compile("model name\\s*: (.*)")
+// .matcher(new String(
+// Files.readAllBytes(Paths.get("/proc/cpuinfo")), "UTF-8"));
+// matcher.find();
+// return matcher.group(1);
+// } catch (Exception ex) { return null; }
+// }
+
+ public void runBare() throws Throwable {
+ currentTestCase = this;
+ if (methodFilter == null
+ || methodFilter.matcher(toString()).find())
+ super.runBare();
+ }
+
+ protected void runTest() throws Throwable {
+ for (int i = 0; i < runsPerTest; i++) {
+ // currentRun = i;
+ if (profileTests)
+ runTestProfiled();
+ else
+ super.runTest();
+ }
+ }
+
+ protected void runTestProfiled() throws Throwable {
+ for (int i = 0; i < 2; i++) {
+ long startTime = System.nanoTime();
+ super.runTest();
+ long elapsedMillis = millisElapsedSince(startTime);
+ if (elapsedMillis < profileThreshold)
+ break;
+ // Never report first run of any test; treat it as a
+ // warmup run, notably to trigger all needed classloading,
+ if (i > 0)
+ System.out.printf("%n%s: %d%n", toString(), elapsedMillis);
+ }
+ }
+
+ /**
+ * Runs all JSR166 unit tests using junit.textui.TestRunner.
+ */
+ // android-note: Removed because no junit.textui
+ // public static void main(String[] args) {
+ // main(suite(), args);
+ // }
+
+ // static class PithyResultPrinter extends junit.textui.ResultPrinter {
+ // PithyResultPrinter(java.io.PrintStream writer) { super(writer); }
+ // long runTime;
+ // public void startTest(Test test) {}
+ // protected void printHeader(long runTime) {
+ // this.runTime = runTime; // defer printing for later
+ // }
+ // protected void printFooter(TestResult result) {
+ // if (result.wasSuccessful()) {
+ // getWriter().println("OK (" + result.runCount() + " tests)"
+ // + " Time: " + elapsedTimeAsString(runTime));
+ // } else {
+ // getWriter().println("Time: " + elapsedTimeAsString(runTime));
+ // super.printFooter(result);
+ // }
+ // }
+ // }
+
+ /**
+ * Returns a TestRunner that doesn't bother with unnecessary
+ * fluff, like printing a "." for each test case.
+ */
+ // static junit.textui.TestRunner newPithyTestRunner() {
+ // junit.textui.TestRunner runner = new junit.textui.TestRunner();
+ // runner.setPrinter(new PithyResultPrinter(System.out));
+ // return runner;
+ // }
+
+ /**
+ * Runs all unit tests in the given test suite.
+ * Actual behavior influenced by jsr166.* system properties.
+ */
+ // static void main(Test suite, String[] args) {
+ // if (useSecurityManager) {
+ // System.err.println("Setting a permissive security manager");
+ // Policy.setPolicy(permissivePolicy());
+ // System.setSecurityManager(new SecurityManager());
+ // }
+ // for (int i = 0; i < suiteRuns; i++) {
+ // TestResult result = newPithyTestRunner().doRun(suite);
+ // if (!result.wasSuccessful())
+ // System.exit(1);
+ // System.gc();
+ // System.runFinalization();
+ // }
+ // }
+
+ public static TestSuite newTestSuite(Object... suiteOrClasses) {
+ TestSuite suite = new TestSuite();
+ for (Object suiteOrClass : suiteOrClasses) {
+ if (suiteOrClass instanceof TestSuite)
+ suite.addTest((TestSuite) suiteOrClass);
+ else if (suiteOrClass instanceof Class)
+ suite.addTest(new TestSuite((Class<?>) suiteOrClass));
+ else
+ throw new ClassCastException("not a test suite or class");
+ }
+ return suite;
+ }
+
+ public static void addNamedTestClasses(TestSuite suite,
+ String... testClassNames) {
+ for (String testClassName : testClassNames) {
+ try {
+ Class<?> testClass = Class.forName(testClassName);
+ Method m = testClass.getDeclaredMethod("suite",
+ new Class<?>[0]);
+ suite.addTest(newTestSuite((Test)m.invoke(null)));
+ } catch (Exception e) {
+ throw new Error("Missing test class", e);
+ }
+ }
+ }
+
+ public static final double JAVA_CLASS_VERSION;
+ public static final String JAVA_SPECIFICATION_VERSION;
+ static {
+ try {
+ JAVA_CLASS_VERSION = java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<Double>() {
+ public Double run() {
+ return Double.valueOf(System.getProperty("java.class.version"));}});
+ JAVA_SPECIFICATION_VERSION = java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<String>() {
+ public String run() {
+ return System.getProperty("java.specification.version");}});
+ } catch (Throwable t) {
+ throw new Error(t);
+ }
+ }
+
+ public static boolean atLeastJava6() { return JAVA_CLASS_VERSION >= 50.0; }
+ public static boolean atLeastJava7() { return JAVA_CLASS_VERSION >= 51.0; }
+ public static boolean atLeastJava8() { return JAVA_CLASS_VERSION >= 52.0; }
+ public static boolean atLeastJava9() {
+ return JAVA_CLASS_VERSION >= 53.0
+ // As of 2015-09, java9 still uses 52.0 class file version
+ || JAVA_SPECIFICATION_VERSION.matches("^(1\\.)?(9|[0-9][0-9])$");
+ }
+ public static boolean atLeastJava10() {
+ return JAVA_CLASS_VERSION >= 54.0
+ || JAVA_SPECIFICATION_VERSION.matches("^(1\\.)?[0-9][0-9]$");
+ }
+
+ /**
+ * Collects all JSR166 unit tests as one suite.
+ */
+ // android-note: Removed because the CTS runner does a bad job of
+ // public static Test suite() {
+ // // Java7+ test classes
+ // TestSuite suite = newTestSuite(
+ // ForkJoinPoolTest.suite(),
+ // ForkJoinTaskTest.suite(),
+ // RecursiveActionTest.suite(),
+ // RecursiveTaskTest.suite(),
+ // LinkedTransferQueueTest.suite(),
+ // PhaserTest.suite(),
+ // ThreadLocalRandomTest.suite(),
+ // AbstractExecutorServiceTest.suite(),
+ // AbstractQueueTest.suite(),
+ // AbstractQueuedSynchronizerTest.suite(),
+ // AbstractQueuedLongSynchronizerTest.suite(),
+ // ArrayBlockingQueueTest.suite(),
+ // ArrayDequeTest.suite(),
+ // AtomicBooleanTest.suite(),
+ // AtomicIntegerArrayTest.suite(),
+ // AtomicIntegerFieldUpdaterTest.suite(),
+ // AtomicIntegerTest.suite(),
+ // AtomicLongArrayTest.suite(),
+ // AtomicLongFieldUpdaterTest.suite(),
+ // AtomicLongTest.suite(),
+ // AtomicMarkableReferenceTest.suite(),
+ // AtomicReferenceArrayTest.suite(),
+ // AtomicReferenceFieldUpdaterTest.suite(),
+ // AtomicReferenceTest.suite(),
+ // AtomicStampedReferenceTest.suite(),
+ // ConcurrentHashMapTest.suite(),
+ // ConcurrentLinkedDequeTest.suite(),
+ // ConcurrentLinkedQueueTest.suite(),
+ // ConcurrentSkipListMapTest.suite(),
+ // ConcurrentSkipListSubMapTest.suite(),
+ // ConcurrentSkipListSetTest.suite(),
+ // ConcurrentSkipListSubSetTest.suite(),
+ // CopyOnWriteArrayListTest.suite(),
+ // CopyOnWriteArraySetTest.suite(),
+ // CountDownLatchTest.suite(),
+ // CyclicBarrierTest.suite(),
+ // DelayQueueTest.suite(),
+ // EntryTest.suite(),
+ // ExchangerTest.suite(),
+ // ExecutorsTest.suite(),
+ // ExecutorCompletionServiceTest.suite(),
+ // FutureTaskTest.suite(),
+ // LinkedBlockingDequeTest.suite(),
+ // LinkedBlockingQueueTest.suite(),
+ // LinkedListTest.suite(),
+ // LockSupportTest.suite(),
+ // PriorityBlockingQueueTest.suite(),
+ // PriorityQueueTest.suite(),
+ // ReentrantLockTest.suite(),
+ // ReentrantReadWriteLockTest.suite(),
+ // ScheduledExecutorTest.suite(),
+ // ScheduledExecutorSubclassTest.suite(),
+ // SemaphoreTest.suite(),
+ // SynchronousQueueTest.suite(),
+ // SystemTest.suite(),
+ // ThreadLocalTest.suite(),
+ // ThreadPoolExecutorTest.suite(),
+ // ThreadPoolExecutorSubclassTest.suite(),
+ // ThreadTest.suite(),
+ // TimeUnitTest.suite(),
+ // TreeMapTest.suite(),
+ // TreeSetTest.suite(),
+ // TreeSubMapTest.suite(),
+ // TreeSubSetTest.suite());
+
+ // // Java8+ test classes
+ // if (atLeastJava8()) {
+ // String[] java8TestClassNames = {
+ // "Atomic8Test",
+ // "CompletableFutureTest",
+ // "ConcurrentHashMap8Test",
+ // "CountedCompleterTest",
+ // "DoubleAccumulatorTest",
+ // "DoubleAdderTest",
+ // "ForkJoinPool8Test",
+ // "ForkJoinTask8Test",
+ // "LongAccumulatorTest",
+ // "LongAdderTest",
+ // "SplittableRandomTest",
+ // "StampedLockTest",
+ // "SubmissionPublisherTest",
+ // "ThreadLocalRandom8Test",
+ // };
+ // addNamedTestClasses(suite, java8TestClassNames);
+ // }
+
+ // // Java9+ test classes
+ // if (atLeastJava9()) {
+ // String[] java9TestClassNames = {
+ // // Currently empty, but expecting varhandle tests
+ // };
+ // addNamedTestClasses(suite, java9TestClassNames);
+ // }
+
+ // return suite;
+ // }
+
+ /** Returns list of junit-style test method names in given class. */
+ public static ArrayList<String> testMethodNames(Class<?> testClass) {
+ Method[] methods = testClass.getDeclaredMethods();
+ ArrayList<String> names = new ArrayList<String>(methods.length);
+ for (Method method : methods) {
+ if (method.getName().startsWith("test")
+ && Modifier.isPublic(method.getModifiers())
+ // method.getParameterCount() requires jdk8+
+ && method.getParameterTypes().length == 0) {
+ names.add(method.getName());
+ }
+ }
+ return names;
+ }
+
+ /**
+ * Returns junit-style testSuite for the given test class, but
+ * parameterized by passing extra data to each test.
+ */
+ public static <ExtraData> Test parameterizedTestSuite
+ (Class<? extends JSR166TestCase> testClass,
+ Class<ExtraData> dataClass,
+ ExtraData data) {
+ try {
+ TestSuite suite = new TestSuite();
+ Constructor c =
+ testClass.getDeclaredConstructor(dataClass, String.class);
+ for (String methodName : testMethodNames(testClass))
+ suite.addTest((Test) c.newInstance(data, methodName));
+ return suite;
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+
+ /**
+ * Returns junit-style testSuite for the jdk8 extension of the
+ * given test class, but parameterized by passing extra data to
+ * each test. Uses reflection to allow compilation in jdk7.
+ */
+ public static <ExtraData> Test jdk8ParameterizedTestSuite
+ (Class<? extends JSR166TestCase> testClass,
+ Class<ExtraData> dataClass,
+ ExtraData data) {
+ if (atLeastJava8()) {
+ String name = testClass.getName();
+ String name8 = name.replaceAll("Test$", "8Test");
+ if (name.equals(name8)) throw new Error(name);
+ try {
+ return (Test)
+ Class.forName(name8)
+ .getMethod("testSuite", new Class[] { dataClass })
+ .invoke(null, data);
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ } else {
+ return new TestSuite();
+ }
+ }
+
+ // Delays for timing-dependent tests, in milliseconds.
public static long SHORT_DELAY_MS;
public static long SMALL_DELAY_MS;
@@ -136,11 +575,13 @@
public static long LONG_DELAY_MS;
/**
- * Returns the shortest timed delay. This could
- * be reimplemented to use for example a Property.
+ * Returns the shortest timed delay. This can be scaled up for
+ * slow machines using the jsr166.delay.factor system property,
+ * or via jtreg's -timeoutFactor: flag.
+ * http://openjdk.java.net/jtreg/command-help.html
*/
protected long getShortDelay() {
- return 50;
+ return (long) (50 * delayFactor * jtregTestTimeoutFactor);
}
/**
@@ -162,11 +603,12 @@
}
/**
- * Returns a new Date instance representing a time delayMillis
- * milliseconds in the future.
+ * Returns a new Date instance representing a time at least
+ * delayMillis milliseconds in the future.
*/
Date delayedDate(long delayMillis) {
- return new Date(System.currentTimeMillis() + delayMillis);
+ // Add 1 because currentTimeMillis is known to round into the past.
+ return new Date(System.currentTimeMillis() + delayMillis + 1);
}
/**
@@ -182,6 +624,8 @@
* the same test have no effect.
*/
public void threadRecordFailure(Throwable t) {
+ System.err.println(t);
+ dumpTestThreads();
threadFailure.compareAndSet(null, t);
}
@@ -189,6 +633,13 @@
setDelays();
}
+ void tearDownFail(String format, Object... args) {
+ String msg = toString() + ": " + String.format(format, args);
+ System.err.println(msg);
+ dumpTestThreads();
+ throw new AssertionFailedError(msg);
+ }
+
/**
* Extra checks that get done for all test cases.
*
@@ -216,16 +667,16 @@
}
if (Thread.interrupted())
- throw new AssertionFailedError("interrupt status set in main thread");
+ tearDownFail("interrupt status set in main thread");
checkForkJoinPoolThreadLeaks();
}
/**
- * Finds missing try { ... } finally { joinPool(e); }
+ * Finds missing PoolCleaners
*/
void checkForkJoinPoolThreadLeaks() throws InterruptedException {
- Thread[] survivors = new Thread[5];
+ Thread[] survivors = new Thread[7];
int count = Thread.enumerate(survivors);
for (int i = 0; i < count; i++) {
Thread thread = survivors[i];
@@ -233,13 +684,15 @@
if (name.startsWith("ForkJoinPool-")) {
// give thread some time to terminate
thread.join(LONG_DELAY_MS);
- if (!thread.isAlive()) continue;
- thread.stop();
- throw new AssertionFailedError
- (String.format("Found leaked ForkJoinPool thread test=%s thread=%s%n",
- toString(), name));
+ if (thread.isAlive())
+ tearDownFail("Found leaked ForkJoinPool thread thread=%s",
+ thread);
}
}
+
+ if (!ForkJoinPool.commonPool()
+ .awaitQuiescence(LONG_DELAY_MS, MILLISECONDS))
+ tearDownFail("ForkJoin common pool thread stuck");
}
/**
@@ -252,7 +705,7 @@
fail(reason);
} catch (AssertionFailedError t) {
threadRecordFailure(t);
- fail(reason);
+ throw t;
}
}
@@ -379,44 +832,148 @@
/**
* Delays, via Thread.sleep, for the given millisecond delay, but
* if the sleep is shorter than specified, may re-sleep or yield
- * until time elapses.
+ * until time elapses. Ensures that the given time, as measured
+ * by System.nanoTime(), has elapsed.
*/
static void delay(long millis) throws InterruptedException {
- long startTime = System.nanoTime();
- long ns = millis * 1000 * 1000;
- for (;;) {
+ long nanos = millis * (1000 * 1000);
+ final long wakeupTime = System.nanoTime() + nanos;
+ do {
if (millis > 0L)
Thread.sleep(millis);
else // too short to sleep
Thread.yield();
- long d = ns - (System.nanoTime() - startTime);
- if (d > 0L)
- millis = d / (1000 * 1000);
- else
- break;
+ nanos = wakeupTime - System.nanoTime();
+ millis = nanos / (1000 * 1000);
+ } while (nanos >= 0L);
+ }
+
+ /**
+ * Allows use of try-with-resources with per-test thread pools.
+ */
+ class PoolCleaner implements AutoCloseable {
+ private final ExecutorService pool;
+ public PoolCleaner(ExecutorService pool) { this.pool = pool; }
+ public void close() { joinPool(pool); }
+ }
+
+ /**
+ * An extension of PoolCleaner that has an action to release the pool.
+ */
+ class PoolCleanerWithReleaser extends PoolCleaner {
+ private final Runnable releaser;
+ public PoolCleanerWithReleaser(ExecutorService pool, Runnable releaser) {
+ super(pool);
+ this.releaser = releaser;
}
+ public void close() {
+ try {
+ releaser.run();
+ } finally {
+ super.close();
+ }
+ }
+ }
+
+ PoolCleaner cleaner(ExecutorService pool) {
+ return new PoolCleaner(pool);
+ }
+
+ PoolCleaner cleaner(ExecutorService pool, Runnable releaser) {
+ return new PoolCleanerWithReleaser(pool, releaser);
+ }
+
+ PoolCleaner cleaner(ExecutorService pool, CountDownLatch latch) {
+ return new PoolCleanerWithReleaser(pool, releaser(latch));
+ }
+
+ Runnable releaser(final CountDownLatch latch) {
+ return new Runnable() { public void run() {
+ do { latch.countDown(); }
+ while (latch.getCount() > 0);
+ }};
+ }
+
+ PoolCleaner cleaner(ExecutorService pool, AtomicBoolean flag) {
+ return new PoolCleanerWithReleaser(pool, releaser(flag));
+ }
+
+ Runnable releaser(final AtomicBoolean flag) {
+ return new Runnable() { public void run() { flag.set(true); }};
}
/**
* Waits out termination of a thread pool or fails doing so.
*/
- void joinPool(ExecutorService exec) {
+ void joinPool(ExecutorService pool) {
try {
- exec.shutdown();
- if (!exec.awaitTermination(2 * LONG_DELAY_MS, MILLISECONDS))
- fail("ExecutorService " + exec +
- " did not terminate in a timely manner");
+ pool.shutdown();
+ if (!pool.awaitTermination(2 * LONG_DELAY_MS, MILLISECONDS)) {
+ try {
+ threadFail("ExecutorService " + pool +
+ " did not terminate in a timely manner");
+ } finally {
+ // last resort, for the benefit of subsequent tests
+ pool.shutdownNow();
+ pool.awaitTermination(MEDIUM_DELAY_MS, MILLISECONDS);
+ }
+ }
} catch (SecurityException ok) {
// Allowed in case test doesn't have privs
} catch (InterruptedException fail) {
- fail("Unexpected InterruptedException");
+ threadFail("Unexpected InterruptedException");
+ }
+ }
+
+ /** Like Runnable, but with the freedom to throw anything */
+ interface Action { public void run() throws Throwable; }
+
+ /**
+ * Runs all the given actions in parallel, failing if any fail.
+ * Useful for running multiple variants of tests that are
+ * necessarily individually slow because they must block.
+ */
+ void testInParallel(Action ... actions) {
+ ExecutorService pool = Executors.newCachedThreadPool();
+ try (PoolCleaner cleaner = cleaner(pool)) {
+ ArrayList<Future<?>> futures = new ArrayList<>(actions.length);
+ for (final Action action : actions)
+ futures.add(pool.submit(new CheckedRunnable() {
+ public void realRun() throws Throwable { action.run();}}));
+ for (Future<?> future : futures)
+ try {
+ assertNull(future.get(LONG_DELAY_MS, MILLISECONDS));
+ } catch (ExecutionException ex) {
+ threadUnexpectedException(ex.getCause());
+ } catch (Exception ex) {
+ threadUnexpectedException(ex);
+ }
}
}
/**
- * A debugging tool to print all stack traces, as jstack does.
+ * A debugging tool to print stack traces of most threads, as jstack does.
+ * Uninteresting threads are filtered out.
*/
- static void printAllStackTraces() {
+ static void dumpTestThreads() {
+ // Android-change no ThreadMXBean
+ // 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)
+ // && info.getLockName().startsWith("java.lang.ref.Reference$Lock"))
+ // continue;
+ // if ("Finalizer".equals(name)
+ // && info.getLockName().startsWith("java.lang.ref.ReferenceQueue$Lock"))
+ // continue;
+ // if ("checkForWedgedTest".equals(name))
+ // continue;
+ // System.err.print(info);
+ // }
+ // System.err.println("------ stacktrace dump end ------");
}
/**
@@ -436,7 +993,7 @@
delay(millis);
assertTrue(thread.isAlive());
} catch (InterruptedException fail) {
- fail("Unexpected InterruptedException");
+ threadFail("Unexpected InterruptedException");
}
}
@@ -458,7 +1015,7 @@
for (Thread thread : threads)
assertTrue(thread.isAlive());
} catch (InterruptedException fail) {
- fail("Unexpected InterruptedException");
+ threadFail("Unexpected InterruptedException");
}
}
@@ -532,6 +1089,12 @@
* getPolicy/setPolicy.
*/
public void runWithPermissions(Runnable r, Permission... permissions) {
+ // Android-changed - no SecurityManager
+ // SecurityManager sm = System.getSecurityManager();
+ // if (sm == null) {
+ // r.run();
+ // }
+ // runWithSecurityManagerWithPermissions(r, permissions);
r.run();
}
@@ -544,6 +1107,30 @@
*/
public void runWithSecurityManagerWithPermissions(Runnable r,
Permission... permissions) {
+ // Android-changed - no SecurityManager
+ // SecurityManager sm = System.getSecurityManager();
+ // if (sm == null) {
+ // Policy savedPolicy = Policy.getPolicy();
+ // try {
+ // Policy.setPolicy(permissivePolicy());
+ // System.setSecurityManager(new SecurityManager());
+ // runWithSecurityManagerWithPermissions(r, permissions);
+ // } finally {
+ // System.setSecurityManager(null);
+ // Policy.setPolicy(savedPolicy);
+ // }
+ // } else {
+ // Policy savedPolicy = Policy.getPolicy();
+ // AdjustablePolicy policy = new AdjustablePolicy(permissions);
+ // Policy.setPolicy(policy);
+
+ // try {
+ // r.run();
+ // } finally {
+ // policy.addPermission(new SecurityPermission("setPolicy"));
+ // Policy.setPolicy(savedPolicy);
+ // }
+ // }
r.run();
}
@@ -648,19 +1235,6 @@
waitForThreadToEnterWaitState(thread, LONG_DELAY_MS);
}
- void waitForThreadToEnterWaitStateNoTimeout(Thread thread) {
- for (;;) {
- Thread.State s = thread.getState();
- if (s == Thread.State.BLOCKED ||
- s == Thread.State.WAITING ||
- s == Thread.State.TIMED_WAITING)
- return;
- else if (s == Thread.State.TERMINATED)
- fail("Unexpected thread termination");
- Thread.yield();
- }
- }
-
/**
* Returns the number of milliseconds since time given by
* startNanoTime, which must have been previously returned from a
@@ -723,7 +1297,7 @@
} finally {
if (t.getState() != Thread.State.TERMINATED) {
t.interrupt();
- fail("Test timed out");
+ threadFail("timed out waiting for thread to terminate");
}
}
}
@@ -848,7 +1422,10 @@
public static final String TEST_STRING = "a test string";
public static class StringTask implements Callable<String> {
- public String call() { return TEST_STRING; }
+ final String value;
+ public StringTask() { this(TEST_STRING); }
+ public StringTask(String value) { this.value = value; }
+ public String call() { return value; }
}
public Callable<String> latchAwaitingStringTask(final CountDownLatch latch) {
@@ -861,24 +1438,50 @@
}};
}
- public Runnable awaiter(final CountDownLatch latch) {
+ public Runnable countDowner(final CountDownLatch latch) {
return new CheckedRunnable() {
public void realRun() throws InterruptedException {
- await(latch);
+ latch.countDown();
}};
}
- public void await(CountDownLatch latch) {
+ class LatchAwaiter extends CheckedRunnable {
+ static final int NEW = 0;
+ static final int RUNNING = 1;
+ static final int DONE = 2;
+ final CountDownLatch latch;
+ int state = NEW;
+ LatchAwaiter(CountDownLatch latch) { this.latch = latch; }
+ public void realRun() throws InterruptedException {
+ state = 1;
+ await(latch);
+ state = 2;
+ }
+ }
+
+ public LatchAwaiter awaiter(CountDownLatch latch) {
+ return new LatchAwaiter(latch);
+ }
+
+ public void await(CountDownLatch latch, long timeoutMillis) {
try {
- assertTrue(latch.await(LONG_DELAY_MS, MILLISECONDS));
+ if (!latch.await(timeoutMillis, MILLISECONDS))
+ fail("timed out waiting for CountDownLatch for "
+ + (timeoutMillis/1000) + " sec");
} catch (Throwable fail) {
threadUnexpectedException(fail);
}
}
+ public void await(CountDownLatch latch) {
+ await(latch, LONG_DELAY_MS);
+ }
+
public void await(Semaphore semaphore) {
try {
- assertTrue(semaphore.tryAcquire(LONG_DELAY_MS, MILLISECONDS));
+ if (!semaphore.tryAcquire(LONG_DELAY_MS, MILLISECONDS))
+ fail("timed out waiting for Semaphore for "
+ + (LONG_DELAY_MS/1000) + " sec");
} catch (Throwable fail) {
threadUnexpectedException(fail);
}
diff --git a/jsr166-tests/src/test/java/jsr166/LinkedBlockingDequeTest.java b/jsr166-tests/src/test/java/jsr166/LinkedBlockingDequeTest.java
index 62802bb..789373d 100644
--- a/jsr166-tests/src/test/java/jsr166/LinkedBlockingDequeTest.java
+++ b/jsr166-tests/src/test/java/jsr166/LinkedBlockingDequeTest.java
@@ -26,25 +26,24 @@
public class LinkedBlockingDequeTest extends JSR166TestCase {
- // android-note: These tests have been moved into their own separate
- // classes to work around CTS issues.
- //
- // public static class Unbounded extends BlockingQueueTest {
- // protected BlockingQueue emptyCollection() {
- // return new LinkedBlockingDeque();
- // }
- // }
- //
- // public static class Bounded extends BlockingQueueTest {
- // protected BlockingQueue emptyCollection() {
- // return new LinkedBlockingDeque(SIZE);
- // }
- // }
+ public static class Unbounded extends BlockingQueueTest {
+ protected BlockingQueue emptyCollection() {
+ return new LinkedBlockingDeque();
+ }
+ }
+
+ public static class Bounded extends BlockingQueueTest {
+ protected BlockingQueue emptyCollection() {
+ return new LinkedBlockingDeque(SIZE);
+ }
+ }
+
+ // android-note: Removed because the CTS runner does a bad job of
+ // retrying tests that have suite() declarations.
//
// public static void main(String[] args) {
// main(suite(), args);
// }
- //
// public static Test suite() {
// return newTestSuite(LinkedBlockingDequeTest.class,
// new Unbounded().testSuite(),
@@ -87,7 +86,7 @@
public void testSize() {
LinkedBlockingDeque q = populatedDeque(SIZE);
for (int i = 0; i < SIZE; ++i) {
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
q.removeFirst();
}
for (int i = 0; i < SIZE; ++i) {
@@ -152,7 +151,7 @@
*/
public void testPollLast() {
LinkedBlockingDeque q = populatedDeque(SIZE);
- for (int i = SIZE-1; i >= 0; --i) {
+ for (int i = SIZE - 1; i >= 0; --i) {
assertEquals(i, q.pollLast());
}
assertNull(q.pollLast());
@@ -191,7 +190,7 @@
*/
public void testPeekLast() {
LinkedBlockingDeque q = populatedDeque(SIZE);
- for (int i = SIZE-1; i >= 0; --i) {
+ for (int i = SIZE - 1; i >= 0; --i) {
assertEquals(i, q.peekLast());
assertEquals(i, q.pollLast());
assertTrue(q.peekLast() == null ||
@@ -221,7 +220,7 @@
*/
public void testLastElement() {
LinkedBlockingDeque q = populatedDeque(SIZE);
- for (int i = SIZE-1; i >= 0; --i) {
+ for (int i = SIZE - 1; i >= 0; --i) {
assertEquals(i, q.getLast());
assertEquals(i, q.pollLast());
}
@@ -286,7 +285,7 @@
}
for (int i = 0; i < SIZE; i += 2) {
assertTrue(q.removeFirstOccurrence(new Integer(i)));
- assertFalse(q.removeFirstOccurrence(new Integer(i+1)));
+ assertFalse(q.removeFirstOccurrence(new Integer(i + 1)));
}
assertTrue(q.isEmpty());
}
@@ -301,7 +300,7 @@
}
for (int i = 0; i < SIZE; i += 2) {
assertTrue(q.removeLastOccurrence(new Integer(i)));
- assertFalse(q.removeLastOccurrence(new Integer(i+1)));
+ assertFalse(q.removeLastOccurrence(new Integer(i + 1)));
}
assertTrue(q.isEmpty());
}
@@ -372,7 +371,7 @@
*/
public void testConstructor5() {
Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
+ for (int i = 0; i < SIZE - 1; ++i)
ints[i] = i;
Collection<Integer> elements = Arrays.asList(ints);
try {
@@ -419,7 +418,7 @@
assertEquals(i, q.remove());
}
for (int i = 0; i < SIZE; ++i) {
- assertEquals(SIZE-i, q.remainingCapacity());
+ assertEquals(SIZE - i, q.remainingCapacity());
assertEquals(SIZE, q.size() + q.remainingCapacity());
assertTrue(q.add(i));
}
@@ -429,8 +428,8 @@
* push(null) throws NPE
*/
public void testPushNull() {
+ LinkedBlockingDeque q = new LinkedBlockingDeque(1);
try {
- LinkedBlockingDeque q = new LinkedBlockingDeque(1);
q.push(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -440,14 +439,14 @@
* push succeeds if not full; throws ISE if full
*/
public void testPush() {
+ LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+ for (int i = 0; i < SIZE; ++i) {
+ Integer x = new Integer(i);
+ q.push(x);
+ assertEquals(x, q.peek());
+ }
+ assertEquals(0, q.remainingCapacity());
try {
- LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
- for (int i = 0; i < SIZE; ++i) {
- Integer x = new Integer(i);
- q.push(x);
- assertEquals(x, q.peek());
- }
- assertEquals(0, q.remainingCapacity());
q.push(new Integer(SIZE));
shouldThrow();
} catch (IllegalStateException success) {}
@@ -518,7 +517,7 @@
public void testAddAll3() {
LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
+ for (int i = 0; i < SIZE - 1; ++i)
ints[i] = new Integer(i);
Collection<Integer> elements = Arrays.asList(ints);
try {
@@ -756,25 +755,23 @@
final CountDownLatch aboutToWait = new CountDownLatch(1);
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
+ long startTime = System.nanoTime();
for (int i = 0; i < SIZE; ++i) {
- long t0 = System.nanoTime();
assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
- assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
}
- long t0 = System.nanoTime();
aboutToWait.countDown();
try {
- q.poll(MEDIUM_DELAY_MS, MILLISECONDS);
+ q.poll(LONG_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {
- assertTrue(millisElapsedSince(t0) < MEDIUM_DELAY_MS);
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}
}});
aboutToWait.await();
- waitForThreadToEnterWaitState(t, SMALL_DELAY_MS);
+ waitForThreadToEnterWaitState(t, LONG_DELAY_MS);
t.interrupt();
- awaitTermination(t, MEDIUM_DELAY_MS);
+ awaitTermination(t);
checkEmpty(q);
}
@@ -1055,17 +1052,18 @@
* returning timeout status
*/
public void testInterruptedTimedPollFirst() throws InterruptedException {
+ final LinkedBlockingDeque q = populatedDeque(SIZE);
final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
- LinkedBlockingDeque q = populatedDeque(SIZE);
+ long startTime = System.nanoTime();
for (int i = 0; i < SIZE; ++i) {
assertEquals(i, q.pollFirst(LONG_DELAY_MS, MILLISECONDS));
}
Thread.currentThread().interrupt();
try {
- q.pollFirst(SMALL_DELAY_MS, MILLISECONDS);
+ q.pollFirst(LONG_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
@@ -1076,6 +1074,7 @@
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}});
await(pleaseInterrupt);
@@ -1251,7 +1250,7 @@
public void testTakeLast() throws InterruptedException {
LinkedBlockingDeque q = populatedDeque(SIZE);
for (int i = 0; i < SIZE; ++i) {
- assertEquals(SIZE-i-1, q.takeLast());
+ assertEquals(SIZE - i - 1, q.takeLast());
}
}
@@ -1264,7 +1263,7 @@
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
for (int i = 0; i < SIZE; ++i) {
- assertEquals(SIZE-i-1, q.takeLast());
+ assertEquals(SIZE - i - 1, q.takeLast());
}
Thread.currentThread().interrupt();
@@ -1294,7 +1293,7 @@
public void testTimedPollLast0() throws InterruptedException {
LinkedBlockingDeque q = populatedDeque(SIZE);
for (int i = 0; i < SIZE; ++i) {
- assertEquals(SIZE-i-1, q.pollLast(0, MILLISECONDS));
+ assertEquals(SIZE - i - 1, q.pollLast(0, MILLISECONDS));
}
assertNull(q.pollLast(0, MILLISECONDS));
}
@@ -1306,7 +1305,7 @@
LinkedBlockingDeque q = populatedDeque(SIZE);
for (int i = 0; i < SIZE; ++i) {
long startTime = System.nanoTime();
- assertEquals(SIZE-i-1, q.pollLast(LONG_DELAY_MS, MILLISECONDS));
+ assertEquals(SIZE - i - 1, q.pollLast(LONG_DELAY_MS, MILLISECONDS));
assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}
long startTime = System.nanoTime();
@@ -1320,12 +1319,14 @@
* returning timeout status
*/
public void testInterruptedTimedPollLast() throws InterruptedException {
+ final LinkedBlockingDeque q = populatedDeque(SIZE);
final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
- LinkedBlockingDeque q = populatedDeque(SIZE);
+ long startTime = System.nanoTime();
for (int i = 0; i < SIZE; ++i) {
- assertEquals(SIZE-i-1, q.pollLast(LONG_DELAY_MS, MILLISECONDS));
+ assertEquals(SIZE - i - 1,
+ q.pollLast(LONG_DELAY_MS, MILLISECONDS));
}
Thread.currentThread().interrupt();
@@ -1341,12 +1342,15 @@
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
+
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}});
await(pleaseInterrupt);
assertThreadStaysAlive(t);
t.interrupt();
awaitTermination(t);
+ checkEmpty(q);
}
/**
@@ -1379,6 +1383,8 @@
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
+
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}});
barrier.await();
@@ -1463,7 +1469,7 @@
assertTrue(changed);
assertTrue(q.containsAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
p.remove();
}
}
@@ -1476,7 +1482,7 @@
LinkedBlockingDeque q = populatedDeque(SIZE);
LinkedBlockingDeque p = populatedDeque(i);
assertTrue(q.removeAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
for (int j = 0; j < i; ++j) {
Integer x = (Integer)(p.remove());
assertFalse(q.contains(x));
@@ -1675,23 +1681,23 @@
final LinkedBlockingDeque q = new LinkedBlockingDeque(2);
q.add(one);
q.add(two);
- ExecutorService executor = Executors.newFixedThreadPool(2);
final CheckedBarrier threadsStarted = new CheckedBarrier(2);
- executor.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- assertFalse(q.offer(three));
- threadsStarted.await();
- assertTrue(q.offer(three, LONG_DELAY_MS, MILLISECONDS));
- assertEquals(0, q.remainingCapacity());
- }});
+ final ExecutorService executor = Executors.newFixedThreadPool(2);
+ try (PoolCleaner cleaner = cleaner(executor)) {
+ executor.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ assertFalse(q.offer(three));
+ threadsStarted.await();
+ assertTrue(q.offer(three, LONG_DELAY_MS, MILLISECONDS));
+ assertEquals(0, q.remainingCapacity());
+ }});
- executor.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- threadsStarted.await();
- assertSame(one, q.take());
- }});
-
- joinPool(executor);
+ executor.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ threadsStarted.await();
+ assertSame(one, q.take());
+ }});
+ }
}
/**
@@ -1700,22 +1706,22 @@
public void testPollInExecutor() {
final LinkedBlockingDeque q = new LinkedBlockingDeque(2);
final CheckedBarrier threadsStarted = new CheckedBarrier(2);
- ExecutorService executor = Executors.newFixedThreadPool(2);
- executor.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- assertNull(q.poll());
- threadsStarted.await();
- assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
- checkEmpty(q);
- }});
+ final ExecutorService executor = Executors.newFixedThreadPool(2);
+ try (PoolCleaner cleaner = cleaner(executor)) {
+ executor.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ assertNull(q.poll());
+ threadsStarted.await();
+ assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+ checkEmpty(q);
+ }});
- executor.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- threadsStarted.await();
- q.put(one);
- }});
-
- joinPool(executor);
+ executor.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ threadsStarted.await();
+ q.put(one);
+ }});
+ }
}
/**
@@ -1767,7 +1773,7 @@
final LinkedBlockingDeque q = populatedDeque(SIZE);
Thread t = new Thread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
- q.put(new Integer(SIZE+1));
+ q.put(new Integer(SIZE + 1));
}});
t.start();
@@ -1792,7 +1798,7 @@
q.drainTo(l, i);
int k = (i < SIZE) ? i : SIZE;
assertEquals(k, l.size());
- assertEquals(SIZE-k, q.size());
+ assertEquals(SIZE - k, q.size());
for (int j = 0; j < k; ++j)
assertEquals(l.get(j), new Integer(j));
do {} while (q.poll() != null);
diff --git a/jsr166-tests/src/test/java/jsr166/LinkedBlockingQueueTest.java b/jsr166-tests/src/test/java/jsr166/LinkedBlockingQueueTest.java
index bd37b2a..faf3f18 100644
--- a/jsr166-tests/src/test/java/jsr166/LinkedBlockingQueueTest.java
+++ b/jsr166-tests/src/test/java/jsr166/LinkedBlockingQueueTest.java
@@ -26,25 +26,27 @@
public class LinkedBlockingQueueTest extends JSR166TestCase {
- // android-note: These tests have been moved into their own separate
+ // android-note: These tests have been moved into their own separate
// classes to work around CTS issues.
//
// public static class Unbounded extends BlockingQueueTest {
- // protected BlockingQueue emptyCollection() {
- // return new LinkedBlockingQueue();
+ // protected BlockingQueue emptyCollection() {
+ // return new LinkedBlockingQueue();
// }
// }
- //
+
// public static class Bounded extends BlockingQueueTest {
- // protected BlockingQueue emptyCollection() {
+ // protected BlockingQueue emptyCollection() {
// return new LinkedBlockingQueue(SIZE);
// }
// }
+
+ // android-note: Removed because the CTS runner does a bad job of
+ // retrying tests that have suite() declarations.
//
// public static void main(String[] args) {
- // main(suite(), args);
+ // main(suite(), args);
// }
- //
// public static Test suite() {
// return newTestSuite(LinkedBlockingQueueTest.class,
// new Unbounded().testSuite(),
@@ -113,7 +115,7 @@
*/
public void testConstructor5() {
Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
+ for (int i = 0; i < SIZE - 1; ++i)
ints[i] = new Integer(i);
Collection<Integer> elements = Arrays.asList(ints);
try {
@@ -160,7 +162,7 @@
assertEquals(i, q.remove());
}
for (int i = 0; i < SIZE; ++i) {
- assertEquals(SIZE-i, q.remainingCapacity());
+ assertEquals(SIZE - i, q.remainingCapacity());
assertEquals(SIZE, q.size() + q.remainingCapacity());
assertTrue(q.add(i));
}
@@ -207,7 +209,7 @@
public void testAddAll3() {
LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE);
Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
+ for (int i = 0; i < SIZE - 1; ++i)
ints[i] = new Integer(i);
Collection<Integer> elements = Arrays.asList(ints);
try {
@@ -445,25 +447,23 @@
final CountDownLatch aboutToWait = new CountDownLatch(1);
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
+ long startTime = System.nanoTime();
for (int i = 0; i < SIZE; ++i) {
- long t0 = System.nanoTime();
assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
- assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
}
- long t0 = System.nanoTime();
aboutToWait.countDown();
try {
- q.poll(MEDIUM_DELAY_MS, MILLISECONDS);
+ q.poll(LONG_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {
- assertTrue(millisElapsedSince(t0) < MEDIUM_DELAY_MS);
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}
}});
- aboutToWait.await();
- waitForThreadToEnterWaitState(t, SMALL_DELAY_MS);
+ await(aboutToWait);
+ waitForThreadToEnterWaitState(t, LONG_DELAY_MS);
t.interrupt();
- awaitTermination(t, MEDIUM_DELAY_MS);
+ awaitTermination(t);
checkEmpty(q);
}
@@ -579,7 +579,7 @@
assertTrue(changed);
assertTrue(q.containsAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
p.remove();
}
}
@@ -592,7 +592,7 @@
LinkedBlockingQueue q = populatedQueue(SIZE);
LinkedBlockingQueue p = populatedQueue(i);
assertTrue(q.removeAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
for (int j = 0; j < i; ++j) {
Integer x = (Integer)(p.remove());
assertFalse(q.contains(x));
@@ -727,23 +727,23 @@
final LinkedBlockingQueue q = new LinkedBlockingQueue(2);
q.add(one);
q.add(two);
- ExecutorService executor = Executors.newFixedThreadPool(2);
final CheckedBarrier threadsStarted = new CheckedBarrier(2);
- executor.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- assertFalse(q.offer(three));
- threadsStarted.await();
- assertTrue(q.offer(three, LONG_DELAY_MS, MILLISECONDS));
- assertEquals(0, q.remainingCapacity());
- }});
+ final ExecutorService executor = Executors.newFixedThreadPool(2);
+ try (PoolCleaner cleaner = cleaner(executor)) {
+ executor.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ assertFalse(q.offer(three));
+ threadsStarted.await();
+ assertTrue(q.offer(three, LONG_DELAY_MS, MILLISECONDS));
+ assertEquals(0, q.remainingCapacity());
+ }});
- executor.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- threadsStarted.await();
- assertSame(one, q.take());
- }});
-
- joinPool(executor);
+ executor.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ threadsStarted.await();
+ assertSame(one, q.take());
+ }});
+ }
}
/**
@@ -752,22 +752,22 @@
public void testPollInExecutor() {
final LinkedBlockingQueue q = new LinkedBlockingQueue(2);
final CheckedBarrier threadsStarted = new CheckedBarrier(2);
- ExecutorService executor = Executors.newFixedThreadPool(2);
- executor.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- assertNull(q.poll());
- threadsStarted.await();
- assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
- checkEmpty(q);
- }});
+ final ExecutorService executor = Executors.newFixedThreadPool(2);
+ try (PoolCleaner cleaner = cleaner(executor)) {
+ executor.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ assertNull(q.poll());
+ threadsStarted.await();
+ assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+ checkEmpty(q);
+ }});
- executor.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- threadsStarted.await();
- q.put(one);
- }});
-
- joinPool(executor);
+ executor.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ threadsStarted.await();
+ q.put(one);
+ }});
+ }
}
/**
@@ -819,7 +819,7 @@
final LinkedBlockingQueue q = populatedQueue(SIZE);
Thread t = new Thread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
- q.put(new Integer(SIZE+1));
+ q.put(new Integer(SIZE + 1));
}});
t.start();
@@ -844,7 +844,7 @@
q.drainTo(l, i);
int k = (i < SIZE) ? i : SIZE;
assertEquals(k, l.size());
- assertEquals(SIZE-k, q.size());
+ assertEquals(SIZE - k, q.size());
for (int j = 0; j < k; ++j)
assertEquals(l.get(j), new Integer(j));
do {} while (q.poll() != null);
diff --git a/jsr166-tests/src/test/java/jsr166/LinkedListTest.java b/jsr166-tests/src/test/java/jsr166/LinkedListTest.java
index 9d9481d..9c971b4 100644
--- a/jsr166-tests/src/test/java/jsr166/LinkedListTest.java
+++ b/jsr166-tests/src/test/java/jsr166/LinkedListTest.java
@@ -25,7 +25,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(LinkedListTest.class);
// }
/**
@@ -91,7 +91,7 @@
public void testSize() {
LinkedList q = populatedQueue(SIZE);
for (int i = 0; i < SIZE; ++i) {
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
q.remove();
}
for (int i = 0; i < SIZE; ++i) {
@@ -106,6 +106,8 @@
public void testOfferNull() {
LinkedList q = new LinkedList();
q.offer(null);
+ assertNull(q.get(0));
+ assertTrue(q.contains(null));
}
/**
@@ -132,8 +134,8 @@
* addAll(null) throws NPE
*/
public void testAddAll1() {
+ LinkedList q = new LinkedList();
try {
- LinkedList q = new LinkedList();
q.addAll(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -245,14 +247,14 @@
assertTrue(q.contains(i));
assertTrue(q.remove((Integer)i));
assertFalse(q.contains(i));
- assertTrue(q.contains(i-1));
+ assertTrue(q.contains(i - 1));
}
for (int i = 0; i < SIZE; i += 2) {
assertTrue(q.contains(i));
assertTrue(q.remove((Integer)i));
assertFalse(q.contains(i));
- assertFalse(q.remove((Integer)(i+1)));
- assertFalse(q.contains(i+1));
+ assertFalse(q.remove((Integer)(i + 1)));
+ assertFalse(q.contains(i + 1));
}
assertTrue(q.isEmpty());
}
@@ -311,7 +313,7 @@
assertTrue(changed);
assertTrue(q.containsAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
p.remove();
}
}
@@ -324,7 +326,7 @@
LinkedList q = populatedQueue(SIZE);
LinkedList p = populatedQueue(i);
assertTrue(q.removeAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
for (int j = 0; j < i; ++j) {
Integer x = (Integer)(p.remove());
assertFalse(q.contains(x));
@@ -549,7 +551,7 @@
*/
public void testPollLast() {
LinkedList q = populatedQueue(SIZE);
- for (int i = SIZE-1; i >= 0; --i) {
+ for (int i = SIZE - 1; i >= 0; --i) {
assertEquals(i, q.pollLast());
}
assertNull(q.pollLast());
@@ -574,7 +576,7 @@
*/
public void testPeekLast() {
LinkedList q = populatedQueue(SIZE);
- for (int i = SIZE-1; i >= 0; --i) {
+ for (int i = SIZE - 1; i >= 0; --i) {
assertEquals(i, q.peekLast());
assertEquals(i, q.pollLast());
assertTrue(q.peekLast() == null ||
@@ -600,7 +602,7 @@
*/
public void testLastElement() {
LinkedList q = populatedQueue(SIZE);
- for (int i = SIZE-1; i >= 0; --i) {
+ for (int i = SIZE - 1; i >= 0; --i) {
assertEquals(i, q.getLast());
assertEquals(i, q.pollLast());
}
@@ -621,7 +623,7 @@
}
for (int i = 0; i < SIZE; i += 2) {
assertTrue(q.removeFirstOccurrence(new Integer(i)));
- assertFalse(q.removeFirstOccurrence(new Integer(i+1)));
+ assertFalse(q.removeFirstOccurrence(new Integer(i + 1)));
}
assertTrue(q.isEmpty());
}
@@ -636,7 +638,7 @@
}
for (int i = 0; i < SIZE; i += 2) {
assertTrue(q.removeLastOccurrence(new Integer(i)));
- assertFalse(q.removeLastOccurrence(new Integer(i+1)));
+ assertFalse(q.removeLastOccurrence(new Integer(i + 1)));
}
assertTrue(q.isEmpty());
}
diff --git a/jsr166-tests/src/test/java/jsr166/LinkedTransferQueueTest.java b/jsr166-tests/src/test/java/jsr166/LinkedTransferQueueTest.java
index 8d3f276..c712592 100644
--- a/jsr166-tests/src/test/java/jsr166/LinkedTransferQueueTest.java
+++ b/jsr166-tests/src/test/java/jsr166/LinkedTransferQueueTest.java
@@ -25,30 +25,33 @@
import junit.framework.Test;
@SuppressWarnings({"unchecked", "rawtypes"})
-// android-changed: Extend BlockingQueueTest directly.
-public class LinkedTransferQueueTest extends BlockingQueueTest {
+public class LinkedTransferQueueTest extends JSR166TestCase {
+ static class Implementation implements CollectionImplementation {
+ public Class<?> klazz() { return LinkedTransferQueue.class; }
+ public Collection emptyCollection() { return new LinkedTransferQueue(); }
+ public Object makeElement(int i) { return i; }
+ public boolean isConcurrent() { return true; }
+ public boolean permitsNulls() { return false; }
+ }
- // android-changed: Extend BlockingQueueTest directly.
- //
- // public static class Generic extends BlockingQueueTest {
- // protected BlockingQueue emptyCollection() {
- // return new LinkedTransferQueue();
- // }
- // }
+ public static class Generic extends BlockingQueueTest {
+ protected BlockingQueue emptyCollection() {
+ return new LinkedTransferQueue();
+ }
+ }
+
+ // android-note: Removed because the CTS runner does a bad job of
+ // retrying tests that have suite() declarations.
//
// public static void main(String[] args) {
// main(suite(), args);
// }
- //
// public static Test suite() {
// return newTestSuite(LinkedTransferQueueTest.class,
- // new Generic().testSuite());
+ // new Generic().testSuite(),
+ // CollectionTest.testSuite(new Implementation()));
// }
- protected BlockingQueue emptyCollection() {
- return new LinkedTransferQueue();
- }
-
/**
* Constructor builds new queue with size being zero and empty
* being true
@@ -87,7 +90,7 @@
*/
public void testConstructor4() {
Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
+ for (int i = 0; i < SIZE - 1; ++i)
ints[i] = i;
Collection<Integer> elements = Arrays.asList(ints);
try {
@@ -141,8 +144,8 @@
* addAll(this) throws IllegalArgumentException
*/
public void testAddAllSelf() {
+ LinkedTransferQueue q = populatedQueue(SIZE);
try {
- LinkedTransferQueue q = populatedQueue(SIZE);
q.addAll(q);
shouldThrow();
} catch (IllegalArgumentException success) {}
@@ -153,12 +156,11 @@
* NullPointerException after possibly adding some elements
*/
public void testAddAll3() {
+ LinkedTransferQueue q = new LinkedTransferQueue();
+ Integer[] ints = new Integer[SIZE];
+ for (int i = 0; i < SIZE - 1; ++i)
+ ints[i] = i;
try {
- LinkedTransferQueue q = new LinkedTransferQueue();
- Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE - 1; ++i) {
- ints[i] = i;
- }
q.addAll(Arrays.asList(ints));
shouldThrow();
} catch (NullPointerException success) {}
@@ -265,12 +267,12 @@
*/
public void testTimedPoll() throws InterruptedException {
LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
- for (int i = 0; i < SIZE; ++i) {
- long startTime = System.nanoTime();
- assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
- assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
- }
long startTime = System.nanoTime();
+ for (int i = 0; i < SIZE; ++i)
+ assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+
+ startTime = System.nanoTime();
assertNull(q.poll(timeoutMillis(), MILLISECONDS));
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
checkEmpty(q);
@@ -285,25 +287,21 @@
final CountDownLatch aboutToWait = new CountDownLatch(1);
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
- for (int i = 0; i < SIZE; ++i) {
- long t0 = System.nanoTime();
+ long startTime = System.nanoTime();
+ for (int i = 0; i < SIZE; ++i)
assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
- assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
- }
- long t0 = System.nanoTime();
aboutToWait.countDown();
try {
- q.poll(MEDIUM_DELAY_MS, MILLISECONDS);
+ q.poll(LONG_DELAY_MS, MILLISECONDS);
shouldThrow();
- } catch (InterruptedException success) {
- assertTrue(millisElapsedSince(t0) < MEDIUM_DELAY_MS);
- }
+ } catch (InterruptedException success) {}
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}});
aboutToWait.await();
- waitForThreadToEnterWaitState(t, SMALL_DELAY_MS);
+ waitForThreadToEnterWaitState(t);
t.interrupt();
- awaitTermination(t, MEDIUM_DELAY_MS);
+ awaitTermination(t);
checkEmpty(q);
}
@@ -315,19 +313,18 @@
final BlockingQueue<Integer> q = populatedQueue(SIZE);
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
+ long startTime = System.nanoTime();
Thread.currentThread().interrupt();
- for (int i = 0; i < SIZE; ++i) {
- long t0 = System.nanoTime();
+ for (int i = 0; i < SIZE; ++i)
assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
- assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
- }
try {
- q.poll(MEDIUM_DELAY_MS, MILLISECONDS);
+ q.poll(LONG_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {}
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}});
- awaitTermination(t, MEDIUM_DELAY_MS);
+ awaitTermination(t);
checkEmpty(q);
}
@@ -598,22 +595,24 @@
public void testOfferInExecutor() {
final LinkedTransferQueue q = new LinkedTransferQueue();
final CheckedBarrier threadsStarted = new CheckedBarrier(2);
- ExecutorService executor = Executors.newFixedThreadPool(2);
+ final ExecutorService executor = Executors.newFixedThreadPool(2);
+ try (PoolCleaner cleaner = cleaner(executor)) {
- executor.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- threadsStarted.await();
- assertTrue(q.offer(one, LONG_DELAY_MS, MILLISECONDS));
- }});
+ executor.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ threadsStarted.await();
+ long startTime = System.nanoTime();
+ assertTrue(q.offer(one, LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+ }});
- executor.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- threadsStarted.await();
- assertSame(one, q.take());
- checkEmpty(q);
- }});
-
- joinPool(executor);
+ executor.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ threadsStarted.await();
+ assertSame(one, q.take());
+ checkEmpty(q);
+ }});
+ }
}
/**
@@ -622,23 +621,25 @@
public void testPollInExecutor() {
final LinkedTransferQueue q = new LinkedTransferQueue();
final CheckedBarrier threadsStarted = new CheckedBarrier(2);
- ExecutorService executor = Executors.newFixedThreadPool(2);
+ final ExecutorService executor = Executors.newFixedThreadPool(2);
+ try (PoolCleaner cleaner = cleaner(executor)) {
- executor.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- assertNull(q.poll());
- threadsStarted.await();
- assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
- checkEmpty(q);
- }});
+ executor.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ assertNull(q.poll());
+ threadsStarted.await();
+ long startTime = System.nanoTime();
+ assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+ checkEmpty(q);
+ }});
- executor.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- threadsStarted.await();
- q.put(one);
- }});
-
- joinPool(executor);
+ executor.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ threadsStarted.await();
+ q.put(one);
+ }});
+ }
}
/**
@@ -699,7 +700,7 @@
assertTrue(l.size() >= SIZE);
for (int i = 0; i < SIZE; ++i)
assertEquals(i, l.get(i));
- awaitTermination(t, MEDIUM_DELAY_MS);
+ awaitTermination(t);
assertTrue(q.size() + l.size() >= SIZE);
}
@@ -736,14 +737,15 @@
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
+ long startTime = System.nanoTime();
assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
assertEquals(0, q.getWaitingConsumerCount());
assertFalse(q.hasWaitingConsumer());
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}});
threadStarted.await();
- // waitForThreadToEnterWaitState(t, SMALL_DELAY_MS);
- waitForThreadToEnterWaitStateNoTimeout(t);
+ waitForThreadToEnterWaitState(t);
assertEquals(1, q.getWaitingConsumerCount());
assertTrue(q.hasWaitingConsumer());
@@ -751,7 +753,7 @@
assertEquals(0, q.getWaitingConsumerCount());
assertFalse(q.hasWaitingConsumer());
- awaitTermination(t, MEDIUM_DELAY_MS);
+ awaitTermination(t);
}
/**
@@ -782,12 +784,11 @@
}});
threadStarted.await();
- // waitForThreadToEnterWaitState(t, SMALL_DELAY_MS);
- waitForThreadToEnterWaitStateNoTimeout(t);
+ waitForThreadToEnterWaitState(t);
assertEquals(1, q.size());
assertSame(five, q.poll());
checkEmpty(q);
- awaitTermination(t, MEDIUM_DELAY_MS);
+ awaitTermination(t);
}
/**
@@ -843,7 +844,7 @@
assertEquals(1, q.size());
assertTrue(q.offer(three));
assertSame(four, q.poll());
- awaitTermination(t, MEDIUM_DELAY_MS);
+ awaitTermination(t);
}
/**
@@ -866,15 +867,15 @@
assertEquals(1, q.size());
assertSame(four, q.take());
checkEmpty(q);
- awaitTermination(t, MEDIUM_DELAY_MS);
+ awaitTermination(t);
}
/**
* tryTransfer(null) throws NullPointerException
*/
public void testTryTransfer1() {
+ final LinkedTransferQueue q = new LinkedTransferQueue();
try {
- final LinkedTransferQueue q = new LinkedTransferQueue();
q.tryTransfer(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -908,9 +909,11 @@
assertTrue(q.tryTransfer(hotPotato));
}});
- assertSame(hotPotato, q.poll(MEDIUM_DELAY_MS, MILLISECONDS));
+ long startTime = System.nanoTime();
+ assertSame(hotPotato, q.poll(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
checkEmpty(q);
- awaitTermination(t, MEDIUM_DELAY_MS);
+ awaitTermination(t);
}
/**
@@ -932,7 +935,7 @@
assertSame(q.take(), hotPotato);
checkEmpty(q);
- awaitTermination(t, MEDIUM_DELAY_MS);
+ awaitTermination(t);
}
/**
@@ -945,6 +948,7 @@
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
+ long startTime = System.nanoTime();
Thread.currentThread().interrupt();
try {
q.tryTransfer(new Object(), LONG_DELAY_MS, MILLISECONDS);
@@ -958,6 +962,7 @@
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}});
await(pleaseInterrupt);
@@ -975,10 +980,10 @@
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
- long t0 = System.nanoTime();
+ long startTime = System.nanoTime();
assertFalse(q.tryTransfer(new Object(),
timeoutMillis(), MILLISECONDS));
- assertTrue(millisElapsedSince(t0) >= timeoutMillis());
+ assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
checkEmpty(q);
}});
@@ -996,7 +1001,9 @@
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
- assertTrue(q.tryTransfer(five, MEDIUM_DELAY_MS, MILLISECONDS));
+ long startTime = System.nanoTime();
+ assertTrue(q.tryTransfer(five, LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
checkEmpty(q);
}});
@@ -1006,7 +1013,7 @@
assertSame(four, q.poll());
assertSame(five, q.poll());
checkEmpty(q);
- awaitTermination(t, MEDIUM_DELAY_MS);
+ awaitTermination(t);
}
/**
@@ -1017,9 +1024,9 @@
final LinkedTransferQueue q = new LinkedTransferQueue();
assertTrue(q.offer(four));
assertEquals(1, q.size());
- long t0 = System.nanoTime();
+ long startTime = System.nanoTime();
assertFalse(q.tryTransfer(five, timeoutMillis(), MILLISECONDS));
- assertTrue(millisElapsedSince(t0) >= timeoutMillis());
+ assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
assertEquals(1, q.size());
assertSame(four, q.poll());
assertNull(q.poll());
diff --git a/jsr166-tests/src/test/java/jsr166/LockSupportTest.java b/jsr166-tests/src/test/java/jsr166/LockSupportTest.java
index 8347b08..b3a8ed8 100644
--- a/jsr166-tests/src/test/java/jsr166/LockSupportTest.java
+++ b/jsr166-tests/src/test/java/jsr166/LockSupportTest.java
@@ -26,9 +26,15 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(LockSupportTest.class);
// }
+ static {
+ // Reduce the risk of rare disastrous classloading in first call to
+ // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773
+ Class<?> ensureLoaded = LockSupport.class;
+ }
+
/**
* Returns the blocker object used by tests in this file.
* Any old object will do; we'll return a convenient one.
diff --git a/jsr166-tests/src/test/java/jsr166/LongAccumulatorTest.java b/jsr166-tests/src/test/java/jsr166/LongAccumulatorTest.java
new file mode 100644
index 0000000..5dd52e9
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/LongAccumulatorTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Phaser;
+import java.util.concurrent.atomic.LongAccumulator;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class LongAccumulatorTest extends JSR166TestCase {
+ // android-note: Removed because the CTS runner does a bad job of
+ // retrying tests that have suite() declarations.
+ //
+ // public static void main(String[] args) {
+ // main(suite(), args);
+ // }
+ // public static Test suite() {
+ // return new TestSuite(LongAccumulatorTest.class);
+ // }
+
+ /**
+ * default constructed initializes to zero
+ */
+ public void testConstructor() {
+ LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+ assertEquals(0, ai.get());
+ }
+
+ /**
+ * accumulate accumulates given value to current, and get returns current value
+ */
+ public void testAccumulateAndGet() {
+ LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+ ai.accumulate(2);
+ assertEquals(2, ai.get());
+ ai.accumulate(-4);
+ assertEquals(2, ai.get());
+ ai.accumulate(4);
+ assertEquals(4, ai.get());
+ }
+
+ /**
+ * reset() causes subsequent get() to return zero
+ */
+ public void testReset() {
+ LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+ ai.accumulate(2);
+ assertEquals(2, ai.get());
+ ai.reset();
+ assertEquals(0, ai.get());
+ }
+
+ /**
+ * getThenReset() returns current value; subsequent get() returns zero
+ */
+ public void testGetThenReset() {
+ LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+ ai.accumulate(2);
+ assertEquals(2, ai.get());
+ assertEquals(2, ai.getThenReset());
+ assertEquals(0, ai.get());
+ }
+
+ /**
+ * toString returns current value.
+ */
+ public void testToString() {
+ LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+ assertEquals("0", ai.toString());
+ ai.accumulate(1);
+ assertEquals(Long.toString(1), ai.toString());
+ }
+
+ /**
+ * intValue returns current value.
+ */
+ public void testIntValue() {
+ LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+ assertEquals(0, ai.intValue());
+ ai.accumulate(1);
+ assertEquals(1, ai.intValue());
+ }
+
+ /**
+ * longValue returns current value.
+ */
+ public void testLongValue() {
+ LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+ assertEquals(0, ai.longValue());
+ ai.accumulate(1);
+ assertEquals(1, ai.longValue());
+ }
+
+ /**
+ * floatValue returns current value.
+ */
+ public void testFloatValue() {
+ LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+ assertEquals(0.0f, ai.floatValue());
+ ai.accumulate(1);
+ assertEquals(1.0f, ai.floatValue());
+ }
+
+ /**
+ * doubleValue returns current value.
+ */
+ public void testDoubleValue() {
+ LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+ assertEquals(0.0, ai.doubleValue());
+ ai.accumulate(1);
+ assertEquals(1.0, ai.doubleValue());
+ }
+
+ /**
+ * accumulates by multiple threads produce correct result
+ */
+ public void testAccumulateAndGetMT() {
+ final int incs = 1000000;
+ final int nthreads = 4;
+ final ExecutorService pool = Executors.newCachedThreadPool();
+ LongAccumulator a = new LongAccumulator(Long::max, 0L);
+ Phaser phaser = new Phaser(nthreads + 1);
+ for (int i = 0; i < nthreads; ++i)
+ pool.execute(new AccTask(a, phaser, incs));
+ phaser.arriveAndAwaitAdvance();
+ phaser.arriveAndAwaitAdvance();
+ long expected = incs - 1;
+ long result = a.get();
+ assertEquals(expected, result);
+ pool.shutdown();
+ }
+
+ static final class AccTask implements Runnable {
+ final LongAccumulator acc;
+ final Phaser phaser;
+ final int incs;
+ volatile long result;
+ AccTask(LongAccumulator acc, Phaser phaser, int incs) {
+ this.acc = acc;
+ this.phaser = phaser;
+ this.incs = incs;
+ }
+
+ public void run() {
+ phaser.arriveAndAwaitAdvance();
+ LongAccumulator a = acc;
+ for (int i = 0; i < incs; ++i)
+ a.accumulate(i);
+ result = a.get();
+ phaser.arrive();
+ }
+ }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/LongAdderTest.java b/jsr166-tests/src/test/java/jsr166/LongAdderTest.java
new file mode 100644
index 0000000..800a9c8
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/LongAdderTest.java
@@ -0,0 +1,198 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.atomic.LongAdder;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class LongAdderTest extends JSR166TestCase {
+ // android-note: Removed because the CTS runner does a bad job of
+ // retrying tests that have suite() declarations.
+ //
+ // public static void main(String[] args) {
+ // main(suite(), args);
+ // }
+ // public static Test suite() {
+ // return new TestSuite(LongAdderTest.class);
+ // }
+
+ /**
+ * default constructed initializes to zero
+ */
+ public void testConstructor() {
+ LongAdder ai = new LongAdder();
+ assertEquals(0, ai.sum());
+ }
+
+ /**
+ * add adds given value to current, and sum returns current value
+ */
+ public void testAddAndSum() {
+ LongAdder ai = new LongAdder();
+ ai.add(2);
+ assertEquals(2, ai.sum());
+ ai.add(-4);
+ assertEquals(-2, ai.sum());
+ }
+
+ /**
+ * decrement decrements and sum returns current value
+ */
+ public void testDecrementAndsum() {
+ LongAdder ai = new LongAdder();
+ ai.decrement();
+ assertEquals(-1, ai.sum());
+ ai.decrement();
+ assertEquals(-2, ai.sum());
+ }
+
+ /**
+ * incrementAndGet increments and returns current value
+ */
+ public void testIncrementAndsum() {
+ LongAdder ai = new LongAdder();
+ ai.increment();
+ assertEquals(1, ai.sum());
+ ai.increment();
+ assertEquals(2, ai.sum());
+ }
+
+ /**
+ * reset() causes subsequent sum() to return zero
+ */
+ public void testReset() {
+ LongAdder ai = new LongAdder();
+ ai.add(2);
+ assertEquals(2, ai.sum());
+ ai.reset();
+ assertEquals(0, ai.sum());
+ }
+
+ /**
+ * sumThenReset() returns sum; subsequent sum() returns zero
+ */
+ public void testSumThenReset() {
+ LongAdder ai = new LongAdder();
+ ai.add(2);
+ assertEquals(2, ai.sum());
+ assertEquals(2, ai.sumThenReset());
+ assertEquals(0, ai.sum());
+ }
+
+ /**
+ * a deserialized serialized adder holds same value
+ */
+ public void testSerialization() throws Exception {
+ LongAdder x = new LongAdder();
+ LongAdder y = serialClone(x);
+ assertNotSame(x, y);
+ x.add(-22);
+ LongAdder z = serialClone(x);
+ assertNotSame(y, z);
+ assertEquals(-22, x.sum());
+ assertEquals(0, y.sum());
+ assertEquals(-22, z.sum());
+ }
+
+ /**
+ * toString returns current value.
+ */
+ public void testToString() {
+ LongAdder ai = new LongAdder();
+ assertEquals("0", ai.toString());
+ ai.increment();
+ assertEquals(Long.toString(1), ai.toString());
+ }
+
+ /**
+ * intValue returns current value.
+ */
+ public void testIntValue() {
+ LongAdder ai = new LongAdder();
+ assertEquals(0, ai.intValue());
+ ai.increment();
+ assertEquals(1, ai.intValue());
+ }
+
+ /**
+ * longValue returns current value.
+ */
+ public void testLongValue() {
+ LongAdder ai = new LongAdder();
+ assertEquals(0, ai.longValue());
+ ai.increment();
+ assertEquals(1, ai.longValue());
+ }
+
+ /**
+ * floatValue returns current value.
+ */
+ public void testFloatValue() {
+ LongAdder ai = new LongAdder();
+ assertEquals(0.0f, ai.floatValue());
+ ai.increment();
+ assertEquals(1.0f, ai.floatValue());
+ }
+
+ /**
+ * doubleValue returns current value.
+ */
+ public void testDoubleValue() {
+ LongAdder ai = new LongAdder();
+ assertEquals(0.0, ai.doubleValue());
+ ai.increment();
+ assertEquals(1.0, ai.doubleValue());
+ }
+
+ /**
+ * adds by multiple threads produce correct sum
+ */
+ public void testAddAndSumMT() throws Throwable {
+ final int incs = 1000000;
+ final int nthreads = 4;
+ final ExecutorService pool = Executors.newCachedThreadPool();
+ LongAdder a = new LongAdder();
+ CyclicBarrier barrier = new CyclicBarrier(nthreads + 1);
+ for (int i = 0; i < nthreads; ++i)
+ pool.execute(new AdderTask(a, barrier, incs));
+ barrier.await();
+ barrier.await();
+ long total = (long)nthreads * incs;
+ long sum = a.sum();
+ assertEquals(sum, total);
+ pool.shutdown();
+ }
+
+ static final class AdderTask implements Runnable {
+ final LongAdder adder;
+ final CyclicBarrier barrier;
+ final int incs;
+ volatile long result;
+ AdderTask(LongAdder adder, CyclicBarrier barrier, int incs) {
+ this.adder = adder;
+ this.barrier = barrier;
+ this.incs = incs;
+ }
+
+ public void run() {
+ try {
+ barrier.await();
+ LongAdder a = adder;
+ for (int i = 0; i < incs; ++i)
+ a.add(1L);
+ result = a.sum();
+ barrier.await();
+ } catch (Throwable t) { throw new Error(t); }
+ }
+ }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/PhaserTest.java b/jsr166-tests/src/test/java/jsr166/PhaserTest.java
index 42d72f4..673e556 100644
--- a/jsr166-tests/src/test/java/jsr166/PhaserTest.java
+++ b/jsr166-tests/src/test/java/jsr166/PhaserTest.java
@@ -28,7 +28,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(PhaserTest.class);
// }
private static final int maxParties = 65535;
@@ -342,8 +342,8 @@
* registered or unarrived parties would become negative
*/
public void testArriveAndDeregister1() {
+ Phaser phaser = new Phaser();
try {
- Phaser phaser = new Phaser();
phaser.arriveAndDeregister();
shouldThrow();
} catch (IllegalStateException success) {}
@@ -629,11 +629,11 @@
threads.add(newStartedThread(new CheckedRunnable() {
public void realRun() {
for (int k = 0; k < 3; k++) {
- assertEquals(2*k+1, phaser.arriveAndAwaitAdvance());
+ assertEquals(2 * k + 1, phaser.arriveAndAwaitAdvance());
count.incrementAndGet();
- assertEquals(2*k+1, phaser.arrive());
- assertEquals(2*k+2, phaser.awaitAdvance(2*k+1));
- assertEquals(4*(k+1), count.get());
+ assertEquals(2 * k + 1, phaser.arrive());
+ assertEquals(2 * k + 2, phaser.awaitAdvance(2 * k + 1));
+ assertEquals(4 * (k + 1), count.get());
}}}));
for (Thread thread : threads)
@@ -689,7 +689,7 @@
for (Phaser phaser : phasers) {
assertEquals(-42, phaser.awaitAdvance(-42));
assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42));
- assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42, SMALL_DELAY_MS, MILLISECONDS));
+ assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42, MEDIUM_DELAY_MS, MILLISECONDS));
}
for (Phaser child : onePartyChildren)
@@ -697,10 +697,10 @@
for (Phaser phaser : phasers) {
assertEquals(-42, phaser.awaitAdvance(-42));
assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42));
- assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42, SMALL_DELAY_MS, MILLISECONDS));
+ assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42, MEDIUM_DELAY_MS, MILLISECONDS));
assertEquals(1, phaser.awaitAdvance(0));
assertEquals(1, phaser.awaitAdvanceInterruptibly(0));
- assertEquals(1, phaser.awaitAdvanceInterruptibly(0, SMALL_DELAY_MS, MILLISECONDS));
+ assertEquals(1, phaser.awaitAdvanceInterruptibly(0, MEDIUM_DELAY_MS, MILLISECONDS));
}
for (Phaser child : onePartyChildren)
@@ -708,13 +708,13 @@
for (Phaser phaser : phasers) {
assertEquals(-42, phaser.awaitAdvance(-42));
assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42));
- assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42, SMALL_DELAY_MS, MILLISECONDS));
+ assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42, MEDIUM_DELAY_MS, MILLISECONDS));
assertEquals(2, phaser.awaitAdvance(0));
assertEquals(2, phaser.awaitAdvanceInterruptibly(0));
- assertEquals(2, phaser.awaitAdvanceInterruptibly(0, SMALL_DELAY_MS, MILLISECONDS));
+ assertEquals(2, phaser.awaitAdvanceInterruptibly(0, MEDIUM_DELAY_MS, MILLISECONDS));
assertEquals(2, phaser.awaitAdvance(1));
assertEquals(2, phaser.awaitAdvanceInterruptibly(1));
- assertEquals(2, phaser.awaitAdvanceInterruptibly(1, SMALL_DELAY_MS, MILLISECONDS));
+ assertEquals(2, phaser.awaitAdvanceInterruptibly(1, MEDIUM_DELAY_MS, MILLISECONDS));
}
}
@@ -752,8 +752,8 @@
* unarrived parties
*/
public void testArriveAndAwaitAdvance1() {
+ Phaser phaser = new Phaser();
try {
- Phaser phaser = new Phaser();
phaser.arriveAndAwaitAdvance();
shouldThrow();
} catch (IllegalStateException success) {}
diff --git a/jsr166-tests/src/test/java/jsr166/PriorityBlockingQueueTest.java b/jsr166-tests/src/test/java/jsr166/PriorityBlockingQueueTest.java
index 64c3b3a..41b06f5 100644
--- a/jsr166-tests/src/test/java/jsr166/PriorityBlockingQueueTest.java
+++ b/jsr166-tests/src/test/java/jsr166/PriorityBlockingQueueTest.java
@@ -27,7 +27,7 @@
public class PriorityBlockingQueueTest extends JSR166TestCase {
- // android-note: These tests have been moved into their own separate
+ // android-note: These tests have been moved into their own separate
// classes to work around CTS issues.
//
// public static class Generic extends BlockingQueueTest {
@@ -35,17 +35,19 @@
// return new PriorityBlockingQueue();
// }
// }
- //
+
// public static class InitialCapacity extends BlockingQueueTest {
// protected BlockingQueue emptyCollection() {
// return new PriorityBlockingQueue(SIZE);
// }
// }
+
+ // android-note: Removed because the CTS runner does a bad job of
+ // retrying tests that have suite() declarations.
//
// public static void main(String[] args) {
// main(suite(), args);
// }
- //
// public static Test suite() {
// return newTestSuite(PriorityBlockingQueueTest.class,
// new Generic().testSuite(),
@@ -67,7 +69,7 @@
PriorityBlockingQueue<Integer> q =
new PriorityBlockingQueue<Integer>(n);
assertTrue(q.isEmpty());
- for (int i = n-1; i >= 0; i -= 2)
+ for (int i = n - 1; i >= 0; i -= 2)
assertTrue(q.offer(new Integer(i)));
for (int i = (n & 1); i < n; i += 2)
assertTrue(q.offer(new Integer(i)));
@@ -121,7 +123,7 @@
*/
public void testConstructor5() {
Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
+ for (int i = 0; i < SIZE - 1; ++i)
ints[i] = i;
Collection<Integer> elements = Arrays.asList(ints);
try {
@@ -153,7 +155,7 @@
for (int i = 0; i < SIZE; ++i)
ints[i] = new Integer(i);
q.addAll(Arrays.asList(ints));
- for (int i = SIZE-1; i >= 0; --i)
+ for (int i = SIZE - 1; i >= 0; --i)
assertEquals(ints[i], q.poll());
}
@@ -225,8 +227,8 @@
* addAll(this) throws IAE
*/
public void testAddAllSelf() {
+ PriorityBlockingQueue q = populatedQueue(SIZE);
try {
- PriorityBlockingQueue q = populatedQueue(SIZE);
q.addAll(q);
shouldThrow();
} catch (IllegalArgumentException success) {}
@@ -237,11 +239,11 @@
* possibly adding some elements
*/
public void testAddAll3() {
+ PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE);
+ Integer[] ints = new Integer[SIZE];
+ for (int i = 0; i < SIZE - 1; ++i)
+ ints[i] = new Integer(i);
try {
- PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE);
- Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
- ints[i] = new Integer(i);
q.addAll(Arrays.asList(ints));
shouldThrow();
} catch (NullPointerException success) {}
@@ -253,7 +255,7 @@
public void testAddAll5() {
Integer[] empty = new Integer[0];
Integer[] ints = new Integer[SIZE];
- for (int i = SIZE-1; i >= 0; --i)
+ for (int i = SIZE - 1; i >= 0; --i)
ints[i] = new Integer(i);
PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE);
assertFalse(q.addAll(Arrays.asList(empty)));
@@ -398,25 +400,23 @@
final CountDownLatch aboutToWait = new CountDownLatch(1);
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
+ long startTime = System.nanoTime();
for (int i = 0; i < SIZE; ++i) {
- long t0 = System.nanoTime();
assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
- assertTrue(millisElapsedSince(t0) < SMALL_DELAY_MS);
}
- long t0 = System.nanoTime();
aboutToWait.countDown();
try {
q.poll(LONG_DELAY_MS, MILLISECONDS);
shouldThrow();
} catch (InterruptedException success) {
- assertTrue(millisElapsedSince(t0) < MEDIUM_DELAY_MS);
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}
}});
aboutToWait.await();
- waitForThreadToEnterWaitState(t, SMALL_DELAY_MS);
+ waitForThreadToEnterWaitState(t, LONG_DELAY_MS);
t.interrupt();
- awaitTermination(t, MEDIUM_DELAY_MS);
+ awaitTermination(t);
}
/**
@@ -517,7 +517,7 @@
assertTrue(changed);
assertTrue(q.containsAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
p.remove();
}
}
@@ -530,7 +530,7 @@
PriorityBlockingQueue q = populatedQueue(SIZE);
PriorityBlockingQueue p = populatedQueue(i);
assertTrue(q.removeAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
for (int j = 0; j < i; ++j) {
Integer x = (Integer)(p.remove());
assertFalse(q.contains(x));
@@ -629,22 +629,22 @@
public void testPollInExecutor() {
final PriorityBlockingQueue q = new PriorityBlockingQueue(2);
final CheckedBarrier threadsStarted = new CheckedBarrier(2);
- ExecutorService executor = Executors.newFixedThreadPool(2);
- executor.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- assertNull(q.poll());
- threadsStarted.await();
- assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
- checkEmpty(q);
- }});
+ final ExecutorService executor = Executors.newFixedThreadPool(2);
+ try (PoolCleaner cleaner = cleaner(executor)) {
+ executor.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ assertNull(q.poll());
+ threadsStarted.await();
+ assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+ checkEmpty(q);
+ }});
- executor.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- threadsStarted.await();
- q.put(one);
- }});
-
- joinPool(executor);
+ executor.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ threadsStarted.await();
+ q.put(one);
+ }});
+ }
}
/**
@@ -694,7 +694,7 @@
final PriorityBlockingQueue q = populatedQueue(SIZE);
Thread t = new Thread(new CheckedRunnable() {
public void realRun() {
- q.put(new Integer(SIZE+1));
+ q.put(new Integer(SIZE + 1));
}});
t.start();
@@ -711,7 +711,7 @@
* drainTo(c, n) empties first min(n, size) elements of queue into c
*/
public void testDrainToN() {
- PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE*2);
+ PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE * 2);
for (int i = 0; i < SIZE + 2; ++i) {
for (int j = 0; j < SIZE; j++)
assertTrue(q.offer(new Integer(j)));
@@ -719,7 +719,7 @@
q.drainTo(l, i);
int k = (i < SIZE) ? i : SIZE;
assertEquals(k, l.size());
- assertEquals(SIZE-k, q.size());
+ assertEquals(SIZE - k, q.size());
for (int j = 0; j < k; ++j)
assertEquals(l.get(j), new Integer(j));
do {} while (q.poll() != null);
diff --git a/jsr166-tests/src/test/java/jsr166/PriorityQueueTest.java b/jsr166-tests/src/test/java/jsr166/PriorityQueueTest.java
index 88cdd37..f64ef68 100644
--- a/jsr166-tests/src/test/java/jsr166/PriorityQueueTest.java
+++ b/jsr166-tests/src/test/java/jsr166/PriorityQueueTest.java
@@ -27,7 +27,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(PriorityQueueTest.class);
// }
static class MyReverseComparator implements Comparator {
@@ -43,7 +43,7 @@
private PriorityQueue<Integer> populatedQueue(int n) {
PriorityQueue<Integer> q = new PriorityQueue<Integer>(n);
assertTrue(q.isEmpty());
- for (int i = n-1; i >= 0; i -= 2)
+ for (int i = n - 1; i >= 0; i -= 2)
assertTrue(q.offer(new Integer(i)));
for (int i = (n & 1); i < n; i += 2)
assertTrue(q.offer(new Integer(i)));
@@ -84,8 +84,7 @@
*/
public void testConstructor4() {
try {
- Integer[] ints = new Integer[SIZE];
- new PriorityQueue(Arrays.asList(ints));
+ new PriorityQueue(Arrays.asList(new Integer[SIZE]));
shouldThrow();
} catch (NullPointerException success) {}
}
@@ -94,10 +93,10 @@
* Initializing from Collection with some null elements throws NPE
*/
public void testConstructor5() {
+ Integer[] ints = new Integer[SIZE];
+ for (int i = 0; i < SIZE - 1; ++i)
+ ints[i] = new Integer(i);
try {
- Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
- ints[i] = new Integer(i);
new PriorityQueue(Arrays.asList(ints));
shouldThrow();
} catch (NullPointerException success) {}
@@ -126,7 +125,7 @@
for (int i = 0; i < SIZE; ++i)
ints[i] = new Integer(i);
q.addAll(Arrays.asList(ints));
- for (int i = SIZE-1; i >= 0; --i)
+ for (int i = SIZE - 1; i >= 0; --i)
assertEquals(ints[i], q.poll());
}
@@ -150,7 +149,7 @@
public void testSize() {
PriorityQueue q = populatedQueue(SIZE);
for (int i = 0; i < SIZE; ++i) {
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
q.remove();
}
for (int i = 0; i < SIZE; ++i) {
@@ -163,8 +162,8 @@
* offer(null) throws NPE
*/
public void testOfferNull() {
+ PriorityQueue q = new PriorityQueue(1);
try {
- PriorityQueue q = new PriorityQueue(1);
q.offer(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -174,8 +173,8 @@
* add(null) throws NPE
*/
public void testAddNull() {
+ PriorityQueue q = new PriorityQueue(1);
try {
- PriorityQueue q = new PriorityQueue(1);
q.add(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -217,8 +216,8 @@
* addAll(null) throws NPE
*/
public void testAddAll1() {
+ PriorityQueue q = new PriorityQueue(1);
try {
- PriorityQueue q = new PriorityQueue(1);
q.addAll(null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -228,10 +227,9 @@
* addAll of a collection with null elements throws NPE
*/
public void testAddAll2() {
+ PriorityQueue q = new PriorityQueue(SIZE);
try {
- PriorityQueue q = new PriorityQueue(SIZE);
- Integer[] ints = new Integer[SIZE];
- q.addAll(Arrays.asList(ints));
+ q.addAll(Arrays.asList(new Integer[SIZE]));
shouldThrow();
} catch (NullPointerException success) {}
}
@@ -241,11 +239,11 @@
* possibly adding some elements
*/
public void testAddAll3() {
+ PriorityQueue q = new PriorityQueue(SIZE);
+ Integer[] ints = new Integer[SIZE];
+ for (int i = 0; i < SIZE - 1; ++i)
+ ints[i] = new Integer(i);
try {
- PriorityQueue q = new PriorityQueue(SIZE);
- Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
- ints[i] = new Integer(i);
q.addAll(Arrays.asList(ints));
shouldThrow();
} catch (NullPointerException success) {}
@@ -258,7 +256,7 @@
Integer[] empty = new Integer[0];
Integer[] ints = new Integer[SIZE];
for (int i = 0; i < SIZE; ++i)
- ints[i] = new Integer(SIZE-1-i);
+ ints[i] = new Integer(SIZE - 1 - i);
PriorityQueue q = new PriorityQueue(SIZE);
assertFalse(q.addAll(Arrays.asList(empty)));
assertTrue(q.addAll(Arrays.asList(ints)));
@@ -329,14 +327,14 @@
assertTrue(q.contains(i));
assertTrue(q.remove(i));
assertFalse(q.contains(i));
- assertTrue(q.contains(i-1));
+ assertTrue(q.contains(i - 1));
}
for (int i = 0; i < SIZE; i += 2) {
assertTrue(q.contains(i));
assertTrue(q.remove(i));
assertFalse(q.contains(i));
- assertFalse(q.remove(i+1));
- assertFalse(q.contains(i+1));
+ assertFalse(q.remove(i + 1));
+ assertFalse(q.contains(i + 1));
}
assertTrue(q.isEmpty());
}
@@ -395,7 +393,7 @@
assertTrue(changed);
assertTrue(q.containsAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
p.remove();
}
}
@@ -408,7 +406,7 @@
PriorityQueue q = populatedQueue(SIZE);
PriorityQueue p = populatedQueue(i);
assertTrue(q.removeAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
for (int j = 0; j < i; ++j) {
Integer x = (Integer)(p.remove());
assertFalse(q.contains(x));
diff --git a/jsr166-tests/src/test/java/jsr166/RecursiveActionTest.java b/jsr166-tests/src/test/java/jsr166/RecursiveActionTest.java
index 1c3bba8..c8e33be 100644
--- a/jsr166-tests/src/test/java/jsr166/RecursiveActionTest.java
+++ b/jsr166-tests/src/test/java/jsr166/RecursiveActionTest.java
@@ -32,7 +32,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(RecursiveActionTest.class);
// }
private static ForkJoinPool mainPool() {
@@ -50,14 +50,12 @@
}
private void testInvokeOnPool(ForkJoinPool pool, RecursiveAction a) {
- try {
+ try (PoolCleaner cleaner = cleaner(pool)) {
checkNotDone(a);
assertNull(pool.invoke(a));
checkCompletedNormally(a);
- } finally {
- joinPool(pool);
}
}
@@ -429,12 +427,12 @@
t = newStartedThread(r);
testInvokeOnPool(mainPool(), a);
- awaitTermination(t, LONG_DELAY_MS);
+ awaitTermination(t);
a.reinitialize();
t = newStartedThread(r);
testInvokeOnPool(singletonPool(), a);
- awaitTermination(t, LONG_DELAY_MS);
+ awaitTermination(t);
}
/**
diff --git a/jsr166-tests/src/test/java/jsr166/RecursiveTaskTest.java b/jsr166-tests/src/test/java/jsr166/RecursiveTaskTest.java
index 7783370..2c07c2a 100644
--- a/jsr166-tests/src/test/java/jsr166/RecursiveTaskTest.java
+++ b/jsr166-tests/src/test/java/jsr166/RecursiveTaskTest.java
@@ -28,7 +28,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(RecursiveTaskTest.class);
// }
private static ForkJoinPool mainPool() {
@@ -46,15 +46,13 @@
}
private <T> T testInvokeOnPool(ForkJoinPool pool, RecursiveTask<T> a) {
- try {
+ try (PoolCleaner cleaner = cleaner(pool)) {
checkNotDone(a);
T result = pool.invoke(a);
checkCompletedNormally(a, result);
return result;
- } finally {
- joinPool(pool);
}
}
@@ -335,6 +333,8 @@
FibTask f = new FibTask(8);
assertSame(f, f.fork());
helpQuiesce();
+ while (!f.isDone()) // wait out race
+ ;
assertEquals(0, getQueuedTaskCount());
checkCompletedNormally(f, 21);
return NoResult;
diff --git a/jsr166-tests/src/test/java/jsr166/ReentrantLockTest.java b/jsr166-tests/src/test/java/jsr166/ReentrantLockTest.java
index 17eaf76..0024ff3 100644
--- a/jsr166-tests/src/test/java/jsr166/ReentrantLockTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ReentrantLockTest.java
@@ -30,8 +30,9 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(ReentrantLockTest.class);
// }
+
/**
* A checked runnable calling lockInterruptibly
*/
@@ -150,7 +151,7 @@
enum AwaitMethod { await, awaitTimed, awaitNanos, awaitUntil }
/**
- * Awaits condition using the specified AwaitMethod.
+ * Awaits condition "indefinitely" using the specified AwaitMethod.
*/
void await(Condition c, AwaitMethod awaitMethod)
throws InterruptedException {
@@ -163,9 +164,10 @@
assertTrue(c.await(timeoutMillis, MILLISECONDS));
break;
case awaitNanos:
- long nanosTimeout = MILLISECONDS.toNanos(timeoutMillis);
- long nanosRemaining = c.awaitNanos(nanosTimeout);
- assertTrue(nanosRemaining > 0);
+ long timeoutNanos = MILLISECONDS.toNanos(timeoutMillis);
+ long nanosRemaining = c.awaitNanos(timeoutNanos);
+ assertTrue(nanosRemaining > timeoutNanos / 2);
+ assertTrue(nanosRemaining <= timeoutNanos);
break;
case awaitUntil:
assertTrue(c.awaitUntil(delayedDate(timeoutMillis)));
@@ -428,7 +430,7 @@
}
for (int i = SIZE; i > 0; i--) {
lock.unlock();
- assertEquals(i-1, lock.getHoldCount());
+ assertEquals(i - 1, lock.getHoldCount());
}
}
@@ -568,11 +570,11 @@
final ReentrantLock lock = new ReentrantLock(fair);
final Condition c = lock.newCondition();
lock.lock();
- long startTime = System.nanoTime();
- long timeoutMillis = 10;
- java.util.Date d = new java.util.Date();
- assertFalse(c.awaitUntil(new java.util.Date(d.getTime() + timeoutMillis)));
- assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+ // We shouldn't assume that nanoTime and currentTimeMillis
+ // use the same time source, so don't use nanoTime here.
+ java.util.Date delayedDate = delayedDate(timeoutMillis());
+ assertFalse(c.awaitUntil(delayedDate));
+ assertTrue(new java.util.Date().getTime() >= delayedDate.getTime());
lock.unlock();
} catch (InterruptedException fail) { threadUnexpectedException(fail); }
}
diff --git a/jsr166-tests/src/test/java/jsr166/ReentrantReadWriteLockTest.java b/jsr166-tests/src/test/java/jsr166/ReentrantReadWriteLockTest.java
index 7ef8ea3..918f45d 100644
--- a/jsr166-tests/src/test/java/jsr166/ReentrantReadWriteLockTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ReentrantReadWriteLockTest.java
@@ -31,7 +31,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(ReentrantReadWriteLockTest.class);
// }
/**
@@ -160,24 +160,26 @@
enum AwaitMethod { await, awaitTimed, awaitNanos, awaitUntil }
/**
- * Awaits condition using the specified AwaitMethod.
+ * Awaits condition "indefinitely" using the specified AwaitMethod.
*/
void await(Condition c, AwaitMethod awaitMethod)
throws InterruptedException {
+ long timeoutMillis = 2 * LONG_DELAY_MS;
switch (awaitMethod) {
case await:
c.await();
break;
case awaitTimed:
- assertTrue(c.await(2 * LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(c.await(timeoutMillis, MILLISECONDS));
break;
case awaitNanos:
- long nanosRemaining = c.awaitNanos(MILLISECONDS.toNanos(2 * LONG_DELAY_MS));
- assertTrue(nanosRemaining > 0);
+ long timeoutNanos = MILLISECONDS.toNanos(timeoutMillis);
+ long nanosRemaining = c.awaitNanos(timeoutNanos);
+ assertTrue(nanosRemaining > timeoutNanos / 2);
+ assertTrue(nanosRemaining <= timeoutNanos);
break;
case awaitUntil:
- java.util.Date d = new java.util.Date();
- assertTrue(c.awaitUntil(new java.util.Date(d.getTime() + 2 * LONG_DELAY_MS)));
+ assertTrue(c.awaitUntil(delayedDate(timeoutMillis)));
break;
default:
throw new AssertionError();
@@ -241,7 +243,7 @@
}
for (int i = SIZE; i > 0; i--) {
lock.writeLock().unlock();
- assertEquals(i-1,lock.getWriteHoldCount());
+ assertEquals(i - 1,lock.getWriteHoldCount());
}
}
@@ -258,7 +260,7 @@
}
for (int i = SIZE; i > 0; i--) {
lock.writeLock().unlock();
- assertEquals(i-1,lock.writeLock().getHoldCount());
+ assertEquals(i - 1,lock.writeLock().getHoldCount());
}
}
@@ -275,7 +277,7 @@
}
for (int i = SIZE; i > 0; i--) {
lock.readLock().unlock();
- assertEquals(i-1,lock.getReadHoldCount());
+ assertEquals(i - 1,lock.getReadHoldCount());
}
}
@@ -972,11 +974,11 @@
new ReentrantReadWriteLock(fair);
final Condition c = lock.writeLock().newCondition();
lock.writeLock().lock();
- long startTime = System.nanoTime();
- long timeoutMillis = 10;
- java.util.Date d = new java.util.Date();
- assertFalse(c.awaitUntil(new java.util.Date(d.getTime() + timeoutMillis)));
- assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+ // We shouldn't assume that nanoTime and currentTimeMillis
+ // use the same time source, so don't use nanoTime here.
+ java.util.Date delayedDate = delayedDate(timeoutMillis());
+ assertFalse(c.awaitUntil(delayedDate));
+ assertTrue(new java.util.Date().getTime() >= delayedDate.getTime());
lock.writeLock().unlock();
} catch (InterruptedException fail) { threadUnexpectedException(fail); }
}
diff --git a/jsr166-tests/src/test/java/jsr166/ScheduledExecutorSubclassTest.java b/jsr166-tests/src/test/java/jsr166/ScheduledExecutorSubclassTest.java
index a93feea..194dd58 100644
--- a/jsr166-tests/src/test/java/jsr166/ScheduledExecutorSubclassTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ScheduledExecutorSubclassTest.java
@@ -7,11 +7,15 @@
package jsr166;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Delayed;
import java.util.concurrent.ExecutionException;
@@ -27,7 +31,9 @@
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
import junit.framework.Test;
import junit.framework.TestSuite;
@@ -40,7 +46,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(ScheduledExecutorSubclassTest.class);
// }
static class CustomTask<V> implements RunnableScheduledFuture<V> {
@@ -101,17 +107,13 @@
* execute successfully executes a runnable
*/
public void testExecute() throws InterruptedException {
- CustomExecutor p = new CustomExecutor(1);
- final CountDownLatch done = new CountDownLatch(1);
- final Runnable task = new CheckedRunnable() {
- public void realRun() {
- done.countDown();
- }};
- try {
+ final CustomExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final CountDownLatch done = new CountDownLatch(1);
+ final Runnable task = new CheckedRunnable() {
+ public void realRun() { done.countDown(); }};
p.execute(task);
- assertTrue(done.await(SMALL_DELAY_MS, MILLISECONDS));
- } finally {
- joinPool(p);
+ await(done);
}
}
@@ -119,10 +121,10 @@
* delayed schedule of callable successfully executes after delay
*/
public void testSchedule1() throws Exception {
- CustomExecutor p = new CustomExecutor(1);
- final long startTime = System.nanoTime();
final CountDownLatch done = new CountDownLatch(1);
- try {
+ final CustomExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ final long startTime = System.nanoTime();
Callable task = new CheckedCallable<Boolean>() {
public Boolean realCall() {
done.countDown();
@@ -132,9 +134,6 @@
Future f = p.schedule(task, timeoutMillis(), MILLISECONDS);
assertSame(Boolean.TRUE, f.get());
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
- assertTrue(done.await(0L, MILLISECONDS));
- } finally {
- joinPool(p);
}
}
@@ -142,10 +141,10 @@
* delayed schedule of runnable successfully executes after delay
*/
public void testSchedule3() throws Exception {
- CustomExecutor p = new CustomExecutor(1);
- final long startTime = System.nanoTime();
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ final CustomExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final long startTime = System.nanoTime();
+ final CountDownLatch done = new CountDownLatch(1);
Runnable task = new CheckedRunnable() {
public void realRun() {
done.countDown();
@@ -155,8 +154,6 @@
await(done);
assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
- } finally {
- joinPool(p);
}
}
@@ -164,10 +161,10 @@
* scheduleAtFixedRate executes runnable after given initial delay
*/
public void testSchedule4() throws InterruptedException {
- CustomExecutor p = new CustomExecutor(1);
- final long startTime = System.nanoTime();
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ final CustomExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final long startTime = System.nanoTime();
+ final CountDownLatch done = new CountDownLatch(1);
Runnable task = new CheckedRunnable() {
public void realRun() {
done.countDown();
@@ -179,8 +176,6 @@
await(done);
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
f.cancel(true);
- } finally {
- joinPool(p);
}
}
@@ -188,10 +183,10 @@
* scheduleWithFixedDelay executes runnable after given initial delay
*/
public void testSchedule5() throws InterruptedException {
- CustomExecutor p = new CustomExecutor(1);
- final long startTime = System.nanoTime();
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ final CustomExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final long startTime = System.nanoTime();
+ final CountDownLatch done = new CountDownLatch(1);
Runnable task = new CheckedRunnable() {
public void realRun() {
done.countDown();
@@ -203,8 +198,6 @@
await(done);
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
f.cancel(true);
- } finally {
- joinPool(p);
}
}
@@ -214,58 +207,77 @@
}
/**
- * scheduleAtFixedRate executes series of tasks at given rate
+ * scheduleAtFixedRate executes series of tasks at given rate.
+ * Eventually, it must hold that:
+ * cycles - 1 <= elapsedMillis/delay < cycles
*/
public void testFixedRateSequence() throws InterruptedException {
- CustomExecutor p = new CustomExecutor(1);
- try {
+ final CustomExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
for (int delay = 1; delay <= LONG_DELAY_MS; delay *= 3) {
- long startTime = System.nanoTime();
- int cycles = 10;
+ final long startTime = System.nanoTime();
+ final int cycles = 8;
final CountDownLatch done = new CountDownLatch(cycles);
- Runnable task = new CheckedRunnable() {
+ final Runnable task = new CheckedRunnable() {
public void realRun() { done.countDown(); }};
- ScheduledFuture h =
+ final ScheduledFuture periodicTask =
p.scheduleAtFixedRate(task, 0, delay, MILLISECONDS);
- done.await();
- h.cancel(true);
- double normalizedTime =
- (double) millisElapsedSince(startTime) / delay;
- if (normalizedTime >= cycles - 1 &&
- normalizedTime <= cycles)
+ final int totalDelayMillis = (cycles - 1) * delay;
+ await(done, totalDelayMillis + LONG_DELAY_MS);
+ periodicTask.cancel(true);
+ final long elapsedMillis = millisElapsedSince(startTime);
+ assertTrue(elapsedMillis >= totalDelayMillis);
+ if (elapsedMillis <= cycles * delay)
return;
+ // else retry with longer delay
}
- throw new AssertionError("unexpected execution rate");
- } finally {
- joinPool(p);
+ fail("unexpected execution rate");
}
}
/**
- * scheduleWithFixedDelay executes series of tasks with given period
+ * scheduleWithFixedDelay executes series of tasks with given period.
+ * Eventually, it must hold that each task starts at least delay and at
+ * most 2 * delay after the termination of the previous task.
*/
public void testFixedDelaySequence() throws InterruptedException {
- CustomExecutor p = new CustomExecutor(1);
- try {
+ final CustomExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
for (int delay = 1; delay <= LONG_DELAY_MS; delay *= 3) {
- long startTime = System.nanoTime();
- int cycles = 10;
+ final long startTime = System.nanoTime();
+ final AtomicLong previous = new AtomicLong(startTime);
+ final AtomicBoolean tryLongerDelay = new AtomicBoolean(false);
+ final int cycles = 8;
final CountDownLatch done = new CountDownLatch(cycles);
- Runnable task = new CheckedRunnable() {
- public void realRun() { done.countDown(); }};
- ScheduledFuture h =
+ final int d = delay;
+ final Runnable task = new CheckedRunnable() {
+ public void realRun() {
+ long now = System.nanoTime();
+ long elapsedMillis
+ = NANOSECONDS.toMillis(now - previous.get());
+ if (done.getCount() == cycles) { // first execution
+ if (elapsedMillis >= d)
+ tryLongerDelay.set(true);
+ } else {
+ assertTrue(elapsedMillis >= d);
+ if (elapsedMillis >= 2 * d)
+ tryLongerDelay.set(true);
+ }
+ previous.set(now);
+ done.countDown();
+ }};
+ final ScheduledFuture periodicTask =
p.scheduleWithFixedDelay(task, 0, delay, MILLISECONDS);
- done.await();
- h.cancel(true);
- double normalizedTime =
- (double) millisElapsedSince(startTime) / delay;
- if (normalizedTime >= cycles - 1 &&
- normalizedTime <= cycles)
+ final int totalDelayMillis = (cycles - 1) * delay;
+ await(done, totalDelayMillis + cycles * LONG_DELAY_MS);
+ periodicTask.cancel(true);
+ final long elapsedMillis = millisElapsedSince(startTime);
+ assertTrue(elapsedMillis >= totalDelayMillis);
+ if (!tryLongerDelay.get())
return;
+ // else retry with longer delay
}
- throw new AssertionError("unexpected execution rate");
- } finally {
- joinPool(p);
+ fail("unexpected execution rate");
}
}
@@ -273,106 +285,107 @@
* execute(null) throws NPE
*/
public void testExecuteNull() throws InterruptedException {
- CustomExecutor se = new CustomExecutor(1);
- try {
- se.execute(null);
- shouldThrow();
- } catch (NullPointerException success) {}
- joinPool(se);
+ final CustomExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.execute(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
+ }
}
/**
* schedule(null) throws NPE
*/
public void testScheduleNull() throws InterruptedException {
- CustomExecutor se = new CustomExecutor(1);
- try {
- TrackedCallable callable = null;
- Future f = se.schedule(callable, SHORT_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {}
- joinPool(se);
+ final CustomExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ TrackedCallable callable = null;
+ Future f = p.schedule(callable, SHORT_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
+ }
}
/**
* execute throws RejectedExecutionException if shutdown
*/
public void testSchedule1_RejectedExecutionException() {
- CustomExecutor se = new CustomExecutor(1);
- try {
- se.shutdown();
- se.schedule(new NoOpRunnable(),
- MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (RejectedExecutionException success) {
- } catch (SecurityException ok) {
+ final CustomExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.shutdown();
+ p.schedule(new NoOpRunnable(),
+ MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (RejectedExecutionException success) {
+ } catch (SecurityException ok) {}
}
-
- joinPool(se);
}
/**
* schedule throws RejectedExecutionException if shutdown
*/
public void testSchedule2_RejectedExecutionException() {
- CustomExecutor se = new CustomExecutor(1);
- try {
- se.shutdown();
- se.schedule(new NoOpCallable(),
- MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (RejectedExecutionException success) {
- } catch (SecurityException ok) {
+ final CustomExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.shutdown();
+ p.schedule(new NoOpCallable(),
+ MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (RejectedExecutionException success) {
+ } catch (SecurityException ok) {}
}
- joinPool(se);
}
/**
* schedule callable throws RejectedExecutionException if shutdown
*/
public void testSchedule3_RejectedExecutionException() {
- CustomExecutor se = new CustomExecutor(1);
- try {
- se.shutdown();
- se.schedule(new NoOpCallable(),
- MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (RejectedExecutionException success) {
- } catch (SecurityException ok) {
+ final CustomExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.shutdown();
+ p.schedule(new NoOpCallable(),
+ MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (RejectedExecutionException success) {
+ } catch (SecurityException ok) {}
}
- joinPool(se);
}
/**
* scheduleAtFixedRate throws RejectedExecutionException if shutdown
*/
public void testScheduleAtFixedRate1_RejectedExecutionException() {
- CustomExecutor se = new CustomExecutor(1);
- try {
- se.shutdown();
- se.scheduleAtFixedRate(new NoOpRunnable(),
- MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (RejectedExecutionException success) {
- } catch (SecurityException ok) {
+ final CustomExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.shutdown();
+ p.scheduleAtFixedRate(new NoOpRunnable(),
+ MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (RejectedExecutionException success) {
+ } catch (SecurityException ok) {}
}
- joinPool(se);
}
/**
* scheduleWithFixedDelay throws RejectedExecutionException if shutdown
*/
public void testScheduleWithFixedDelay1_RejectedExecutionException() {
- CustomExecutor se = new CustomExecutor(1);
- try {
- se.shutdown();
- se.scheduleWithFixedDelay(new NoOpRunnable(),
- MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (RejectedExecutionException success) {
- } catch (SecurityException ok) {
+ final CustomExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.shutdown();
+ p.scheduleWithFixedDelay(new NoOpRunnable(),
+ MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (RejectedExecutionException success) {
+ } catch (SecurityException ok) {}
}
- joinPool(se);
}
/**
@@ -380,22 +393,19 @@
* thread becomes active
*/
public void testGetActiveCount() throws InterruptedException {
- final ThreadPoolExecutor p = new CustomExecutor(2);
- final CountDownLatch threadStarted = new CountDownLatch(1);
final CountDownLatch done = new CountDownLatch(1);
- try {
+ final ThreadPoolExecutor p = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
assertEquals(0, p.getActiveCount());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
assertEquals(1, p.getActiveCount());
- done.await();
+ await(done);
}});
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
assertEquals(1, p.getActiveCount());
- } finally {
- done.countDown();
- joinPool(p);
}
}
@@ -405,10 +415,10 @@
*/
public void testGetCompletedTaskCount() throws InterruptedException {
final ThreadPoolExecutor p = new CustomExecutor(2);
- final CountDownLatch threadStarted = new CountDownLatch(1);
- final CountDownLatch threadProceed = new CountDownLatch(1);
- final CountDownLatch threadDone = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
+ final CountDownLatch threadProceed = new CountDownLatch(1);
+ final CountDownLatch threadDone = new CountDownLatch(1);
assertEquals(0, p.getCompletedTaskCount());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
@@ -427,8 +437,6 @@
fail("timed out");
Thread.yield();
}
- } finally {
- joinPool(p);
}
}
@@ -436,9 +444,10 @@
* getCorePoolSize returns size given in constructor if not otherwise set
*/
public void testGetCorePoolSize() {
- CustomExecutor p = new CustomExecutor(1);
- assertEquals(1, p.getCorePoolSize());
- joinPool(p);
+ final CustomExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertEquals(1, p.getCorePoolSize());
+ }
}
/**
@@ -447,25 +456,22 @@
*/
public void testGetLargestPoolSize() throws InterruptedException {
final int THREADS = 3;
- final ThreadPoolExecutor p = new CustomExecutor(THREADS);
- final CountDownLatch threadsStarted = new CountDownLatch(THREADS);
final CountDownLatch done = new CountDownLatch(1);
- try {
+ final ThreadPoolExecutor p = new CustomExecutor(THREADS);
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ final CountDownLatch threadsStarted = new CountDownLatch(THREADS);
assertEquals(0, p.getLargestPoolSize());
for (int i = 0; i < THREADS; i++)
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadsStarted.countDown();
- done.await();
+ await(done);
assertEquals(THREADS, p.getLargestPoolSize());
}});
- assertTrue(threadsStarted.await(SMALL_DELAY_MS, MILLISECONDS));
- assertEquals(THREADS, p.getLargestPoolSize());
- } finally {
- done.countDown();
- joinPool(p);
+ await(threadsStarted);
assertEquals(THREADS, p.getLargestPoolSize());
}
+ assertEquals(THREADS, p.getLargestPoolSize());
}
/**
@@ -473,22 +479,19 @@
* become active
*/
public void testGetPoolSize() throws InterruptedException {
- final ThreadPoolExecutor p = new CustomExecutor(1);
- final CountDownLatch threadStarted = new CountDownLatch(1);
final CountDownLatch done = new CountDownLatch(1);
- try {
+ final ThreadPoolExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
assertEquals(0, p.getPoolSize());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
assertEquals(1, p.getPoolSize());
- done.await();
+ await(done);
}});
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
assertEquals(1, p.getPoolSize());
- } finally {
- done.countDown();
- joinPool(p);
}
}
@@ -497,58 +500,70 @@
* submitted
*/
public void testGetTaskCount() throws InterruptedException {
- final ThreadPoolExecutor p = new CustomExecutor(1);
- final CountDownLatch threadStarted = new CountDownLatch(1);
+ final int TASKS = 3;
final CountDownLatch done = new CountDownLatch(1);
- final int TASKS = 5;
- try {
+ final ThreadPoolExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
assertEquals(0, p.getTaskCount());
- for (int i = 0; i < TASKS; i++)
+ assertEquals(0, p.getCompletedTaskCount());
+ p.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ threadStarted.countDown();
+ await(done);
+ }});
+ await(threadStarted);
+ assertEquals(1, p.getTaskCount());
+ assertEquals(0, p.getCompletedTaskCount());
+ for (int i = 0; i < TASKS; i++) {
+ assertEquals(1 + i, p.getTaskCount());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
- done.await();
+ assertEquals(1 + TASKS, p.getTaskCount());
+ await(done);
}});
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
- assertEquals(TASKS, p.getTaskCount());
- } finally {
- done.countDown();
- joinPool(p);
+ }
+ assertEquals(1 + TASKS, p.getTaskCount());
+ assertEquals(0, p.getCompletedTaskCount());
}
+ assertEquals(1 + TASKS, p.getTaskCount());
+ assertEquals(1 + TASKS, p.getCompletedTaskCount());
}
/**
* getThreadFactory returns factory in constructor if not set
*/
public void testGetThreadFactory() {
- ThreadFactory tf = new SimpleThreadFactory();
- CustomExecutor p = new CustomExecutor(1, tf);
- assertSame(tf, p.getThreadFactory());
- joinPool(p);
+ final ThreadFactory threadFactory = new SimpleThreadFactory();
+ final CustomExecutor p = new CustomExecutor(1, threadFactory);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertSame(threadFactory, p.getThreadFactory());
+ }
}
/**
* setThreadFactory sets the thread factory returned by getThreadFactory
*/
public void testSetThreadFactory() {
- ThreadFactory tf = new SimpleThreadFactory();
- CustomExecutor p = new CustomExecutor(1);
- p.setThreadFactory(tf);
- assertSame(tf, p.getThreadFactory());
- joinPool(p);
+ final ThreadFactory threadFactory = new SimpleThreadFactory();
+ final CustomExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ p.setThreadFactory(threadFactory);
+ assertSame(threadFactory, p.getThreadFactory());
+ }
}
/**
* setThreadFactory(null) throws NPE
*/
public void testSetThreadFactoryNull() {
- CustomExecutor p = new CustomExecutor(1);
- try {
- p.setThreadFactory(null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(p);
+ final CustomExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.setThreadFactory(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -556,91 +571,84 @@
* isShutdown is false before shutdown, true after
*/
public void testIsShutdown() {
- CustomExecutor p = new CustomExecutor(1);
- try {
+ final CustomExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
assertFalse(p.isShutdown());
- }
- finally {
try { p.shutdown(); } catch (SecurityException ok) { return; }
+ assertTrue(p.isShutdown());
}
- assertTrue(p.isShutdown());
}
/**
* isTerminated is false before termination, true after
*/
public void testIsTerminated() throws InterruptedException {
- final ThreadPoolExecutor p = new CustomExecutor(1);
- final CountDownLatch threadStarted = new CountDownLatch(1);
final CountDownLatch done = new CountDownLatch(1);
- assertFalse(p.isTerminated());
- try {
+ final ThreadPoolExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
assertFalse(p.isTerminated());
threadStarted.countDown();
- done.await();
+ await(done);
}});
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
+ assertFalse(p.isTerminated());
assertFalse(p.isTerminating());
done.countDown();
- } finally {
try { p.shutdown(); } catch (SecurityException ok) { return; }
+ assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(p.isTerminated());
}
- assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
- assertTrue(p.isTerminated());
}
/**
* isTerminating is not true when running or when terminated
*/
public void testIsTerminating() throws InterruptedException {
- final ThreadPoolExecutor p = new CustomExecutor(1);
- final CountDownLatch threadStarted = new CountDownLatch(1);
final CountDownLatch done = new CountDownLatch(1);
- try {
+ final ThreadPoolExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
assertFalse(p.isTerminating());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
assertFalse(p.isTerminating());
threadStarted.countDown();
- done.await();
+ await(done);
}});
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
assertFalse(p.isTerminating());
done.countDown();
- } finally {
try { p.shutdown(); } catch (SecurityException ok) { return; }
+ assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(p.isTerminated());
+ assertFalse(p.isTerminating());
}
- assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
- assertTrue(p.isTerminated());
- assertFalse(p.isTerminating());
}
/**
* getQueue returns the work queue, which contains queued tasks
*/
public void testGetQueue() throws InterruptedException {
- ScheduledThreadPoolExecutor p = new CustomExecutor(1);
- final CountDownLatch threadStarted = new CountDownLatch(1);
final CountDownLatch done = new CountDownLatch(1);
- try {
+ final ScheduledThreadPoolExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
ScheduledFuture[] tasks = new ScheduledFuture[5];
for (int i = 0; i < tasks.length; i++) {
Runnable r = new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
- done.await();
+ await(done);
}};
tasks[i] = p.schedule(r, 1, MILLISECONDS);
}
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
BlockingQueue<Runnable> q = p.getQueue();
assertTrue(q.contains(tasks[tasks.length - 1]));
assertFalse(q.contains(tasks[0]));
- } finally {
- done.countDown();
- joinPool(p);
}
}
@@ -648,20 +656,20 @@
* remove(task) removes queued task, and fails to remove active task
*/
public void testRemove() throws InterruptedException {
- final ScheduledThreadPoolExecutor p = new CustomExecutor(1);
- ScheduledFuture[] tasks = new ScheduledFuture[5];
- final CountDownLatch threadStarted = new CountDownLatch(1);
final CountDownLatch done = new CountDownLatch(1);
- try {
+ final ScheduledThreadPoolExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ ScheduledFuture[] tasks = new ScheduledFuture[5];
+ final CountDownLatch threadStarted = new CountDownLatch(1);
for (int i = 0; i < tasks.length; i++) {
Runnable r = new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
- done.await();
+ await(done);
}};
tasks[i] = p.schedule(r, 1, MILLISECONDS);
}
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
BlockingQueue<Runnable> q = p.getQueue();
assertFalse(p.remove((Runnable)tasks[0]));
assertTrue(q.contains((Runnable)tasks[4]));
@@ -672,9 +680,6 @@
assertTrue(q.contains((Runnable)tasks[3]));
assertTrue(p.remove((Runnable)tasks[3]));
assertFalse(q.contains((Runnable)tasks[3]));
- } finally {
- done.countDown();
- joinPool(p);
}
}
@@ -682,12 +687,15 @@
* purge removes cancelled tasks from the queue
*/
public void testPurge() throws InterruptedException {
- CustomExecutor p = new CustomExecutor(1);
- ScheduledFuture[] tasks = new ScheduledFuture[5];
- for (int i = 0; i < tasks.length; i++)
- tasks[i] = p.schedule(new SmallPossiblyInterruptedRunnable(),
- LONG_DELAY_MS, MILLISECONDS);
- try {
+ final ScheduledFuture[] tasks = new ScheduledFuture[5];
+ final Runnable releaser = new Runnable() { public void run() {
+ for (ScheduledFuture task : tasks)
+ if (task != null) task.cancel(true); }};
+ final CustomExecutor p = new CustomExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p, releaser)) {
+ for (int i = 0; i < tasks.length; i++)
+ tasks[i] = p.schedule(new SmallPossiblyInterruptedRunnable(),
+ LONG_DELAY_MS, MILLISECONDS);
int max = tasks.length;
if (tasks[4].cancel(true)) --max;
if (tasks[3].cancel(true)) --max;
@@ -699,159 +707,185 @@
long count = p.getTaskCount();
if (count == max)
return;
- } while (millisElapsedSince(startTime) < MEDIUM_DELAY_MS);
+ } while (millisElapsedSince(startTime) < LONG_DELAY_MS);
fail("Purge failed to remove cancelled tasks");
- } finally {
- for (ScheduledFuture task : tasks)
- task.cancel(true);
- joinPool(p);
}
}
/**
- * shutdownNow returns a list containing tasks that were not run
+ * shutdownNow returns a list containing tasks that were not run,
+ * and those tasks are drained from the queue
*/
- public void testShutdownNow() {
- CustomExecutor p = new CustomExecutor(1);
- for (int i = 0; i < 5; i++)
- p.schedule(new SmallPossiblyInterruptedRunnable(),
- LONG_DELAY_MS, MILLISECONDS);
+ public void testShutdownNow() throws InterruptedException {
+ final int poolSize = 2;
+ final int count = 5;
+ final AtomicInteger ran = new AtomicInteger(0);
+ final CustomExecutor p = new CustomExecutor(poolSize);
+ final CountDownLatch threadsStarted = new CountDownLatch(poolSize);
+ Runnable waiter = new CheckedRunnable() { public void realRun() {
+ threadsStarted.countDown();
+ try {
+ MILLISECONDS.sleep(2 * LONG_DELAY_MS);
+ } catch (InterruptedException success) {}
+ ran.getAndIncrement();
+ }};
+ for (int i = 0; i < count; i++)
+ p.execute(waiter);
+ await(threadsStarted);
+ assertEquals(poolSize, p.getActiveCount());
+ assertEquals(0, p.getCompletedTaskCount());
+ final List<Runnable> queuedTasks;
try {
- List<Runnable> l = p.shutdownNow();
- assertTrue(p.isShutdown());
- assertEquals(5, l.size());
+ queuedTasks = p.shutdownNow();
} catch (SecurityException ok) {
- // Allowed in case test doesn't have privs
- } finally {
- joinPool(p);
+ return; // Allowed in case test doesn't have privs
}
- }
-
- /**
- * In default setting, shutdown cancels periodic but not delayed
- * tasks at shutdown
- */
- public void testShutdown1() throws InterruptedException {
- CustomExecutor p = new CustomExecutor(1);
- assertTrue(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
- assertFalse(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
-
- ScheduledFuture[] tasks = new ScheduledFuture[5];
- for (int i = 0; i < tasks.length; i++)
- tasks[i] = p.schedule(new NoOpRunnable(),
- SHORT_DELAY_MS, MILLISECONDS);
- try { p.shutdown(); } catch (SecurityException ok) { return; }
- BlockingQueue<Runnable> q = p.getQueue();
- for (ScheduledFuture task : tasks) {
- assertFalse(task.isDone());
- assertFalse(task.isCancelled());
- assertTrue(q.contains(task));
- }
- assertTrue(p.isShutdown());
- assertTrue(p.awaitTermination(SMALL_DELAY_MS, MILLISECONDS));
- assertTrue(p.isTerminated());
- for (ScheduledFuture task : tasks) {
- assertTrue(task.isDone());
- assertFalse(task.isCancelled());
- }
- }
-
- /**
- * If setExecuteExistingDelayedTasksAfterShutdownPolicy is false,
- * delayed tasks are cancelled at shutdown
- */
- public void testShutdown2() throws InterruptedException {
- CustomExecutor p = new CustomExecutor(1);
- p.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
- assertFalse(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
- assertFalse(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
- ScheduledFuture[] tasks = new ScheduledFuture[5];
- for (int i = 0; i < tasks.length; i++)
- tasks[i] = p.schedule(new NoOpRunnable(),
- SHORT_DELAY_MS, MILLISECONDS);
- BlockingQueue q = p.getQueue();
- assertEquals(tasks.length, q.size());
- try { p.shutdown(); } catch (SecurityException ok) { return; }
- assertTrue(p.isShutdown());
- assertTrue(q.isEmpty());
- assertTrue(p.awaitTermination(SMALL_DELAY_MS, MILLISECONDS));
- assertTrue(p.isTerminated());
- for (ScheduledFuture task : tasks) {
- assertTrue(task.isDone());
- assertTrue(task.isCancelled());
- }
- }
-
- /**
- * If setContinueExistingPeriodicTasksAfterShutdownPolicy is set false,
- * periodic tasks are cancelled at shutdown
- */
- public void testShutdown3() throws InterruptedException {
- CustomExecutor p = new CustomExecutor(1);
- assertTrue(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
- assertFalse(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
- p.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
- assertTrue(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
- assertFalse(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
- long initialDelay = LONG_DELAY_MS;
- ScheduledFuture task =
- p.scheduleAtFixedRate(new NoOpRunnable(), initialDelay,
- 5, MILLISECONDS);
- try { p.shutdown(); } catch (SecurityException ok) { return; }
assertTrue(p.isShutdown());
assertTrue(p.getQueue().isEmpty());
- assertTrue(task.isDone());
- assertTrue(task.isCancelled());
- joinPool(p);
+ assertEquals(count - poolSize, queuedTasks.size());
+ assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(p.isTerminated());
+ assertEquals(poolSize, ran.get());
+ assertEquals(poolSize, p.getCompletedTaskCount());
}
/**
- * if setContinueExistingPeriodicTasksAfterShutdownPolicy is true,
- * periodic tasks are not cancelled at shutdown
+ * shutdownNow returns a list containing tasks that were not run,
+ * and those tasks are drained from the queue
*/
- public void testShutdown4() throws InterruptedException {
- CustomExecutor p = new CustomExecutor(1);
- final CountDownLatch counter = new CountDownLatch(2);
+ public void testShutdownNow_delayedTasks() throws InterruptedException {
+ final CustomExecutor p = new CustomExecutor(1);
+ List<ScheduledFuture> tasks = new ArrayList<>();
+ for (int i = 0; i < 3; i++) {
+ Runnable r = new NoOpRunnable();
+ tasks.add(p.schedule(r, 9, SECONDS));
+ tasks.add(p.scheduleAtFixedRate(r, 9, 9, SECONDS));
+ tasks.add(p.scheduleWithFixedDelay(r, 9, 9, SECONDS));
+ }
+ if (testImplementationDetails)
+ assertEquals(new HashSet(tasks), new HashSet(p.getQueue()));
+ final List<Runnable> queuedTasks;
try {
- p.setContinueExistingPeriodicTasksAfterShutdownPolicy(true);
- assertTrue(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
- assertTrue(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
- final Runnable r = new CheckedRunnable() {
- public void realRun() {
- counter.countDown();
- }};
- ScheduledFuture task =
- p.scheduleAtFixedRate(r, 1, 1, MILLISECONDS);
+ queuedTasks = p.shutdownNow();
+ } catch (SecurityException ok) {
+ return; // Allowed in case test doesn't have privs
+ }
+ assertTrue(p.isShutdown());
+ assertTrue(p.getQueue().isEmpty());
+ if (testImplementationDetails)
+ assertEquals(new HashSet(tasks), new HashSet(queuedTasks));
+ assertEquals(tasks.size(), queuedTasks.size());
+ for (ScheduledFuture task : tasks) {
+ assertFalse(((CustomTask)task).ran);
assertFalse(task.isDone());
assertFalse(task.isCancelled());
- try { p.shutdown(); } catch (SecurityException ok) { return; }
- assertFalse(task.isCancelled());
- assertFalse(p.isTerminated());
- assertTrue(p.isShutdown());
- assertTrue(counter.await(SMALL_DELAY_MS, MILLISECONDS));
- assertFalse(task.isCancelled());
- assertTrue(task.cancel(false));
- assertTrue(task.isDone());
- assertTrue(task.isCancelled());
- assertTrue(p.awaitTermination(SMALL_DELAY_MS, MILLISECONDS));
- assertTrue(p.isTerminated());
}
- finally {
- joinPool(p);
- }
+ assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(p.isTerminated());
}
/**
+ * By default, periodic tasks are cancelled at shutdown.
+ * By default, delayed tasks keep running after shutdown.
+ * Check that changing the default values work:
+ * - setExecuteExistingDelayedTasksAfterShutdownPolicy
+ * - setContinueExistingPeriodicTasksAfterShutdownPolicy
+ */
+ public void testShutdown_cancellation() throws Exception {
+ Boolean[] allBooleans = { null, Boolean.FALSE, Boolean.TRUE };
+ for (Boolean policy : allBooleans)
+ {
+ final int poolSize = 2;
+ final CustomExecutor p = new CustomExecutor(poolSize);
+ final boolean effectiveDelayedPolicy = (policy != Boolean.FALSE);
+ final boolean effectivePeriodicPolicy = (policy == Boolean.TRUE);
+ final boolean effectiveRemovePolicy = (policy == Boolean.TRUE);
+ if (policy != null) {
+ p.setExecuteExistingDelayedTasksAfterShutdownPolicy(policy);
+ p.setContinueExistingPeriodicTasksAfterShutdownPolicy(policy);
+ p.setRemoveOnCancelPolicy(policy);
+ }
+ assertEquals(effectiveDelayedPolicy,
+ p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
+ assertEquals(effectivePeriodicPolicy,
+ p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
+ assertEquals(effectiveRemovePolicy,
+ p.getRemoveOnCancelPolicy());
+ // Strategy: Wedge the pool with poolSize "blocker" threads
+ final AtomicInteger ran = new AtomicInteger(0);
+ final CountDownLatch poolBlocked = new CountDownLatch(poolSize);
+ final CountDownLatch unblock = new CountDownLatch(1);
+ final CountDownLatch periodicLatch1 = new CountDownLatch(2);
+ final CountDownLatch periodicLatch2 = new CountDownLatch(2);
+ Runnable task = new CheckedRunnable() { public void realRun()
+ throws InterruptedException {
+ poolBlocked.countDown();
+ assertTrue(unblock.await(LONG_DELAY_MS, MILLISECONDS));
+ ran.getAndIncrement();
+ }};
+ List<Future<?>> blockers = new ArrayList<>();
+ List<Future<?>> periodics = new ArrayList<>();
+ List<Future<?>> delayeds = new ArrayList<>();
+ for (int i = 0; i < poolSize; i++)
+ blockers.add(p.submit(task));
+ assertTrue(poolBlocked.await(LONG_DELAY_MS, MILLISECONDS));
+
+ periodics.add(p.scheduleAtFixedRate(countDowner(periodicLatch1),
+ 1, 1, MILLISECONDS));
+ periodics.add(p.scheduleWithFixedDelay(countDowner(periodicLatch2),
+ 1, 1, MILLISECONDS));
+ delayeds.add(p.schedule(task, 1, MILLISECONDS));
+
+ assertTrue(p.getQueue().containsAll(periodics));
+ assertTrue(p.getQueue().containsAll(delayeds));
+ try { p.shutdown(); } catch (SecurityException ok) { return; }
+ assertTrue(p.isShutdown());
+ assertFalse(p.isTerminated());
+ for (Future<?> periodic : periodics) {
+ assertTrue(effectivePeriodicPolicy ^ periodic.isCancelled());
+ assertTrue(effectivePeriodicPolicy ^ periodic.isDone());
+ }
+ for (Future<?> delayed : delayeds) {
+ assertTrue(effectiveDelayedPolicy ^ delayed.isCancelled());
+ assertTrue(effectiveDelayedPolicy ^ delayed.isDone());
+ }
+ if (testImplementationDetails) {
+ assertEquals(effectivePeriodicPolicy,
+ p.getQueue().containsAll(periodics));
+ assertEquals(effectiveDelayedPolicy,
+ p.getQueue().containsAll(delayeds));
+ }
+ // Release all pool threads
+ unblock.countDown();
+
+ for (Future<?> delayed : delayeds) {
+ if (effectiveDelayedPolicy) {
+ assertNull(delayed.get());
+ }
+ }
+ if (effectivePeriodicPolicy) {
+ assertTrue(periodicLatch1.await(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(periodicLatch2.await(LONG_DELAY_MS, MILLISECONDS));
+ for (Future<?> periodic : periodics) {
+ assertTrue(periodic.cancel(false));
+ assertTrue(periodic.isCancelled());
+ assertTrue(periodic.isDone());
+ }
+ }
+ assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(p.isTerminated());
+ assertEquals(2 + (effectiveDelayedPolicy ? 1 : 0), ran.get());
+ }}
+
+ /**
* completed submit of callable returns result
*/
public void testSubmitCallable() throws Exception {
- ExecutorService e = new CustomExecutor(2);
- try {
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
Future<String> future = e.submit(new StringTask());
String result = future.get();
assertSame(TEST_STRING, result);
- } finally {
- joinPool(e);
}
}
@@ -859,13 +893,11 @@
* completed submit of runnable returns successfully
*/
public void testSubmitRunnable() throws Exception {
- ExecutorService e = new CustomExecutor(2);
- try {
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
Future<?> future = e.submit(new NoOpRunnable());
future.get();
assertTrue(future.isDone());
- } finally {
- joinPool(e);
}
}
@@ -873,13 +905,11 @@
* completed submit of (runnable, result) returns result
*/
public void testSubmitRunnable2() throws Exception {
- ExecutorService e = new CustomExecutor(2);
- try {
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
String result = future.get();
assertSame(TEST_STRING, result);
- } finally {
- joinPool(e);
}
}
@@ -887,13 +917,12 @@
* invokeAny(null) throws NPE
*/
public void testInvokeAny1() throws Exception {
- ExecutorService e = new CustomExecutor(2);
- try {
- e.invokeAny(null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAny(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -901,13 +930,12 @@
* invokeAny(empty collection) throws IAE
*/
public void testInvokeAny2() throws Exception {
- ExecutorService e = new CustomExecutor(2);
- try {
- e.invokeAny(new ArrayList<Callable<String>>());
- shouldThrow();
- } catch (IllegalArgumentException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAny(new ArrayList<Callable<String>>());
+ shouldThrow();
+ } catch (IllegalArgumentException success) {}
}
}
@@ -915,18 +943,17 @@
* invokeAny(c) throws NPE if c has null elements
*/
public void testInvokeAny3() throws Exception {
- CountDownLatch latch = new CountDownLatch(1);
- ExecutorService e = new CustomExecutor(2);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(latchAwaitingStringTask(latch));
- l.add(null);
- try {
- e.invokeAny(l);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(latchAwaitingStringTask(latch));
+ l.add(null);
+ try {
+ e.invokeAny(l);
+ shouldThrow();
+ } catch (NullPointerException success) {}
latch.countDown();
- joinPool(e);
}
}
@@ -934,16 +961,16 @@
* invokeAny(c) throws ExecutionException if no task completes
*/
public void testInvokeAny4() throws Exception {
- ExecutorService e = new CustomExecutor(2);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new NPETask());
- try {
- e.invokeAny(l);
- shouldThrow();
- } catch (ExecutionException success) {
- assertTrue(success.getCause() instanceof NullPointerException);
- } finally {
- joinPool(e);
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new NPETask());
+ try {
+ e.invokeAny(l);
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertTrue(success.getCause() instanceof NullPointerException);
+ }
}
}
@@ -951,15 +978,13 @@
* invokeAny(c) returns result of some task
*/
public void testInvokeAny5() throws Exception {
- ExecutorService e = new CustomExecutor(2);
- try {
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
String result = e.invokeAny(l);
assertSame(TEST_STRING, result);
- } finally {
- joinPool(e);
}
}
@@ -967,13 +992,12 @@
* invokeAll(null) throws NPE
*/
public void testInvokeAll1() throws Exception {
- ExecutorService e = new CustomExecutor(2);
- try {
- e.invokeAll(null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAll(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -981,12 +1005,10 @@
* invokeAll(empty collection) returns empty collection
*/
public void testInvokeAll2() throws Exception {
- ExecutorService e = new CustomExecutor(2);
- try {
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>());
assertTrue(r.isEmpty());
- } finally {
- joinPool(e);
}
}
@@ -994,16 +1016,15 @@
* invokeAll(c) throws NPE if c has null elements
*/
public void testInvokeAll3() throws Exception {
- ExecutorService e = new CustomExecutor(2);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- l.add(null);
- try {
- e.invokeAll(l);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new StringTask());
+ l.add(null);
+ try {
+ e.invokeAll(l);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1011,18 +1032,18 @@
* get of invokeAll(c) throws exception on failed task
*/
public void testInvokeAll4() throws Exception {
- ExecutorService e = new CustomExecutor(2);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new NPETask());
- List<Future<String>> futures = e.invokeAll(l);
- assertEquals(1, futures.size());
- try {
- futures.get(0).get();
- shouldThrow();
- } catch (ExecutionException success) {
- assertTrue(success.getCause() instanceof NullPointerException);
- } finally {
- joinPool(e);
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new NPETask());
+ List<Future<String>> futures = e.invokeAll(l);
+ assertEquals(1, futures.size());
+ try {
+ futures.get(0).get();
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertTrue(success.getCause() instanceof NullPointerException);
+ }
}
}
@@ -1030,8 +1051,8 @@
* invokeAll(c) returns results of all completed tasks
*/
public void testInvokeAll5() throws Exception {
- ExecutorService e = new CustomExecutor(2);
- try {
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
@@ -1039,8 +1060,6 @@
assertEquals(2, futures.size());
for (Future<String> future : futures)
assertSame(TEST_STRING, future.get());
- } finally {
- joinPool(e);
}
}
@@ -1048,13 +1067,12 @@
* timed invokeAny(null) throws NPE
*/
public void testTimedInvokeAny1() throws Exception {
- ExecutorService e = new CustomExecutor(2);
- try {
- e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1062,15 +1080,14 @@
* timed invokeAny(,,null) throws NPE
*/
public void testTimedInvokeAnyNullTimeUnit() throws Exception {
- ExecutorService e = new CustomExecutor(2);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- try {
- e.invokeAny(l, MEDIUM_DELAY_MS, null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new StringTask());
+ try {
+ e.invokeAny(l, MEDIUM_DELAY_MS, null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1078,13 +1095,12 @@
* timed invokeAny(empty collection) throws IAE
*/
public void testTimedInvokeAny2() throws Exception {
- ExecutorService e = new CustomExecutor(2);
- try {
- e.invokeAny(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (IllegalArgumentException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAny(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (IllegalArgumentException success) {}
}
}
@@ -1093,17 +1109,16 @@
*/
public void testTimedInvokeAny3() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
- ExecutorService e = new CustomExecutor(2);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(latchAwaitingStringTask(latch));
- l.add(null);
- try {
- e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(latchAwaitingStringTask(latch));
+ l.add(null);
+ try {
+ e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
latch.countDown();
- joinPool(e);
}
}
@@ -1111,16 +1126,18 @@
* timed invokeAny(c) throws ExecutionException if no task completes
*/
public void testTimedInvokeAny4() throws Exception {
- ExecutorService e = new CustomExecutor(2);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new NPETask());
- try {
- e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (ExecutionException success) {
- assertTrue(success.getCause() instanceof NullPointerException);
- } finally {
- joinPool(e);
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ long startTime = System.nanoTime();
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new NPETask());
+ try {
+ e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertTrue(success.getCause() instanceof NullPointerException);
+ }
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}
}
@@ -1128,15 +1145,15 @@
* timed invokeAny(c) returns result of some task
*/
public void testTimedInvokeAny5() throws Exception {
- ExecutorService e = new CustomExecutor(2);
- try {
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ long startTime = System.nanoTime();
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
- String result = e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
assertSame(TEST_STRING, result);
- } finally {
- joinPool(e);
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}
}
@@ -1144,13 +1161,12 @@
* timed invokeAll(null) throws NPE
*/
public void testTimedInvokeAll1() throws Exception {
- ExecutorService e = new CustomExecutor(2);
- try {
- e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1158,15 +1174,14 @@
* timed invokeAll(,,null) throws NPE
*/
public void testTimedInvokeAllNullTimeUnit() throws Exception {
- ExecutorService e = new CustomExecutor(2);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- try {
- e.invokeAll(l, MEDIUM_DELAY_MS, null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new StringTask());
+ try {
+ e.invokeAll(l, MEDIUM_DELAY_MS, null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1174,12 +1189,10 @@
* timed invokeAll(empty collection) returns empty collection
*/
public void testTimedInvokeAll2() throws Exception {
- ExecutorService e = new CustomExecutor(2);
- try {
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
assertTrue(r.isEmpty());
- } finally {
- joinPool(e);
}
}
@@ -1187,16 +1200,15 @@
* timed invokeAll(c) throws NPE if c has null elements
*/
public void testTimedInvokeAll3() throws Exception {
- ExecutorService e = new CustomExecutor(2);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- l.add(null);
- try {
- e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new StringTask());
+ l.add(null);
+ try {
+ e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1204,19 +1216,19 @@
* get of element of invokeAll(c) throws exception on failed task
*/
public void testTimedInvokeAll4() throws Exception {
- ExecutorService e = new CustomExecutor(2);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new NPETask());
- List<Future<String>> futures =
- e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
- assertEquals(1, futures.size());
- try {
- futures.get(0).get();
- shouldThrow();
- } catch (ExecutionException success) {
- assertTrue(success.getCause() instanceof NullPointerException);
- } finally {
- joinPool(e);
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new NPETask());
+ List<Future<String>> futures =
+ e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ assertEquals(1, futures.size());
+ try {
+ futures.get(0).get();
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertTrue(success.getCause() instanceof NullPointerException);
+ }
}
}
@@ -1224,18 +1236,16 @@
* timed invokeAll(c) returns results of all completed tasks
*/
public void testTimedInvokeAll5() throws Exception {
- ExecutorService e = new CustomExecutor(2);
- try {
+ final ExecutorService e = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
List<Future<String>> futures =
- e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
assertEquals(2, futures.size());
for (Future<String> future : futures)
assertSame(TEST_STRING, future.get());
- } finally {
- joinPool(e);
}
}
@@ -1243,21 +1253,37 @@
* timed invokeAll(c) cancels tasks not completed by timeout
*/
public void testTimedInvokeAll6() throws Exception {
- ExecutorService e = new CustomExecutor(2);
- try {
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- l.add(Executors.callable(new MediumPossiblyInterruptedRunnable(), TEST_STRING));
- l.add(new StringTask());
- List<Future<String>> futures =
- e.invokeAll(l, SHORT_DELAY_MS, MILLISECONDS);
- assertEquals(l.size(), futures.size());
- for (Future future : futures)
- assertTrue(future.isDone());
- assertFalse(futures.get(0).isCancelled());
- assertTrue(futures.get(1).isCancelled());
- } finally {
- joinPool(e);
+ for (long timeout = timeoutMillis();;) {
+ final CountDownLatch done = new CountDownLatch(1);
+ final Callable<String> waiter = new CheckedCallable<String>() {
+ public String realCall() {
+ try { done.await(LONG_DELAY_MS, MILLISECONDS); }
+ catch (InterruptedException ok) {}
+ return "1"; }};
+ final ExecutorService p = new CustomExecutor(2);
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ List<Callable<String>> tasks = new ArrayList<>();
+ tasks.add(new StringTask("0"));
+ tasks.add(waiter);
+ tasks.add(new StringTask("2"));
+ long startTime = System.nanoTime();
+ List<Future<String>> futures =
+ p.invokeAll(tasks, timeout, MILLISECONDS);
+ assertEquals(tasks.size(), futures.size());
+ assertTrue(millisElapsedSince(startTime) >= timeout);
+ for (Future future : futures)
+ assertTrue(future.isDone());
+ assertTrue(futures.get(1).isCancelled());
+ try {
+ assertEquals("0", futures.get(0).get());
+ assertEquals("2", futures.get(2).get());
+ break;
+ } catch (CancellationException retryWithLongerTimeout) {
+ timeout *= 2;
+ if (timeout >= LONG_DELAY_MS / 2)
+ fail("expected exactly one task to be cancelled");
+ }
+ }
}
}
diff --git a/jsr166-tests/src/test/java/jsr166/ScheduledExecutorTest.java b/jsr166-tests/src/test/java/jsr166/ScheduledExecutorTest.java
index a2e83d0..81f7370 100644
--- a/jsr166-tests/src/test/java/jsr166/ScheduledExecutorTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ScheduledExecutorTest.java
@@ -9,11 +9,15 @@
package jsr166;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
@@ -24,7 +28,9 @@
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
import junit.framework.Test;
import junit.framework.TestSuite;
@@ -37,24 +43,20 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(ScheduledExecutorTest.class);
// }
/**
* execute successfully executes a runnable
*/
public void testExecute() throws InterruptedException {
- ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
- final CountDownLatch done = new CountDownLatch(1);
- final Runnable task = new CheckedRunnable() {
- public void realRun() {
- done.countDown();
- }};
- try {
+ final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final CountDownLatch done = new CountDownLatch(1);
+ final Runnable task = new CheckedRunnable() {
+ public void realRun() { done.countDown(); }};
p.execute(task);
- assertTrue(done.await(SMALL_DELAY_MS, MILLISECONDS));
- } finally {
- joinPool(p);
+ assertTrue(done.await(LONG_DELAY_MS, MILLISECONDS));
}
}
@@ -62,10 +64,10 @@
* delayed schedule of callable successfully executes after delay
*/
public void testSchedule1() throws Exception {
- ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
- final long startTime = System.nanoTime();
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final long startTime = System.nanoTime();
+ final CountDownLatch done = new CountDownLatch(1);
Callable task = new CheckedCallable<Boolean>() {
public Boolean realCall() {
done.countDown();
@@ -76,8 +78,6 @@
assertSame(Boolean.TRUE, f.get());
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
assertTrue(done.await(0L, MILLISECONDS));
- } finally {
- joinPool(p);
}
}
@@ -85,10 +85,10 @@
* delayed schedule of runnable successfully executes after delay
*/
public void testSchedule3() throws Exception {
- ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
- final long startTime = System.nanoTime();
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final long startTime = System.nanoTime();
+ final CountDownLatch done = new CountDownLatch(1);
Runnable task = new CheckedRunnable() {
public void realRun() {
done.countDown();
@@ -98,8 +98,6 @@
await(done);
assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
- } finally {
- joinPool(p);
}
}
@@ -107,10 +105,10 @@
* scheduleAtFixedRate executes runnable after given initial delay
*/
public void testSchedule4() throws Exception {
- ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
- final long startTime = System.nanoTime();
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final long startTime = System.nanoTime();
+ final CountDownLatch done = new CountDownLatch(1);
Runnable task = new CheckedRunnable() {
public void realRun() {
done.countDown();
@@ -122,8 +120,6 @@
await(done);
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
f.cancel(true);
- } finally {
- joinPool(p);
}
}
@@ -131,10 +127,10 @@
* scheduleWithFixedDelay executes runnable after given initial delay
*/
public void testSchedule5() throws Exception {
- ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
- final long startTime = System.nanoTime();
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final long startTime = System.nanoTime();
+ final CountDownLatch done = new CountDownLatch(1);
Runnable task = new CheckedRunnable() {
public void realRun() {
done.countDown();
@@ -146,8 +142,6 @@
await(done);
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
f.cancel(true);
- } finally {
- joinPool(p);
}
}
@@ -157,58 +151,77 @@
}
/**
- * scheduleAtFixedRate executes series of tasks at given rate
+ * scheduleAtFixedRate executes series of tasks at given rate.
+ * Eventually, it must hold that:
+ * cycles - 1 <= elapsedMillis/delay < cycles
*/
public void testFixedRateSequence() throws InterruptedException {
- ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
- try {
+ final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
for (int delay = 1; delay <= LONG_DELAY_MS; delay *= 3) {
- long startTime = System.nanoTime();
- int cycles = 10;
+ final long startTime = System.nanoTime();
+ final int cycles = 8;
final CountDownLatch done = new CountDownLatch(cycles);
- Runnable task = new CheckedRunnable() {
+ final Runnable task = new CheckedRunnable() {
public void realRun() { done.countDown(); }};
- ScheduledFuture h =
+ final ScheduledFuture periodicTask =
p.scheduleAtFixedRate(task, 0, delay, MILLISECONDS);
- done.await();
- h.cancel(true);
- double normalizedTime =
- (double) millisElapsedSince(startTime) / delay;
- if (normalizedTime >= cycles - 1 &&
- normalizedTime <= cycles)
+ final int totalDelayMillis = (cycles - 1) * delay;
+ await(done, totalDelayMillis + LONG_DELAY_MS);
+ periodicTask.cancel(true);
+ final long elapsedMillis = millisElapsedSince(startTime);
+ assertTrue(elapsedMillis >= totalDelayMillis);
+ if (elapsedMillis <= cycles * delay)
return;
+ // else retry with longer delay
}
- throw new AssertionError("unexpected execution rate");
- } finally {
- joinPool(p);
+ fail("unexpected execution rate");
}
}
/**
- * scheduleWithFixedDelay executes series of tasks with given period
+ * scheduleWithFixedDelay executes series of tasks with given period.
+ * Eventually, it must hold that each task starts at least delay and at
+ * most 2 * delay after the termination of the previous task.
*/
public void testFixedDelaySequence() throws InterruptedException {
- ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
- try {
+ final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
for (int delay = 1; delay <= LONG_DELAY_MS; delay *= 3) {
- long startTime = System.nanoTime();
- int cycles = 10;
+ final long startTime = System.nanoTime();
+ final AtomicLong previous = new AtomicLong(startTime);
+ final AtomicBoolean tryLongerDelay = new AtomicBoolean(false);
+ final int cycles = 8;
final CountDownLatch done = new CountDownLatch(cycles);
- Runnable task = new CheckedRunnable() {
- public void realRun() { done.countDown(); }};
- ScheduledFuture h =
+ final int d = delay;
+ final Runnable task = new CheckedRunnable() {
+ public void realRun() {
+ long now = System.nanoTime();
+ long elapsedMillis
+ = NANOSECONDS.toMillis(now - previous.get());
+ if (done.getCount() == cycles) { // first execution
+ if (elapsedMillis >= d)
+ tryLongerDelay.set(true);
+ } else {
+ assertTrue(elapsedMillis >= d);
+ if (elapsedMillis >= 2 * d)
+ tryLongerDelay.set(true);
+ }
+ previous.set(now);
+ done.countDown();
+ }};
+ final ScheduledFuture periodicTask =
p.scheduleWithFixedDelay(task, 0, delay, MILLISECONDS);
- done.await();
- h.cancel(true);
- double normalizedTime =
- (double) millisElapsedSince(startTime) / delay;
- if (normalizedTime >= cycles - 1 &&
- normalizedTime <= cycles)
+ final int totalDelayMillis = (cycles - 1) * delay;
+ await(done, totalDelayMillis + cycles * LONG_DELAY_MS);
+ periodicTask.cancel(true);
+ final long elapsedMillis = millisElapsedSince(startTime);
+ assertTrue(elapsedMillis >= totalDelayMillis);
+ if (!tryLongerDelay.get())
return;
+ // else retry with longer delay
}
- throw new AssertionError("unexpected execution rate");
- } finally {
- joinPool(p);
+ fail("unexpected execution rate");
}
}
@@ -216,108 +229,107 @@
* execute(null) throws NPE
*/
public void testExecuteNull() throws InterruptedException {
- ScheduledThreadPoolExecutor se = null;
- try {
- se = new ScheduledThreadPoolExecutor(1);
- se.execute(null);
- shouldThrow();
- } catch (NullPointerException success) {}
-
- joinPool(se);
+ final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.execute(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
+ }
}
/**
* schedule(null) throws NPE
*/
public void testScheduleNull() throws InterruptedException {
- ScheduledThreadPoolExecutor se = new ScheduledThreadPoolExecutor(1);
- try {
- TrackedCallable callable = null;
- Future f = se.schedule(callable, SHORT_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {}
- joinPool(se);
+ final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ TrackedCallable callable = null;
+ Future f = p.schedule(callable, SHORT_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
+ }
}
/**
* execute throws RejectedExecutionException if shutdown
*/
public void testSchedule1_RejectedExecutionException() throws InterruptedException {
- ScheduledThreadPoolExecutor se = new ScheduledThreadPoolExecutor(1);
- try {
- se.shutdown();
- se.schedule(new NoOpRunnable(),
- MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (RejectedExecutionException success) {
- } catch (SecurityException ok) {
+ final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.shutdown();
+ p.schedule(new NoOpRunnable(),
+ MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (RejectedExecutionException success) {
+ } catch (SecurityException ok) {}
}
-
- joinPool(se);
}
/**
* schedule throws RejectedExecutionException if shutdown
*/
public void testSchedule2_RejectedExecutionException() throws InterruptedException {
- ScheduledThreadPoolExecutor se = new ScheduledThreadPoolExecutor(1);
- try {
- se.shutdown();
- se.schedule(new NoOpCallable(),
- MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (RejectedExecutionException success) {
- } catch (SecurityException ok) {
+ final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.shutdown();
+ p.schedule(new NoOpCallable(),
+ MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (RejectedExecutionException success) {
+ } catch (SecurityException ok) {}
}
- joinPool(se);
}
/**
* schedule callable throws RejectedExecutionException if shutdown
*/
public void testSchedule3_RejectedExecutionException() throws InterruptedException {
- ScheduledThreadPoolExecutor se = new ScheduledThreadPoolExecutor(1);
- try {
- se.shutdown();
- se.schedule(new NoOpCallable(),
- MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (RejectedExecutionException success) {
- } catch (SecurityException ok) {
+ final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.shutdown();
+ p.schedule(new NoOpCallable(),
+ MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (RejectedExecutionException success) {
+ } catch (SecurityException ok) {}
}
- joinPool(se);
}
/**
* scheduleAtFixedRate throws RejectedExecutionException if shutdown
*/
public void testScheduleAtFixedRate1_RejectedExecutionException() throws InterruptedException {
- ScheduledThreadPoolExecutor se = new ScheduledThreadPoolExecutor(1);
- try {
- se.shutdown();
- se.scheduleAtFixedRate(new NoOpRunnable(),
- MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (RejectedExecutionException success) {
- } catch (SecurityException ok) {
+ final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.shutdown();
+ p.scheduleAtFixedRate(new NoOpRunnable(),
+ MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (RejectedExecutionException success) {
+ } catch (SecurityException ok) {}
}
- joinPool(se);
}
/**
* scheduleWithFixedDelay throws RejectedExecutionException if shutdown
*/
public void testScheduleWithFixedDelay1_RejectedExecutionException() throws InterruptedException {
- ScheduledThreadPoolExecutor se = new ScheduledThreadPoolExecutor(1);
- try {
- se.shutdown();
- se.scheduleWithFixedDelay(new NoOpRunnable(),
- MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (RejectedExecutionException success) {
- } catch (SecurityException ok) {
+ final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.shutdown();
+ p.scheduleWithFixedDelay(new NoOpRunnable(),
+ MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (RejectedExecutionException success) {
+ } catch (SecurityException ok) {}
}
- joinPool(se);
}
/**
@@ -325,22 +337,19 @@
* thread becomes active
*/
public void testGetActiveCount() throws InterruptedException {
- final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(2);
- final CountDownLatch threadStarted = new CountDownLatch(1);
final CountDownLatch done = new CountDownLatch(1);
- try {
+ final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
assertEquals(0, p.getActiveCount());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
assertEquals(1, p.getActiveCount());
- done.await();
+ await(done);
}});
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
assertEquals(1, p.getActiveCount());
- } finally {
- done.countDown();
- joinPool(p);
}
}
@@ -350,10 +359,10 @@
*/
public void testGetCompletedTaskCount() throws InterruptedException {
final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(2);
- final CountDownLatch threadStarted = new CountDownLatch(1);
- final CountDownLatch threadProceed = new CountDownLatch(1);
- final CountDownLatch threadDone = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
+ final CountDownLatch threadProceed = new CountDownLatch(1);
+ final CountDownLatch threadDone = new CountDownLatch(1);
assertEquals(0, p.getCompletedTaskCount());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
@@ -372,8 +381,6 @@
fail("timed out");
Thread.yield();
}
- } finally {
- joinPool(p);
}
}
@@ -382,8 +389,9 @@
*/
public void testGetCorePoolSize() throws InterruptedException {
ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
- assertEquals(1, p.getCorePoolSize());
- joinPool(p);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertEquals(1, p.getCorePoolSize());
+ }
}
/**
@@ -395,22 +403,19 @@
final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(THREADS);
final CountDownLatch threadsStarted = new CountDownLatch(THREADS);
final CountDownLatch done = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p, done)) {
assertEquals(0, p.getLargestPoolSize());
for (int i = 0; i < THREADS; i++)
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadsStarted.countDown();
- done.await();
+ await(done);
assertEquals(THREADS, p.getLargestPoolSize());
}});
- assertTrue(threadsStarted.await(SMALL_DELAY_MS, MILLISECONDS));
- assertEquals(THREADS, p.getLargestPoolSize());
- } finally {
- done.countDown();
- joinPool(p);
+ await(threadsStarted);
assertEquals(THREADS, p.getLargestPoolSize());
}
+ assertEquals(THREADS, p.getLargestPoolSize());
}
/**
@@ -421,19 +426,16 @@
final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
final CountDownLatch threadStarted = new CountDownLatch(1);
final CountDownLatch done = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p, done)) {
assertEquals(0, p.getPoolSize());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
assertEquals(1, p.getPoolSize());
- done.await();
+ await(done);
}});
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
assertEquals(1, p.getPoolSize());
- } finally {
- done.countDown();
- joinPool(p);
}
}
@@ -442,58 +444,71 @@
* submitted
*/
public void testGetTaskCount() throws InterruptedException {
- final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
- final CountDownLatch threadStarted = new CountDownLatch(1);
+ final int TASKS = 3;
final CountDownLatch done = new CountDownLatch(1);
- final int TASKS = 5;
- try {
+ final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
assertEquals(0, p.getTaskCount());
- for (int i = 0; i < TASKS; i++)
+ assertEquals(0, p.getCompletedTaskCount());
+ p.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ threadStarted.countDown();
+ await(done);
+ }});
+ await(threadStarted);
+ assertEquals(1, p.getTaskCount());
+ assertEquals(0, p.getCompletedTaskCount());
+ for (int i = 0; i < TASKS; i++) {
+ assertEquals(1 + i, p.getTaskCount());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
- done.await();
+ assertEquals(1 + TASKS, p.getTaskCount());
+ await(done);
}});
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
- assertEquals(TASKS, p.getTaskCount());
- } finally {
- done.countDown();
- joinPool(p);
+ }
+ assertEquals(1 + TASKS, p.getTaskCount());
+ assertEquals(0, p.getCompletedTaskCount());
}
+ assertEquals(1 + TASKS, p.getTaskCount());
+ assertEquals(1 + TASKS, p.getCompletedTaskCount());
}
/**
* getThreadFactory returns factory in constructor if not set
*/
public void testGetThreadFactory() throws InterruptedException {
- ThreadFactory tf = new SimpleThreadFactory();
- ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1, tf);
- assertSame(tf, p.getThreadFactory());
- joinPool(p);
+ final ThreadFactory threadFactory = new SimpleThreadFactory();
+ final ScheduledThreadPoolExecutor p =
+ new ScheduledThreadPoolExecutor(1, threadFactory);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertSame(threadFactory, p.getThreadFactory());
+ }
}
/**
* setThreadFactory sets the thread factory returned by getThreadFactory
*/
public void testSetThreadFactory() throws InterruptedException {
- ThreadFactory tf = new SimpleThreadFactory();
- ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
- p.setThreadFactory(tf);
- assertSame(tf, p.getThreadFactory());
- joinPool(p);
+ ThreadFactory threadFactory = new SimpleThreadFactory();
+ final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ p.setThreadFactory(threadFactory);
+ assertSame(threadFactory, p.getThreadFactory());
+ }
}
/**
* setThreadFactory(null) throws NPE
*/
public void testSetThreadFactoryNull() throws InterruptedException {
- ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
- try {
- p.setThreadFactory(null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(p);
+ final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.setThreadFactory(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -502,7 +517,7 @@
*/
public void testIsShutdown() {
- ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+ final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
try {
assertFalse(p.isShutdown());
}
@@ -517,24 +532,23 @@
*/
public void testIsTerminated() throws InterruptedException {
final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
- final CountDownLatch threadStarted = new CountDownLatch(1);
- final CountDownLatch done = new CountDownLatch(1);
- assertFalse(p.isTerminated());
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
+ final CountDownLatch done = new CountDownLatch(1);
+ assertFalse(p.isTerminated());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
assertFalse(p.isTerminated());
threadStarted.countDown();
- done.await();
+ await(done);
}});
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
assertFalse(p.isTerminating());
done.countDown();
- } finally {
try { p.shutdown(); } catch (SecurityException ok) { return; }
+ assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(p.isTerminated());
}
- assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
- assertTrue(p.isTerminated());
}
/**
@@ -544,49 +558,45 @@
final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
final CountDownLatch threadStarted = new CountDownLatch(1);
final CountDownLatch done = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
assertFalse(p.isTerminating());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
assertFalse(p.isTerminating());
threadStarted.countDown();
- done.await();
+ await(done);
}});
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
assertFalse(p.isTerminating());
done.countDown();
- } finally {
try { p.shutdown(); } catch (SecurityException ok) { return; }
+ assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(p.isTerminated());
+ assertFalse(p.isTerminating());
}
- assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
- assertTrue(p.isTerminated());
- assertFalse(p.isTerminating());
}
/**
* getQueue returns the work queue, which contains queued tasks
*/
public void testGetQueue() throws InterruptedException {
- ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
- final CountDownLatch threadStarted = new CountDownLatch(1);
final CountDownLatch done = new CountDownLatch(1);
- try {
+ final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
ScheduledFuture[] tasks = new ScheduledFuture[5];
for (int i = 0; i < tasks.length; i++) {
Runnable r = new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
- done.await();
+ await(done);
}};
tasks[i] = p.schedule(r, 1, MILLISECONDS);
}
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
BlockingQueue<Runnable> q = p.getQueue();
assertTrue(q.contains(tasks[tasks.length - 1]));
assertFalse(q.contains(tasks[0]));
- } finally {
- done.countDown();
- joinPool(p);
}
}
@@ -594,20 +604,20 @@
* remove(task) removes queued task, and fails to remove active task
*/
public void testRemove() throws InterruptedException {
- final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
- ScheduledFuture[] tasks = new ScheduledFuture[5];
- final CountDownLatch threadStarted = new CountDownLatch(1);
final CountDownLatch done = new CountDownLatch(1);
- try {
+ final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ ScheduledFuture[] tasks = new ScheduledFuture[5];
+ final CountDownLatch threadStarted = new CountDownLatch(1);
for (int i = 0; i < tasks.length; i++) {
Runnable r = new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
- done.await();
+ await(done);
}};
tasks[i] = p.schedule(r, 1, MILLISECONDS);
}
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
BlockingQueue<Runnable> q = p.getQueue();
assertFalse(p.remove((Runnable)tasks[0]));
assertTrue(q.contains((Runnable)tasks[4]));
@@ -618,9 +628,6 @@
assertTrue(q.contains((Runnable)tasks[3]));
assertTrue(p.remove((Runnable)tasks[3]));
assertFalse(q.contains((Runnable)tasks[3]));
- } finally {
- done.countDown();
- joinPool(p);
}
}
@@ -628,12 +635,15 @@
* purge eventually removes cancelled tasks from the queue
*/
public void testPurge() throws InterruptedException {
- ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
- ScheduledFuture[] tasks = new ScheduledFuture[5];
- for (int i = 0; i < tasks.length; i++)
- tasks[i] = p.schedule(new SmallPossiblyInterruptedRunnable(),
- LONG_DELAY_MS, MILLISECONDS);
- try {
+ final ScheduledFuture[] tasks = new ScheduledFuture[5];
+ final Runnable releaser = new Runnable() { public void run() {
+ for (ScheduledFuture task : tasks)
+ if (task != null) task.cancel(true); }};
+ final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p, releaser)) {
+ for (int i = 0; i < tasks.length; i++)
+ tasks[i] = p.schedule(new SmallPossiblyInterruptedRunnable(),
+ LONG_DELAY_MS, MILLISECONDS);
int max = tasks.length;
if (tasks[4].cancel(true)) --max;
if (tasks[3].cancel(true)) --max;
@@ -645,159 +655,186 @@
long count = p.getTaskCount();
if (count == max)
return;
- } while (millisElapsedSince(startTime) < MEDIUM_DELAY_MS);
+ } while (millisElapsedSince(startTime) < LONG_DELAY_MS);
fail("Purge failed to remove cancelled tasks");
- } finally {
- for (ScheduledFuture task : tasks)
- task.cancel(true);
- joinPool(p);
}
}
/**
- * shutdownNow returns a list containing tasks that were not run
+ * shutdownNow returns a list containing tasks that were not run,
+ * and those tasks are drained from the queue
*/
- public void testShutdownNow() {
- ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
- for (int i = 0; i < 5; i++)
- p.schedule(new SmallPossiblyInterruptedRunnable(),
- LONG_DELAY_MS, MILLISECONDS);
+ public void testShutdownNow() throws InterruptedException {
+ final int poolSize = 2;
+ final int count = 5;
+ final AtomicInteger ran = new AtomicInteger(0);
+ final ScheduledThreadPoolExecutor p =
+ new ScheduledThreadPoolExecutor(poolSize);
+ final CountDownLatch threadsStarted = new CountDownLatch(poolSize);
+ Runnable waiter = new CheckedRunnable() { public void realRun() {
+ threadsStarted.countDown();
+ try {
+ MILLISECONDS.sleep(2 * LONG_DELAY_MS);
+ } catch (InterruptedException success) {}
+ ran.getAndIncrement();
+ }};
+ for (int i = 0; i < count; i++)
+ p.execute(waiter);
+ await(threadsStarted);
+ assertEquals(poolSize, p.getActiveCount());
+ assertEquals(0, p.getCompletedTaskCount());
+ final List<Runnable> queuedTasks;
try {
- List<Runnable> l = p.shutdownNow();
- assertTrue(p.isShutdown());
- assertEquals(5, l.size());
+ queuedTasks = p.shutdownNow();
} catch (SecurityException ok) {
- // Allowed in case test doesn't have privs
- } finally {
- joinPool(p);
+ return; // Allowed in case test doesn't have privs
}
- }
-
- /**
- * In default setting, shutdown cancels periodic but not delayed
- * tasks at shutdown
- */
- public void testShutdown1() throws InterruptedException {
- ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
- assertTrue(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
- assertFalse(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
-
- ScheduledFuture[] tasks = new ScheduledFuture[5];
- for (int i = 0; i < tasks.length; i++)
- tasks[i] = p.schedule(new NoOpRunnable(),
- SHORT_DELAY_MS, MILLISECONDS);
- try { p.shutdown(); } catch (SecurityException ok) { return; }
- BlockingQueue<Runnable> q = p.getQueue();
- for (ScheduledFuture task : tasks) {
- assertFalse(task.isDone());
- assertFalse(task.isCancelled());
- assertTrue(q.contains(task));
- }
- assertTrue(p.isShutdown());
- assertTrue(p.awaitTermination(SMALL_DELAY_MS, MILLISECONDS));
- assertTrue(p.isTerminated());
- for (ScheduledFuture task : tasks) {
- assertTrue(task.isDone());
- assertFalse(task.isCancelled());
- }
- }
-
- /**
- * If setExecuteExistingDelayedTasksAfterShutdownPolicy is false,
- * delayed tasks are cancelled at shutdown
- */
- public void testShutdown2() throws InterruptedException {
- ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
- p.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
- assertFalse(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
- assertFalse(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
- ScheduledFuture[] tasks = new ScheduledFuture[5];
- for (int i = 0; i < tasks.length; i++)
- tasks[i] = p.schedule(new NoOpRunnable(),
- SHORT_DELAY_MS, MILLISECONDS);
- BlockingQueue q = p.getQueue();
- assertEquals(tasks.length, q.size());
- try { p.shutdown(); } catch (SecurityException ok) { return; }
- assertTrue(p.isShutdown());
- assertTrue(q.isEmpty());
- assertTrue(p.awaitTermination(SMALL_DELAY_MS, MILLISECONDS));
- assertTrue(p.isTerminated());
- for (ScheduledFuture task : tasks) {
- assertTrue(task.isDone());
- assertTrue(task.isCancelled());
- }
- }
-
- /**
- * If setContinueExistingPeriodicTasksAfterShutdownPolicy is set false,
- * periodic tasks are cancelled at shutdown
- */
- public void testShutdown3() throws InterruptedException {
- ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
- assertTrue(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
- assertFalse(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
- p.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
- assertTrue(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
- assertFalse(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
- long initialDelay = LONG_DELAY_MS;
- ScheduledFuture task =
- p.scheduleAtFixedRate(new NoOpRunnable(), initialDelay,
- 5, MILLISECONDS);
- try { p.shutdown(); } catch (SecurityException ok) { return; }
assertTrue(p.isShutdown());
assertTrue(p.getQueue().isEmpty());
- assertTrue(task.isDone());
- assertTrue(task.isCancelled());
- joinPool(p);
+ assertEquals(count - poolSize, queuedTasks.size());
+ assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(p.isTerminated());
+ assertEquals(poolSize, ran.get());
+ assertEquals(poolSize, p.getCompletedTaskCount());
}
/**
- * if setContinueExistingPeriodicTasksAfterShutdownPolicy is true,
- * periodic tasks are not cancelled at shutdown
+ * shutdownNow returns a list containing tasks that were not run,
+ * and those tasks are drained from the queue
*/
- public void testShutdown4() throws InterruptedException {
- ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
- final CountDownLatch counter = new CountDownLatch(2);
+ public void testShutdownNow_delayedTasks() throws InterruptedException {
+ final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+ List<ScheduledFuture> tasks = new ArrayList<>();
+ for (int i = 0; i < 3; i++) {
+ Runnable r = new NoOpRunnable();
+ tasks.add(p.schedule(r, 9, SECONDS));
+ tasks.add(p.scheduleAtFixedRate(r, 9, 9, SECONDS));
+ tasks.add(p.scheduleWithFixedDelay(r, 9, 9, SECONDS));
+ }
+ if (testImplementationDetails)
+ assertEquals(new HashSet(tasks), new HashSet(p.getQueue()));
+ final List<Runnable> queuedTasks;
try {
- p.setContinueExistingPeriodicTasksAfterShutdownPolicy(true);
- assertTrue(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
- assertTrue(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
- final Runnable r = new CheckedRunnable() {
- public void realRun() {
- counter.countDown();
- }};
- ScheduledFuture task =
- p.scheduleAtFixedRate(r, 1, 1, MILLISECONDS);
+ queuedTasks = p.shutdownNow();
+ } catch (SecurityException ok) {
+ return; // Allowed in case test doesn't have privs
+ }
+ assertTrue(p.isShutdown());
+ assertTrue(p.getQueue().isEmpty());
+ if (testImplementationDetails)
+ assertEquals(new HashSet(tasks), new HashSet(queuedTasks));
+ assertEquals(tasks.size(), queuedTasks.size());
+ for (ScheduledFuture task : tasks) {
assertFalse(task.isDone());
assertFalse(task.isCancelled());
- try { p.shutdown(); } catch (SecurityException ok) { return; }
- assertFalse(task.isCancelled());
- assertFalse(p.isTerminated());
- assertTrue(p.isShutdown());
- assertTrue(counter.await(SMALL_DELAY_MS, MILLISECONDS));
- assertFalse(task.isCancelled());
- assertTrue(task.cancel(false));
- assertTrue(task.isDone());
- assertTrue(task.isCancelled());
- assertTrue(p.awaitTermination(SMALL_DELAY_MS, MILLISECONDS));
- assertTrue(p.isTerminated());
}
- finally {
- joinPool(p);
- }
+ assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(p.isTerminated());
}
/**
+ * By default, periodic tasks are cancelled at shutdown.
+ * By default, delayed tasks keep running after shutdown.
+ * Check that changing the default values work:
+ * - setExecuteExistingDelayedTasksAfterShutdownPolicy
+ * - setContinueExistingPeriodicTasksAfterShutdownPolicy
+ */
+ public void testShutdown_cancellation() throws Exception {
+ Boolean[] allBooleans = { null, Boolean.FALSE, Boolean.TRUE };
+ for (Boolean policy : allBooleans)
+ {
+ final int poolSize = 2;
+ final ScheduledThreadPoolExecutor p
+ = new ScheduledThreadPoolExecutor(poolSize);
+ final boolean effectiveDelayedPolicy = (policy != Boolean.FALSE);
+ final boolean effectivePeriodicPolicy = (policy == Boolean.TRUE);
+ final boolean effectiveRemovePolicy = (policy == Boolean.TRUE);
+ if (policy != null) {
+ p.setExecuteExistingDelayedTasksAfterShutdownPolicy(policy);
+ p.setContinueExistingPeriodicTasksAfterShutdownPolicy(policy);
+ p.setRemoveOnCancelPolicy(policy);
+ }
+ assertEquals(effectiveDelayedPolicy,
+ p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
+ assertEquals(effectivePeriodicPolicy,
+ p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
+ assertEquals(effectiveRemovePolicy,
+ p.getRemoveOnCancelPolicy());
+ // Strategy: Wedge the pool with poolSize "blocker" threads
+ final AtomicInteger ran = new AtomicInteger(0);
+ final CountDownLatch poolBlocked = new CountDownLatch(poolSize);
+ final CountDownLatch unblock = new CountDownLatch(1);
+ final CountDownLatch periodicLatch1 = new CountDownLatch(2);
+ final CountDownLatch periodicLatch2 = new CountDownLatch(2);
+ Runnable task = new CheckedRunnable() { public void realRun()
+ throws InterruptedException {
+ poolBlocked.countDown();
+ assertTrue(unblock.await(LONG_DELAY_MS, MILLISECONDS));
+ ran.getAndIncrement();
+ }};
+ List<Future<?>> blockers = new ArrayList<>();
+ List<Future<?>> periodics = new ArrayList<>();
+ List<Future<?>> delayeds = new ArrayList<>();
+ for (int i = 0; i < poolSize; i++)
+ blockers.add(p.submit(task));
+ assertTrue(poolBlocked.await(LONG_DELAY_MS, MILLISECONDS));
+
+ periodics.add(p.scheduleAtFixedRate(countDowner(periodicLatch1),
+ 1, 1, MILLISECONDS));
+ periodics.add(p.scheduleWithFixedDelay(countDowner(periodicLatch2),
+ 1, 1, MILLISECONDS));
+ delayeds.add(p.schedule(task, 1, MILLISECONDS));
+
+ assertTrue(p.getQueue().containsAll(periodics));
+ assertTrue(p.getQueue().containsAll(delayeds));
+ try { p.shutdown(); } catch (SecurityException ok) { return; }
+ assertTrue(p.isShutdown());
+ assertFalse(p.isTerminated());
+ for (Future<?> periodic : periodics) {
+ assertTrue(effectivePeriodicPolicy ^ periodic.isCancelled());
+ assertTrue(effectivePeriodicPolicy ^ periodic.isDone());
+ }
+ for (Future<?> delayed : delayeds) {
+ assertTrue(effectiveDelayedPolicy ^ delayed.isCancelled());
+ assertTrue(effectiveDelayedPolicy ^ delayed.isDone());
+ }
+ if (testImplementationDetails) {
+ assertEquals(effectivePeriodicPolicy,
+ p.getQueue().containsAll(periodics));
+ assertEquals(effectiveDelayedPolicy,
+ p.getQueue().containsAll(delayeds));
+ }
+ // Release all pool threads
+ unblock.countDown();
+
+ for (Future<?> delayed : delayeds) {
+ if (effectiveDelayedPolicy) {
+ assertNull(delayed.get());
+ }
+ }
+ if (effectivePeriodicPolicy) {
+ assertTrue(periodicLatch1.await(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(periodicLatch2.await(LONG_DELAY_MS, MILLISECONDS));
+ for (Future<?> periodic : periodics) {
+ assertTrue(periodic.cancel(false));
+ assertTrue(periodic.isCancelled());
+ assertTrue(periodic.isDone());
+ }
+ }
+ assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(p.isTerminated());
+ assertEquals(2 + (effectiveDelayedPolicy ? 1 : 0), ran.get());
+ }}
+
+ /**
* completed submit of callable returns result
*/
public void testSubmitCallable() throws Exception {
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- try {
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
Future<String> future = e.submit(new StringTask());
String result = future.get();
assertSame(TEST_STRING, result);
- } finally {
- joinPool(e);
}
}
@@ -805,13 +842,11 @@
* completed submit of runnable returns successfully
*/
public void testSubmitRunnable() throws Exception {
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- try {
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
Future<?> future = e.submit(new NoOpRunnable());
future.get();
assertTrue(future.isDone());
- } finally {
- joinPool(e);
}
}
@@ -819,13 +854,11 @@
* completed submit of (runnable, result) returns result
*/
public void testSubmitRunnable2() throws Exception {
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- try {
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
String result = future.get();
assertSame(TEST_STRING, result);
- } finally {
- joinPool(e);
}
}
@@ -833,13 +866,12 @@
* invokeAny(null) throws NPE
*/
public void testInvokeAny1() throws Exception {
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- try {
- e.invokeAny(null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAny(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -847,13 +879,12 @@
* invokeAny(empty collection) throws IAE
*/
public void testInvokeAny2() throws Exception {
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- try {
- e.invokeAny(new ArrayList<Callable<String>>());
- shouldThrow();
- } catch (IllegalArgumentException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAny(new ArrayList<Callable<String>>());
+ shouldThrow();
+ } catch (IllegalArgumentException success) {}
}
}
@@ -862,17 +893,16 @@
*/
public void testInvokeAny3() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(latchAwaitingStringTask(latch));
- l.add(null);
- try {
- e.invokeAny(l);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(latchAwaitingStringTask(latch));
+ l.add(null);
+ try {
+ e.invokeAny(l);
+ shouldThrow();
+ } catch (NullPointerException success) {}
latch.countDown();
- joinPool(e);
}
}
@@ -880,16 +910,16 @@
* invokeAny(c) throws ExecutionException if no task completes
*/
public void testInvokeAny4() throws Exception {
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new NPETask());
- try {
- e.invokeAny(l);
- shouldThrow();
- } catch (ExecutionException success) {
- assertTrue(success.getCause() instanceof NullPointerException);
- } finally {
- joinPool(e);
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new NPETask());
+ try {
+ e.invokeAny(l);
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertTrue(success.getCause() instanceof NullPointerException);
+ }
}
}
@@ -897,15 +927,13 @@
* invokeAny(c) returns result of some task
*/
public void testInvokeAny5() throws Exception {
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- try {
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
String result = e.invokeAny(l);
assertSame(TEST_STRING, result);
- } finally {
- joinPool(e);
}
}
@@ -913,13 +941,12 @@
* invokeAll(null) throws NPE
*/
public void testInvokeAll1() throws Exception {
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- try {
- e.invokeAll(null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAll(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -927,12 +954,10 @@
* invokeAll(empty collection) returns empty collection
*/
public void testInvokeAll2() throws Exception {
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- try {
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>());
assertTrue(r.isEmpty());
- } finally {
- joinPool(e);
}
}
@@ -940,16 +965,15 @@
* invokeAll(c) throws NPE if c has null elements
*/
public void testInvokeAll3() throws Exception {
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- l.add(null);
- try {
- e.invokeAll(l);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new StringTask());
+ l.add(null);
+ try {
+ e.invokeAll(l);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -957,18 +981,18 @@
* get of invokeAll(c) throws exception on failed task
*/
public void testInvokeAll4() throws Exception {
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new NPETask());
- List<Future<String>> futures = e.invokeAll(l);
- assertEquals(1, futures.size());
- try {
- futures.get(0).get();
- shouldThrow();
- } catch (ExecutionException success) {
- assertTrue(success.getCause() instanceof NullPointerException);
- } finally {
- joinPool(e);
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new NPETask());
+ List<Future<String>> futures = e.invokeAll(l);
+ assertEquals(1, futures.size());
+ try {
+ futures.get(0).get();
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertTrue(success.getCause() instanceof NullPointerException);
+ }
}
}
@@ -976,8 +1000,8 @@
* invokeAll(c) returns results of all completed tasks
*/
public void testInvokeAll5() throws Exception {
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- try {
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
@@ -985,8 +1009,6 @@
assertEquals(2, futures.size());
for (Future<String> future : futures)
assertSame(TEST_STRING, future.get());
- } finally {
- joinPool(e);
}
}
@@ -994,13 +1016,12 @@
* timed invokeAny(null) throws NPE
*/
public void testTimedInvokeAny1() throws Exception {
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- try {
- e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1008,15 +1029,14 @@
* timed invokeAny(,,null) throws NPE
*/
public void testTimedInvokeAnyNullTimeUnit() throws Exception {
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- try {
- e.invokeAny(l, MEDIUM_DELAY_MS, null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new StringTask());
+ try {
+ e.invokeAny(l, MEDIUM_DELAY_MS, null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1024,13 +1044,12 @@
* timed invokeAny(empty collection) throws IAE
*/
public void testTimedInvokeAny2() throws Exception {
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- try {
- e.invokeAny(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (IllegalArgumentException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAny(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (IllegalArgumentException success) {}
}
}
@@ -1039,17 +1058,16 @@
*/
public void testTimedInvokeAny3() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(latchAwaitingStringTask(latch));
- l.add(null);
- try {
- e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(latchAwaitingStringTask(latch));
+ l.add(null);
+ try {
+ e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
latch.countDown();
- joinPool(e);
}
}
@@ -1057,16 +1075,18 @@
* timed invokeAny(c) throws ExecutionException if no task completes
*/
public void testTimedInvokeAny4() throws Exception {
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new NPETask());
- try {
- e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (ExecutionException success) {
- assertTrue(success.getCause() instanceof NullPointerException);
- } finally {
- joinPool(e);
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ long startTime = System.nanoTime();
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new NPETask());
+ try {
+ e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertTrue(success.getCause() instanceof NullPointerException);
+ }
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}
}
@@ -1074,15 +1094,15 @@
* timed invokeAny(c) returns result of some task
*/
public void testTimedInvokeAny5() throws Exception {
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- try {
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ long startTime = System.nanoTime();
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
- String result = e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
assertSame(TEST_STRING, result);
- } finally {
- joinPool(e);
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}
}
@@ -1090,13 +1110,12 @@
* timed invokeAll(null) throws NPE
*/
public void testTimedInvokeAll1() throws Exception {
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- try {
- e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1104,15 +1123,14 @@
* timed invokeAll(,,null) throws NPE
*/
public void testTimedInvokeAllNullTimeUnit() throws Exception {
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- try {
- e.invokeAll(l, MEDIUM_DELAY_MS, null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new StringTask());
+ try {
+ e.invokeAll(l, MEDIUM_DELAY_MS, null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1120,12 +1138,11 @@
* timed invokeAll(empty collection) returns empty collection
*/
public void testTimedInvokeAll2() throws Exception {
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- try {
- List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(),
+ MEDIUM_DELAY_MS, MILLISECONDS);
assertTrue(r.isEmpty());
- } finally {
- joinPool(e);
}
}
@@ -1133,16 +1150,15 @@
* timed invokeAll(c) throws NPE if c has null elements
*/
public void testTimedInvokeAll3() throws Exception {
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- l.add(null);
- try {
- e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new StringTask());
+ l.add(null);
+ try {
+ e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1150,19 +1166,19 @@
* get of element of invokeAll(c) throws exception on failed task
*/
public void testTimedInvokeAll4() throws Exception {
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new NPETask());
- List<Future<String>> futures =
- e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
- assertEquals(1, futures.size());
- try {
- futures.get(0).get();
- shouldThrow();
- } catch (ExecutionException success) {
- assertTrue(success.getCause() instanceof NullPointerException);
- } finally {
- joinPool(e);
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new NPETask());
+ List<Future<String>> futures =
+ e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+ assertEquals(1, futures.size());
+ try {
+ futures.get(0).get();
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertTrue(success.getCause() instanceof NullPointerException);
+ }
}
}
@@ -1170,18 +1186,16 @@
* timed invokeAll(c) returns results of all completed tasks
*/
public void testTimedInvokeAll5() throws Exception {
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- try {
+ final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
List<Future<String>> futures =
- e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
assertEquals(2, futures.size());
for (Future<String> future : futures)
assertSame(TEST_STRING, future.get());
- } finally {
- joinPool(e);
}
}
@@ -1189,21 +1203,60 @@
* timed invokeAll(c) cancels tasks not completed by timeout
*/
public void testTimedInvokeAll6() throws Exception {
- ExecutorService e = new ScheduledThreadPoolExecutor(2);
- try {
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- l.add(Executors.callable(new MediumPossiblyInterruptedRunnable(), TEST_STRING));
- l.add(new StringTask());
- List<Future<String>> futures =
- e.invokeAll(l, SHORT_DELAY_MS, MILLISECONDS);
- assertEquals(l.size(), futures.size());
- for (Future future : futures)
- assertTrue(future.isDone());
- assertFalse(futures.get(0).isCancelled());
- assertTrue(futures.get(1).isCancelled());
- } finally {
- joinPool(e);
+ for (long timeout = timeoutMillis();;) {
+ final CountDownLatch done = new CountDownLatch(1);
+ final Callable<String> waiter = new CheckedCallable<String>() {
+ public String realCall() {
+ try { done.await(LONG_DELAY_MS, MILLISECONDS); }
+ catch (InterruptedException ok) {}
+ return "1"; }};
+ final ExecutorService p = new ScheduledThreadPoolExecutor(2);
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ List<Callable<String>> tasks = new ArrayList<>();
+ tasks.add(new StringTask("0"));
+ tasks.add(waiter);
+ tasks.add(new StringTask("2"));
+ long startTime = System.nanoTime();
+ List<Future<String>> futures =
+ p.invokeAll(tasks, timeout, MILLISECONDS);
+ assertEquals(tasks.size(), futures.size());
+ assertTrue(millisElapsedSince(startTime) >= timeout);
+ for (Future future : futures)
+ assertTrue(future.isDone());
+ assertTrue(futures.get(1).isCancelled());
+ try {
+ assertEquals("0", futures.get(0).get());
+ assertEquals("2", futures.get(2).get());
+ break;
+ } catch (CancellationException retryWithLongerTimeout) {
+ timeout *= 2;
+ if (timeout >= LONG_DELAY_MS / 2)
+ fail("expected exactly one task to be cancelled");
+ }
+ }
+ }
+ }
+
+ /**
+ * A fixed delay task with overflowing period should not prevent a
+ * one-shot task from executing.
+ * https://bugs.openjdk.java.net/browse/JDK-8051859
+ */
+ public void testScheduleWithFixedDelay_overflow() throws Exception {
+ final CountDownLatch delayedDone = new CountDownLatch(1);
+ final CountDownLatch immediateDone = new CountDownLatch(1);
+ final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final Runnable immediate = new Runnable() { public void run() {
+ immediateDone.countDown();
+ }};
+ final Runnable delayed = new Runnable() { public void run() {
+ delayedDone.countDown();
+ p.submit(immediate);
+ }};
+ p.scheduleWithFixedDelay(delayed, 0L, Long.MAX_VALUE, SECONDS);
+ await(delayedDone);
+ await(immediateDone);
}
}
diff --git a/jsr166-tests/src/test/java/jsr166/SemaphoreTest.java b/jsr166-tests/src/test/java/jsr166/SemaphoreTest.java
index db4f4b4..09c82c8 100644
--- a/jsr166-tests/src/test/java/jsr166/SemaphoreTest.java
+++ b/jsr166-tests/src/test/java/jsr166/SemaphoreTest.java
@@ -26,8 +26,9 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(SemaphoreTest.class);
// }
+
/**
* Subclass to expose protected methods
*/
@@ -471,11 +472,16 @@
clone.release();
assertEquals(2, s.availablePermits());
assertEquals(1, clone.availablePermits());
+ assertFalse(s.hasQueuedThreads());
+ assertFalse(clone.hasQueuedThreads());
+ } catch (InterruptedException e) { threadUnexpectedException(e); }
- s = new Semaphore(0, fair);
+ {
+ PublicSemaphore s = new PublicSemaphore(0, fair);
Thread t = newStartedThread(new InterruptibleLockRunnable(s));
- waitForQueuedThreads(s);
- clone = serialClone(s);
+ // waitForQueuedThreads(s); // suffers from "flicker", so ...
+ waitForQueuedThread(s, t); // ... we use this instead
+ PublicSemaphore clone = serialClone(s);
assertEquals(fair, s.isFair());
assertEquals(fair, clone.isFair());
assertEquals(0, s.availablePermits());
@@ -486,7 +492,7 @@
awaitTermination(t);
assertFalse(s.hasQueuedThreads());
assertFalse(clone.hasQueuedThreads());
- } catch (InterruptedException e) { threadUnexpectedException(e); }
+ }
}
/**
@@ -594,7 +600,7 @@
s.acquire(3);
}});
- waitForQueuedThreads(s);
+ waitForQueuedThread(s, t1);
Thread t2 = newStartedThread(new CheckedRunnable() {
public void realRun() throws InterruptedException {
diff --git a/jsr166-tests/src/test/java/jsr166/StampedLockTest.java b/jsr166-tests/src/test/java/jsr166/StampedLockTest.java
new file mode 100644
index 0000000..d347c7d
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/StampedLockTest.java
@@ -0,0 +1,884 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.StampedLock;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class StampedLockTest extends JSR166TestCase {
+ // android-note: Removed because the CTS runner does a bad job of
+ // retrying tests that have suite() declarations.
+ //
+ // public static void main(String[] args) {
+ // main(suite(), args);
+ // }
+ // public static Test suite() {
+ // return new TestSuite(StampedLockTest.class);
+ // }
+
+ /**
+ * A runnable calling writeLockInterruptibly
+ */
+ class InterruptibleLockRunnable extends CheckedRunnable {
+ final StampedLock lock;
+ InterruptibleLockRunnable(StampedLock l) { lock = l; }
+ public void realRun() throws InterruptedException {
+ lock.writeLockInterruptibly();
+ }
+ }
+
+ /**
+ * A runnable calling writeLockInterruptibly that expects to be
+ * interrupted
+ */
+ class InterruptedLockRunnable extends CheckedInterruptedRunnable {
+ final StampedLock lock;
+ InterruptedLockRunnable(StampedLock l) { lock = l; }
+ public void realRun() throws InterruptedException {
+ lock.writeLockInterruptibly();
+ }
+ }
+
+ /**
+ * Releases write lock, checking isWriteLocked before and after
+ */
+ void releaseWriteLock(StampedLock lock, long s) {
+ assertTrue(lock.isWriteLocked());
+ lock.unlockWrite(s);
+ assertFalse(lock.isWriteLocked());
+ }
+
+ /**
+ * Constructed StampedLock is in unlocked state
+ */
+ public void testConstructor() {
+ StampedLock lock;
+ lock = new StampedLock();
+ assertFalse(lock.isWriteLocked());
+ assertFalse(lock.isReadLocked());
+ assertEquals(lock.getReadLockCount(), 0);
+ }
+
+ /**
+ * write-locking and read-locking an unlocked lock succeed
+ */
+ public void testLock() {
+ StampedLock lock = new StampedLock();
+ assertFalse(lock.isWriteLocked());
+ assertFalse(lock.isReadLocked());
+ assertEquals(lock.getReadLockCount(), 0);
+ long s = lock.writeLock();
+ assertTrue(lock.isWriteLocked());
+ assertFalse(lock.isReadLocked());
+ assertEquals(lock.getReadLockCount(), 0);
+ lock.unlockWrite(s);
+ assertFalse(lock.isWriteLocked());
+ assertFalse(lock.isReadLocked());
+ assertEquals(lock.getReadLockCount(), 0);
+ long rs = lock.readLock();
+ assertFalse(lock.isWriteLocked());
+ assertTrue(lock.isReadLocked());
+ assertEquals(lock.getReadLockCount(), 1);
+ lock.unlockRead(rs);
+ assertFalse(lock.isWriteLocked());
+ assertFalse(lock.isReadLocked());
+ assertEquals(lock.getReadLockCount(), 0);
+ }
+
+ /**
+ * unlock releases either a read or write lock
+ */
+ public void testUnlock() {
+ StampedLock lock = new StampedLock();
+ assertFalse(lock.isWriteLocked());
+ assertFalse(lock.isReadLocked());
+ assertEquals(lock.getReadLockCount(), 0);
+ long s = lock.writeLock();
+ assertTrue(lock.isWriteLocked());
+ assertFalse(lock.isReadLocked());
+ assertEquals(lock.getReadLockCount(), 0);
+ lock.unlock(s);
+ assertFalse(lock.isWriteLocked());
+ assertFalse(lock.isReadLocked());
+ assertEquals(lock.getReadLockCount(), 0);
+ long rs = lock.readLock();
+ assertFalse(lock.isWriteLocked());
+ assertTrue(lock.isReadLocked());
+ assertEquals(lock.getReadLockCount(), 1);
+ lock.unlock(rs);
+ assertFalse(lock.isWriteLocked());
+ assertFalse(lock.isReadLocked());
+ assertEquals(lock.getReadLockCount(), 0);
+ }
+
+ /**
+ * tryUnlockRead/Write succeeds if locked in associated mode else
+ * returns false
+ */
+ public void testTryUnlock() {
+ StampedLock lock = new StampedLock();
+ assertFalse(lock.isWriteLocked());
+ assertFalse(lock.isReadLocked());
+ assertEquals(lock.getReadLockCount(), 0);
+ long s = lock.writeLock();
+ assertTrue(lock.isWriteLocked());
+ assertFalse(lock.isReadLocked());
+ assertEquals(lock.getReadLockCount(), 0);
+ assertFalse(lock.tryUnlockRead());
+ assertTrue(lock.tryUnlockWrite());
+ assertFalse(lock.tryUnlockWrite());
+ assertFalse(lock.tryUnlockRead());
+ assertFalse(lock.isWriteLocked());
+ assertFalse(lock.isReadLocked());
+ assertEquals(lock.getReadLockCount(), 0);
+ long rs = lock.readLock();
+ assertFalse(lock.isWriteLocked());
+ assertTrue(lock.isReadLocked());
+ assertEquals(lock.getReadLockCount(), 1);
+ assertFalse(lock.tryUnlockWrite());
+ assertTrue(lock.tryUnlockRead());
+ assertFalse(lock.tryUnlockRead());
+ assertFalse(lock.tryUnlockWrite());
+ assertFalse(lock.isWriteLocked());
+ assertFalse(lock.isReadLocked());
+ assertEquals(lock.getReadLockCount(), 0);
+ }
+
+ /**
+ * write-unlocking an unlocked lock throws IllegalMonitorStateException
+ */
+ public void testWriteUnlock_IMSE() {
+ StampedLock lock = new StampedLock();
+ try {
+ lock.unlockWrite(0L);
+ shouldThrow();
+ } catch (IllegalMonitorStateException success) {}
+ }
+
+ /**
+ * write-unlocking an unlocked lock throws IllegalMonitorStateException
+ */
+ public void testWriteUnlock_IMSE2() {
+ StampedLock lock = new StampedLock();
+ long s = lock.writeLock();
+ lock.unlockWrite(s);
+ try {
+ lock.unlockWrite(s);
+ shouldThrow();
+ } catch (IllegalMonitorStateException success) {}
+ }
+
+ /**
+ * write-unlocking after readlock throws IllegalMonitorStateException
+ */
+ public void testWriteUnlock_IMSE3() {
+ StampedLock lock = new StampedLock();
+ long s = lock.readLock();
+ try {
+ lock.unlockWrite(s);
+ shouldThrow();
+ } catch (IllegalMonitorStateException success) {}
+ }
+
+ /**
+ * read-unlocking an unlocked lock throws IllegalMonitorStateException
+ */
+ public void testReadUnlock_IMSE() {
+ StampedLock lock = new StampedLock();
+ long s = lock.readLock();
+ lock.unlockRead(s);
+ try {
+ lock.unlockRead(s);
+ shouldThrow();
+ } catch (IllegalMonitorStateException success) {}
+ }
+
+ /**
+ * read-unlocking an unlocked lock throws IllegalMonitorStateException
+ */
+ public void testReadUnlock_IMSE2() {
+ StampedLock lock = new StampedLock();
+ try {
+ lock.unlockRead(0L);
+ shouldThrow();
+ } catch (IllegalMonitorStateException success) {}
+ }
+
+ /**
+ * read-unlocking after writeLock throws IllegalMonitorStateException
+ */
+ public void testReadUnlock_IMSE3() {
+ StampedLock lock = new StampedLock();
+ long s = lock.writeLock();
+ try {
+ lock.unlockRead(s);
+ shouldThrow();
+ } catch (IllegalMonitorStateException success) {}
+ }
+
+ /**
+ * validate(0) fails
+ */
+ public void testValidate0() {
+ StampedLock lock = new StampedLock();
+ assertFalse(lock.validate(0L));
+ }
+
+ /**
+ * A stamp obtained from a successful lock operation validates
+ */
+ public void testValidate() throws InterruptedException {
+ StampedLock lock = new StampedLock();
+ long s = lock.writeLock();
+ assertTrue(lock.validate(s));
+ lock.unlockWrite(s);
+ s = lock.readLock();
+ assertTrue(lock.validate(s));
+ lock.unlockRead(s);
+ assertTrue((s = lock.tryWriteLock()) != 0L);
+ assertTrue(lock.validate(s));
+ lock.unlockWrite(s);
+ assertTrue((s = lock.tryReadLock()) != 0L);
+ assertTrue(lock.validate(s));
+ lock.unlockRead(s);
+ assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L);
+ assertTrue(lock.validate(s));
+ lock.unlockWrite(s);
+ assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L);
+ assertTrue(lock.validate(s));
+ lock.unlockRead(s);
+ assertTrue((s = lock.tryOptimisticRead()) != 0L);
+ }
+
+ /**
+ * A stamp obtained from an unsuccessful lock operation does not validate
+ */
+ public void testValidate2() throws InterruptedException {
+ StampedLock lock = new StampedLock();
+ long s;
+ assertTrue((s = lock.writeLock()) != 0L);
+ assertTrue(lock.validate(s));
+ assertFalse(lock.validate(lock.tryWriteLock()));
+ assertFalse(lock.validate(lock.tryWriteLock(10L, MILLISECONDS)));
+ assertFalse(lock.validate(lock.tryReadLock()));
+ assertFalse(lock.validate(lock.tryReadLock(10L, MILLISECONDS)));
+ assertFalse(lock.validate(lock.tryOptimisticRead()));
+ lock.unlockWrite(s);
+ }
+
+ /**
+ * writeLockInterruptibly is interruptible
+ */
+ public void testWriteLockInterruptibly_Interruptible()
+ throws InterruptedException {
+ final CountDownLatch running = new CountDownLatch(1);
+ final StampedLock lock = new StampedLock();
+ long s = lock.writeLock();
+ Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+ public void realRun() throws InterruptedException {
+ running.countDown();
+ lock.writeLockInterruptibly();
+ }});
+
+ running.await();
+ waitForThreadToEnterWaitState(t, 100);
+ t.interrupt();
+ awaitTermination(t);
+ releaseWriteLock(lock, s);
+ }
+
+ /**
+ * timed tryWriteLock is interruptible
+ */
+ public void testWriteTryLock_Interruptible() throws InterruptedException {
+ final CountDownLatch running = new CountDownLatch(1);
+ final StampedLock lock = new StampedLock();
+ long s = lock.writeLock();
+ Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+ public void realRun() throws InterruptedException {
+ running.countDown();
+ lock.tryWriteLock(2 * LONG_DELAY_MS, MILLISECONDS);
+ }});
+
+ running.await();
+ waitForThreadToEnterWaitState(t, 100);
+ t.interrupt();
+ awaitTermination(t);
+ releaseWriteLock(lock, s);
+ }
+
+ /**
+ * readLockInterruptibly is interruptible
+ */
+ public void testReadLockInterruptibly_Interruptible()
+ throws InterruptedException {
+ final CountDownLatch running = new CountDownLatch(1);
+ final StampedLock lock = new StampedLock();
+ long s = lock.writeLock();
+ Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+ public void realRun() throws InterruptedException {
+ running.countDown();
+ lock.readLockInterruptibly();
+ }});
+
+ running.await();
+ waitForThreadToEnterWaitState(t, 100);
+ t.interrupt();
+ awaitTermination(t);
+ releaseWriteLock(lock, s);
+ }
+
+ /**
+ * timed tryReadLock is interruptible
+ */
+ public void testReadTryLock_Interruptible() throws InterruptedException {
+ final CountDownLatch running = new CountDownLatch(1);
+ final StampedLock lock = new StampedLock();
+ long s = lock.writeLock();
+ Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+ public void realRun() throws InterruptedException {
+ running.countDown();
+ lock.tryReadLock(2 * LONG_DELAY_MS, MILLISECONDS);
+ }});
+
+ running.await();
+ waitForThreadToEnterWaitState(t, 100);
+ t.interrupt();
+ awaitTermination(t);
+ releaseWriteLock(lock, s);
+ }
+
+ /**
+ * tryWriteLock on an unlocked lock succeeds
+ */
+ public void testWriteTryLock() {
+ final StampedLock lock = new StampedLock();
+ long s = lock.tryWriteLock();
+ assertTrue(s != 0L);
+ assertTrue(lock.isWriteLocked());
+ long s2 = lock.tryWriteLock();
+ assertEquals(s2, 0L);
+ releaseWriteLock(lock, s);
+ }
+
+ /**
+ * tryWriteLock fails if locked
+ */
+ public void testWriteTryLockWhenLocked() {
+ final StampedLock lock = new StampedLock();
+ long s = lock.writeLock();
+ Thread t = newStartedThread(new CheckedRunnable() {
+ public void realRun() {
+ long ws = lock.tryWriteLock();
+ assertTrue(ws == 0L);
+ }});
+
+ awaitTermination(t);
+ releaseWriteLock(lock, s);
+ }
+
+ /**
+ * tryReadLock fails if write-locked
+ */
+ public void testReadTryLockWhenLocked() {
+ final StampedLock lock = new StampedLock();
+ long s = lock.writeLock();
+ Thread t = newStartedThread(new CheckedRunnable() {
+ public void realRun() {
+ long rs = lock.tryReadLock();
+ assertEquals(rs, 0L);
+ }});
+
+ awaitTermination(t);
+ releaseWriteLock(lock, s);
+ }
+
+ /**
+ * Multiple threads can hold a read lock when not write-locked
+ */
+ public void testMultipleReadLocks() {
+ final StampedLock lock = new StampedLock();
+ final long s = lock.readLock();
+ Thread t = newStartedThread(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ long s2 = lock.tryReadLock();
+ assertTrue(s2 != 0L);
+ lock.unlockRead(s2);
+ long s3 = lock.tryReadLock(LONG_DELAY_MS, MILLISECONDS);
+ assertTrue(s3 != 0L);
+ lock.unlockRead(s3);
+ long s4 = lock.readLock();
+ lock.unlockRead(s4);
+ }});
+
+ awaitTermination(t);
+ lock.unlockRead(s);
+ }
+
+ /**
+ * A writelock succeeds only after a reading thread unlocks
+ */
+ public void testWriteAfterReadLock() throws InterruptedException {
+ final CountDownLatch running = new CountDownLatch(1);
+ final StampedLock lock = new StampedLock();
+ long rs = lock.readLock();
+ Thread t = newStartedThread(new CheckedRunnable() {
+ public void realRun() {
+ running.countDown();
+ long s = lock.writeLock();
+ lock.unlockWrite(s);
+ }});
+
+ running.await();
+ waitForThreadToEnterWaitState(t, 100);
+ assertFalse(lock.isWriteLocked());
+ lock.unlockRead(rs);
+ awaitTermination(t);
+ assertFalse(lock.isWriteLocked());
+ }
+
+ /**
+ * A writelock succeeds only after reading threads unlock
+ */
+ public void testWriteAfterMultipleReadLocks() {
+ final StampedLock lock = new StampedLock();
+ long s = lock.readLock();
+ Thread t1 = newStartedThread(new CheckedRunnable() {
+ public void realRun() {
+ long rs = lock.readLock();
+ lock.unlockRead(rs);
+ }});
+
+ awaitTermination(t1);
+
+ Thread t2 = newStartedThread(new CheckedRunnable() {
+ public void realRun() {
+ long ws = lock.writeLock();
+ lock.unlockWrite(ws);
+ }});
+
+ assertFalse(lock.isWriteLocked());
+ lock.unlockRead(s);
+ awaitTermination(t2);
+ assertFalse(lock.isWriteLocked());
+ }
+
+ /**
+ * Readlocks succeed only after a writing thread unlocks
+ */
+ public void testReadAfterWriteLock() {
+ final StampedLock lock = new StampedLock();
+ final long s = lock.writeLock();
+ Thread t1 = newStartedThread(new CheckedRunnable() {
+ public void realRun() {
+ long rs = lock.readLock();
+ lock.unlockRead(rs);
+ }});
+ Thread t2 = newStartedThread(new CheckedRunnable() {
+ public void realRun() {
+ long rs = lock.readLock();
+ lock.unlockRead(rs);
+ }});
+
+ releaseWriteLock(lock, s);
+ awaitTermination(t1);
+ awaitTermination(t2);
+ }
+
+ /**
+ * tryReadLock succeeds if readlocked but not writelocked
+ */
+ public void testTryLockWhenReadLocked() {
+ final StampedLock lock = new StampedLock();
+ long s = lock.readLock();
+ Thread t = newStartedThread(new CheckedRunnable() {
+ public void realRun() {
+ long rs = lock.tryReadLock();
+ threadAssertTrue(rs != 0L);
+ lock.unlockRead(rs);
+ }});
+
+ awaitTermination(t);
+ lock.unlockRead(s);
+ }
+
+ /**
+ * tryWriteLock fails when readlocked
+ */
+ public void testWriteTryLockWhenReadLocked() {
+ final StampedLock lock = new StampedLock();
+ long s = lock.readLock();
+ Thread t = newStartedThread(new CheckedRunnable() {
+ public void realRun() {
+ long ws = lock.tryWriteLock();
+ threadAssertEquals(ws, 0L);
+ }});
+
+ awaitTermination(t);
+ lock.unlockRead(s);
+ }
+
+ /**
+ * timed tryWriteLock times out if locked
+ */
+ public void testWriteTryLock_Timeout() {
+ final StampedLock lock = new StampedLock();
+ long s = lock.writeLock();
+ Thread t = newStartedThread(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ long startTime = System.nanoTime();
+ long timeoutMillis = 10;
+ long ws = lock.tryWriteLock(timeoutMillis, MILLISECONDS);
+ assertEquals(ws, 0L);
+ assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+ }});
+
+ awaitTermination(t);
+ releaseWriteLock(lock, s);
+ }
+
+ /**
+ * timed tryReadLock times out if write-locked
+ */
+ public void testReadTryLock_Timeout() {
+ final StampedLock lock = new StampedLock();
+ long s = lock.writeLock();
+ Thread t = newStartedThread(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ long startTime = System.nanoTime();
+ long timeoutMillis = 10;
+ long rs = lock.tryReadLock(timeoutMillis, MILLISECONDS);
+ assertEquals(rs, 0L);
+ assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+ }});
+
+ awaitTermination(t);
+ assertTrue(lock.isWriteLocked());
+ lock.unlockWrite(s);
+ }
+
+ /**
+ * writeLockInterruptibly succeeds if unlocked, else is interruptible
+ */
+ public void testWriteLockInterruptibly() throws InterruptedException {
+ final CountDownLatch running = new CountDownLatch(1);
+ final StampedLock lock = new StampedLock();
+ long s = lock.writeLockInterruptibly();
+ Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+ public void realRun() throws InterruptedException {
+ running.countDown();
+ lock.writeLockInterruptibly();
+ }});
+
+ running.await();
+ waitForThreadToEnterWaitState(t, 100);
+ t.interrupt();
+ assertTrue(lock.isWriteLocked());
+ awaitTermination(t);
+ releaseWriteLock(lock, s);
+ }
+
+ /**
+ * readLockInterruptibly succeeds if lock free else is interruptible
+ */
+ public void testReadLockInterruptibly() throws InterruptedException {
+ final CountDownLatch running = new CountDownLatch(1);
+ final StampedLock lock = new StampedLock();
+ long s;
+ s = lock.readLockInterruptibly();
+ lock.unlockRead(s);
+ s = lock.writeLockInterruptibly();
+ Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+ public void realRun() throws InterruptedException {
+ running.countDown();
+ lock.readLockInterruptibly();
+ }});
+
+ running.await();
+ waitForThreadToEnterWaitState(t, 100);
+ t.interrupt();
+ awaitTermination(t);
+ releaseWriteLock(lock, s);
+ }
+
+ /**
+ * A serialized lock deserializes as unlocked
+ */
+ public void testSerialization() {
+ StampedLock lock = new StampedLock();
+ lock.writeLock();
+ StampedLock clone = serialClone(lock);
+ assertTrue(lock.isWriteLocked());
+ assertFalse(clone.isWriteLocked());
+ long s = clone.writeLock();
+ assertTrue(clone.isWriteLocked());
+ clone.unlockWrite(s);
+ assertFalse(clone.isWriteLocked());
+ }
+
+ /**
+ * toString indicates current lock state
+ */
+ public void testToString() {
+ StampedLock lock = new StampedLock();
+ assertTrue(lock.toString().contains("Unlocked"));
+ long s = lock.writeLock();
+ assertTrue(lock.toString().contains("Write-locked"));
+ lock.unlockWrite(s);
+ s = lock.readLock();
+ assertTrue(lock.toString().contains("Read-locks"));
+ }
+
+ /**
+ * tryOptimisticRead succeeds and validates if unlocked, fails if locked
+ */
+ public void testValidateOptimistic() throws InterruptedException {
+ StampedLock lock = new StampedLock();
+ long s, p;
+ assertTrue((p = lock.tryOptimisticRead()) != 0L);
+ assertTrue(lock.validate(p));
+ assertTrue((s = lock.writeLock()) != 0L);
+ assertFalse((p = lock.tryOptimisticRead()) != 0L);
+ assertTrue(lock.validate(s));
+ lock.unlockWrite(s);
+ assertTrue((p = lock.tryOptimisticRead()) != 0L);
+ assertTrue(lock.validate(p));
+ assertTrue((s = lock.readLock()) != 0L);
+ assertTrue(lock.validate(s));
+ assertTrue((p = lock.tryOptimisticRead()) != 0L);
+ assertTrue(lock.validate(p));
+ lock.unlockRead(s);
+ assertTrue((s = lock.tryWriteLock()) != 0L);
+ assertTrue(lock.validate(s));
+ assertFalse((p = lock.tryOptimisticRead()) != 0L);
+ lock.unlockWrite(s);
+ assertTrue((s = lock.tryReadLock()) != 0L);
+ assertTrue(lock.validate(s));
+ assertTrue((p = lock.tryOptimisticRead()) != 0L);
+ lock.unlockRead(s);
+ assertTrue(lock.validate(p));
+ assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L);
+ assertFalse((p = lock.tryOptimisticRead()) != 0L);
+ assertTrue(lock.validate(s));
+ lock.unlockWrite(s);
+ assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L);
+ assertTrue(lock.validate(s));
+ assertTrue((p = lock.tryOptimisticRead()) != 0L);
+ lock.unlockRead(s);
+ assertTrue((p = lock.tryOptimisticRead()) != 0L);
+ }
+
+ /**
+ * tryOptimisticRead stamp does not validate if a write lock intervenes
+ */
+ public void testValidateOptimisticWriteLocked() {
+ StampedLock lock = new StampedLock();
+ long s, p;
+ assertTrue((p = lock.tryOptimisticRead()) != 0L);
+ assertTrue((s = lock.writeLock()) != 0L);
+ assertFalse(lock.validate(p));
+ assertFalse((p = lock.tryOptimisticRead()) != 0L);
+ assertTrue(lock.validate(s));
+ lock.unlockWrite(s);
+ }
+
+ /**
+ * tryOptimisticRead stamp does not validate if a write lock
+ * intervenes in another thread
+ */
+ public void testValidateOptimisticWriteLocked2()
+ throws InterruptedException {
+ final CountDownLatch running = new CountDownLatch(1);
+ final StampedLock lock = new StampedLock();
+ long s, p;
+ assertTrue((p = lock.tryOptimisticRead()) != 0L);
+ Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+ public void realRun() throws InterruptedException {
+ lock.writeLockInterruptibly();
+ running.countDown();
+ lock.writeLockInterruptibly();
+ }});
+
+ running.await();
+ assertFalse(lock.validate(p));
+ assertFalse((p = lock.tryOptimisticRead()) != 0L);
+ t.interrupt();
+ awaitTermination(t);
+ }
+
+ /**
+ * tryConvertToOptimisticRead succeeds and validates if successfully locked,
+ */
+ public void testTryConvertToOptimisticRead() throws InterruptedException {
+ StampedLock lock = new StampedLock();
+ long s, p;
+ s = 0L;
+ assertFalse((p = lock.tryConvertToOptimisticRead(s)) != 0L);
+ assertTrue((s = lock.tryOptimisticRead()) != 0L);
+ assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
+ assertTrue((s = lock.writeLock()) != 0L);
+ assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
+ assertTrue(lock.validate(p));
+ assertTrue((s = lock.readLock()) != 0L);
+ assertTrue(lock.validate(s));
+ assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
+ assertTrue(lock.validate(p));
+ assertTrue((s = lock.tryWriteLock()) != 0L);
+ assertTrue(lock.validate(s));
+ assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
+ assertTrue(lock.validate(p));
+ assertTrue((s = lock.tryReadLock()) != 0L);
+ assertTrue(lock.validate(s));
+ assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
+ assertTrue(lock.validate(p));
+ assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L);
+ assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
+ assertTrue(lock.validate(p));
+ assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L);
+ assertTrue(lock.validate(s));
+ assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
+ assertTrue(lock.validate(p));
+ }
+
+ /**
+ * tryConvertToReadLock succeeds and validates if successfully locked
+ * or lock free;
+ */
+ public void testTryConvertToReadLock() throws InterruptedException {
+ StampedLock lock = new StampedLock();
+ long s, p;
+ s = 0L;
+ assertFalse((p = lock.tryConvertToReadLock(s)) != 0L);
+ assertTrue((s = lock.tryOptimisticRead()) != 0L);
+ assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
+ lock.unlockRead(p);
+ assertTrue((s = lock.writeLock()) != 0L);
+ assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
+ assertTrue(lock.validate(p));
+ lock.unlockRead(p);
+ assertTrue((s = lock.readLock()) != 0L);
+ assertTrue(lock.validate(s));
+ assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
+ assertTrue(lock.validate(p));
+ lock.unlockRead(p);
+ assertTrue((s = lock.tryWriteLock()) != 0L);
+ assertTrue(lock.validate(s));
+ assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
+ assertTrue(lock.validate(p));
+ lock.unlockRead(p);
+ assertTrue((s = lock.tryReadLock()) != 0L);
+ assertTrue(lock.validate(s));
+ assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
+ assertTrue(lock.validate(p));
+ lock.unlockRead(p);
+ assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L);
+ assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
+ assertTrue(lock.validate(p));
+ lock.unlockRead(p);
+ assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L);
+ assertTrue(lock.validate(s));
+ assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
+ assertTrue(lock.validate(p));
+ lock.unlockRead(p);
+ }
+
+ /**
+ * tryConvertToWriteLock succeeds and validates if successfully locked
+ * or lock free;
+ */
+ public void testTryConvertToWriteLock() throws InterruptedException {
+ StampedLock lock = new StampedLock();
+ long s, p;
+ s = 0L;
+ assertFalse((p = lock.tryConvertToWriteLock(s)) != 0L);
+ assertTrue((s = lock.tryOptimisticRead()) != 0L);
+ assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
+ lock.unlockWrite(p);
+ assertTrue((s = lock.writeLock()) != 0L);
+ assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
+ assertTrue(lock.validate(p));
+ lock.unlockWrite(p);
+ assertTrue((s = lock.readLock()) != 0L);
+ assertTrue(lock.validate(s));
+ assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
+ assertTrue(lock.validate(p));
+ lock.unlockWrite(p);
+ assertTrue((s = lock.tryWriteLock()) != 0L);
+ assertTrue(lock.validate(s));
+ assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
+ assertTrue(lock.validate(p));
+ lock.unlockWrite(p);
+ assertTrue((s = lock.tryReadLock()) != 0L);
+ assertTrue(lock.validate(s));
+ assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
+ assertTrue(lock.validate(p));
+ lock.unlockWrite(p);
+ assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L);
+ assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
+ assertTrue(lock.validate(p));
+ lock.unlockWrite(p);
+ assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L);
+ assertTrue(lock.validate(s));
+ assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
+ assertTrue(lock.validate(p));
+ lock.unlockWrite(p);
+ }
+
+ /**
+ * asWriteLock can be locked and unlocked
+ */
+ public void testAsWriteLock() {
+ StampedLock sl = new StampedLock();
+ Lock lock = sl.asWriteLock();
+ lock.lock();
+ assertFalse(lock.tryLock());
+ lock.unlock();
+ assertTrue(lock.tryLock());
+ }
+
+ /**
+ * asReadLock can be locked and unlocked
+ */
+ public void testAsReadLock() {
+ StampedLock sl = new StampedLock();
+ Lock lock = sl.asReadLock();
+ lock.lock();
+ lock.unlock();
+ assertTrue(lock.tryLock());
+ }
+
+ /**
+ * asReadWriteLock.writeLock can be locked and unlocked
+ */
+ public void testAsReadWriteLockWriteLock() {
+ StampedLock sl = new StampedLock();
+ Lock lock = sl.asReadWriteLock().writeLock();
+ lock.lock();
+ assertFalse(lock.tryLock());
+ lock.unlock();
+ assertTrue(lock.tryLock());
+ }
+
+ /**
+ * asReadWriteLock.readLock can be locked and unlocked
+ */
+ public void testAsReadWriteLockReadLock() {
+ StampedLock sl = new StampedLock();
+ Lock lock = sl.asReadWriteLock().readLock();
+ lock.lock();
+ lock.unlock();
+ assertTrue(lock.tryLock());
+ }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/SynchronousQueueTest.java b/jsr166-tests/src/test/java/jsr166/SynchronousQueueTest.java
index 605a955..9d3f212 100644
--- a/jsr166-tests/src/test/java/jsr166/SynchronousQueueTest.java
+++ b/jsr166-tests/src/test/java/jsr166/SynchronousQueueTest.java
@@ -25,7 +25,7 @@
public class SynchronousQueueTest extends JSR166TestCase {
- // android-note: These tests have been moved into their own separate
+ // android-note: These tests have been moved into their own separate
// classes to work around CTS issues.
//
// public static class Fair extends BlockingQueueTest {
@@ -33,17 +33,19 @@
// return new SynchronousQueue(true);
// }
// }
- //
+
// public static class NonFair extends BlockingQueueTest {
// protected BlockingQueue emptyCollection() {
// return new SynchronousQueue(false);
// }
// }
+
+ // android-note: Removed because the CTS runner does a bad job of
+ // retrying tests that have suite() declarations.
//
// public static void main(String[] args) {
// main(suite(), args);
// }
- //
// public static Test suite() {
// return newTestSuite(SynchronousQueueTest.class,
// new Fair().testSuite(),
@@ -262,7 +264,6 @@
pleaseOffer.countDown();
startTime = System.nanoTime();
assertSame(zero, q.poll(LONG_DELAY_MS, MILLISECONDS));
- assertTrue(millisElapsedSince(startTime) < MEDIUM_DELAY_MS);
Thread.currentThread().interrupt();
try {
@@ -277,13 +278,15 @@
shouldThrow();
} catch (InterruptedException success) {}
assertFalse(Thread.interrupted());
+
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}});
await(pleaseOffer);
long startTime = System.nanoTime();
try { assertTrue(q.offer(zero, LONG_DELAY_MS, MILLISECONDS)); }
catch (InterruptedException e) { threadUnexpectedException(e); }
- assertTrue(millisElapsedSince(startTime) < MEDIUM_DELAY_MS);
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
await(pleaseInterrupt);
assertThreadStaysAlive(t);
@@ -474,24 +477,24 @@
public void testOfferInExecutor_fair() { testOfferInExecutor(true); }
public void testOfferInExecutor(boolean fair) {
final SynchronousQueue q = new SynchronousQueue(fair);
- ExecutorService executor = Executors.newFixedThreadPool(2);
final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+ final ExecutorService executor = Executors.newFixedThreadPool(2);
+ try (PoolCleaner cleaner = cleaner(executor)) {
- executor.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- assertFalse(q.offer(one));
- threadsStarted.await();
- assertTrue(q.offer(one, LONG_DELAY_MS, MILLISECONDS));
- assertEquals(0, q.remainingCapacity());
- }});
+ executor.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ assertFalse(q.offer(one));
+ threadsStarted.await();
+ assertTrue(q.offer(one, LONG_DELAY_MS, MILLISECONDS));
+ assertEquals(0, q.remainingCapacity());
+ }});
- executor.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- threadsStarted.await();
- assertSame(one, q.take());
- }});
-
- joinPool(executor);
+ executor.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ threadsStarted.await();
+ assertSame(one, q.take());
+ }});
+ }
}
/**
@@ -502,22 +505,22 @@
public void testPollInExecutor(boolean fair) {
final SynchronousQueue q = new SynchronousQueue(fair);
final CheckedBarrier threadsStarted = new CheckedBarrier(2);
- ExecutorService executor = Executors.newFixedThreadPool(2);
- executor.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- assertNull(q.poll());
- threadsStarted.await();
- assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
- assertTrue(q.isEmpty());
- }});
+ final ExecutorService executor = Executors.newFixedThreadPool(2);
+ try (PoolCleaner cleaner = cleaner(executor)) {
+ executor.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ assertNull(q.poll());
+ threadsStarted.await();
+ assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(q.isEmpty());
+ }});
- executor.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- threadsStarted.await();
- q.put(one);
- }});
-
- joinPool(executor);
+ executor.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ threadsStarted.await();
+ q.put(one);
+ }});
+ }
}
/**
@@ -595,10 +598,12 @@
}});
ArrayList l = new ArrayList();
- delay(SHORT_DELAY_MS);
- q.drainTo(l, 1);
+ int drained;
+ while ((drained = q.drainTo(l, 1)) == 0) Thread.yield();
+ assertEquals(1, drained);
assertEquals(1, l.size());
- q.drainTo(l, 1);
+ while ((drained = q.drainTo(l, 1)) == 0) Thread.yield();
+ assertEquals(1, drained);
assertEquals(2, l.size());
assertTrue(l.contains(one));
assertTrue(l.contains(two));
diff --git a/jsr166-tests/src/test/java/jsr166/SystemTest.java b/jsr166-tests/src/test/java/jsr166/SystemTest.java
index 6918374..412ce17 100644
--- a/jsr166-tests/src/test/java/jsr166/SystemTest.java
+++ b/jsr166-tests/src/test/java/jsr166/SystemTest.java
@@ -19,7 +19,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(SystemTest.class);
// }
/**
diff --git a/jsr166-tests/src/test/java/jsr166/ThreadLocalRandom8Test.java b/jsr166-tests/src/test/java/jsr166/ThreadLocalRandom8Test.java
new file mode 100644
index 0000000..614af83
--- /dev/null
+++ b/jsr166-tests/src/test/java/jsr166/ThreadLocalRandom8Test.java
@@ -0,0 +1,241 @@
+/*
+ * 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/
+ */
+
+package jsr166;
+
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.LongAdder;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ThreadLocalRandom8Test extends JSR166TestCase {
+
+ // android-note: Removed because the CTS runner does a bad job of
+ // retrying tests that have suite() declarations.
+ //
+ // public static void main(String[] args) {
+ // main(suite(), args);
+ // }
+ // public static Test suite() {
+ // return new TestSuite(ThreadLocalRandom8Test.class);
+ // }
+
+ // max sampled int bound
+ static final int MAX_INT_BOUND = (1 << 26);
+
+ // max sampled long bound
+ static final long MAX_LONG_BOUND = (1L << 42);
+
+ // Number of replications for other checks
+ static final int REPS =
+ Integer.getInteger("ThreadLocalRandom8Test.reps", 4);
+
+ /**
+ * Invoking sized ints, long, doubles, with negative sizes throws
+ * IllegalArgumentException
+ */
+ // TODO(streams):
+ // public void testBadStreamSize() {
+ // ThreadLocalRandom r = ThreadLocalRandom.current();
+ // Runnable[] throwingActions = {
+ // () -> r.ints(-1L),
+ // () -> r.ints(-1L, 2, 3),
+ // () -> r.longs(-1L),
+ // () -> r.longs(-1L, -1L, 1L),
+ // () -> r.doubles(-1L),
+ // () -> r.doubles(-1L, .5, .6),
+ // };
+ // assertThrows(IllegalArgumentException.class, throwingActions);
+ // }
+
+ // /**
+ // * Invoking bounded ints, long, doubles, with illegal bounds throws
+ // * IllegalArgumentException
+ // */
+ // public void testBadStreamBounds() {
+ // ThreadLocalRandom r = ThreadLocalRandom.current();
+ // Runnable[] throwingActions = {
+ // () -> r.ints(2, 1),
+ // () -> r.ints(10, 42, 42),
+ // () -> r.longs(-1L, -1L),
+ // () -> r.longs(10, 1L, -2L),
+ // () -> r.doubles(0.0, 0.0),
+ // () -> r.doubles(10, .5, .4),
+ // };
+ // assertThrows(IllegalArgumentException.class, throwingActions);
+ // }
+
+ // /**
+ // * A parallel sized stream of ints generates the given number of values
+ // */
+ // public void testIntsCount() {
+ // LongAdder counter = new LongAdder();
+ // ThreadLocalRandom r = ThreadLocalRandom.current();
+ // long size = 0;
+ // for (int reps = 0; reps < REPS; ++reps) {
+ // counter.reset();
+ // r.ints(size).parallel().forEach(x -> counter.increment());
+ // assertEquals(size, counter.sum());
+ // size += 524959;
+ // }
+ // }
+
+ // /**
+ // * A parallel sized stream of longs generates the given number of values
+ // */
+ // public void testLongsCount() {
+ // LongAdder counter = new LongAdder();
+ // ThreadLocalRandom r = ThreadLocalRandom.current();
+ // long size = 0;
+ // for (int reps = 0; reps < REPS; ++reps) {
+ // counter.reset();
+ // r.longs(size).parallel().forEach(x -> counter.increment());
+ // assertEquals(size, counter.sum());
+ // size += 524959;
+ // }
+ // }
+
+ // /**
+ // * A parallel sized stream of doubles generates the given number of values
+ // */
+ // public void testDoublesCount() {
+ // LongAdder counter = new LongAdder();
+ // ThreadLocalRandom r = ThreadLocalRandom.current();
+ // long size = 0;
+ // for (int reps = 0; reps < REPS; ++reps) {
+ // counter.reset();
+ // r.doubles(size).parallel().forEach(x -> counter.increment());
+ // assertEquals(size, counter.sum());
+ // size += 524959;
+ // }
+ // }
+
+ // /**
+ // * Each of a parallel sized stream of bounded ints is within bounds
+ // */
+ // public void testBoundedInts() {
+ // AtomicInteger fails = new AtomicInteger(0);
+ // ThreadLocalRandom r = ThreadLocalRandom.current();
+ // long size = 12345L;
+ // for (int least = -15485867; least < MAX_INT_BOUND; least += 524959) {
+ // for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 67867967) {
+ // final int lo = least, hi = bound;
+ // r.ints(size, lo, hi).parallel().forEach(
+ // x -> {
+ // if (x < lo || x >= hi)
+ // fails.getAndIncrement(); });
+ // }
+ // }
+ // assertEquals(0, fails.get());
+ // }
+
+ // /**
+ // * Each of a parallel sized stream of bounded longs is within bounds
+ // */
+ // public void testBoundedLongs() {
+ // AtomicInteger fails = new AtomicInteger(0);
+ // ThreadLocalRandom r = ThreadLocalRandom.current();
+ // long size = 123L;
+ // for (long least = -86028121; least < MAX_LONG_BOUND; least += 1982451653L) {
+ // for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) {
+ // final long lo = least, hi = bound;
+ // r.longs(size, lo, hi).parallel().forEach(
+ // x -> {
+ // if (x < lo || x >= hi)
+ // fails.getAndIncrement(); });
+ // }
+ // }
+ // assertEquals(0, fails.get());
+ // }
+
+ // /**
+ // * Each of a parallel sized stream of bounded doubles is within bounds
+ // */
+ // public void testBoundedDoubles() {
+ // AtomicInteger fails = new AtomicInteger(0);
+ // ThreadLocalRandom r = ThreadLocalRandom.current();
+ // long size = 456;
+ // for (double least = 0.00011; least < 1.0e20; least *= 9) {
+ // for (double bound = least * 1.0011; bound < 1.0e20; bound *= 17) {
+ // final double lo = least, hi = bound;
+ // r.doubles(size, lo, hi).parallel().forEach(
+ // x -> {
+ // if (x < lo || x >= hi)
+ // fails.getAndIncrement(); });
+ // }
+ // }
+ // assertEquals(0, fails.get());
+ // }
+
+ // /**
+ // * A parallel unsized stream of ints generates at least 100 values
+ // */
+ // public void testUnsizedIntsCount() {
+ // LongAdder counter = new LongAdder();
+ // ThreadLocalRandom r = ThreadLocalRandom.current();
+ // long size = 100;
+ // r.ints().limit(size).parallel().forEach(x -> counter.increment());
+ // assertEquals(size, counter.sum());
+ // }
+
+ // /**
+ // * A parallel unsized stream of longs generates at least 100 values
+ // */
+ // public void testUnsizedLongsCount() {
+ // LongAdder counter = new LongAdder();
+ // ThreadLocalRandom r = ThreadLocalRandom.current();
+ // long size = 100;
+ // r.longs().limit(size).parallel().forEach(x -> counter.increment());
+ // assertEquals(size, counter.sum());
+ // }
+
+ // /**
+ // * A parallel unsized stream of doubles generates at least 100 values
+ // */
+ // public void testUnsizedDoublesCount() {
+ // LongAdder counter = new LongAdder();
+ // ThreadLocalRandom r = ThreadLocalRandom.current();
+ // long size = 100;
+ // r.doubles().limit(size).parallel().forEach(x -> counter.increment());
+ // assertEquals(size, counter.sum());
+ // }
+
+ // /**
+ // * A sequential unsized stream of ints generates at least 100 values
+ // */
+ // public void testUnsizedIntsCountSeq() {
+ // LongAdder counter = new LongAdder();
+ // ThreadLocalRandom r = ThreadLocalRandom.current();
+ // long size = 100;
+ // r.ints().limit(size).forEach(x -> counter.increment());
+ // assertEquals(size, counter.sum());
+ // }
+
+ // /**
+ // * A sequential unsized stream of longs generates at least 100 values
+ // */
+ // public void testUnsizedLongsCountSeq() {
+ // LongAdder counter = new LongAdder();
+ // ThreadLocalRandom r = ThreadLocalRandom.current();
+ // long size = 100;
+ // r.longs().limit(size).forEach(x -> counter.increment());
+ // assertEquals(size, counter.sum());
+ // }
+
+ // /**
+ // * A sequential unsized stream of doubles generates at least 100 values
+ // */
+ // public void testUnsizedDoublesCountSeq() {
+ // LongAdder counter = new LongAdder();
+ // ThreadLocalRandom r = ThreadLocalRandom.current();
+ // long size = 100;
+ // r.doubles().limit(size).forEach(x -> counter.increment());
+ // assertEquals(size, counter.sum());
+ // }
+
+}
diff --git a/jsr166-tests/src/test/java/jsr166/ThreadLocalRandomTest.java b/jsr166-tests/src/test/java/jsr166/ThreadLocalRandomTest.java
index 4ae141d..5d9f894 100644
--- a/jsr166-tests/src/test/java/jsr166/ThreadLocalRandomTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ThreadLocalRandomTest.java
@@ -22,7 +22,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(ThreadLocalRandomTest.class);
// }
/*
diff --git a/jsr166-tests/src/test/java/jsr166/ThreadLocalTest.java b/jsr166-tests/src/test/java/jsr166/ThreadLocalTest.java
index 7f5f072..8bfcf70 100644
--- a/jsr166-tests/src/test/java/jsr166/ThreadLocalTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ThreadLocalTest.java
@@ -19,7 +19,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(ThreadLocalTest.class);
// }
static ThreadLocal<Integer> tl = new ThreadLocal<Integer>() {
diff --git a/jsr166-tests/src/test/java/jsr166/ThreadPoolExecutorSubclassTest.java b/jsr166-tests/src/test/java/jsr166/ThreadPoolExecutorSubclassTest.java
index 5f38d39..a502392 100644
--- a/jsr166-tests/src/test/java/jsr166/ThreadPoolExecutorSubclassTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ThreadPoolExecutorSubclassTest.java
@@ -9,12 +9,14 @@
package jsr166;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
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.ExecutionException;
import java.util.concurrent.Executors;
@@ -30,6 +32,7 @@
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
@@ -44,7 +47,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(ThreadPoolExecutorSubclassTest.class);
// }
static class CustomTask<V> implements RunnableFuture<V> {
@@ -103,11 +106,13 @@
}
lock.lock();
try {
- result = v;
- exception = e;
- done = true;
- thread = null;
- cond.signalAll();
+ if (!done) {
+ result = v;
+ exception = e;
+ done = true;
+ thread = null;
+ cond.signalAll();
+ }
}
finally { lock.unlock(); }
}
@@ -116,6 +121,8 @@
try {
while (!done)
cond.await();
+ if (cancelled)
+ throw new CancellationException();
if (exception != null)
throw new ExecutionException(exception);
return result;
@@ -127,12 +134,13 @@
long nanos = unit.toNanos(timeout);
lock.lock();
try {
- for (;;) {
- if (done) break;
- if (nanos < 0)
+ while (!done) {
+ if (nanos <= 0L)
throw new TimeoutException();
nanos = cond.awaitNanos(nanos);
}
+ if (cancelled)
+ throw new CancellationException();
if (exception != null)
throw new ExecutionException(exception);
return result;
@@ -229,18 +237,14 @@
public void testExecute() throws InterruptedException {
final ThreadPoolExecutor p =
new CustomTPE(1, 1,
- LONG_DELAY_MS, MILLISECONDS,
+ 2 * LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- final CountDownLatch done = new CountDownLatch(1);
- final Runnable task = new CheckedRunnable() {
- public void realRun() {
- done.countDown();
- }};
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final CountDownLatch done = new CountDownLatch(1);
+ final Runnable task = new CheckedRunnable() {
+ public void realRun() { done.countDown(); }};
p.execute(task);
- assertTrue(done.await(SMALL_DELAY_MS, MILLISECONDS));
- } finally {
- joinPool(p);
+ assertTrue(done.await(LONG_DELAY_MS, MILLISECONDS));
}
}
@@ -249,25 +253,22 @@
* thread becomes active
*/
public void testGetActiveCount() throws InterruptedException {
+ final CountDownLatch done = new CountDownLatch(1);
final ThreadPoolExecutor p =
new CustomTPE(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- final CountDownLatch threadStarted = new CountDownLatch(1);
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
assertEquals(0, p.getActiveCount());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
assertEquals(1, p.getActiveCount());
- done.await();
+ await(done);
}});
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
assertEquals(1, p.getActiveCount());
- } finally {
- done.countDown();
- joinPool(p);
}
}
@@ -275,28 +276,48 @@
* prestartCoreThread starts a thread if under corePoolSize, else doesn't
*/
public void testPrestartCoreThread() {
- ThreadPoolExecutor p = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- assertEquals(0, p.getPoolSize());
- assertTrue(p.prestartCoreThread());
- assertEquals(1, p.getPoolSize());
- assertTrue(p.prestartCoreThread());
- assertEquals(2, p.getPoolSize());
- assertFalse(p.prestartCoreThread());
- assertEquals(2, p.getPoolSize());
- joinPool(p);
+ final ThreadPoolExecutor p =
+ new CustomTPE(2, 6,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertEquals(0, p.getPoolSize());
+ assertTrue(p.prestartCoreThread());
+ assertEquals(1, p.getPoolSize());
+ assertTrue(p.prestartCoreThread());
+ assertEquals(2, p.getPoolSize());
+ assertFalse(p.prestartCoreThread());
+ assertEquals(2, p.getPoolSize());
+ p.setCorePoolSize(4);
+ assertTrue(p.prestartCoreThread());
+ assertEquals(3, p.getPoolSize());
+ assertTrue(p.prestartCoreThread());
+ assertEquals(4, p.getPoolSize());
+ assertFalse(p.prestartCoreThread());
+ assertEquals(4, p.getPoolSize());
+ }
}
/**
* prestartAllCoreThreads starts all corePoolSize threads
*/
public void testPrestartAllCoreThreads() {
- ThreadPoolExecutor p = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- assertEquals(0, p.getPoolSize());
- p.prestartAllCoreThreads();
- assertEquals(2, p.getPoolSize());
- p.prestartAllCoreThreads();
- assertEquals(2, p.getPoolSize());
- joinPool(p);
+ final ThreadPoolExecutor p =
+ new CustomTPE(2, 6,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertEquals(0, p.getPoolSize());
+ p.prestartAllCoreThreads();
+ assertEquals(2, p.getPoolSize());
+ p.prestartAllCoreThreads();
+ assertEquals(2, p.getPoolSize());
+ p.setCorePoolSize(4);
+ p.prestartAllCoreThreads();
+ assertEquals(4, p.getPoolSize());
+ p.prestartAllCoreThreads();
+ assertEquals(4, p.getPoolSize());
+ }
}
/**
@@ -308,10 +329,10 @@
new CustomTPE(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- final CountDownLatch threadStarted = new CountDownLatch(1);
- final CountDownLatch threadProceed = new CountDownLatch(1);
- final CountDownLatch threadDone = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
+ final CountDownLatch threadProceed = new CountDownLatch(1);
+ final CountDownLatch threadDone = new CountDownLatch(1);
assertEquals(0, p.getCompletedTaskCount());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
@@ -330,8 +351,6 @@
fail("timed out");
Thread.yield();
}
- } finally {
- joinPool(p);
}
}
@@ -339,52 +358,72 @@
* getCorePoolSize returns size given in constructor if not otherwise set
*/
public void testGetCorePoolSize() {
- ThreadPoolExecutor p = new CustomTPE(1, 1, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- assertEquals(1, p.getCorePoolSize());
- joinPool(p);
+ final ThreadPoolExecutor p =
+ new CustomTPE(1, 1,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertEquals(1, p.getCorePoolSize());
+ }
}
/**
* getKeepAliveTime returns value given in constructor if not otherwise set
*/
public void testGetKeepAliveTime() {
- ThreadPoolExecutor p = new CustomTPE(2, 2, 1000, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- assertEquals(1, p.getKeepAliveTime(TimeUnit.SECONDS));
- joinPool(p);
+ final ThreadPoolExecutor p =
+ new CustomTPE(2, 2,
+ 1000, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertEquals(1, p.getKeepAliveTime(SECONDS));
+ }
}
/**
* getThreadFactory returns factory in constructor if not set
*/
public void testGetThreadFactory() {
- ThreadFactory tf = new SimpleThreadFactory();
- ThreadPoolExecutor p = new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10), tf, new NoOpREHandler());
- assertSame(tf, p.getThreadFactory());
- joinPool(p);
+ final ThreadFactory threadFactory = new SimpleThreadFactory();
+ final ThreadPoolExecutor p =
+ new CustomTPE(1, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10),
+ threadFactory,
+ new NoOpREHandler());
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertSame(threadFactory, p.getThreadFactory());
+ }
}
/**
* setThreadFactory sets the thread factory returned by getThreadFactory
*/
public void testSetThreadFactory() {
- ThreadPoolExecutor p = new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- ThreadFactory tf = new SimpleThreadFactory();
- p.setThreadFactory(tf);
- assertSame(tf, p.getThreadFactory());
- joinPool(p);
+ final ThreadPoolExecutor p =
+ new CustomTPE(1, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(p)) {
+ ThreadFactory threadFactory = new SimpleThreadFactory();
+ p.setThreadFactory(threadFactory);
+ assertSame(threadFactory, p.getThreadFactory());
+ }
}
/**
* setThreadFactory(null) throws NPE
*/
public void testSetThreadFactoryNull() {
- ThreadPoolExecutor p = new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- try {
- p.setThreadFactory(null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(p);
+ final ThreadPoolExecutor p =
+ new CustomTPE(1, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.setThreadFactory(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -392,10 +431,15 @@
* getRejectedExecutionHandler returns handler in constructor if not set
*/
public void testGetRejectedExecutionHandler() {
- RejectedExecutionHandler h = new NoOpREHandler();
- ThreadPoolExecutor p = new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10), h);
- assertSame(h, p.getRejectedExecutionHandler());
- joinPool(p);
+ final RejectedExecutionHandler handler = new NoOpREHandler();
+ final ThreadPoolExecutor p =
+ new CustomTPE(1, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10),
+ handler);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertSame(handler, p.getRejectedExecutionHandler());
+ }
}
/**
@@ -403,24 +447,30 @@
* getRejectedExecutionHandler
*/
public void testSetRejectedExecutionHandler() {
- ThreadPoolExecutor p = new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- RejectedExecutionHandler h = new NoOpREHandler();
- p.setRejectedExecutionHandler(h);
- assertSame(h, p.getRejectedExecutionHandler());
- joinPool(p);
+ final ThreadPoolExecutor p =
+ new CustomTPE(1, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(p)) {
+ RejectedExecutionHandler handler = new NoOpREHandler();
+ p.setRejectedExecutionHandler(handler);
+ assertSame(handler, p.getRejectedExecutionHandler());
+ }
}
/**
* setRejectedExecutionHandler(null) throws NPE
*/
public void testSetRejectedExecutionHandlerNull() {
- ThreadPoolExecutor p = new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- try {
- p.setRejectedExecutionHandler(null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(p);
+ final ThreadPoolExecutor p =
+ new CustomTPE(1, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.setRejectedExecutionHandler(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -430,28 +480,25 @@
*/
public void testGetLargestPoolSize() throws InterruptedException {
final int THREADS = 3;
+ final CountDownLatch done = new CountDownLatch(1);
final ThreadPoolExecutor p =
new CustomTPE(THREADS, THREADS,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- final CountDownLatch threadsStarted = new CountDownLatch(THREADS);
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p, done)) {
assertEquals(0, p.getLargestPoolSize());
+ final CountDownLatch threadsStarted = new CountDownLatch(THREADS);
for (int i = 0; i < THREADS; i++)
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadsStarted.countDown();
- done.await();
+ await(done);
assertEquals(THREADS, p.getLargestPoolSize());
}});
- assertTrue(threadsStarted.await(SMALL_DELAY_MS, MILLISECONDS));
- assertEquals(THREADS, p.getLargestPoolSize());
- } finally {
- done.countDown();
- joinPool(p);
+ await(threadsStarted);
assertEquals(THREADS, p.getLargestPoolSize());
}
+ assertEquals(THREADS, p.getLargestPoolSize());
}
/**
@@ -459,9 +506,17 @@
* otherwise set
*/
public void testGetMaximumPoolSize() {
- ThreadPoolExecutor p = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- assertEquals(2, p.getMaximumPoolSize());
- joinPool(p);
+ final ThreadPoolExecutor p =
+ new CustomTPE(2, 3,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertEquals(3, p.getMaximumPoolSize());
+ p.setMaximumPoolSize(5);
+ assertEquals(5, p.getMaximumPoolSize());
+ p.setMaximumPoolSize(4);
+ assertEquals(4, p.getMaximumPoolSize());
+ }
}
/**
@@ -469,25 +524,22 @@
* become active
*/
public void testGetPoolSize() throws InterruptedException {
+ final CountDownLatch done = new CountDownLatch(1);
final ThreadPoolExecutor p =
new CustomTPE(1, 1,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- final CountDownLatch threadStarted = new CountDownLatch(1);
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p, done)) {
assertEquals(0, p.getPoolSize());
+ final CountDownLatch threadStarted = new CountDownLatch(1);
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
assertEquals(1, p.getPoolSize());
- done.await();
+ await(done);
}});
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
assertEquals(1, p.getPoolSize());
- } finally {
- done.countDown();
- joinPool(p);
}
}
@@ -495,38 +547,53 @@
* getTaskCount increases, but doesn't overestimate, when tasks submitted
*/
public void testGetTaskCount() throws InterruptedException {
+ final int TASKS = 3;
+ final CountDownLatch done = new CountDownLatch(1);
final ThreadPoolExecutor p =
new CustomTPE(1, 1,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- final CountDownLatch threadStarted = new CountDownLatch(1);
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
assertEquals(0, p.getTaskCount());
+ assertEquals(0, p.getCompletedTaskCount());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
- assertEquals(1, p.getTaskCount());
- done.await();
+ await(done);
}});
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
assertEquals(1, p.getTaskCount());
- } finally {
- done.countDown();
- joinPool(p);
+ assertEquals(0, p.getCompletedTaskCount());
+ for (int i = 0; i < TASKS; i++) {
+ assertEquals(1 + i, p.getTaskCount());
+ p.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ threadStarted.countDown();
+ assertEquals(1 + TASKS, p.getTaskCount());
+ await(done);
+ }});
+ }
+ assertEquals(1 + TASKS, p.getTaskCount());
+ assertEquals(0, p.getCompletedTaskCount());
}
+ assertEquals(1 + TASKS, p.getTaskCount());
+ assertEquals(1 + TASKS, p.getCompletedTaskCount());
}
/**
* isShutdown is false before shutdown, true after
*/
public void testIsShutdown() {
-
- ThreadPoolExecutor p = new CustomTPE(1, 1, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- assertFalse(p.isShutdown());
- try { p.shutdown(); } catch (SecurityException ok) { return; }
- assertTrue(p.isShutdown());
- joinPool(p);
+ final ThreadPoolExecutor p =
+ new CustomTPE(1, 1,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertFalse(p.isShutdown());
+ try { p.shutdown(); } catch (SecurityException ok) { return; }
+ assertTrue(p.isShutdown());
+ }
}
/**
@@ -537,25 +604,24 @@
new CustomTPE(1, 1,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- final CountDownLatch threadStarted = new CountDownLatch(1);
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
+ final CountDownLatch done = new CountDownLatch(1);
assertFalse(p.isTerminating());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
assertFalse(p.isTerminating());
threadStarted.countDown();
- done.await();
+ await(done);
}});
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
assertFalse(p.isTerminating());
done.countDown();
- } finally {
try { p.shutdown(); } catch (SecurityException ok) { return; }
+ assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(p.isTerminated());
+ assertFalse(p.isTerminating());
}
- assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
- assertTrue(p.isTerminated());
- assertFalse(p.isTerminating());
}
/**
@@ -566,59 +632,55 @@
new CustomTPE(1, 1,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- final CountDownLatch threadStarted = new CountDownLatch(1);
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
+ final CountDownLatch done = new CountDownLatch(1);
assertFalse(p.isTerminating());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
assertFalse(p.isTerminating());
threadStarted.countDown();
- done.await();
+ await(done);
}});
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
assertFalse(p.isTerminating());
done.countDown();
- } finally {
try { p.shutdown(); } catch (SecurityException ok) { return; }
+ assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(p.isTerminated());
+ assertFalse(p.isTerminating());
}
- assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
- assertTrue(p.isTerminated());
- assertFalse(p.isTerminating());
}
/**
* getQueue returns the work queue, which contains queued tasks
*/
public void testGetQueue() throws InterruptedException {
+ final CountDownLatch done = new CountDownLatch(1);
final BlockingQueue<Runnable> q = new ArrayBlockingQueue<Runnable>(10);
final ThreadPoolExecutor p =
new CustomTPE(1, 1,
LONG_DELAY_MS, MILLISECONDS,
q);
- final CountDownLatch threadStarted = new CountDownLatch(1);
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
FutureTask[] tasks = new FutureTask[5];
for (int i = 0; i < tasks.length; i++) {
Callable task = new CheckedCallable<Boolean>() {
public Boolean realCall() throws InterruptedException {
threadStarted.countDown();
assertSame(q, p.getQueue());
- done.await();
+ await(done);
return Boolean.TRUE;
}};
tasks[i] = new FutureTask(task);
p.execute(tasks[i]);
}
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
assertSame(q, p.getQueue());
assertFalse(q.contains(tasks[0]));
assertTrue(q.contains(tasks[tasks.length - 1]));
assertEquals(tasks.length - 1, q.size());
- } finally {
- done.countDown();
- joinPool(p);
}
}
@@ -626,24 +688,24 @@
* remove(task) removes queued task, and fails to remove active task
*/
public void testRemove() throws InterruptedException {
+ final CountDownLatch done = new CountDownLatch(1);
BlockingQueue<Runnable> q = new ArrayBlockingQueue<Runnable>(10);
final ThreadPoolExecutor p =
new CustomTPE(1, 1,
LONG_DELAY_MS, MILLISECONDS,
q);
- Runnable[] tasks = new Runnable[6];
- final CountDownLatch threadStarted = new CountDownLatch(1);
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ Runnable[] tasks = new Runnable[6];
+ final CountDownLatch threadStarted = new CountDownLatch(1);
for (int i = 0; i < tasks.length; i++) {
tasks[i] = new CheckedRunnable() {
- public void realRun() throws InterruptedException {
- threadStarted.countDown();
- done.await();
- }};
+ public void realRun() throws InterruptedException {
+ threadStarted.countDown();
+ await(done);
+ }};
p.execute(tasks[i]);
}
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
assertFalse(p.remove(tasks[0]));
assertTrue(q.contains(tasks[4]));
assertTrue(q.contains(tasks[3]));
@@ -653,9 +715,6 @@
assertTrue(q.contains(tasks[3]));
assertTrue(p.remove(tasks[3]));
assertFalse(q.contains(tasks[3]));
- } finally {
- done.countDown();
- joinPool(p);
}
}
@@ -670,19 +729,19 @@
new CustomTPE(1, 1,
LONG_DELAY_MS, MILLISECONDS,
q);
- FutureTask[] tasks = new FutureTask[5];
- try {
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ FutureTask[] tasks = new FutureTask[5];
for (int i = 0; i < tasks.length; i++) {
Callable task = new CheckedCallable<Boolean>() {
public Boolean realCall() throws InterruptedException {
threadStarted.countDown();
- done.await();
+ await(done);
return Boolean.TRUE;
}};
tasks[i] = new FutureTask(task);
p.execute(tasks[i]);
}
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
assertEquals(tasks.length, p.getTaskCount());
assertEquals(tasks.length - 1, q.size());
assertEquals(1L, p.getActiveCount());
@@ -695,29 +754,47 @@
p.purge(); // Nothing to do
assertEquals(tasks.length - 3, q.size());
assertEquals(tasks.length - 2, p.getTaskCount());
- } finally {
- done.countDown();
- joinPool(p);
}
}
/**
- * shutdownNow returns a list containing tasks that were not run
+ * shutdownNow returns a list containing tasks that were not run,
+ * and those tasks are drained from the queue
*/
- public void testShutdownNow() {
- ThreadPoolExecutor p = new CustomTPE(1, 1, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- List l;
- try {
- for (int i = 0; i < 5; i++)
- p.execute(new MediumPossiblyInterruptedRunnable());
- }
- finally {
+ public void testShutdownNow() throws InterruptedException {
+ final int poolSize = 2;
+ final int count = 5;
+ final AtomicInteger ran = new AtomicInteger(0);
+ final ThreadPoolExecutor p =
+ new CustomTPE(poolSize, poolSize,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ final CountDownLatch threadsStarted = new CountDownLatch(poolSize);
+ Runnable waiter = new CheckedRunnable() { public void realRun() {
+ threadsStarted.countDown();
try {
- l = p.shutdownNow();
- } catch (SecurityException ok) { return; }
+ MILLISECONDS.sleep(2 * LONG_DELAY_MS);
+ } catch (InterruptedException success) {}
+ ran.getAndIncrement();
+ }};
+ for (int i = 0; i < count; i++)
+ p.execute(waiter);
+ await(threadsStarted);
+ assertEquals(poolSize, p.getActiveCount());
+ assertEquals(0, p.getCompletedTaskCount());
+ final List<Runnable> queuedTasks;
+ try {
+ queuedTasks = p.shutdownNow();
+ } catch (SecurityException ok) {
+ return; // Allowed in case test doesn't have privs
}
assertTrue(p.isShutdown());
- assertTrue(l.size() <= 4);
+ assertTrue(p.getQueue().isEmpty());
+ assertEquals(count - poolSize, queuedTasks.size());
+ assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(p.isTerminated());
+ assertEquals(poolSize, ran.get());
+ assertEquals(poolSize, p.getCompletedTaskCount());
}
// Exception Tests
@@ -727,7 +804,8 @@
*/
public void testConstructor1() {
try {
- new CustomTPE(-1,1,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+ new CustomTPE(-1, 1, 1L, SECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
shouldThrow();
} catch (IllegalArgumentException success) {}
}
@@ -737,7 +815,8 @@
*/
public void testConstructor2() {
try {
- new CustomTPE(1,-1,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+ new CustomTPE(1, -1, 1L, SECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
shouldThrow();
} catch (IllegalArgumentException success) {}
}
@@ -747,7 +826,8 @@
*/
public void testConstructor3() {
try {
- new CustomTPE(1,0,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+ new CustomTPE(1, 0, 1L, SECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
shouldThrow();
} catch (IllegalArgumentException success) {}
}
@@ -757,7 +837,8 @@
*/
public void testConstructor4() {
try {
- new CustomTPE(1,2,-1L,MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+ new CustomTPE(1, 2, -1L, SECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
shouldThrow();
} catch (IllegalArgumentException success) {}
}
@@ -767,7 +848,8 @@
*/
public void testConstructor5() {
try {
- new CustomTPE(2,1,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
+ new CustomTPE(2, 1, 1L, SECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
shouldThrow();
} catch (IllegalArgumentException success) {}
}
@@ -777,7 +859,7 @@
*/
public void testConstructorNullPointerException() {
try {
- new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS,null);
+ new CustomTPE(1, 2, 1L, SECONDS, null);
shouldThrow();
} catch (NullPointerException success) {}
}
@@ -787,7 +869,9 @@
*/
public void testConstructor6() {
try {
- new CustomTPE(-1,1,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new SimpleThreadFactory());
+ new CustomTPE(-1, 1, 1L, SECONDS,
+ new ArrayBlockingQueue<Runnable>(10),
+ new SimpleThreadFactory());
shouldThrow();
} catch (IllegalArgumentException success) {}
}
@@ -797,7 +881,9 @@
*/
public void testConstructor7() {
try {
- new CustomTPE(1,-1,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new SimpleThreadFactory());
+ new CustomTPE(1,-1, 1L, SECONDS,
+ new ArrayBlockingQueue<Runnable>(10),
+ new SimpleThreadFactory());
shouldThrow();
} catch (IllegalArgumentException success) {}
}
@@ -807,7 +893,9 @@
*/
public void testConstructor8() {
try {
- new CustomTPE(1,0,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new SimpleThreadFactory());
+ new CustomTPE(1, 0, 1L, SECONDS,
+ new ArrayBlockingQueue<Runnable>(10),
+ new SimpleThreadFactory());
shouldThrow();
} catch (IllegalArgumentException success) {}
}
@@ -817,7 +905,9 @@
*/
public void testConstructor9() {
try {
- new CustomTPE(1,2,-1L,MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new SimpleThreadFactory());
+ new CustomTPE(1, 2, -1L, SECONDS,
+ new ArrayBlockingQueue<Runnable>(10),
+ new SimpleThreadFactory());
shouldThrow();
} catch (IllegalArgumentException success) {}
}
@@ -827,7 +917,9 @@
*/
public void testConstructor10() {
try {
- new CustomTPE(2,1,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new SimpleThreadFactory());
+ new CustomTPE(2, 1, 1L, SECONDS,
+ new ArrayBlockingQueue<Runnable>(10),
+ new SimpleThreadFactory());
shouldThrow();
} catch (IllegalArgumentException success) {}
}
@@ -837,7 +929,7 @@
*/
public void testConstructorNullPointerException2() {
try {
- new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS,null,new SimpleThreadFactory());
+ new CustomTPE(1, 2, 1L, SECONDS, null, new SimpleThreadFactory());
shouldThrow();
} catch (NullPointerException success) {}
}
@@ -847,8 +939,9 @@
*/
public void testConstructorNullPointerException3() {
try {
- ThreadFactory f = null;
- new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS,new ArrayBlockingQueue<Runnable>(10),f);
+ new CustomTPE(1, 2, 1L, SECONDS,
+ new ArrayBlockingQueue<Runnable>(10),
+ (ThreadFactory) null);
shouldThrow();
} catch (NullPointerException success) {}
}
@@ -858,7 +951,9 @@
*/
public void testConstructor11() {
try {
- new CustomTPE(-1,1,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new NoOpREHandler());
+ new CustomTPE(-1, 1, 1L, SECONDS,
+ new ArrayBlockingQueue<Runnable>(10),
+ new NoOpREHandler());
shouldThrow();
} catch (IllegalArgumentException success) {}
}
@@ -868,7 +963,9 @@
*/
public void testConstructor12() {
try {
- new CustomTPE(1,-1,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new NoOpREHandler());
+ new CustomTPE(1, -1, 1L, SECONDS,
+ new ArrayBlockingQueue<Runnable>(10),
+ new NoOpREHandler());
shouldThrow();
} catch (IllegalArgumentException success) {}
}
@@ -878,7 +975,9 @@
*/
public void testConstructor13() {
try {
- new CustomTPE(1,0,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new NoOpREHandler());
+ new CustomTPE(1, 0, 1L, SECONDS,
+ new ArrayBlockingQueue<Runnable>(10),
+ new NoOpREHandler());
shouldThrow();
} catch (IllegalArgumentException success) {}
}
@@ -888,7 +987,9 @@
*/
public void testConstructor14() {
try {
- new CustomTPE(1,2,-1L,MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new NoOpREHandler());
+ new CustomTPE(1, 2, -1L, SECONDS,
+ new ArrayBlockingQueue<Runnable>(10),
+ new NoOpREHandler());
shouldThrow();
} catch (IllegalArgumentException success) {}
}
@@ -898,7 +999,9 @@
*/
public void testConstructor15() {
try {
- new CustomTPE(2,1,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new NoOpREHandler());
+ new CustomTPE(2, 1, 1L, SECONDS,
+ new ArrayBlockingQueue<Runnable>(10),
+ new NoOpREHandler());
shouldThrow();
} catch (IllegalArgumentException success) {}
}
@@ -908,7 +1011,9 @@
*/
public void testConstructorNullPointerException4() {
try {
- new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS,null,new NoOpREHandler());
+ new CustomTPE(1, 2, 1L, SECONDS,
+ null,
+ new NoOpREHandler());
shouldThrow();
} catch (NullPointerException success) {}
}
@@ -918,8 +1023,9 @@
*/
public void testConstructorNullPointerException5() {
try {
- RejectedExecutionHandler r = null;
- new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS,new ArrayBlockingQueue<Runnable>(10),r);
+ new CustomTPE(1, 2, 1L, SECONDS,
+ new ArrayBlockingQueue<Runnable>(10),
+ (RejectedExecutionHandler) null);
shouldThrow();
} catch (NullPointerException success) {}
}
@@ -929,7 +1035,10 @@
*/
public void testConstructor16() {
try {
- new CustomTPE(-1,1,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new SimpleThreadFactory(),new NoOpREHandler());
+ new CustomTPE(-1, 1, 1L, SECONDS,
+ new ArrayBlockingQueue<Runnable>(10),
+ new SimpleThreadFactory(),
+ new NoOpREHandler());
shouldThrow();
} catch (IllegalArgumentException success) {}
}
@@ -939,7 +1048,10 @@
*/
public void testConstructor17() {
try {
- new CustomTPE(1,-1,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new SimpleThreadFactory(),new NoOpREHandler());
+ new CustomTPE(1, -1, 1L, SECONDS,
+ new ArrayBlockingQueue<Runnable>(10),
+ new SimpleThreadFactory(),
+ new NoOpREHandler());
shouldThrow();
} catch (IllegalArgumentException success) {}
}
@@ -949,7 +1061,10 @@
*/
public void testConstructor18() {
try {
- new CustomTPE(1,0,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new SimpleThreadFactory(),new NoOpREHandler());
+ new CustomTPE(1, 0, 1L, SECONDS,
+ new ArrayBlockingQueue<Runnable>(10),
+ new SimpleThreadFactory(),
+ new NoOpREHandler());
shouldThrow();
} catch (IllegalArgumentException success) {}
}
@@ -959,7 +1074,10 @@
*/
public void testConstructor19() {
try {
- new CustomTPE(1,2,-1L,MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new SimpleThreadFactory(),new NoOpREHandler());
+ new CustomTPE(1, 2, -1L, SECONDS,
+ new ArrayBlockingQueue<Runnable>(10),
+ new SimpleThreadFactory(),
+ new NoOpREHandler());
shouldThrow();
} catch (IllegalArgumentException success) {}
}
@@ -969,7 +1087,10 @@
*/
public void testConstructor20() {
try {
- new CustomTPE(2,1,LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),new SimpleThreadFactory(),new NoOpREHandler());
+ new CustomTPE(2, 1, 1L, SECONDS,
+ new ArrayBlockingQueue<Runnable>(10),
+ new SimpleThreadFactory(),
+ new NoOpREHandler());
shouldThrow();
} catch (IllegalArgumentException success) {}
}
@@ -979,7 +1100,10 @@
*/
public void testConstructorNullPointerException6() {
try {
- new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS,null,new SimpleThreadFactory(),new NoOpREHandler());
+ new CustomTPE(1, 2, 1L, SECONDS,
+ null,
+ new SimpleThreadFactory(),
+ new NoOpREHandler());
shouldThrow();
} catch (NullPointerException success) {}
}
@@ -989,8 +1113,10 @@
*/
public void testConstructorNullPointerException7() {
try {
- RejectedExecutionHandler r = null;
- new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS,new ArrayBlockingQueue<Runnable>(10),new SimpleThreadFactory(),r);
+ new CustomTPE(1, 2, 1L, SECONDS,
+ new ArrayBlockingQueue<Runnable>(10),
+ new SimpleThreadFactory(),
+ (RejectedExecutionHandler) null);
shouldThrow();
} catch (NullPointerException success) {}
}
@@ -1000,8 +1126,7 @@
*/
public void testConstructorNullPointerException8() {
try {
- new CustomTPE(1, 2,
- LONG_DELAY_MS, MILLISECONDS,
+ new CustomTPE(1, 2, 1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10),
(ThreadFactory) null,
new NoOpREHandler());
@@ -1013,15 +1138,15 @@
* execute throws RejectedExecutionException if saturated.
*/
public void testSaturatedExecute() {
- ThreadPoolExecutor p =
+ final CountDownLatch done = new CountDownLatch(1);
+ final ThreadPoolExecutor p =
new CustomTPE(1, 1,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(1));
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p, done)) {
Runnable task = new CheckedRunnable() {
public void realRun() throws InterruptedException {
- done.await();
+ await(done);
}};
for (int i = 0; i < 2; ++i)
p.execute(task);
@@ -1032,9 +1157,6 @@
} catch (RejectedExecutionException success) {}
assertTrue(p.getTaskCount() <= 2);
}
- } finally {
- done.countDown();
- joinPool(p);
}
}
@@ -1042,24 +1164,26 @@
* executor using CallerRunsPolicy runs task if saturated.
*/
public void testSaturatedExecute2() {
- RejectedExecutionHandler h = new CustomTPE.CallerRunsPolicy();
- ThreadPoolExecutor p = new CustomTPE(1, 1,
- LONG_DELAY_MS, MILLISECONDS,
- new ArrayBlockingQueue<Runnable>(1),
- h);
- try {
+ final CountDownLatch done = new CountDownLatch(1);
+ final ThreadPoolExecutor p =
+ new CustomTPE(1, 1,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(1),
+ new CustomTPE.CallerRunsPolicy());
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ Runnable blocker = new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ await(done);
+ }};
+ p.execute(blocker);
TrackedNoOpRunnable[] tasks = new TrackedNoOpRunnable[5];
- for (int i = 0; i < tasks.length; ++i)
+ for (int i = 0; i < tasks.length; i++)
tasks[i] = new TrackedNoOpRunnable();
- TrackedLongRunnable mr = new TrackedLongRunnable();
- p.execute(mr);
- for (int i = 0; i < tasks.length; ++i)
+ for (int i = 0; i < tasks.length; i++)
p.execute(tasks[i]);
- for (int i = 1; i < tasks.length; ++i)
+ for (int i = 1; i < tasks.length; i++)
assertTrue(tasks[i].done);
- try { p.shutdownNow(); } catch (SecurityException ok) { return; }
- } finally {
- joinPool(p);
+ assertFalse(tasks[0].done); // waiting in queue
}
}
@@ -1067,77 +1191,88 @@
* executor using DiscardPolicy drops task if saturated.
*/
public void testSaturatedExecute3() {
- RejectedExecutionHandler h = new CustomTPE.DiscardPolicy();
- ThreadPoolExecutor p =
+ final TrackedNoOpRunnable[] tasks = new TrackedNoOpRunnable[5];
+ for (int i = 0; i < tasks.length; ++i)
+ tasks[i] = new TrackedNoOpRunnable();
+ final CountDownLatch done = new CountDownLatch(1);
+ final ThreadPoolExecutor p =
new CustomTPE(1, 1,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(1),
- h);
- try {
- TrackedNoOpRunnable[] tasks = new TrackedNoOpRunnable[5];
- for (int i = 0; i < tasks.length; ++i)
- tasks[i] = new TrackedNoOpRunnable();
- p.execute(new TrackedLongRunnable());
+ new CustomTPE.DiscardPolicy());
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ p.execute(awaiter(done));
+
for (TrackedNoOpRunnable task : tasks)
p.execute(task);
- for (TrackedNoOpRunnable task : tasks)
- assertFalse(task.done);
- try { p.shutdownNow(); } catch (SecurityException ok) { return; }
- } finally {
- joinPool(p);
+ for (int i = 1; i < tasks.length; i++)
+ assertFalse(tasks[i].done);
}
+ for (int i = 1; i < tasks.length; i++)
+ assertFalse(tasks[i].done);
+ assertTrue(tasks[0].done); // was waiting in queue
}
/**
* executor using DiscardOldestPolicy drops oldest task if saturated.
*/
public void testSaturatedExecute4() {
- RejectedExecutionHandler h = new CustomTPE.DiscardOldestPolicy();
- ThreadPoolExecutor p = new CustomTPE(1,1, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(1), h);
- try {
- p.execute(new TrackedLongRunnable());
- TrackedLongRunnable r2 = new TrackedLongRunnable();
+ final CountDownLatch done = new CountDownLatch(1);
+ LatchAwaiter r1 = awaiter(done);
+ LatchAwaiter r2 = awaiter(done);
+ LatchAwaiter r3 = awaiter(done);
+ final ThreadPoolExecutor p =
+ new CustomTPE(1, 1,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(1),
+ new CustomTPE.DiscardOldestPolicy());
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ assertEquals(LatchAwaiter.NEW, r1.state);
+ assertEquals(LatchAwaiter.NEW, r2.state);
+ assertEquals(LatchAwaiter.NEW, r3.state);
+ p.execute(r1);
p.execute(r2);
assertTrue(p.getQueue().contains(r2));
- TrackedNoOpRunnable r3 = new TrackedNoOpRunnable();
p.execute(r3);
assertFalse(p.getQueue().contains(r2));
assertTrue(p.getQueue().contains(r3));
- try { p.shutdownNow(); } catch (SecurityException ok) { return; }
- } finally {
- joinPool(p);
}
+ assertEquals(LatchAwaiter.DONE, r1.state);
+ assertEquals(LatchAwaiter.NEW, r2.state);
+ assertEquals(LatchAwaiter.DONE, r3.state);
}
/**
* execute throws RejectedExecutionException if shutdown
*/
public void testRejectedExecutionExceptionOnShutdown() {
- ThreadPoolExecutor p =
- new CustomTPE(1,1,LONG_DELAY_MS, MILLISECONDS,new ArrayBlockingQueue<Runnable>(1));
+ final ThreadPoolExecutor p =
+ new CustomTPE(1, 1,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(1));
try { p.shutdown(); } catch (SecurityException ok) { return; }
- try {
- p.execute(new NoOpRunnable());
- shouldThrow();
- } catch (RejectedExecutionException success) {}
-
- joinPool(p);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.execute(new NoOpRunnable());
+ shouldThrow();
+ } catch (RejectedExecutionException success) {}
+ }
}
/**
* execute using CallerRunsPolicy drops task on shutdown
*/
public void testCallerRunsOnShutdown() {
- RejectedExecutionHandler h = new CustomTPE.CallerRunsPolicy();
- ThreadPoolExecutor p = new CustomTPE(1,1, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(1), h);
-
+ final ThreadPoolExecutor p =
+ new CustomTPE(1, 1,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(1),
+ new CustomTPE.CallerRunsPolicy());
try { p.shutdown(); } catch (SecurityException ok) { return; }
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
TrackedNoOpRunnable r = new TrackedNoOpRunnable();
p.execute(r);
assertFalse(r.done);
- } finally {
- joinPool(p);
}
}
@@ -1145,16 +1280,16 @@
* execute using DiscardPolicy drops task on shutdown
*/
public void testDiscardOnShutdown() {
- RejectedExecutionHandler h = new CustomTPE.DiscardPolicy();
- ThreadPoolExecutor p = new CustomTPE(1,1, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(1), h);
-
+ final ThreadPoolExecutor p =
+ new CustomTPE(1, 1,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(1),
+ new CustomTPE.DiscardPolicy());
try { p.shutdown(); } catch (SecurityException ok) { return; }
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
TrackedNoOpRunnable r = new TrackedNoOpRunnable();
p.execute(r);
assertFalse(r.done);
- } finally {
- joinPool(p);
}
}
@@ -1162,16 +1297,17 @@
* execute using DiscardOldestPolicy drops task on shutdown
*/
public void testDiscardOldestOnShutdown() {
- RejectedExecutionHandler h = new CustomTPE.DiscardOldestPolicy();
- ThreadPoolExecutor p = new CustomTPE(1,1, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(1), h);
+ final ThreadPoolExecutor p =
+ new CustomTPE(1, 1,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(1),
+ new CustomTPE.DiscardOldestPolicy());
try { p.shutdown(); } catch (SecurityException ok) { return; }
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
TrackedNoOpRunnable r = new TrackedNoOpRunnable();
p.execute(r);
assertFalse(r.done);
- } finally {
- joinPool(p);
}
}
@@ -1179,30 +1315,32 @@
* execute(null) throws NPE
*/
public void testExecuteNull() {
- ThreadPoolExecutor p = null;
- try {
- p = new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS,new ArrayBlockingQueue<Runnable>(10));
- p.execute(null);
- shouldThrow();
- } catch (NullPointerException success) {}
-
- joinPool(p);
+ final ThreadPoolExecutor p =
+ new CustomTPE(1, 2,
+ 1L, SECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.execute(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
+ }
}
/**
* setCorePoolSize of negative value throws IllegalArgumentException
*/
public void testCorePoolSizeIllegalArgumentException() {
- ThreadPoolExecutor p =
- new CustomTPE(1,2,LONG_DELAY_MS, MILLISECONDS,new ArrayBlockingQueue<Runnable>(10));
- try {
- p.setCorePoolSize(-1);
- shouldThrow();
- } catch (IllegalArgumentException success) {
- } finally {
- try { p.shutdown(); } catch (SecurityException ok) { return; }
+ final ThreadPoolExecutor p =
+ new CustomTPE(1, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.setCorePoolSize(-1);
+ shouldThrow();
+ } catch (IllegalArgumentException success) {}
}
- joinPool(p);
}
/**
@@ -1210,16 +1348,16 @@
* if given a value less the core pool size
*/
public void testMaximumPoolSizeIllegalArgumentException() {
- ThreadPoolExecutor p =
- new CustomTPE(2,3,LONG_DELAY_MS, MILLISECONDS,new ArrayBlockingQueue<Runnable>(10));
- try {
- p.setMaximumPoolSize(1);
- shouldThrow();
- } catch (IllegalArgumentException success) {
- } finally {
- try { p.shutdown(); } catch (SecurityException ok) { return; }
+ final ThreadPoolExecutor p =
+ new CustomTPE(2, 3,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.setMaximumPoolSize(1);
+ shouldThrow();
+ } catch (IllegalArgumentException success) {}
}
- joinPool(p);
}
/**
@@ -1227,16 +1365,16 @@
* if given a negative value
*/
public void testMaximumPoolSizeIllegalArgumentException2() {
- ThreadPoolExecutor p =
- new CustomTPE(2,3,LONG_DELAY_MS, MILLISECONDS,new ArrayBlockingQueue<Runnable>(10));
- try {
- p.setMaximumPoolSize(-1);
- shouldThrow();
- } catch (IllegalArgumentException success) {
- } finally {
- try { p.shutdown(); } catch (SecurityException ok) { return; }
+ final ThreadPoolExecutor p =
+ new CustomTPE(2, 3,
+ LONG_DELAY_MS,
+ MILLISECONDS,new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.setMaximumPoolSize(-1);
+ shouldThrow();
+ } catch (IllegalArgumentException success) {}
}
- joinPool(p);
}
/**
@@ -1244,17 +1382,16 @@
* when given a negative value
*/
public void testKeepAliveTimeIllegalArgumentException() {
- ThreadPoolExecutor p =
- new CustomTPE(2,3,LONG_DELAY_MS, MILLISECONDS,new ArrayBlockingQueue<Runnable>(10));
-
- try {
- p.setKeepAliveTime(-1,MILLISECONDS);
- shouldThrow();
- } catch (IllegalArgumentException success) {
- } finally {
- try { p.shutdown(); } catch (SecurityException ok) { return; }
+ final ThreadPoolExecutor p =
+ new CustomTPE(2, 3,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.setKeepAliveTime(-1, MILLISECONDS);
+ shouldThrow();
+ } catch (IllegalArgumentException success) {}
}
- joinPool(p);
}
/**
@@ -1262,9 +1399,11 @@
*/
public void testTerminated() {
CustomTPE p = new CustomTPE();
- try { p.shutdown(); } catch (SecurityException ok) { return; }
- assertTrue(p.terminatedCalled());
- joinPool(p);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try { p.shutdown(); } catch (SecurityException ok) { return; }
+ assertTrue(p.terminatedCalled());
+ assertTrue(p.isShutdown());
+ }
}
/**
@@ -1272,7 +1411,7 @@
*/
public void testBeforeAfter() throws InterruptedException {
CustomTPE p = new CustomTPE();
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
final CountDownLatch done = new CountDownLatch(1);
p.execute(new CheckedRunnable() {
public void realRun() {
@@ -1282,9 +1421,6 @@
assertEquals(0, done.getCount());
assertTrue(p.afterCalled());
assertTrue(p.beforeCalled());
- try { p.shutdown(); } catch (SecurityException ok) { return; }
- } finally {
- joinPool(p);
}
}
@@ -1292,13 +1428,14 @@
* completed submit of callable returns result
*/
public void testSubmitCallable() throws Exception {
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- try {
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
Future<String> future = e.submit(new StringTask());
String result = future.get();
assertSame(TEST_STRING, result);
- } finally {
- joinPool(e);
}
}
@@ -1306,13 +1443,14 @@
* completed submit of runnable returns successfully
*/
public void testSubmitRunnable() throws Exception {
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- try {
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
Future<?> future = e.submit(new NoOpRunnable());
future.get();
assertTrue(future.isDone());
- } finally {
- joinPool(e);
}
}
@@ -1320,13 +1458,14 @@
* completed submit of (runnable, result) returns result
*/
public void testSubmitRunnable2() throws Exception {
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- try {
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
String result = future.get();
assertSame(TEST_STRING, result);
- } finally {
- joinPool(e);
}
}
@@ -1334,13 +1473,15 @@
* invokeAny(null) throws NPE
*/
public void testInvokeAny1() throws Exception {
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- try {
- e.invokeAny(null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAny(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1348,13 +1489,15 @@
* invokeAny(empty collection) throws IAE
*/
public void testInvokeAny2() throws Exception {
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- try {
- e.invokeAny(new ArrayList<Callable<String>>());
- shouldThrow();
- } catch (IllegalArgumentException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAny(new ArrayList<Callable<String>>());
+ shouldThrow();
+ } catch (IllegalArgumentException success) {}
}
}
@@ -1363,17 +1506,19 @@
*/
public void testInvokeAny3() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(latchAwaitingStringTask(latch));
- l.add(null);
- try {
- e.invokeAny(l);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(latchAwaitingStringTask(latch));
+ l.add(null);
+ try {
+ e.invokeAny(l);
+ shouldThrow();
+ } catch (NullPointerException success) {}
latch.countDown();
- joinPool(e);
}
}
@@ -1381,16 +1526,19 @@
* invokeAny(c) throws ExecutionException if no task completes
*/
public void testInvokeAny4() throws Exception {
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new NPETask());
- try {
- e.invokeAny(l);
- shouldThrow();
- } catch (ExecutionException success) {
- assertTrue(success.getCause() instanceof NullPointerException);
- } finally {
- joinPool(e);
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new NPETask());
+ try {
+ e.invokeAny(l);
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertTrue(success.getCause() instanceof NullPointerException);
+ }
}
}
@@ -1398,15 +1546,16 @@
* invokeAny(c) returns result of some task
*/
public void testInvokeAny5() throws Exception {
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- try {
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
String result = e.invokeAny(l);
assertSame(TEST_STRING, result);
- } finally {
- joinPool(e);
}
}
@@ -1414,13 +1563,15 @@
* invokeAll(null) throws NPE
*/
public void testInvokeAll1() throws Exception {
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- try {
- e.invokeAll(null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAll(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1428,12 +1579,13 @@
* invokeAll(empty collection) returns empty collection
*/
public void testInvokeAll2() throws Exception {
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- try {
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>());
assertTrue(r.isEmpty());
- } finally {
- joinPool(e);
}
}
@@ -1441,16 +1593,18 @@
* invokeAll(c) throws NPE if c has null elements
*/
public void testInvokeAll3() throws Exception {
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- l.add(null);
- try {
- e.invokeAll(l);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new StringTask());
+ l.add(null);
+ try {
+ e.invokeAll(l);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1458,18 +1612,21 @@
* get of element of invokeAll(c) throws exception on failed task
*/
public void testInvokeAll4() throws Exception {
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new NPETask());
- List<Future<String>> futures = e.invokeAll(l);
- assertEquals(1, futures.size());
- try {
- futures.get(0).get();
- shouldThrow();
- } catch (ExecutionException success) {
- assertTrue(success.getCause() instanceof NullPointerException);
- } finally {
- joinPool(e);
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new NPETask());
+ List<Future<String>> futures = e.invokeAll(l);
+ assertEquals(1, futures.size());
+ try {
+ futures.get(0).get();
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertTrue(success.getCause() instanceof NullPointerException);
+ }
}
}
@@ -1477,8 +1634,11 @@
* invokeAll(c) returns results of all completed tasks
*/
public void testInvokeAll5() throws Exception {
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- try {
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
@@ -1486,8 +1646,6 @@
assertEquals(2, futures.size());
for (Future<String> future : futures)
assertSame(TEST_STRING, future.get());
- } finally {
- joinPool(e);
}
}
@@ -1495,13 +1653,15 @@
* timed invokeAny(null) throws NPE
*/
public void testTimedInvokeAny1() throws Exception {
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- try {
- e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1509,15 +1669,17 @@
* timed invokeAny(,,null) throws NPE
*/
public void testTimedInvokeAnyNullTimeUnit() throws Exception {
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- try {
- e.invokeAny(l, MEDIUM_DELAY_MS, null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new StringTask());
+ try {
+ e.invokeAny(l, MEDIUM_DELAY_MS, null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1525,13 +1687,16 @@
* timed invokeAny(empty collection) throws IAE
*/
public void testTimedInvokeAny2() throws Exception {
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- try {
- e.invokeAny(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (IllegalArgumentException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAny(new ArrayList<Callable<String>>(),
+ MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (IllegalArgumentException success) {}
}
}
@@ -1540,17 +1705,19 @@
*/
public void testTimedInvokeAny3() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(latchAwaitingStringTask(latch));
- l.add(null);
- try {
- e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(latchAwaitingStringTask(latch));
+ l.add(null);
+ try {
+ e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
latch.countDown();
- joinPool(e);
}
}
@@ -1558,16 +1725,21 @@
* timed invokeAny(c) throws ExecutionException if no task completes
*/
public void testTimedInvokeAny4() throws Exception {
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new NPETask());
- try {
- e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (ExecutionException success) {
- assertTrue(success.getCause() instanceof NullPointerException);
- } finally {
- joinPool(e);
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
+ long startTime = System.nanoTime();
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new NPETask());
+ try {
+ e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertTrue(success.getCause() instanceof NullPointerException);
+ }
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}
}
@@ -1575,15 +1747,18 @@
* timed invokeAny(c) returns result of some task
*/
public void testTimedInvokeAny5() throws Exception {
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- try {
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
+ long startTime = System.nanoTime();
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
- String result = e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
assertSame(TEST_STRING, result);
- } finally {
- joinPool(e);
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}
}
@@ -1591,13 +1766,15 @@
* timed invokeAll(null) throws NPE
*/
public void testTimedInvokeAll1() throws Exception {
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- try {
- e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1605,15 +1782,17 @@
* timed invokeAll(,,null) throws NPE
*/
public void testTimedInvokeAllNullTimeUnit() throws Exception {
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- try {
- e.invokeAll(l, MEDIUM_DELAY_MS, null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new StringTask());
+ try {
+ e.invokeAll(l, MEDIUM_DELAY_MS, null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1621,12 +1800,14 @@
* timed invokeAll(empty collection) returns empty collection
*/
public void testTimedInvokeAll2() throws Exception {
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- try {
- List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(),
+ MEDIUM_DELAY_MS, MILLISECONDS);
assertTrue(r.isEmpty());
- } finally {
- joinPool(e);
}
}
@@ -1634,16 +1815,18 @@
* timed invokeAll(c) throws NPE if c has null elements
*/
public void testTimedInvokeAll3() throws Exception {
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- l.add(null);
- try {
- e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new StringTask());
+ l.add(null);
+ try {
+ e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1651,19 +1834,22 @@
* get of element of invokeAll(c) throws exception on failed task
*/
public void testTimedInvokeAll4() throws Exception {
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new NPETask());
- List<Future<String>> futures =
- e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
- assertEquals(1, futures.size());
- try {
- futures.get(0).get();
- shouldThrow();
- } catch (ExecutionException success) {
- assertTrue(success.getCause() instanceof NullPointerException);
- } finally {
- joinPool(e);
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new NPETask());
+ List<Future<String>> futures =
+ e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+ assertEquals(1, futures.size());
+ try {
+ futures.get(0).get();
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertTrue(success.getCause() instanceof NullPointerException);
+ }
}
}
@@ -1671,18 +1857,19 @@
* timed invokeAll(c) returns results of all completed tasks
*/
public void testTimedInvokeAll5() throws Exception {
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- try {
+ final ExecutorService e =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
List<Future<String>> futures =
- e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
assertEquals(2, futures.size());
for (Future<String> future : futures)
assertSame(TEST_STRING, future.get());
- } finally {
- joinPool(e);
}
}
@@ -1690,21 +1877,40 @@
* timed invokeAll(c) cancels tasks not completed by timeout
*/
public void testTimedInvokeAll6() throws Exception {
- ExecutorService e = new CustomTPE(2, 2, LONG_DELAY_MS, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- try {
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- l.add(Executors.callable(new MediumPossiblyInterruptedRunnable(), TEST_STRING));
- l.add(new StringTask());
- List<Future<String>> futures =
- e.invokeAll(l, SHORT_DELAY_MS, MILLISECONDS);
- assertEquals(l.size(), futures.size());
- for (Future future : futures)
- assertTrue(future.isDone());
- assertFalse(futures.get(0).isCancelled());
- assertTrue(futures.get(1).isCancelled());
- } finally {
- joinPool(e);
+ for (long timeout = timeoutMillis();;) {
+ final CountDownLatch done = new CountDownLatch(1);
+ final Callable<String> waiter = new CheckedCallable<String>() {
+ public String realCall() {
+ try { done.await(LONG_DELAY_MS, MILLISECONDS); }
+ catch (InterruptedException ok) {}
+ return "1"; }};
+ final ExecutorService p =
+ new CustomTPE(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ List<Callable<String>> tasks = new ArrayList<>();
+ tasks.add(new StringTask("0"));
+ tasks.add(waiter);
+ tasks.add(new StringTask("2"));
+ long startTime = System.nanoTime();
+ List<Future<String>> futures =
+ p.invokeAll(tasks, timeout, MILLISECONDS);
+ assertEquals(tasks.size(), futures.size());
+ assertTrue(millisElapsedSince(startTime) >= timeout);
+ for (Future future : futures)
+ assertTrue(future.isDone());
+ assertTrue(futures.get(1).isCancelled());
+ try {
+ assertEquals("0", futures.get(0).get());
+ assertEquals("2", futures.get(2).get());
+ break;
+ } catch (CancellationException retryWithLongerTimeout) {
+ timeout *= 2;
+ if (timeout >= LONG_DELAY_MS / 2)
+ fail("expected exactly one task to be cancelled");
+ }
+ }
}
}
@@ -1718,7 +1924,7 @@
LONG_DELAY_MS, MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
new FailingThreadFactory());
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
final int TASKS = 100;
final CountDownLatch done = new CountDownLatch(TASKS);
for (int k = 0; k < TASKS; ++k)
@@ -1727,8 +1933,6 @@
done.countDown();
}});
assertTrue(done.await(LONG_DELAY_MS, MILLISECONDS));
- } finally {
- joinPool(e);
}
}
@@ -1736,38 +1940,40 @@
* allowsCoreThreadTimeOut is by default false.
*/
public void testAllowsCoreThreadTimeOut() {
- ThreadPoolExecutor p = new CustomTPE(2, 2, 1000, MILLISECONDS, new ArrayBlockingQueue<Runnable>(10));
- assertFalse(p.allowsCoreThreadTimeOut());
- joinPool(p);
+ final ThreadPoolExecutor p =
+ new CustomTPE(2, 2,
+ 1000, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertFalse(p.allowsCoreThreadTimeOut());
+ }
}
/**
* allowCoreThreadTimeOut(true) causes idle threads to time out
*/
public void testAllowCoreThreadTimeOut_true() throws Exception {
- long coreThreadTimeOut = SHORT_DELAY_MS;
+ long keepAliveTime = timeoutMillis();
final ThreadPoolExecutor p =
new CustomTPE(2, 10,
- coreThreadTimeOut, MILLISECONDS,
+ keepAliveTime, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- final CountDownLatch threadStarted = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
p.allowCoreThreadTimeOut(true);
p.execute(new CheckedRunnable() {
- public void realRun() throws InterruptedException {
+ public void realRun() {
threadStarted.countDown();
assertEquals(1, p.getPoolSize());
}});
await(threadStarted);
- delay(coreThreadTimeOut);
+ delay(keepAliveTime);
long startTime = System.nanoTime();
while (p.getPoolSize() > 0
&& millisElapsedSince(startTime) < LONG_DELAY_MS)
Thread.yield();
assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
assertEquals(0, p.getPoolSize());
- } finally {
- joinPool(p);
}
}
@@ -1775,23 +1981,59 @@
* allowCoreThreadTimeOut(false) causes idle threads not to time out
*/
public void testAllowCoreThreadTimeOut_false() throws Exception {
- long coreThreadTimeOut = SHORT_DELAY_MS;
+ long keepAliveTime = timeoutMillis();
final ThreadPoolExecutor p =
new CustomTPE(2, 10,
- coreThreadTimeOut, MILLISECONDS,
+ keepAliveTime, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- final CountDownLatch threadStarted = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
p.allowCoreThreadTimeOut(false);
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
assertTrue(p.getPoolSize() >= 1);
}});
- delay(2 * coreThreadTimeOut);
+ delay(2 * keepAliveTime);
assertTrue(p.getPoolSize() >= 1);
- } finally {
- joinPool(p);
+ }
+ }
+
+ /**
+ * get(cancelled task) throws CancellationException
+ * (in part, a test of CustomTPE itself)
+ */
+ public void testGet_cancelled() throws Exception {
+ final CountDownLatch done = new CountDownLatch(1);
+ final ExecutorService e =
+ new CustomTPE(1, 1,
+ LONG_DELAY_MS, MILLISECONDS,
+ new LinkedBlockingQueue<Runnable>());
+ try (PoolCleaner cleaner = cleaner(e, done)) {
+ final CountDownLatch blockerStarted = new CountDownLatch(1);
+ final List<Future<?>> futures = new ArrayList<>();
+ for (int i = 0; i < 2; i++) {
+ Runnable r = new CheckedRunnable() { public void realRun()
+ throws Throwable {
+ blockerStarted.countDown();
+ assertTrue(done.await(2 * LONG_DELAY_MS, MILLISECONDS));
+ }};
+ futures.add(e.submit(r));
+ }
+ await(blockerStarted);
+ for (Future<?> future : futures) future.cancel(false);
+ for (Future<?> future : futures) {
+ try {
+ future.get();
+ shouldThrow();
+ } catch (CancellationException success) {}
+ try {
+ future.get(LONG_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (CancellationException success) {}
+ assertTrue(future.isCancelled());
+ assertTrue(future.isDone());
+ }
}
}
diff --git a/jsr166-tests/src/test/java/jsr166/ThreadPoolExecutorTest.java b/jsr166-tests/src/test/java/jsr166/ThreadPoolExecutorTest.java
index 52a7002..7fe26f4 100644
--- a/jsr166-tests/src/test/java/jsr166/ThreadPoolExecutorTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ThreadPoolExecutorTest.java
@@ -10,12 +10,14 @@
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
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.ExecutionException;
import java.util.concurrent.Executors;
@@ -29,6 +31,7 @@
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
import junit.framework.Test;
import junit.framework.TestSuite;
@@ -41,7 +44,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(ThreadPoolExecutorTest.class);
// }
static class ExtendedTPE extends ThreadPoolExecutor {
@@ -89,16 +92,12 @@
new ThreadPoolExecutor(1, 1,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- final CountDownLatch done = new CountDownLatch(1);
- final Runnable task = new CheckedRunnable() {
- public void realRun() {
- done.countDown();
- }};
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final CountDownLatch done = new CountDownLatch(1);
+ final Runnable task = new CheckedRunnable() {
+ public void realRun() { done.countDown(); }};
p.execute(task);
- assertTrue(done.await(SMALL_DELAY_MS, MILLISECONDS));
- } finally {
- joinPool(p);
+ assertTrue(done.await(LONG_DELAY_MS, MILLISECONDS));
}
}
@@ -107,25 +106,22 @@
* thread becomes active
*/
public void testGetActiveCount() throws InterruptedException {
+ final CountDownLatch done = new CountDownLatch(1);
final ThreadPoolExecutor p =
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- final CountDownLatch threadStarted = new CountDownLatch(1);
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
assertEquals(0, p.getActiveCount());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
assertEquals(1, p.getActiveCount());
- done.await();
+ await(done);
}});
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
assertEquals(1, p.getActiveCount());
- } finally {
- done.countDown();
- joinPool(p);
}
}
@@ -134,17 +130,25 @@
*/
public void testPrestartCoreThread() {
final ThreadPoolExecutor p =
- new ThreadPoolExecutor(2, 2,
+ new ThreadPoolExecutor(2, 6,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- assertEquals(0, p.getPoolSize());
- assertTrue(p.prestartCoreThread());
- assertEquals(1, p.getPoolSize());
- assertTrue(p.prestartCoreThread());
- assertEquals(2, p.getPoolSize());
- assertFalse(p.prestartCoreThread());
- assertEquals(2, p.getPoolSize());
- joinPool(p);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertEquals(0, p.getPoolSize());
+ assertTrue(p.prestartCoreThread());
+ assertEquals(1, p.getPoolSize());
+ assertTrue(p.prestartCoreThread());
+ assertEquals(2, p.getPoolSize());
+ assertFalse(p.prestartCoreThread());
+ assertEquals(2, p.getPoolSize());
+ p.setCorePoolSize(4);
+ assertTrue(p.prestartCoreThread());
+ assertEquals(3, p.getPoolSize());
+ assertTrue(p.prestartCoreThread());
+ assertEquals(4, p.getPoolSize());
+ assertFalse(p.prestartCoreThread());
+ assertEquals(4, p.getPoolSize());
+ }
}
/**
@@ -152,15 +156,21 @@
*/
public void testPrestartAllCoreThreads() {
final ThreadPoolExecutor p =
- new ThreadPoolExecutor(2, 2,
+ new ThreadPoolExecutor(2, 6,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- assertEquals(0, p.getPoolSize());
- p.prestartAllCoreThreads();
- assertEquals(2, p.getPoolSize());
- p.prestartAllCoreThreads();
- assertEquals(2, p.getPoolSize());
- joinPool(p);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertEquals(0, p.getPoolSize());
+ p.prestartAllCoreThreads();
+ assertEquals(2, p.getPoolSize());
+ p.prestartAllCoreThreads();
+ assertEquals(2, p.getPoolSize());
+ p.setCorePoolSize(4);
+ p.prestartAllCoreThreads();
+ assertEquals(4, p.getPoolSize());
+ p.prestartAllCoreThreads();
+ assertEquals(4, p.getPoolSize());
+ }
}
/**
@@ -172,10 +182,10 @@
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- final CountDownLatch threadStarted = new CountDownLatch(1);
- final CountDownLatch threadProceed = new CountDownLatch(1);
- final CountDownLatch threadDone = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
+ final CountDownLatch threadProceed = new CountDownLatch(1);
+ final CountDownLatch threadDone = new CountDownLatch(1);
assertEquals(0, p.getCompletedTaskCount());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
@@ -194,8 +204,6 @@
fail("timed out");
Thread.yield();
}
- } finally {
- joinPool(p);
}
}
@@ -207,8 +215,9 @@
new ThreadPoolExecutor(1, 1,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- assertEquals(1, p.getCorePoolSize());
- joinPool(p);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertEquals(1, p.getCorePoolSize());
+ }
}
/**
@@ -219,23 +228,25 @@
new ThreadPoolExecutor(2, 2,
1000, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- assertEquals(1, p.getKeepAliveTime(TimeUnit.SECONDS));
- joinPool(p);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertEquals(1, p.getKeepAliveTime(SECONDS));
+ }
}
/**
* getThreadFactory returns factory in constructor if not set
*/
public void testGetThreadFactory() {
- ThreadFactory tf = new SimpleThreadFactory();
+ ThreadFactory threadFactory = new SimpleThreadFactory();
final ThreadPoolExecutor p =
new ThreadPoolExecutor(1, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10),
- tf,
+ threadFactory,
new NoOpREHandler());
- assertSame(tf, p.getThreadFactory());
- joinPool(p);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertSame(threadFactory, p.getThreadFactory());
+ }
}
/**
@@ -246,10 +257,11 @@
new ThreadPoolExecutor(1, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- ThreadFactory tf = new SimpleThreadFactory();
- p.setThreadFactory(tf);
- assertSame(tf, p.getThreadFactory());
- joinPool(p);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ ThreadFactory threadFactory = new SimpleThreadFactory();
+ p.setThreadFactory(threadFactory);
+ assertSame(threadFactory, p.getThreadFactory());
+ }
}
/**
@@ -260,12 +272,11 @@
new ThreadPoolExecutor(1, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- try {
- p.setThreadFactory(null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(p);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.setThreadFactory(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -273,14 +284,15 @@
* getRejectedExecutionHandler returns handler in constructor if not set
*/
public void testGetRejectedExecutionHandler() {
- final RejectedExecutionHandler h = new NoOpREHandler();
+ final RejectedExecutionHandler handler = new NoOpREHandler();
final ThreadPoolExecutor p =
new ThreadPoolExecutor(1, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10),
- h);
- assertSame(h, p.getRejectedExecutionHandler());
- joinPool(p);
+ handler);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertSame(handler, p.getRejectedExecutionHandler());
+ }
}
/**
@@ -292,10 +304,11 @@
new ThreadPoolExecutor(1, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- RejectedExecutionHandler h = new NoOpREHandler();
- p.setRejectedExecutionHandler(h);
- assertSame(h, p.getRejectedExecutionHandler());
- joinPool(p);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ RejectedExecutionHandler handler = new NoOpREHandler();
+ p.setRejectedExecutionHandler(handler);
+ assertSame(handler, p.getRejectedExecutionHandler());
+ }
}
/**
@@ -306,12 +319,11 @@
new ThreadPoolExecutor(1, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- try {
- p.setRejectedExecutionHandler(null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(p);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.setRejectedExecutionHandler(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -321,28 +333,25 @@
*/
public void testGetLargestPoolSize() throws InterruptedException {
final int THREADS = 3;
+ final CountDownLatch done = new CountDownLatch(1);
final ThreadPoolExecutor p =
new ThreadPoolExecutor(THREADS, THREADS,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- final CountDownLatch threadsStarted = new CountDownLatch(THREADS);
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p, done)) {
assertEquals(0, p.getLargestPoolSize());
+ final CountDownLatch threadsStarted = new CountDownLatch(THREADS);
for (int i = 0; i < THREADS; i++)
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadsStarted.countDown();
- done.await();
+ await(done);
assertEquals(THREADS, p.getLargestPoolSize());
}});
- assertTrue(threadsStarted.await(SMALL_DELAY_MS, MILLISECONDS));
- assertEquals(THREADS, p.getLargestPoolSize());
- } finally {
- done.countDown();
- joinPool(p);
+ await(threadsStarted);
assertEquals(THREADS, p.getLargestPoolSize());
}
+ assertEquals(THREADS, p.getLargestPoolSize());
}
/**
@@ -354,8 +363,13 @@
new ThreadPoolExecutor(2, 3,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- assertEquals(3, p.getMaximumPoolSize());
- joinPool(p);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertEquals(3, p.getMaximumPoolSize());
+ p.setMaximumPoolSize(5);
+ assertEquals(5, p.getMaximumPoolSize());
+ p.setMaximumPoolSize(4);
+ assertEquals(4, p.getMaximumPoolSize());
+ }
}
/**
@@ -363,25 +377,22 @@
* become active
*/
public void testGetPoolSize() throws InterruptedException {
+ final CountDownLatch done = new CountDownLatch(1);
final ThreadPoolExecutor p =
new ThreadPoolExecutor(1, 1,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- final CountDownLatch threadStarted = new CountDownLatch(1);
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p, done)) {
assertEquals(0, p.getPoolSize());
+ final CountDownLatch threadStarted = new CountDownLatch(1);
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
assertEquals(1, p.getPoolSize());
- done.await();
+ await(done);
}});
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
assertEquals(1, p.getPoolSize());
- } finally {
- done.countDown();
- joinPool(p);
}
}
@@ -389,26 +400,38 @@
* getTaskCount increases, but doesn't overestimate, when tasks submitted
*/
public void testGetTaskCount() throws InterruptedException {
+ final int TASKS = 3;
+ final CountDownLatch done = new CountDownLatch(1);
final ThreadPoolExecutor p =
new ThreadPoolExecutor(1, 1,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- final CountDownLatch threadStarted = new CountDownLatch(1);
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
assertEquals(0, p.getTaskCount());
+ assertEquals(0, p.getCompletedTaskCount());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
- assertEquals(1, p.getTaskCount());
- done.await();
+ await(done);
}});
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
assertEquals(1, p.getTaskCount());
- } finally {
- done.countDown();
- joinPool(p);
+ assertEquals(0, p.getCompletedTaskCount());
+ for (int i = 0; i < TASKS; i++) {
+ assertEquals(1 + i, p.getTaskCount());
+ p.execute(new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ threadStarted.countDown();
+ assertEquals(1 + TASKS, p.getTaskCount());
+ await(done);
+ }});
+ }
+ assertEquals(1 + TASKS, p.getTaskCount());
+ assertEquals(0, p.getCompletedTaskCount());
}
+ assertEquals(1 + TASKS, p.getTaskCount());
+ assertEquals(1 + TASKS, p.getCompletedTaskCount());
}
/**
@@ -419,10 +442,11 @@
new ThreadPoolExecutor(1, 1,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- assertFalse(p.isShutdown());
- try { p.shutdown(); } catch (SecurityException ok) { return; }
- assertTrue(p.isShutdown());
- joinPool(p);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertFalse(p.isShutdown());
+ try { p.shutdown(); } catch (SecurityException ok) { return; }
+ assertTrue(p.isShutdown());
+ }
}
/**
@@ -433,26 +457,28 @@
new ThreadPoolExecutor(1, 1,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- assertFalse(p.isTerminated());
- assertFalse(p.awaitTermination(Long.MIN_VALUE, NANOSECONDS));
- assertFalse(p.awaitTermination(Long.MIN_VALUE, MILLISECONDS));
- assertFalse(p.awaitTermination(-1L, NANOSECONDS));
- assertFalse(p.awaitTermination(-1L, MILLISECONDS));
- assertFalse(p.awaitTermination(0L, NANOSECONDS));
- assertFalse(p.awaitTermination(0L, MILLISECONDS));
- long timeoutNanos = 999999L;
- long startTime = System.nanoTime();
- assertFalse(p.awaitTermination(timeoutNanos, NANOSECONDS));
- assertTrue(System.nanoTime() - startTime >= timeoutNanos);
- assertFalse(p.isTerminated());
- startTime = System.nanoTime();
- long timeoutMillis = timeoutMillis();
- assertFalse(p.awaitTermination(timeoutMillis, MILLISECONDS));
- assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
- assertFalse(p.isTerminated());
- p.shutdown();
- assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
- assertTrue(p.isTerminated());
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertFalse(p.isTerminated());
+ assertFalse(p.awaitTermination(Long.MIN_VALUE, NANOSECONDS));
+ assertFalse(p.awaitTermination(Long.MIN_VALUE, MILLISECONDS));
+ assertFalse(p.awaitTermination(-1L, NANOSECONDS));
+ assertFalse(p.awaitTermination(-1L, MILLISECONDS));
+ assertFalse(p.awaitTermination(0L, NANOSECONDS));
+ assertFalse(p.awaitTermination(0L, MILLISECONDS));
+ long timeoutNanos = 999999L;
+ long startTime = System.nanoTime();
+ assertFalse(p.awaitTermination(timeoutNanos, NANOSECONDS));
+ assertTrue(System.nanoTime() - startTime >= timeoutNanos);
+ assertFalse(p.isTerminated());
+ startTime = System.nanoTime();
+ long timeoutMillis = timeoutMillis();
+ assertFalse(p.awaitTermination(timeoutMillis, MILLISECONDS));
+ assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+ assertFalse(p.isTerminated());
+ try { p.shutdown(); } catch (SecurityException ok) { return; }
+ assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(p.isTerminated());
+ }
}
/**
@@ -463,24 +489,24 @@
new ThreadPoolExecutor(1, 1,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- final CountDownLatch threadStarted = new CountDownLatch(1);
- final CountDownLatch done = new CountDownLatch(1);
- assertFalse(p.isTerminated());
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
+ final CountDownLatch done = new CountDownLatch(1);
+ assertFalse(p.isTerminating());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
- assertFalse(p.isTerminated());
+ assertFalse(p.isTerminating());
threadStarted.countDown();
- done.await();
+ await(done);
}});
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
assertFalse(p.isTerminating());
done.countDown();
- } finally {
try { p.shutdown(); } catch (SecurityException ok) { return; }
+ assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(p.isTerminated());
+ assertFalse(p.isTerminating());
}
- assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
- assertTrue(p.isTerminated());
}
/**
@@ -491,59 +517,55 @@
new ThreadPoolExecutor(1, 1,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- final CountDownLatch threadStarted = new CountDownLatch(1);
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
+ final CountDownLatch done = new CountDownLatch(1);
assertFalse(p.isTerminating());
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
assertFalse(p.isTerminating());
threadStarted.countDown();
- done.await();
+ await(done);
}});
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
assertFalse(p.isTerminating());
done.countDown();
- } finally {
try { p.shutdown(); } catch (SecurityException ok) { return; }
+ assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(p.isTerminated());
+ assertFalse(p.isTerminating());
}
- assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
- assertTrue(p.isTerminated());
- assertFalse(p.isTerminating());
}
/**
* getQueue returns the work queue, which contains queued tasks
*/
public void testGetQueue() throws InterruptedException {
+ final CountDownLatch done = new CountDownLatch(1);
final BlockingQueue<Runnable> q = new ArrayBlockingQueue<Runnable>(10);
final ThreadPoolExecutor p =
new ThreadPoolExecutor(1, 1,
LONG_DELAY_MS, MILLISECONDS,
q);
- final CountDownLatch threadStarted = new CountDownLatch(1);
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
FutureTask[] tasks = new FutureTask[5];
for (int i = 0; i < tasks.length; i++) {
Callable task = new CheckedCallable<Boolean>() {
public Boolean realCall() throws InterruptedException {
threadStarted.countDown();
assertSame(q, p.getQueue());
- done.await();
+ await(done);
return Boolean.TRUE;
}};
tasks[i] = new FutureTask(task);
p.execute(tasks[i]);
}
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
assertSame(q, p.getQueue());
assertFalse(q.contains(tasks[0]));
assertTrue(q.contains(tasks[tasks.length - 1]));
assertEquals(tasks.length - 1, q.size());
- } finally {
- done.countDown();
- joinPool(p);
}
}
@@ -551,24 +573,24 @@
* remove(task) removes queued task, and fails to remove active task
*/
public void testRemove() throws InterruptedException {
+ final CountDownLatch done = new CountDownLatch(1);
BlockingQueue<Runnable> q = new ArrayBlockingQueue<Runnable>(10);
final ThreadPoolExecutor p =
new ThreadPoolExecutor(1, 1,
LONG_DELAY_MS, MILLISECONDS,
q);
- Runnable[] tasks = new Runnable[5];
- final CountDownLatch threadStarted = new CountDownLatch(1);
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ Runnable[] tasks = new Runnable[6];
+ final CountDownLatch threadStarted = new CountDownLatch(1);
for (int i = 0; i < tasks.length; i++) {
tasks[i] = new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
- done.await();
+ await(done);
}};
p.execute(tasks[i]);
}
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
assertFalse(p.remove(tasks[0]));
assertTrue(q.contains(tasks[4]));
assertTrue(q.contains(tasks[3]));
@@ -578,9 +600,6 @@
assertTrue(q.contains(tasks[3]));
assertTrue(p.remove(tasks[3]));
assertFalse(q.contains(tasks[3]));
- } finally {
- done.countDown();
- joinPool(p);
}
}
@@ -595,19 +614,19 @@
new ThreadPoolExecutor(1, 1,
LONG_DELAY_MS, MILLISECONDS,
q);
- FutureTask[] tasks = new FutureTask[5];
- try {
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ FutureTask[] tasks = new FutureTask[5];
for (int i = 0; i < tasks.length; i++) {
Callable task = new CheckedCallable<Boolean>() {
public Boolean realCall() throws InterruptedException {
threadStarted.countDown();
- done.await();
+ await(done);
return Boolean.TRUE;
}};
tasks[i] = new FutureTask(task);
p.execute(tasks[i]);
}
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
assertEquals(tasks.length, p.getTaskCount());
assertEquals(tasks.length - 1, q.size());
assertEquals(1L, p.getActiveCount());
@@ -620,32 +639,47 @@
p.purge(); // Nothing to do
assertEquals(tasks.length - 3, q.size());
assertEquals(tasks.length - 2, p.getTaskCount());
- } finally {
- done.countDown();
- joinPool(p);
}
}
/**
- * shutdownNow returns a list containing tasks that were not run
+ * shutdownNow returns a list containing tasks that were not run,
+ * and those tasks are drained from the queue
*/
- public void testShutdownNow() {
+ public void testShutdownNow() throws InterruptedException {
+ final int poolSize = 2;
+ final int count = 5;
+ final AtomicInteger ran = new AtomicInteger(0);
final ThreadPoolExecutor p =
- new ThreadPoolExecutor(1, 1,
+ new ThreadPoolExecutor(poolSize, poolSize,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- List l;
- try {
- for (int i = 0; i < 5; i++)
- p.execute(new MediumPossiblyInterruptedRunnable());
- }
- finally {
+ final CountDownLatch threadsStarted = new CountDownLatch(poolSize);
+ Runnable waiter = new CheckedRunnable() { public void realRun() {
+ threadsStarted.countDown();
try {
- l = p.shutdownNow();
- } catch (SecurityException ok) { return; }
+ MILLISECONDS.sleep(2 * LONG_DELAY_MS);
+ } catch (InterruptedException success) {}
+ ran.getAndIncrement();
+ }};
+ for (int i = 0; i < count; i++)
+ p.execute(waiter);
+ await(threadsStarted);
+ assertEquals(poolSize, p.getActiveCount());
+ assertEquals(0, p.getCompletedTaskCount());
+ final List<Runnable> queuedTasks;
+ try {
+ queuedTasks = p.shutdownNow();
+ } catch (SecurityException ok) {
+ return; // Allowed in case test doesn't have privs
}
assertTrue(p.isShutdown());
- assertTrue(l.size() <= 4);
+ assertTrue(p.getQueue().isEmpty());
+ assertEquals(count - poolSize, queuedTasks.size());
+ assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+ assertTrue(p.isTerminated());
+ assertEquals(poolSize, ran.get());
+ assertEquals(poolSize, p.getCompletedTaskCount());
}
// Exception Tests
@@ -655,8 +689,7 @@
*/
public void testConstructor1() {
try {
- new ThreadPoolExecutor(-1, 1,
- LONG_DELAY_MS, MILLISECONDS,
+ new ThreadPoolExecutor(-1, 1, 1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10));
shouldThrow();
} catch (IllegalArgumentException success) {}
@@ -667,8 +700,7 @@
*/
public void testConstructor2() {
try {
- new ThreadPoolExecutor(1, -1,
- LONG_DELAY_MS, MILLISECONDS,
+ new ThreadPoolExecutor(1, -1, 1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10));
shouldThrow();
} catch (IllegalArgumentException success) {}
@@ -679,8 +711,7 @@
*/
public void testConstructor3() {
try {
- new ThreadPoolExecutor(1, 0,
- LONG_DELAY_MS, MILLISECONDS,
+ new ThreadPoolExecutor(1, 0, 1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10));
shouldThrow();
} catch (IllegalArgumentException success) {}
@@ -691,8 +722,7 @@
*/
public void testConstructor4() {
try {
- new ThreadPoolExecutor(1, 2,
- -1L, MILLISECONDS,
+ new ThreadPoolExecutor(1, 2, -1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10));
shouldThrow();
} catch (IllegalArgumentException success) {}
@@ -703,8 +733,7 @@
*/
public void testConstructor5() {
try {
- new ThreadPoolExecutor(2, 1,
- LONG_DELAY_MS, MILLISECONDS,
+ new ThreadPoolExecutor(2, 1, 1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10));
shouldThrow();
} catch (IllegalArgumentException success) {}
@@ -715,8 +744,7 @@
*/
public void testConstructorNullPointerException() {
try {
- new ThreadPoolExecutor(1, 2,
- LONG_DELAY_MS, MILLISECONDS,
+ new ThreadPoolExecutor(1, 2, 1L, SECONDS,
(BlockingQueue) null);
shouldThrow();
} catch (NullPointerException success) {}
@@ -727,8 +755,7 @@
*/
public void testConstructor6() {
try {
- new ThreadPoolExecutor(-1, 1,
- LONG_DELAY_MS, MILLISECONDS,
+ new ThreadPoolExecutor(-1, 1, 1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10),
new SimpleThreadFactory());
shouldThrow();
@@ -740,8 +767,7 @@
*/
public void testConstructor7() {
try {
- new ThreadPoolExecutor(1, -1,
- LONG_DELAY_MS, MILLISECONDS,
+ new ThreadPoolExecutor(1, -1, 1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10),
new SimpleThreadFactory());
shouldThrow();
@@ -753,8 +779,7 @@
*/
public void testConstructor8() {
try {
- new ThreadPoolExecutor(1, 0,
- LONG_DELAY_MS, MILLISECONDS,
+ new ThreadPoolExecutor(1, 0, 1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10),
new SimpleThreadFactory());
shouldThrow();
@@ -766,8 +791,7 @@
*/
public void testConstructor9() {
try {
- new ThreadPoolExecutor(1, 2,
- -1L, MILLISECONDS,
+ new ThreadPoolExecutor(1, 2, -1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10),
new SimpleThreadFactory());
shouldThrow();
@@ -779,8 +803,7 @@
*/
public void testConstructor10() {
try {
- new ThreadPoolExecutor(2, 1,
- LONG_DELAY_MS, MILLISECONDS,
+ new ThreadPoolExecutor(2, 1, 1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10),
new SimpleThreadFactory());
shouldThrow();
@@ -792,8 +815,7 @@
*/
public void testConstructorNullPointerException2() {
try {
- new ThreadPoolExecutor(1, 2,
- LONG_DELAY_MS, MILLISECONDS,
+ new ThreadPoolExecutor(1, 2, 1L, SECONDS,
(BlockingQueue) null,
new SimpleThreadFactory());
shouldThrow();
@@ -805,8 +827,7 @@
*/
public void testConstructorNullPointerException3() {
try {
- new ThreadPoolExecutor(1, 2,
- LONG_DELAY_MS, MILLISECONDS,
+ new ThreadPoolExecutor(1, 2, 1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10),
(ThreadFactory) null);
shouldThrow();
@@ -818,8 +839,7 @@
*/
public void testConstructor11() {
try {
- new ThreadPoolExecutor(-1, 1,
- LONG_DELAY_MS, MILLISECONDS,
+ new ThreadPoolExecutor(-1, 1, 1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10),
new NoOpREHandler());
shouldThrow();
@@ -831,8 +851,7 @@
*/
public void testConstructor12() {
try {
- new ThreadPoolExecutor(1, -1,
- LONG_DELAY_MS, MILLISECONDS,
+ new ThreadPoolExecutor(1, -1, 1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10),
new NoOpREHandler());
shouldThrow();
@@ -844,8 +863,7 @@
*/
public void testConstructor13() {
try {
- new ThreadPoolExecutor(1, 0,
- LONG_DELAY_MS, MILLISECONDS,
+ new ThreadPoolExecutor(1, 0, 1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10),
new NoOpREHandler());
shouldThrow();
@@ -857,8 +875,7 @@
*/
public void testConstructor14() {
try {
- new ThreadPoolExecutor(1, 2,
- -1L, MILLISECONDS,
+ new ThreadPoolExecutor(1, 2, -1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10),
new NoOpREHandler());
shouldThrow();
@@ -870,8 +887,7 @@
*/
public void testConstructor15() {
try {
- new ThreadPoolExecutor(2, 1,
- LONG_DELAY_MS, MILLISECONDS,
+ new ThreadPoolExecutor(2, 1, 1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10),
new NoOpREHandler());
shouldThrow();
@@ -883,8 +899,7 @@
*/
public void testConstructorNullPointerException4() {
try {
- new ThreadPoolExecutor(1, 2,
- LONG_DELAY_MS, MILLISECONDS,
+ new ThreadPoolExecutor(1, 2, 1L, SECONDS,
(BlockingQueue) null,
new NoOpREHandler());
shouldThrow();
@@ -896,8 +911,7 @@
*/
public void testConstructorNullPointerException5() {
try {
- new ThreadPoolExecutor(1, 2,
- LONG_DELAY_MS, MILLISECONDS,
+ new ThreadPoolExecutor(1, 2, 1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10),
(RejectedExecutionHandler) null);
shouldThrow();
@@ -909,8 +923,7 @@
*/
public void testConstructor16() {
try {
- new ThreadPoolExecutor(-1, 1,
- LONG_DELAY_MS, MILLISECONDS,
+ new ThreadPoolExecutor(-1, 1, 1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10),
new SimpleThreadFactory(),
new NoOpREHandler());
@@ -923,8 +936,7 @@
*/
public void testConstructor17() {
try {
- new ThreadPoolExecutor(1, -1,
- LONG_DELAY_MS, MILLISECONDS,
+ new ThreadPoolExecutor(1, -1, 1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10),
new SimpleThreadFactory(),
new NoOpREHandler());
@@ -937,8 +949,7 @@
*/
public void testConstructor18() {
try {
- new ThreadPoolExecutor(1, 0,
- LONG_DELAY_MS, MILLISECONDS,
+ new ThreadPoolExecutor(1, 0, 1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10),
new SimpleThreadFactory(),
new NoOpREHandler());
@@ -951,8 +962,7 @@
*/
public void testConstructor19() {
try {
- new ThreadPoolExecutor(1, 2,
- -1L, MILLISECONDS,
+ new ThreadPoolExecutor(1, 2, -1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10),
new SimpleThreadFactory(),
new NoOpREHandler());
@@ -965,8 +975,7 @@
*/
public void testConstructor20() {
try {
- new ThreadPoolExecutor(2, 1,
- LONG_DELAY_MS, MILLISECONDS,
+ new ThreadPoolExecutor(2, 1, 1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10),
new SimpleThreadFactory(),
new NoOpREHandler());
@@ -979,8 +988,7 @@
*/
public void testConstructorNullPointerException6() {
try {
- new ThreadPoolExecutor(1, 2,
- LONG_DELAY_MS, MILLISECONDS,
+ new ThreadPoolExecutor(1, 2, 1L, SECONDS,
(BlockingQueue) null,
new SimpleThreadFactory(),
new NoOpREHandler());
@@ -993,8 +1001,7 @@
*/
public void testConstructorNullPointerException7() {
try {
- new ThreadPoolExecutor(1, 2,
- LONG_DELAY_MS, MILLISECONDS,
+ new ThreadPoolExecutor(1, 2, 1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10),
new SimpleThreadFactory(),
(RejectedExecutionHandler) null);
@@ -1007,8 +1014,7 @@
*/
public void testConstructorNullPointerException8() {
try {
- new ThreadPoolExecutor(1, 2,
- LONG_DELAY_MS, MILLISECONDS,
+ new ThreadPoolExecutor(1, 2, 1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10),
(ThreadFactory) null,
new NoOpREHandler());
@@ -1020,31 +1026,28 @@
* get of submitted callable throws InterruptedException if interrupted
*/
public void testInterruptedSubmit() throws InterruptedException {
+ final CountDownLatch done = new CountDownLatch(1);
final ThreadPoolExecutor p =
new ThreadPoolExecutor(1, 1,
- 60, TimeUnit.SECONDS,
+ 60, SECONDS,
new ArrayBlockingQueue<Runnable>(10));
- final CountDownLatch threadStarted = new CountDownLatch(1);
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
Thread t = newStartedThread(new CheckedInterruptedRunnable() {
public void realRun() throws Exception {
Callable task = new CheckedCallable<Boolean>() {
public Boolean realCall() throws InterruptedException {
threadStarted.countDown();
- done.await();
+ await(done);
return Boolean.TRUE;
}};
p.submit(task).get();
}});
- assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
+ await(threadStarted);
t.interrupt();
- awaitTermination(t, MEDIUM_DELAY_MS);
- } finally {
- done.countDown();
- joinPool(p);
+ awaitTermination(t);
}
}
@@ -1052,15 +1055,15 @@
* execute throws RejectedExecutionException if saturated.
*/
public void testSaturatedExecute() {
- ThreadPoolExecutor p =
+ final CountDownLatch done = new CountDownLatch(1);
+ final ThreadPoolExecutor p =
new ThreadPoolExecutor(1, 1,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(1));
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p, done)) {
Runnable task = new CheckedRunnable() {
public void realRun() throws InterruptedException {
- done.await();
+ await(done);
}};
for (int i = 0; i < 2; ++i)
p.execute(task);
@@ -1071,9 +1074,6 @@
} catch (RejectedExecutionException success) {}
assertTrue(p.getTaskCount() <= 2);
}
- } finally {
- done.countDown();
- joinPool(p);
}
}
@@ -1081,15 +1081,15 @@
* submit(runnable) throws RejectedExecutionException if saturated.
*/
public void testSaturatedSubmitRunnable() {
- ThreadPoolExecutor p =
+ final CountDownLatch done = new CountDownLatch(1);
+ final ThreadPoolExecutor p =
new ThreadPoolExecutor(1, 1,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(1));
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p, done)) {
Runnable task = new CheckedRunnable() {
public void realRun() throws InterruptedException {
- done.await();
+ await(done);
}};
for (int i = 0; i < 2; ++i)
p.submit(task);
@@ -1100,9 +1100,6 @@
} catch (RejectedExecutionException success) {}
assertTrue(p.getTaskCount() <= 2);
}
- } finally {
- done.countDown();
- joinPool(p);
}
}
@@ -1110,15 +1107,15 @@
* submit(callable) throws RejectedExecutionException if saturated.
*/
public void testSaturatedSubmitCallable() {
- ThreadPoolExecutor p =
+ final CountDownLatch done = new CountDownLatch(1);
+ final ThreadPoolExecutor p =
new ThreadPoolExecutor(1, 1,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(1));
- final CountDownLatch done = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p, done)) {
Runnable task = new CheckedRunnable() {
public void realRun() throws InterruptedException {
- done.await();
+ await(done);
}};
for (int i = 0; i < 2; ++i)
p.submit(Executors.callable(task));
@@ -1129,9 +1126,6 @@
} catch (RejectedExecutionException success) {}
assertTrue(p.getTaskCount() <= 2);
}
- } finally {
- done.countDown();
- joinPool(p);
}
}
@@ -1139,26 +1133,28 @@
* executor using CallerRunsPolicy runs task if saturated.
*/
public void testSaturatedExecute2() {
- RejectedExecutionHandler h = new ThreadPoolExecutor.CallerRunsPolicy();
final ThreadPoolExecutor p =
new ThreadPoolExecutor(1, 1,
LONG_DELAY_MS,
MILLISECONDS,
new ArrayBlockingQueue<Runnable>(1),
- h);
- try {
+ new ThreadPoolExecutor.CallerRunsPolicy());
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final CountDownLatch done = new CountDownLatch(1);
+ Runnable blocker = new CheckedRunnable() {
+ public void realRun() throws InterruptedException {
+ await(done);
+ }};
+ p.execute(blocker);
TrackedNoOpRunnable[] tasks = new TrackedNoOpRunnable[5];
- for (int i = 0; i < tasks.length; ++i)
+ for (int i = 0; i < tasks.length; i++)
tasks[i] = new TrackedNoOpRunnable();
- TrackedLongRunnable mr = new TrackedLongRunnable();
- p.execute(mr);
- for (int i = 0; i < tasks.length; ++i)
+ for (int i = 0; i < tasks.length; i++)
p.execute(tasks[i]);
- for (int i = 1; i < tasks.length; ++i)
+ for (int i = 1; i < tasks.length; i++)
assertTrue(tasks[i].done);
- try { p.shutdownNow(); } catch (SecurityException ok) { return; }
- } finally {
- joinPool(p);
+ assertFalse(tasks[0].done); // waiting in queue
+ done.countDown();
}
}
@@ -1166,67 +1162,72 @@
* executor using DiscardPolicy drops task if saturated.
*/
public void testSaturatedExecute3() {
- RejectedExecutionHandler h = new ThreadPoolExecutor.DiscardPolicy();
+ final CountDownLatch done = new CountDownLatch(1);
+ final TrackedNoOpRunnable[] tasks = new TrackedNoOpRunnable[5];
+ for (int i = 0; i < tasks.length; ++i)
+ tasks[i] = new TrackedNoOpRunnable();
final ThreadPoolExecutor p =
new ThreadPoolExecutor(1, 1,
- LONG_DELAY_MS, MILLISECONDS,
- new ArrayBlockingQueue<Runnable>(1),
- h);
- try {
- TrackedNoOpRunnable[] tasks = new TrackedNoOpRunnable[5];
- for (int i = 0; i < tasks.length; ++i)
- tasks[i] = new TrackedNoOpRunnable();
- p.execute(new TrackedLongRunnable());
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(1),
+ new ThreadPoolExecutor.DiscardPolicy());
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ p.execute(awaiter(done));
+
for (TrackedNoOpRunnable task : tasks)
p.execute(task);
- for (TrackedNoOpRunnable task : tasks)
- assertFalse(task.done);
- try { p.shutdownNow(); } catch (SecurityException ok) { return; }
- } finally {
- joinPool(p);
+ for (int i = 1; i < tasks.length; i++)
+ assertFalse(tasks[i].done);
}
+ for (int i = 1; i < tasks.length; i++)
+ assertFalse(tasks[i].done);
+ assertTrue(tasks[0].done); // was waiting in queue
}
/**
* executor using DiscardOldestPolicy drops oldest task if saturated.
*/
public void testSaturatedExecute4() {
- RejectedExecutionHandler h = new ThreadPoolExecutor.DiscardOldestPolicy();
+ final CountDownLatch done = new CountDownLatch(1);
+ LatchAwaiter r1 = awaiter(done);
+ LatchAwaiter r2 = awaiter(done);
+ LatchAwaiter r3 = awaiter(done);
final ThreadPoolExecutor p =
new ThreadPoolExecutor(1, 1,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(1),
- h);
- try {
- p.execute(new TrackedLongRunnable());
- TrackedLongRunnable r2 = new TrackedLongRunnable();
+ new ThreadPoolExecutor.DiscardOldestPolicy());
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ assertEquals(LatchAwaiter.NEW, r1.state);
+ assertEquals(LatchAwaiter.NEW, r2.state);
+ assertEquals(LatchAwaiter.NEW, r3.state);
+ p.execute(r1);
p.execute(r2);
assertTrue(p.getQueue().contains(r2));
- TrackedNoOpRunnable r3 = new TrackedNoOpRunnable();
p.execute(r3);
assertFalse(p.getQueue().contains(r2));
assertTrue(p.getQueue().contains(r3));
- try { p.shutdownNow(); } catch (SecurityException ok) { return; }
- } finally {
- joinPool(p);
}
+ assertEquals(LatchAwaiter.DONE, r1.state);
+ assertEquals(LatchAwaiter.NEW, r2.state);
+ assertEquals(LatchAwaiter.DONE, r3.state);
}
/**
* execute throws RejectedExecutionException if shutdown
*/
public void testRejectedExecutionExceptionOnShutdown() {
- ThreadPoolExecutor p =
+ final ThreadPoolExecutor p =
new ThreadPoolExecutor(1, 1,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(1));
try { p.shutdown(); } catch (SecurityException ok) { return; }
- try {
- p.execute(new NoOpRunnable());
- shouldThrow();
- } catch (RejectedExecutionException success) {}
-
- joinPool(p);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.execute(new NoOpRunnable());
+ shouldThrow();
+ } catch (RejectedExecutionException success) {}
+ }
}
/**
@@ -1240,12 +1241,10 @@
new ArrayBlockingQueue<Runnable>(1), h);
try { p.shutdown(); } catch (SecurityException ok) { return; }
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
TrackedNoOpRunnable r = new TrackedNoOpRunnable();
p.execute(r);
assertFalse(r.done);
- } finally {
- joinPool(p);
}
}
@@ -1253,20 +1252,17 @@
* execute using DiscardPolicy drops task on shutdown
*/
public void testDiscardOnShutdown() {
- RejectedExecutionHandler h = new ThreadPoolExecutor.DiscardPolicy();
- ThreadPoolExecutor p =
+ final ThreadPoolExecutor p =
new ThreadPoolExecutor(1, 1,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(1),
- h);
+ new ThreadPoolExecutor.DiscardPolicy());
try { p.shutdown(); } catch (SecurityException ok) { return; }
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
TrackedNoOpRunnable r = new TrackedNoOpRunnable();
p.execute(r);
assertFalse(r.done);
- } finally {
- joinPool(p);
}
}
@@ -1274,20 +1270,17 @@
* execute using DiscardOldestPolicy drops task on shutdown
*/
public void testDiscardOldestOnShutdown() {
- RejectedExecutionHandler h = new ThreadPoolExecutor.DiscardOldestPolicy();
- ThreadPoolExecutor p =
+ final ThreadPoolExecutor p =
new ThreadPoolExecutor(1, 1,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(1),
- h);
+ new ThreadPoolExecutor.DiscardOldestPolicy());
try { p.shutdown(); } catch (SecurityException ok) { return; }
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
TrackedNoOpRunnable r = new TrackedNoOpRunnable();
p.execute(r);
assertFalse(r.done);
- } finally {
- joinPool(p);
}
}
@@ -1295,34 +1288,32 @@
* execute(null) throws NPE
*/
public void testExecuteNull() {
- ThreadPoolExecutor p =
+ final ThreadPoolExecutor p =
new ThreadPoolExecutor(1, 2,
- LONG_DELAY_MS, MILLISECONDS,
+ 1L, SECONDS,
new ArrayBlockingQueue<Runnable>(10));
- try {
- p.execute(null);
- shouldThrow();
- } catch (NullPointerException success) {}
-
- joinPool(p);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.execute(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
+ }
}
/**
* setCorePoolSize of negative value throws IllegalArgumentException
*/
public void testCorePoolSizeIllegalArgumentException() {
- ThreadPoolExecutor p =
+ final ThreadPoolExecutor p =
new ThreadPoolExecutor(1, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- try {
- p.setCorePoolSize(-1);
- shouldThrow();
- } catch (IllegalArgumentException success) {
- } finally {
- try { p.shutdown(); } catch (SecurityException ok) { return; }
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.setCorePoolSize(-1);
+ shouldThrow();
+ } catch (IllegalArgumentException success) {}
}
- joinPool(p);
}
/**
@@ -1330,18 +1321,16 @@
* given a value less the core pool size
*/
public void testMaximumPoolSizeIllegalArgumentException() {
- ThreadPoolExecutor p =
+ final ThreadPoolExecutor p =
new ThreadPoolExecutor(2, 3,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- try {
- p.setMaximumPoolSize(1);
- shouldThrow();
- } catch (IllegalArgumentException success) {
- } finally {
- try { p.shutdown(); } catch (SecurityException ok) { return; }
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.setMaximumPoolSize(1);
+ shouldThrow();
+ } catch (IllegalArgumentException success) {}
}
- joinPool(p);
}
/**
@@ -1349,18 +1338,45 @@
* if given a negative value
*/
public void testMaximumPoolSizeIllegalArgumentException2() {
- ThreadPoolExecutor p =
+ final ThreadPoolExecutor p =
new ThreadPoolExecutor(2, 3,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- try {
- p.setMaximumPoolSize(-1);
- shouldThrow();
- } catch (IllegalArgumentException success) {
- } finally {
- try { p.shutdown(); } catch (SecurityException ok) { return; }
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.setMaximumPoolSize(-1);
+ shouldThrow();
+ } catch (IllegalArgumentException success) {}
}
- joinPool(p);
+ }
+
+ /**
+ * Configuration changes that allow core pool size greater than
+ * max pool size result in IllegalArgumentException.
+ */
+ public void testPoolSizeInvariants() {
+ final ThreadPoolExecutor p =
+ new ThreadPoolExecutor(1, 1,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(p)) {
+ for (int s = 1; s < 5; s++) {
+ p.setMaximumPoolSize(s);
+ p.setCorePoolSize(s);
+ try {
+ p.setMaximumPoolSize(s - 1);
+ shouldThrow();
+ } catch (IllegalArgumentException success) {}
+ assertEquals(s, p.getCorePoolSize());
+ assertEquals(s, p.getMaximumPoolSize());
+ try {
+ p.setCorePoolSize(s + 1);
+ shouldThrow();
+ } catch (IllegalArgumentException success) {}
+ assertEquals(s, p.getCorePoolSize());
+ assertEquals(s, p.getMaximumPoolSize());
+ }
+ }
}
/**
@@ -1368,18 +1384,16 @@
* when given a negative value
*/
public void testKeepAliveTimeIllegalArgumentException() {
- ThreadPoolExecutor p =
+ final ThreadPoolExecutor p =
new ThreadPoolExecutor(2, 3,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- try {
- p.setKeepAliveTime(-1,MILLISECONDS);
- shouldThrow();
- } catch (IllegalArgumentException success) {
- } finally {
- try { p.shutdown(); } catch (SecurityException ok) { return; }
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try {
+ p.setKeepAliveTime(-1, MILLISECONDS);
+ shouldThrow();
+ } catch (IllegalArgumentException success) {}
}
- joinPool(p);
}
/**
@@ -1387,9 +1401,11 @@
*/
public void testTerminated() {
ExtendedTPE p = new ExtendedTPE();
- try { p.shutdown(); } catch (SecurityException ok) { return; }
- assertTrue(p.terminatedCalled());
- joinPool(p);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ try { p.shutdown(); } catch (SecurityException ok) { return; }
+ assertTrue(p.terminatedCalled());
+ assertTrue(p.isShutdown());
+ }
}
/**
@@ -1397,7 +1413,7 @@
*/
public void testBeforeAfter() throws InterruptedException {
ExtendedTPE p = new ExtendedTPE();
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
final CountDownLatch done = new CountDownLatch(1);
p.execute(new CheckedRunnable() {
public void realRun() {
@@ -1407,9 +1423,6 @@
assertEquals(0, done.getCount());
assertTrue(p.afterCalled());
assertTrue(p.beforeCalled());
- try { p.shutdown(); } catch (SecurityException ok) { return; }
- } finally {
- joinPool(p);
}
}
@@ -1417,16 +1430,14 @@
* completed submit of callable returns result
*/
public void testSubmitCallable() throws Exception {
- ExecutorService e =
+ final ExecutorService e =
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
Future<String> future = e.submit(new StringTask());
String result = future.get();
assertSame(TEST_STRING, result);
- } finally {
- joinPool(e);
}
}
@@ -1434,16 +1445,14 @@
* completed submit of runnable returns successfully
*/
public void testSubmitRunnable() throws Exception {
- ExecutorService e =
+ final ExecutorService e =
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
Future<?> future = e.submit(new NoOpRunnable());
future.get();
assertTrue(future.isDone());
- } finally {
- joinPool(e);
}
}
@@ -1451,16 +1460,14 @@
* completed submit of (runnable, result) returns result
*/
public void testSubmitRunnable2() throws Exception {
- ExecutorService e =
+ final ExecutorService e =
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
String result = future.get();
assertSame(TEST_STRING, result);
- } finally {
- joinPool(e);
}
}
@@ -1468,16 +1475,15 @@
* invokeAny(null) throws NPE
*/
public void testInvokeAny1() throws Exception {
- ExecutorService e =
+ final ExecutorService e =
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- try {
- e.invokeAny(null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAny(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1485,16 +1491,15 @@
* invokeAny(empty collection) throws IAE
*/
public void testInvokeAny2() throws Exception {
- ExecutorService e =
+ final ExecutorService e =
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- try {
- e.invokeAny(new ArrayList<Callable<String>>());
- shouldThrow();
- } catch (IllegalArgumentException success) {
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAny(new ArrayList<Callable<String>>());
+ shouldThrow();
+ } catch (IllegalArgumentException success) {}
}
}
@@ -1507,16 +1512,15 @@
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(latchAwaitingStringTask(latch));
- l.add(null);
- try {
- e.invokeAny(l);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(latchAwaitingStringTask(latch));
+ l.add(null);
+ try {
+ e.invokeAny(l);
+ shouldThrow();
+ } catch (NullPointerException success) {}
latch.countDown();
- joinPool(e);
}
}
@@ -1524,19 +1528,19 @@
* invokeAny(c) throws ExecutionException if no task completes
*/
public void testInvokeAny4() throws Exception {
- ExecutorService e =
+ final ExecutorService e =
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new NPETask());
- try {
- e.invokeAny(l);
- shouldThrow();
- } catch (ExecutionException success) {
- assertTrue(success.getCause() instanceof NullPointerException);
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new NPETask());
+ try {
+ e.invokeAny(l);
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertTrue(success.getCause() instanceof NullPointerException);
+ }
}
}
@@ -1544,18 +1548,16 @@
* invokeAny(c) returns result of some task
*/
public void testInvokeAny5() throws Exception {
- ExecutorService e =
+ final ExecutorService e =
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
String result = e.invokeAny(l);
assertSame(TEST_STRING, result);
- } finally {
- joinPool(e);
}
}
@@ -1563,16 +1565,15 @@
* invokeAll(null) throws NPE
*/
public void testInvokeAll1() throws Exception {
- ExecutorService e =
+ final ExecutorService e =
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- try {
- e.invokeAll(null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAll(null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1580,15 +1581,13 @@
* invokeAll(empty collection) returns empty collection
*/
public void testInvokeAll2() throws InterruptedException {
- ExecutorService e =
+ final ExecutorService e =
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>());
assertTrue(r.isEmpty());
- } finally {
- joinPool(e);
}
}
@@ -1596,19 +1595,18 @@
* invokeAll(c) throws NPE if c has null elements
*/
public void testInvokeAll3() throws Exception {
- ExecutorService e =
+ final ExecutorService e =
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- l.add(null);
- try {
- e.invokeAll(l);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new StringTask());
+ l.add(null);
+ try {
+ e.invokeAll(l);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1616,11 +1614,11 @@
* get of element of invokeAll(c) throws exception on failed task
*/
public void testInvokeAll4() throws Exception {
- ExecutorService e =
+ final ExecutorService e =
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new NPETask());
List<Future<String>> futures = e.invokeAll(l);
@@ -1631,8 +1629,6 @@
} catch (ExecutionException success) {
assertTrue(success.getCause() instanceof NullPointerException);
}
- } finally {
- joinPool(e);
}
}
@@ -1640,11 +1636,11 @@
* invokeAll(c) returns results of all completed tasks
*/
public void testInvokeAll5() throws Exception {
- ExecutorService e =
+ final ExecutorService e =
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
@@ -1652,8 +1648,6 @@
assertEquals(2, futures.size());
for (Future<String> future : futures)
assertSame(TEST_STRING, future.get());
- } finally {
- joinPool(e);
}
}
@@ -1661,16 +1655,15 @@
* timed invokeAny(null) throws NPE
*/
public void testTimedInvokeAny1() throws Exception {
- ExecutorService e =
+ final ExecutorService e =
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- try {
- e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1678,18 +1671,17 @@
* timed invokeAny(,,null) throws NPE
*/
public void testTimedInvokeAnyNullTimeUnit() throws Exception {
- ExecutorService e =
+ final ExecutorService e =
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- try {
- e.invokeAny(l, MEDIUM_DELAY_MS, null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new StringTask());
+ try {
+ e.invokeAny(l, MEDIUM_DELAY_MS, null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1697,16 +1689,16 @@
* timed invokeAny(empty collection) throws IAE
*/
public void testTimedInvokeAny2() throws Exception {
- ExecutorService e =
+ final ExecutorService e =
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- try {
- e.invokeAny(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (IllegalArgumentException success) {
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAny(new ArrayList<Callable<String>>(),
+ MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (IllegalArgumentException success) {}
}
}
@@ -1719,16 +1711,15 @@
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(latchAwaitingStringTask(latch));
- l.add(null);
- try {
- e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(latchAwaitingStringTask(latch));
+ l.add(null);
+ try {
+ e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
latch.countDown();
- joinPool(e);
}
}
@@ -1736,19 +1727,21 @@
* timed invokeAny(c) throws ExecutionException if no task completes
*/
public void testTimedInvokeAny4() throws Exception {
- ExecutorService e =
+ final ExecutorService e =
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new NPETask());
- try {
- e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (ExecutionException success) {
- assertTrue(success.getCause() instanceof NullPointerException);
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ long startTime = System.nanoTime();
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new NPETask());
+ try {
+ e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertTrue(success.getCause() instanceof NullPointerException);
+ }
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}
}
@@ -1756,18 +1749,18 @@
* timed invokeAny(c) returns result of some task
*/
public void testTimedInvokeAny5() throws Exception {
- ExecutorService e =
+ final ExecutorService e =
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
+ long startTime = System.nanoTime();
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
- String result = e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
assertSame(TEST_STRING, result);
- } finally {
- joinPool(e);
+ assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
}
}
@@ -1775,16 +1768,15 @@
* timed invokeAll(null) throws NPE
*/
public void testTimedInvokeAll1() throws Exception {
- ExecutorService e =
+ final ExecutorService e =
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- try {
- e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ try {
+ e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1792,18 +1784,17 @@
* timed invokeAll(,,null) throws NPE
*/
public void testTimedInvokeAllNullTimeUnit() throws Exception {
- ExecutorService e =
+ final ExecutorService e =
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- try {
- e.invokeAll(l, MEDIUM_DELAY_MS, null);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new StringTask());
+ try {
+ e.invokeAll(l, MEDIUM_DELAY_MS, null);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1811,15 +1802,14 @@
* timed invokeAll(empty collection) returns empty collection
*/
public void testTimedInvokeAll2() throws InterruptedException {
- ExecutorService e =
+ final ExecutorService e =
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- try {
- List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(),
+ MEDIUM_DELAY_MS, MILLISECONDS);
assertTrue(r.isEmpty());
- } finally {
- joinPool(e);
}
}
@@ -1827,19 +1817,18 @@
* timed invokeAll(c) throws NPE if c has null elements
*/
public void testTimedInvokeAll3() throws Exception {
- ExecutorService e =
+ final ExecutorService e =
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- l.add(null);
- try {
- e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
- shouldThrow();
- } catch (NullPointerException success) {
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new StringTask());
+ l.add(null);
+ try {
+ e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (NullPointerException success) {}
}
}
@@ -1847,22 +1836,22 @@
* get of element of invokeAll(c) throws exception on failed task
*/
public void testTimedInvokeAll4() throws Exception {
- ExecutorService e =
+ final ExecutorService e =
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new NPETask());
- List<Future<String>> futures =
- e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
- assertEquals(1, futures.size());
- try {
- futures.get(0).get();
- shouldThrow();
- } catch (ExecutionException success) {
- assertTrue(success.getCause() instanceof NullPointerException);
- } finally {
- joinPool(e);
+ try (PoolCleaner cleaner = cleaner(e)) {
+ List<Callable<String>> l = new ArrayList<Callable<String>>();
+ l.add(new NPETask());
+ List<Future<String>> futures =
+ e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+ assertEquals(1, futures.size());
+ try {
+ futures.get(0).get();
+ shouldThrow();
+ } catch (ExecutionException success) {
+ assertTrue(success.getCause() instanceof NullPointerException);
+ }
}
}
@@ -1870,21 +1859,19 @@
* timed invokeAll(c) returns results of all completed tasks
*/
public void testTimedInvokeAll5() throws Exception {
- ExecutorService e =
+ final ExecutorService e =
new ThreadPoolExecutor(2, 2,
LONG_DELAY_MS, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
List<Callable<String>> l = new ArrayList<Callable<String>>();
l.add(new StringTask());
l.add(new StringTask());
List<Future<String>> futures =
- e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+ e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
assertEquals(2, futures.size());
for (Future<String> future : futures)
assertSame(TEST_STRING, future.get());
- } finally {
- joinPool(e);
}
}
@@ -1892,24 +1879,40 @@
* timed invokeAll(c) cancels tasks not completed by timeout
*/
public void testTimedInvokeAll6() throws Exception {
- ExecutorService e =
- new ThreadPoolExecutor(2, 2,
- LONG_DELAY_MS, MILLISECONDS,
- new ArrayBlockingQueue<Runnable>(10));
- try {
- List<Callable<String>> l = new ArrayList<Callable<String>>();
- l.add(new StringTask());
- l.add(Executors.callable(new MediumPossiblyInterruptedRunnable(), TEST_STRING));
- l.add(new StringTask());
- List<Future<String>> futures =
- e.invokeAll(l, SHORT_DELAY_MS, MILLISECONDS);
- assertEquals(l.size(), futures.size());
- for (Future future : futures)
- assertTrue(future.isDone());
- assertFalse(futures.get(0).isCancelled());
- assertTrue(futures.get(1).isCancelled());
- } finally {
- joinPool(e);
+ for (long timeout = timeoutMillis();;) {
+ final CountDownLatch done = new CountDownLatch(1);
+ final Callable<String> waiter = new CheckedCallable<String>() {
+ public String realCall() {
+ try { done.await(LONG_DELAY_MS, MILLISECONDS); }
+ catch (InterruptedException ok) {}
+ return "1"; }};
+ final ExecutorService p =
+ new ThreadPoolExecutor(2, 2,
+ LONG_DELAY_MS, MILLISECONDS,
+ new ArrayBlockingQueue<Runnable>(10));
+ try (PoolCleaner cleaner = cleaner(p, done)) {
+ List<Callable<String>> tasks = new ArrayList<>();
+ tasks.add(new StringTask("0"));
+ tasks.add(waiter);
+ tasks.add(new StringTask("2"));
+ long startTime = System.nanoTime();
+ List<Future<String>> futures =
+ p.invokeAll(tasks, timeout, MILLISECONDS);
+ assertEquals(tasks.size(), futures.size());
+ assertTrue(millisElapsedSince(startTime) >= timeout);
+ for (Future future : futures)
+ assertTrue(future.isDone());
+ assertTrue(futures.get(1).isCancelled());
+ try {
+ assertEquals("0", futures.get(0).get());
+ assertEquals("2", futures.get(2).get());
+ break;
+ } catch (CancellationException retryWithLongerTimeout) {
+ timeout *= 2;
+ if (timeout >= LONG_DELAY_MS / 2)
+ fail("expected exactly one task to be cancelled");
+ }
+ }
}
}
@@ -1923,7 +1926,7 @@
LONG_DELAY_MS, MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
new FailingThreadFactory());
- try {
+ try (PoolCleaner cleaner = cleaner(e)) {
final int TASKS = 100;
final CountDownLatch done = new CountDownLatch(TASKS);
for (int k = 0; k < TASKS; ++k)
@@ -1932,8 +1935,6 @@
done.countDown();
}});
assertTrue(done.await(LONG_DELAY_MS, MILLISECONDS));
- } finally {
- joinPool(e);
}
}
@@ -1945,21 +1946,22 @@
new ThreadPoolExecutor(2, 2,
1000, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- assertFalse(p.allowsCoreThreadTimeOut());
- joinPool(p);
+ try (PoolCleaner cleaner = cleaner(p)) {
+ assertFalse(p.allowsCoreThreadTimeOut());
+ }
}
/**
* allowCoreThreadTimeOut(true) causes idle threads to time out
*/
public void testAllowCoreThreadTimeOut_true() throws Exception {
- long coreThreadTimeOut = SHORT_DELAY_MS;
+ long keepAliveTime = timeoutMillis();
final ThreadPoolExecutor p =
new ThreadPoolExecutor(2, 10,
- coreThreadTimeOut, MILLISECONDS,
+ keepAliveTime, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- final CountDownLatch threadStarted = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
p.allowCoreThreadTimeOut(true);
p.execute(new CheckedRunnable() {
public void realRun() {
@@ -1967,15 +1969,13 @@
assertEquals(1, p.getPoolSize());
}});
await(threadStarted);
- delay(coreThreadTimeOut);
+ delay(keepAliveTime);
long startTime = System.nanoTime();
while (p.getPoolSize() > 0
&& millisElapsedSince(startTime) < LONG_DELAY_MS)
Thread.yield();
assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
assertEquals(0, p.getPoolSize());
- } finally {
- joinPool(p);
}
}
@@ -1983,23 +1983,21 @@
* allowCoreThreadTimeOut(false) causes idle threads not to time out
*/
public void testAllowCoreThreadTimeOut_false() throws Exception {
- long coreThreadTimeOut = SHORT_DELAY_MS;
+ long keepAliveTime = timeoutMillis();
final ThreadPoolExecutor p =
new ThreadPoolExecutor(2, 10,
- coreThreadTimeOut, MILLISECONDS,
+ keepAliveTime, MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10));
- final CountDownLatch threadStarted = new CountDownLatch(1);
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
+ final CountDownLatch threadStarted = new CountDownLatch(1);
p.allowCoreThreadTimeOut(false);
p.execute(new CheckedRunnable() {
public void realRun() throws InterruptedException {
threadStarted.countDown();
assertTrue(p.getPoolSize() >= 1);
}});
- delay(2 * coreThreadTimeOut);
+ delay(2 * keepAliveTime);
assertTrue(p.getPoolSize() >= 1);
- } finally {
- joinPool(p);
}
}
@@ -2015,9 +2013,10 @@
done.countDown();
}};
final ThreadPoolExecutor p =
- new ThreadPoolExecutor(1, 30, 60, TimeUnit.SECONDS,
+ new ThreadPoolExecutor(1, 30,
+ 60, SECONDS,
new ArrayBlockingQueue(30));
- try {
+ try (PoolCleaner cleaner = cleaner(p)) {
for (int i = 0; i < nTasks; ++i) {
for (;;) {
try {
@@ -2029,8 +2028,43 @@
}
// enough time to run all tasks
assertTrue(done.await(nTasks * SHORT_DELAY_MS, MILLISECONDS));
- } finally {
- joinPool(p);
+ }
+ }
+
+ /**
+ * get(cancelled task) throws CancellationException
+ */
+ public void testGet_cancelled() throws Exception {
+ final CountDownLatch done = new CountDownLatch(1);
+ final ExecutorService e =
+ new ThreadPoolExecutor(1, 1,
+ LONG_DELAY_MS, MILLISECONDS,
+ new LinkedBlockingQueue<Runnable>());
+ try (PoolCleaner cleaner = cleaner(e, done)) {
+ final CountDownLatch blockerStarted = new CountDownLatch(1);
+ final List<Future<?>> futures = new ArrayList<>();
+ for (int i = 0; i < 2; i++) {
+ Runnable r = new CheckedRunnable() { public void realRun()
+ throws Throwable {
+ blockerStarted.countDown();
+ assertTrue(done.await(2 * LONG_DELAY_MS, MILLISECONDS));
+ }};
+ futures.add(e.submit(r));
+ }
+ await(blockerStarted);
+ for (Future<?> future : futures) future.cancel(false);
+ for (Future<?> future : futures) {
+ try {
+ future.get();
+ shouldThrow();
+ } catch (CancellationException success) {}
+ try {
+ future.get(LONG_DELAY_MS, MILLISECONDS);
+ shouldThrow();
+ } catch (CancellationException success) {}
+ assertTrue(future.isCancelled());
+ assertTrue(future.isDone());
+ }
}
}
diff --git a/jsr166-tests/src/test/java/jsr166/ThreadTest.java b/jsr166-tests/src/test/java/jsr166/ThreadTest.java
index 27f22ca..e69b422 100644
--- a/jsr166-tests/src/test/java/jsr166/ThreadTest.java
+++ b/jsr166-tests/src/test/java/jsr166/ThreadTest.java
@@ -19,7 +19,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(ThreadTest.class);
// }
static class MyHandler implements Thread.UncaughtExceptionHandler {
@@ -57,8 +57,7 @@
// default uncaught exception handler installed by the framework.
//
// assertEquals(null, Thread.getDefaultUncaughtExceptionHandler());
-
- // failure due to securityException is OK.
+ // failure due to SecurityException is OK.
// Would be nice to explicitly test both ways, but cannot yet.
Thread.UncaughtExceptionHandler defaultHandler
= Thread.getDefaultUncaughtExceptionHandler();
diff --git a/jsr166-tests/src/test/java/jsr166/TimeUnitTest.java b/jsr166-tests/src/test/java/jsr166/TimeUnitTest.java
index 2c9529b..b21fa7d 100644
--- a/jsr166-tests/src/test/java/jsr166/TimeUnitTest.java
+++ b/jsr166-tests/src/test/java/jsr166/TimeUnitTest.java
@@ -30,7 +30,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(TimeUnitTest.class);
// }
// (loops to 88888 check increments at all time divisions.)
@@ -433,8 +433,8 @@
* a deserialized serialized unit is the same instance
*/
public void testSerialization() throws Exception {
- TimeUnit x = MILLISECONDS;
- assertSame(x, serialClone(x));
+ for (TimeUnit x : TimeUnit.values())
+ assertSame(x, serialClone(x));
}
}
diff --git a/jsr166-tests/src/test/java/jsr166/TreeMapTest.java b/jsr166-tests/src/test/java/jsr166/TreeMapTest.java
index afc73de..e445609 100644
--- a/jsr166-tests/src/test/java/jsr166/TreeMapTest.java
+++ b/jsr166-tests/src/test/java/jsr166/TreeMapTest.java
@@ -29,7 +29,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(TreeMapTest.class);
// }
/**
diff --git a/jsr166-tests/src/test/java/jsr166/TreeSetTest.java b/jsr166-tests/src/test/java/jsr166/TreeSetTest.java
index a935637..c3093f6 100644
--- a/jsr166-tests/src/test/java/jsr166/TreeSetTest.java
+++ b/jsr166-tests/src/test/java/jsr166/TreeSetTest.java
@@ -29,7 +29,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(TreeSetTest.class);
// }
static class MyReverseComparator implements Comparator {
@@ -50,7 +50,7 @@
private TreeSet<Integer> populatedSet(int n) {
TreeSet<Integer> q = new TreeSet<Integer>();
assertTrue(q.isEmpty());
- for (int i = n-1; i >= 0; i -= 2)
+ for (int i = n - 1; i >= 0; i -= 2)
assertTrue(q.add(new Integer(i)));
for (int i = (n & 1); i < n; i += 2)
assertTrue(q.add(new Integer(i)));
@@ -96,8 +96,7 @@
*/
public void testConstructor4() {
try {
- Integer[] ints = new Integer[SIZE];
- new TreeSet(Arrays.asList(ints));
+ new TreeSet(Arrays.asList(new Integer[SIZE]));
shouldThrow();
} catch (NullPointerException success) {}
}
@@ -106,10 +105,10 @@
* Initializing from Collection with some null elements throws NPE
*/
public void testConstructor5() {
+ Integer[] ints = new Integer[SIZE];
+ for (int i = 0; i < SIZE - 1; ++i)
+ ints[i] = new Integer(i);
try {
- Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
- ints[i] = new Integer(i);
new TreeSet(Arrays.asList(ints));
shouldThrow();
} catch (NullPointerException success) {}
@@ -138,7 +137,7 @@
for (int i = 0; i < SIZE; ++i)
ints[i] = new Integer(i);
q.addAll(Arrays.asList(ints));
- for (int i = SIZE-1; i >= 0; --i)
+ for (int i = SIZE - 1; i >= 0; --i)
assertEquals(ints[i], q.pollFirst());
}
@@ -162,7 +161,7 @@
public void testSize() {
TreeSet q = populatedSet(SIZE);
for (int i = 0; i < SIZE; ++i) {
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
q.pollFirst();
}
for (int i = 0; i < SIZE; ++i) {
@@ -242,7 +241,7 @@
public void testAddAll3() {
TreeSet q = new TreeSet();
Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
+ for (int i = 0; i < SIZE - 1; ++i)
ints[i] = new Integer(i);
try {
q.addAll(Arrays.asList(ints));
@@ -257,7 +256,7 @@
Integer[] empty = new Integer[0];
Integer[] ints = new Integer[SIZE];
for (int i = 0; i < SIZE; ++i)
- ints[i] = new Integer(SIZE-1-i);
+ ints[i] = new Integer(SIZE - 1 - i);
TreeSet q = new TreeSet();
assertFalse(q.addAll(Arrays.asList(empty)));
assertTrue(q.addAll(Arrays.asList(ints)));
@@ -281,7 +280,7 @@
*/
public void testPollLast() {
TreeSet q = populatedSet(SIZE);
- for (int i = SIZE-1; i >= 0; --i) {
+ for (int i = SIZE - 1; i >= 0; --i) {
assertEquals(i, q.pollLast());
}
assertNull(q.pollFirst());
@@ -296,14 +295,14 @@
assertTrue(q.contains(i));
assertTrue(q.remove(i));
assertFalse(q.contains(i));
- assertTrue(q.contains(i-1));
+ assertTrue(q.contains(i - 1));
}
for (int i = 0; i < SIZE; i += 2) {
assertTrue(q.contains(i));
assertTrue(q.remove(i));
assertFalse(q.contains(i));
- assertFalse(q.remove(i+1));
- assertFalse(q.contains(i+1));
+ assertFalse(q.remove(i + 1));
+ assertFalse(q.contains(i + 1));
}
assertTrue(q.isEmpty());
}
@@ -362,7 +361,7 @@
assertTrue(changed);
assertTrue(q.containsAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
p.pollFirst();
}
}
@@ -375,7 +374,7 @@
TreeSet q = populatedSet(SIZE);
TreeSet p = populatedSet(i);
assertTrue(q.removeAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
for (int j = 0; j < i; ++j) {
Integer x = (Integer)(p.pollFirst());
assertFalse(q.contains(x));
@@ -909,18 +908,18 @@
else if (element > max)
return -1;
int result = bs.nextSetBit(element);
- return result > max ? -1 : result;
+ return (result > max) ? -1 : result;
}
int higherAscending(int element) {
return ceilingAscending(element + 1);
}
private int firstAscending() {
int result = ceilingAscending(min);
- return result > max ? -1 : result;
+ return (result > max) ? -1 : result;
}
private int lastAscending() {
int result = floorAscending(max);
- return result < min ? -1 : result;
+ return (result < min) ? -1 : result;
}
}
ReferenceSet rs = new ReferenceSet();
@@ -981,7 +980,7 @@
}
static boolean eq(Integer i, int j) {
- return i == null ? j == -1 : i == j;
+ return (i == null) ? j == -1 : i == j;
}
}
diff --git a/jsr166-tests/src/test/java/jsr166/TreeSubMapTest.java b/jsr166-tests/src/test/java/jsr166/TreeSubMapTest.java
index 18a9e37..09b809e 100644
--- a/jsr166-tests/src/test/java/jsr166/TreeSubMapTest.java
+++ b/jsr166-tests/src/test/java/jsr166/TreeSubMapTest.java
@@ -27,7 +27,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(TreeSubMapTest.class);
// }
/**
diff --git a/jsr166-tests/src/test/java/jsr166/TreeSubSetTest.java b/jsr166-tests/src/test/java/jsr166/TreeSubSetTest.java
index 5398c4e..31403be 100644
--- a/jsr166-tests/src/test/java/jsr166/TreeSubSetTest.java
+++ b/jsr166-tests/src/test/java/jsr166/TreeSubSetTest.java
@@ -25,7 +25,7 @@
// main(suite(), args);
// }
// public static Test suite() {
- // return new TestSuite(...);
+ // return new TestSuite(TreeSubSetTest.class);
// }
static class MyReverseComparator implements Comparator {
@@ -42,7 +42,7 @@
TreeSet<Integer> q = new TreeSet<Integer>();
assertTrue(q.isEmpty());
- for (int i = n-1; i >= 0; i -= 2)
+ for (int i = n - 1; i >= 0; i -= 2)
assertTrue(q.add(new Integer(i)));
for (int i = (n & 1); i < n; i += 2)
assertTrue(q.add(new Integer(i)));
@@ -124,7 +124,7 @@
public void testSize() {
NavigableSet q = populatedSet(SIZE);
for (int i = 0; i < SIZE; ++i) {
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
q.pollFirst();
}
for (int i = 0; i < SIZE; ++i) {
@@ -203,8 +203,8 @@
public void testAddAll3() {
NavigableSet q = set0();
Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
- ints[i] = new Integer(i+SIZE);
+ for (int i = 0; i < SIZE - 1; ++i)
+ ints[i] = new Integer(i + SIZE);
try {
q.addAll(Arrays.asList(ints));
shouldThrow();
@@ -218,7 +218,7 @@
Integer[] empty = new Integer[0];
Integer[] ints = new Integer[SIZE];
for (int i = 0; i < SIZE; ++i)
- ints[i] = new Integer(SIZE-1- i);
+ ints[i] = new Integer(SIZE - 1 - i);
NavigableSet q = set0();
assertFalse(q.addAll(Arrays.asList(empty)));
assertTrue(q.addAll(Arrays.asList(ints)));
@@ -246,14 +246,14 @@
assertTrue(q.contains(i));
assertTrue(q.remove(i));
assertFalse(q.contains(i));
- assertTrue(q.contains(i-1));
+ assertTrue(q.contains(i - 1));
}
for (int i = 0; i < SIZE; i += 2) {
assertTrue(q.contains(i));
assertTrue(q.remove(i));
assertFalse(q.contains(i));
- assertFalse(q.remove(i+1));
- assertFalse(q.contains(i+1));
+ assertFalse(q.remove(i + 1));
+ assertFalse(q.contains(i + 1));
}
assertTrue(q.isEmpty());
}
@@ -312,7 +312,7 @@
assertTrue(changed);
assertTrue(q.containsAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
p.pollFirst();
}
}
@@ -325,7 +325,7 @@
NavigableSet q = populatedSet(SIZE);
NavigableSet p = populatedSet(i);
assertTrue(q.removeAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
for (int j = 0; j < i; ++j) {
Integer x = (Integer)(p.pollFirst());
assertFalse(q.contains(x));
@@ -620,7 +620,7 @@
public void testDescendingSize() {
NavigableSet q = populatedSet(SIZE);
for (int i = 0; i < SIZE; ++i) {
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
q.pollFirst();
}
for (int i = 0; i < SIZE; ++i) {
@@ -688,8 +688,8 @@
public void testDescendingAddAll3() {
NavigableSet q = dset0();
Integer[] ints = new Integer[SIZE];
- for (int i = 0; i < SIZE-1; ++i)
- ints[i] = new Integer(i+SIZE);
+ for (int i = 0; i < SIZE - 1; ++i)
+ ints[i] = new Integer(i + SIZE);
try {
q.addAll(Arrays.asList(ints));
shouldThrow();
@@ -703,7 +703,7 @@
Integer[] empty = new Integer[0];
Integer[] ints = new Integer[SIZE];
for (int i = 0; i < SIZE; ++i)
- ints[i] = new Integer(SIZE-1- i);
+ ints[i] = new Integer(SIZE - 1 - i);
NavigableSet q = dset0();
assertFalse(q.addAll(Arrays.asList(empty)));
assertTrue(q.addAll(Arrays.asList(ints)));
@@ -732,7 +732,7 @@
}
for (int i = 0; i < SIZE; i += 2) {
assertTrue(q.remove(new Integer(i)));
- assertFalse(q.remove(new Integer(i+1)));
+ assertFalse(q.remove(new Integer(i + 1)));
}
assertTrue(q.isEmpty());
}
@@ -791,7 +791,7 @@
assertTrue(changed);
assertTrue(q.containsAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
p.pollFirst();
}
}
@@ -804,7 +804,7 @@
NavigableSet q = populatedSet(SIZE);
NavigableSet p = populatedSet(i);
assertTrue(q.removeAll(p));
- assertEquals(SIZE-i, q.size());
+ assertEquals(SIZE - i, q.size());
for (int j = 0; j < i; ++j) {
Integer x = (Integer)(p.pollFirst());
assertFalse(q.contains(x));
diff --git a/luni/src/main/java/java/util/concurrent/AbstractExecutorService.java b/luni/src/main/java/java/util/concurrent/AbstractExecutorService.java
index f444b29..213baf2 100644
--- a/luni/src/main/java/java/util/concurrent/AbstractExecutorService.java
+++ b/luni/src/main/java/java/util/concurrent/AbstractExecutorService.java
@@ -6,7 +6,12 @@
package java.util.concurrent;
-import java.util.*;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
/**
* Provides default implementations of {@link ExecutorService}
@@ -23,7 +28,7 @@
* <p><b>Extension example</b>. Here is a sketch of a class
* that customizes {@link ThreadPoolExecutor} to use
* a {@code CustomTask} class instead of the default {@code FutureTask}:
- * <pre> {@code
+ * <pre> {@code
* public class CustomThreadPoolExecutor extends ThreadPoolExecutor {
*
* static class CustomTask<V> implements RunnableFuture<V> {...}
@@ -118,7 +123,7 @@
int ntasks = tasks.size();
if (ntasks == 0)
throw new IllegalArgumentException();
- ArrayList<Future<T>> futures = new ArrayList<Future<T>>(ntasks);
+ ArrayList<Future<T>> futures = new ArrayList<>(ntasks);
ExecutorCompletionService<T> ecs =
new ExecutorCompletionService<T>(this);
@@ -151,7 +156,7 @@
else if (active == 0)
break;
else if (timed) {
- f = ecs.poll(nanos, TimeUnit.NANOSECONDS);
+ f = ecs.poll(nanos, NANOSECONDS);
if (f == null)
throw new TimeoutException();
nanos = deadline - System.nanoTime();
@@ -176,8 +181,7 @@
throw ee;
} finally {
- for (int i = 0, size = futures.size(); i < size; i++)
- futures.get(i).cancel(true);
+ cancelAll(futures);
}
}
@@ -201,8 +205,7 @@
throws InterruptedException {
if (tasks == null)
throw new NullPointerException();
- ArrayList<Future<T>> futures = new ArrayList<Future<T>>(tasks.size());
- boolean done = false;
+ ArrayList<Future<T>> futures = new ArrayList<>(tasks.size());
try {
for (Callable<T> t : tasks) {
RunnableFuture<T> f = newTaskFor(t);
@@ -212,19 +215,15 @@
for (int i = 0, size = futures.size(); i < size; i++) {
Future<T> f = futures.get(i);
if (!f.isDone()) {
- try {
- f.get();
- } catch (CancellationException ignore) {
- } catch (ExecutionException ignore) {
- }
+ try { f.get(); }
+ catch (CancellationException ignore) {}
+ catch (ExecutionException ignore) {}
}
}
- done = true;
return futures;
- } finally {
- if (!done)
- for (int i = 0, size = futures.size(); i < size; i++)
- futures.get(i).cancel(true);
+ } catch (Throwable t) {
+ cancelAll(futures);
+ throw t;
}
}
@@ -233,47 +232,52 @@
throws InterruptedException {
if (tasks == null)
throw new NullPointerException();
- long nanos = unit.toNanos(timeout);
- ArrayList<Future<T>> futures = new ArrayList<Future<T>>(tasks.size());
- boolean done = false;
- try {
+ final long nanos = unit.toNanos(timeout);
+ final long deadline = System.nanoTime() + nanos;
+ ArrayList<Future<T>> futures = new ArrayList<>(tasks.size());
+ int j = 0;
+ timedOut: try {
for (Callable<T> t : tasks)
futures.add(newTaskFor(t));
- final long deadline = System.nanoTime() + nanos;
final int size = futures.size();
// Interleave time checks and calls to execute in case
// executor doesn't have any/much parallelism.
for (int i = 0; i < size; i++) {
+ if (((i == 0) ? nanos : deadline - System.nanoTime()) <= 0L)
+ break timedOut;
execute((Runnable)futures.get(i));
- nanos = deadline - System.nanoTime();
- if (nanos <= 0L)
- return futures;
}
- for (int i = 0; i < size; i++) {
- Future<T> f = futures.get(i);
+ for (; j < size; j++) {
+ Future<T> f = futures.get(j);
if (!f.isDone()) {
- if (nanos <= 0L)
- return futures;
- try {
- f.get(nanos, TimeUnit.NANOSECONDS);
- } catch (CancellationException ignore) {
- } catch (ExecutionException ignore) {
- } catch (TimeoutException toe) {
- return futures;
+ try { f.get(deadline - System.nanoTime(), NANOSECONDS); }
+ catch (CancellationException ignore) {}
+ catch (ExecutionException ignore) {}
+ catch (TimeoutException timedOut) {
+ break timedOut;
}
- nanos = deadline - System.nanoTime();
}
}
- done = true;
return futures;
- } finally {
- if (!done)
- for (int i = 0, size = futures.size(); i < size; i++)
- futures.get(i).cancel(true);
+ } catch (Throwable t) {
+ cancelAll(futures);
+ throw t;
}
+ // Timed out before all the tasks could be completed; cancel remaining
+ cancelAll(futures, j);
+ return futures;
}
+ private static <T> void cancelAll(ArrayList<Future<T>> futures) {
+ cancelAll(futures, 0);
+ }
+
+ /** Cancels all futures with index at least j. */
+ private static <T> void cancelAll(ArrayList<Future<T>> futures, int j) {
+ for (int size = futures.size(); j < size; j++)
+ futures.get(j).cancel(true);
+ }
}
diff --git a/luni/src/main/java/java/util/concurrent/ArrayBlockingQueue.java b/luni/src/main/java/java/util/concurrent/ArrayBlockingQueue.java
index 9dca1b3..3183c01 100644
--- a/luni/src/main/java/java/util/concurrent/ArrayBlockingQueue.java
+++ b/luni/src/main/java/java/util/concurrent/ArrayBlockingQueue.java
@@ -7,11 +7,14 @@
package java.util.concurrent;
import java.lang.ref.WeakReference;
-import java.util.Arrays;
import java.util.AbstractQueue;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.Spliterator;
+import java.util.Spliterators;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
@@ -92,7 +95,7 @@
* are known not to be any. Allows queue operations to update
* iterator state.
*/
- transient Itrs itrs = null;
+ transient Itrs itrs;
// Internal helper methods
@@ -237,10 +240,8 @@
try {
int i = 0;
try {
- for (E e : c) {
- if (e == null) throw new NullPointerException();
- items[i++] = e;
- }
+ for (E e : c)
+ items[i++] = Objects.requireNonNull(e);
} catch (ArrayIndexOutOfBoundsException ex) {
throw new IllegalArgumentException();
}
@@ -276,7 +277,7 @@
* @throws NullPointerException if the specified element is null
*/
public boolean offer(E e) {
- if (e == null) throw new NullPointerException();
+ Objects.requireNonNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
@@ -299,7 +300,7 @@
* @throws NullPointerException {@inheritDoc}
*/
public void put(E e) throws InterruptedException {
- if (e == null) throw new NullPointerException();
+ Objects.requireNonNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
@@ -322,13 +323,13 @@
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
- if (e == null) throw new NullPointerException();
+ Objects.requireNonNull(e);
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length) {
- if (nanos <= 0)
+ if (nanos <= 0L)
return false;
nanos = notFull.awaitNanos(nanos);
}
@@ -367,7 +368,7 @@
lock.lockInterruptibly();
try {
while (count == 0) {
- if (nanos <= 0)
+ if (nanos <= 0L)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
@@ -584,27 +585,7 @@
}
public String toString() {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- int k = count;
- if (k == 0)
- return "[]";
-
- final Object[] items = this.items;
- StringBuilder sb = new StringBuilder();
- sb.append('[');
- for (int i = takeIndex; ; ) {
- Object e = items[i];
- sb.append(e == this ? "(this Collection)" : e);
- if (--k == 0)
- return sb.append(']').toString();
- sb.append(',').append(' ');
- if (++i == items.length) i = 0;
- }
- } finally {
- lock.unlock();
- }
+ return Helpers.collectionToString(this);
}
/**
@@ -612,12 +593,12 @@
* The queue will be empty after this call returns.
*/
public void clear() {
- final Object[] items = this.items;
final ReentrantLock lock = this.lock;
lock.lock();
try {
int k = count;
if (k > 0) {
+ final Object[] items = this.items;
final int putIndex = this.putIndex;
int i = takeIndex;
do {
@@ -653,7 +634,7 @@
* @throws IllegalArgumentException {@inheritDoc}
*/
public int drainTo(Collection<? super E> c, int maxElements) {
- if (c == null) throw new NullPointerException();
+ Objects.requireNonNull(c);
if (c == this)
throw new IllegalArgumentException();
if (maxElements <= 0)
@@ -1333,4 +1314,27 @@
// }
}
+ /**
+ * Returns a {@link Spliterator} over the elements in this queue.
+ *
+ * <p>The returned spliterator is
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
+ *
+ * <p>The {@code Spliterator} reports {@link Spliterator#CONCURRENT},
+ * {@link Spliterator#ORDERED}, and {@link Spliterator#NONNULL}.
+ *
+ * @implNote
+ * The {@code Spliterator} implements {@code trySplit} to permit limited
+ * parallelism.
+ *
+ * @return a {@code Spliterator} over the elements in this queue
+ * @since 1.8
+ */
+ public Spliterator<E> spliterator() {
+ return Spliterators.spliterator
+ (this, (Spliterator.ORDERED |
+ Spliterator.NONNULL |
+ Spliterator.CONCURRENT));
+ }
+
}
diff --git a/luni/src/main/java/java/util/concurrent/BlockingDeque.java b/luni/src/main/java/java/util/concurrent/BlockingDeque.java
index b1437cc..b52f719 100644
--- a/luni/src/main/java/java/util/concurrent/BlockingDeque.java
+++ b/luni/src/main/java/java/util/concurrent/BlockingDeque.java
@@ -6,7 +6,13 @@
package java.util.concurrent;
-import java.util.*;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+// BEGIN android-note
+// fixed framework docs link to "Collection#optional"
+// END android-note
/**
* A {@link Deque} that additionally supports blocking operations that wait
@@ -22,7 +28,6 @@
* and the fourth blocks for only a given maximum time limit before giving
* up. These methods are summarized in the following table:
*
- * <p>
* <table BORDER CELLPADDING=3 CELLSPACING=1>
* <caption>Summary of BlockingDeque methods</caption>
* <tr>
@@ -98,7 +103,6 @@
* {@code BlockingQueue} interface are precisely equivalent to
* {@code BlockingDeque} methods as indicated in the following table:
*
- * <p>
* <table BORDER CELLPADDING=3 CELLSPACING=1>
* <caption>Comparison of BlockingQueue and BlockingDeque methods</caption>
* <tr>
@@ -169,7 +173,7 @@
*
* @since 1.6
* @author Doug Lea
- * @param <E> the type of elements held in this collection
+ * @param <E> the type of elements held in this deque
*/
public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {
/*
@@ -375,9 +379,9 @@
* @return {@code true} if an element was removed as a result of this call
* @throws ClassCastException if the class of the specified element
* is incompatible with this deque
- * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null
- * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean removeFirstOccurrence(Object o);
@@ -393,9 +397,9 @@
* @return {@code true} if an element was removed as a result of this call
* @throws ClassCastException if the class of the specified element
* is incompatible with this deque
- * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null
- * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean removeLastOccurrence(Object o);
@@ -570,9 +574,9 @@
* @return {@code true} if this deque changed as a result of the call
* @throws ClassCastException if the class of the specified element
* is incompatible with this deque
- * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null
- * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean remove(Object o);
@@ -585,18 +589,18 @@
* @return {@code true} if this deque contains the specified element
* @throws ClassCastException if the class of the specified element
* is incompatible with this deque
- * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null
- * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
- public boolean contains(Object o);
+ boolean contains(Object o);
/**
* Returns the number of elements in this deque.
*
* @return the number of elements in this deque
*/
- public int size();
+ int size();
/**
* Returns an iterator over the elements in this deque in proper sequence.
diff --git a/luni/src/main/java/java/util/concurrent/BlockingQueue.java b/luni/src/main/java/java/util/concurrent/BlockingQueue.java
index 33d83b7..2a56179 100644
--- a/luni/src/main/java/java/util/concurrent/BlockingQueue.java
+++ b/luni/src/main/java/java/util/concurrent/BlockingQueue.java
@@ -10,7 +10,8 @@
import java.util.Queue;
// BEGIN android-note
-// removed link to collections framework docs
+// removed link to collections framework docs from header
+// fixed framework docs link to "Collection#optional"
// END android-note
/**
@@ -28,7 +29,6 @@
* and the fourth blocks for only a given maximum time limit before giving
* up. These methods are summarized in the following table:
*
- * <p>
* <table BORDER CELLPADDING=3 CELLSPACING=1>
* <caption>Summary of BlockingQueue methods</caption>
* <tr>
@@ -103,7 +103,7 @@
* Usage example, based on a typical producer-consumer scenario.
* Note that a {@code BlockingQueue} can safely be used with multiple
* producers and multiple consumers.
- * <pre> {@code
+ * <pre> {@code
* class Producer implements Runnable {
* private final BlockingQueue queue;
* Producer(BlockingQueue q) { queue = q; }
@@ -147,7 +147,7 @@
*
* @since 1.5
* @author Doug Lea
- * @param <E> the type of elements held in this collection
+ * @param <E> the type of elements held in this queue
*/
public interface BlockingQueue<E> extends Queue<E> {
/**
@@ -275,9 +275,9 @@
* @return {@code true} if this queue changed as a result of the call
* @throws ClassCastException if the class of the specified element
* is incompatible with this queue
- * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null
- * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean remove(Object o);
@@ -290,11 +290,11 @@
* @return {@code true} if this queue contains the specified element
* @throws ClassCastException if the class of the specified element
* is incompatible with this queue
- * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null
- * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
- public boolean contains(Object o);
+ boolean contains(Object o);
/**
* Removes all available elements from this queue and adds them
diff --git a/luni/src/main/java/java/util/concurrent/Callable.java b/luni/src/main/java/java/util/concurrent/Callable.java
index a3b3883..a22ec50 100644
--- a/luni/src/main/java/java/util/concurrent/Callable.java
+++ b/luni/src/main/java/java/util/concurrent/Callable.java
@@ -25,6 +25,7 @@
* @author Doug Lea
* @param <V> the result type of method {@code call}
*/
+@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
diff --git a/luni/src/main/java/java/util/concurrent/CompletableFuture.java b/luni/src/main/java/java/util/concurrent/CompletableFuture.java
new file mode 100644
index 0000000..8383a68
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/CompletableFuture.java
@@ -0,0 +1,2770 @@
+/*
+ * 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/
+ */
+
+package java.util.concurrent;
+
+import java.util.concurrent.locks.LockSupport;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+/**
+ * A {@link Future} that may be explicitly completed (setting its
+ * value and status), and may be used as a {@link CompletionStage},
+ * supporting dependent functions and actions that trigger upon its
+ * completion.
+ *
+ * <p>When two or more threads attempt to
+ * {@link #complete complete},
+ * {@link #completeExceptionally completeExceptionally}, or
+ * {@link #cancel cancel}
+ * a CompletableFuture, only one of them succeeds.
+ *
+ * <p>In addition to these and related methods for directly
+ * manipulating status and results, CompletableFuture implements
+ * interface {@link CompletionStage} with the following policies: <ul>
+ *
+ * <li>Actions supplied for dependent completions of
+ * <em>non-async</em> methods may be performed by the thread that
+ * completes the current CompletableFuture, or by any other caller of
+ * a completion method.
+ *
+ * <li>All <em>async</em> methods without an explicit Executor
+ * argument are performed using the {@link ForkJoinPool#commonPool()}
+ * (unless it does not support a parallelism level of at least two, in
+ * which case, a new Thread is created to run each task).
+ * To simplify monitoring, debugging,
+ * and tracking, all generated asynchronous tasks are instances of the
+ * marker interface {@link AsynchronousCompletionTask}. Operations
+ * with time-delays can use adapter methods defined in this class, for
+ * example: {@code supplyAsync(supplier, delayedExecutor(timeout,
+ * timeUnit))}. To support methods with delays and timeouts, this
+ * class maintains at most one daemon thread for triggering and
+ * cancelling actions, not for running them.
+ *
+ * <li>All CompletionStage methods are implemented independently of
+ * other public methods, so the behavior of one method is not impacted
+ * by overrides of others in subclasses.
+ *
+ * </ul>
+ *
+ * <p>CompletableFuture also implements {@link Future} with the following
+ * policies: <ul>
+ *
+ * <li>Since (unlike {@link FutureTask}) this class has no direct
+ * control over the computation that causes it to be completed,
+ * cancellation is treated as just another form of exceptional
+ * completion. Method {@link #cancel cancel} has the same effect as
+ * {@code completeExceptionally(new CancellationException())}. Method
+ * {@link #isCompletedExceptionally} can be used to determine if a
+ * CompletableFuture completed in any exceptional fashion.
+ *
+ * <li>In case of exceptional completion with a CompletionException,
+ * methods {@link #get()} and {@link #get(long, TimeUnit)} throw an
+ * {@link ExecutionException} with the same cause as held in the
+ * corresponding CompletionException. To simplify usage in most
+ * contexts, this class also defines methods {@link #join()} and
+ * {@link #getNow} that instead throw the CompletionException directly
+ * in these cases.
+ * </ul>
+ *
+ * <p>Arguments used to pass a completion result (that is, for
+ * parameters of type {@code T}) for methods accepting them may be
+ * null, but passing a null value for any other parameter will result
+ * in a {@link NullPointerException} being thrown.
+ *
+ * @author Doug Lea
+ * @since 1.8
+ * @param <T> The result type returned by this future's {@code join}
+ * and {@code get} methods
+ */
+public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
+
+ /*
+ * Overview:
+ *
+ * A CompletableFuture may have dependent completion actions,
+ * collected in a linked stack. It atomically completes by CASing
+ * a result field, and then pops off and runs those actions. This
+ * applies across normal vs exceptional outcomes, sync vs async
+ * actions, binary triggers, and various forms of completions.
+ *
+ * Non-nullness of field result (set via CAS) indicates done. An
+ * AltResult is used to box null as a result, as well as to hold
+ * exceptions. Using a single field makes completion simple to
+ * detect and trigger. Encoding and decoding is straightforward
+ * but adds to the sprawl of trapping and associating exceptions
+ * with targets. Minor simplifications rely on (static) NIL (to
+ * box null results) being the only AltResult with a null
+ * exception field, so we don't usually need explicit comparisons.
+ * Even though some of the generics casts are unchecked (see
+ * SuppressWarnings annotations), they are placed to be
+ * appropriate even if checked.
+ *
+ * Dependent actions are represented by Completion objects linked
+ * as Treiber stacks headed by field "stack". There are Completion
+ * classes for each kind of action, grouped into single-input
+ * (UniCompletion), two-input (BiCompletion), projected
+ * (BiCompletions using either (not both) of two inputs), shared
+ * (CoCompletion, used by the second of two sources), zero-input
+ * source actions, and Signallers that unblock waiters. Class
+ * Completion extends ForkJoinTask to enable async execution
+ * (adding no space overhead because we exploit its "tag" methods
+ * to maintain claims). It is also declared as Runnable to allow
+ * usage with arbitrary executors.
+ *
+ * Support for each kind of CompletionStage relies on a separate
+ * class, along with two CompletableFuture methods:
+ *
+ * * A Completion class with name X corresponding to function,
+ * prefaced with "Uni", "Bi", or "Or". Each class contains
+ * fields for source(s), actions, and dependent. They are
+ * boringly similar, differing from others only with respect to
+ * underlying functional forms. We do this so that users don't
+ * encounter layers of adapters in common usages.
+ *
+ * * Boolean CompletableFuture method x(...) (for example
+ * uniApply) takes all of the arguments needed to check that an
+ * action is triggerable, and then either runs the action or
+ * arranges its async execution by executing its Completion
+ * argument, if present. The method returns true if known to be
+ * complete.
+ *
+ * * Completion method tryFire(int mode) invokes the associated x
+ * method with its held arguments, and on success cleans up.
+ * The mode argument allows tryFire to be called twice (SYNC,
+ * then ASYNC); the first to screen and trap exceptions while
+ * arranging to execute, and the second when called from a
+ * task. (A few classes are not used async so take slightly
+ * different forms.) The claim() callback suppresses function
+ * invocation if already claimed by another thread.
+ *
+ * * CompletableFuture method xStage(...) is called from a public
+ * stage method of CompletableFuture x. It screens user
+ * arguments and invokes and/or creates the stage object. If
+ * not async and x is already complete, the action is run
+ * immediately. Otherwise a Completion c is created, pushed to
+ * x's stack (unless done), and started or triggered via
+ * c.tryFire. This also covers races possible if x completes
+ * while pushing. Classes with two inputs (for example BiApply)
+ * deal with races across both while pushing actions. The
+ * second completion is a CoCompletion pointing to the first,
+ * shared so that at most one performs the action. The
+ * multiple-arity methods allOf and anyOf do this pairwise to
+ * form trees of completions.
+ *
+ * Note that the generic type parameters of methods vary according
+ * to whether "this" is a source, dependent, or completion.
+ *
+ * Method postComplete is called upon completion unless the target
+ * is guaranteed not to be observable (i.e., not yet returned or
+ * linked). Multiple threads can call postComplete, which
+ * atomically pops each dependent action, and tries to trigger it
+ * via method tryFire, in NESTED mode. Triggering can propagate
+ * recursively, so NESTED mode returns its completed dependent (if
+ * one exists) for further processing by its caller (see method
+ * postFire).
+ *
+ * Blocking methods get() and join() rely on Signaller Completions
+ * that wake up waiting threads. The mechanics are similar to
+ * Treiber stack wait-nodes used in FutureTask, Phaser, and
+ * SynchronousQueue. See their internal documentation for
+ * algorithmic details.
+ *
+ * Without precautions, CompletableFutures would be prone to
+ * garbage accumulation as chains of Completions build up, each
+ * pointing back to its sources. So we null out fields as soon as
+ * possible. The screening checks needed anyway harmlessly ignore
+ * null arguments that may have been obtained during races with
+ * threads nulling out fields. We also try to unlink fired
+ * Completions from stacks that might never be popped (see method
+ * postFire). Completion fields need not be declared as final or
+ * volatile because they are only visible to other threads upon
+ * safe publication.
+ */
+
+ volatile Object result; // Either the result or boxed AltResult
+ volatile Completion stack; // Top of Treiber stack of dependent actions
+
+ final boolean internalComplete(Object r) { // CAS from null to r
+ return U.compareAndSwapObject(this, RESULT, null, r);
+ }
+
+ final boolean casStack(Completion cmp, Completion val) {
+ return U.compareAndSwapObject(this, STACK, cmp, val);
+ }
+
+ /** Returns true if successfully pushed c onto stack. */
+ final boolean tryPushStack(Completion c) {
+ Completion h = stack;
+ lazySetNext(c, h);
+ return U.compareAndSwapObject(this, STACK, h, c);
+ }
+
+ /** Unconditionally pushes c onto stack, retrying if necessary. */
+ final void pushStack(Completion c) {
+ do {} while (!tryPushStack(c));
+ }
+
+ /* ------------- Encoding and decoding outcomes -------------- */
+
+ static final class AltResult { // See above
+ final Throwable ex; // null only for NIL
+ AltResult(Throwable x) { this.ex = x; }
+ }
+
+ /** The encoding of the null value. */
+ static final AltResult NIL = new AltResult(null);
+
+ /** Completes with the null value, unless already completed. */
+ final boolean completeNull() {
+ return U.compareAndSwapObject(this, RESULT, null,
+ NIL);
+ }
+
+ /** Returns the encoding of the given non-exceptional value. */
+ final Object encodeValue(T t) {
+ return (t == null) ? NIL : t;
+ }
+
+ /** Completes with a non-exceptional result, unless already completed. */
+ final boolean completeValue(T t) {
+ return U.compareAndSwapObject(this, RESULT, null,
+ (t == null) ? NIL : t);
+ }
+
+ /**
+ * Returns the encoding of the given (non-null) exception as a
+ * wrapped CompletionException unless it is one already.
+ */
+ static AltResult encodeThrowable(Throwable x) {
+ return new AltResult((x instanceof CompletionException) ? x :
+ new CompletionException(x));
+ }
+
+ /** Completes with an exceptional result, unless already completed. */
+ final boolean completeThrowable(Throwable x) {
+ return U.compareAndSwapObject(this, RESULT, null,
+ encodeThrowable(x));
+ }
+
+ /**
+ * Returns the encoding of the given (non-null) exception as a
+ * wrapped CompletionException unless it is one already. May
+ * return the given Object r (which must have been the result of a
+ * source future) if it is equivalent, i.e. if this is a simple
+ * relay of an existing CompletionException.
+ */
+ static Object encodeThrowable(Throwable x, Object r) {
+ if (!(x instanceof CompletionException))
+ x = new CompletionException(x);
+ else if (r instanceof AltResult && x == ((AltResult)r).ex)
+ return r;
+ return new AltResult(x);
+ }
+
+ /**
+ * Completes with the given (non-null) exceptional result as a
+ * wrapped CompletionException unless it is one already, unless
+ * already completed. May complete with the given Object r
+ * (which must have been the result of a source future) if it is
+ * equivalent, i.e. if this is a simple propagation of an
+ * existing CompletionException.
+ */
+ final boolean completeThrowable(Throwable x, Object r) {
+ return U.compareAndSwapObject(this, RESULT, null,
+ encodeThrowable(x, r));
+ }
+
+ /**
+ * Returns the encoding of the given arguments: if the exception
+ * is non-null, encodes as AltResult. Otherwise uses the given
+ * value, boxed as NIL if null.
+ */
+ Object encodeOutcome(T t, Throwable x) {
+ return (x == null) ? (t == null) ? NIL : t : encodeThrowable(x);
+ }
+
+ /**
+ * Returns the encoding of a copied outcome; if exceptional,
+ * rewraps as a CompletionException, else returns argument.
+ */
+ static Object encodeRelay(Object r) {
+ Throwable x;
+ return (((r instanceof AltResult) &&
+ (x = ((AltResult)r).ex) != null &&
+ !(x instanceof CompletionException)) ?
+ new AltResult(new CompletionException(x)) : r);
+ }
+
+ /**
+ * Completes with r or a copy of r, unless already completed.
+ * If exceptional, r is first coerced to a CompletionException.
+ */
+ final boolean completeRelay(Object r) {
+ return U.compareAndSwapObject(this, RESULT, null,
+ encodeRelay(r));
+ }
+
+ /**
+ * Reports result using Future.get conventions.
+ */
+ private static <T> T reportGet(Object r)
+ throws InterruptedException, ExecutionException {
+ if (r == null) // by convention below, null means interrupted
+ throw new InterruptedException();
+ if (r instanceof AltResult) {
+ Throwable x, cause;
+ if ((x = ((AltResult)r).ex) == null)
+ return null;
+ if (x instanceof CancellationException)
+ throw (CancellationException)x;
+ if ((x instanceof CompletionException) &&
+ (cause = x.getCause()) != null)
+ x = cause;
+ throw new ExecutionException(x);
+ }
+ @SuppressWarnings("unchecked") T t = (T) r;
+ return t;
+ }
+
+ /**
+ * Decodes outcome to return result or throw unchecked exception.
+ */
+ private static <T> T reportJoin(Object r) {
+ if (r instanceof AltResult) {
+ Throwable x;
+ if ((x = ((AltResult)r).ex) == null)
+ return null;
+ if (x instanceof CancellationException)
+ throw (CancellationException)x;
+ if (x instanceof CompletionException)
+ throw (CompletionException)x;
+ throw new CompletionException(x);
+ }
+ @SuppressWarnings("unchecked") T t = (T) r;
+ return t;
+ }
+
+ /* ------------- Async task preliminaries -------------- */
+
+ /**
+ * A marker interface identifying asynchronous tasks produced by
+ * {@code async} methods. This may be useful for monitoring,
+ * debugging, and tracking asynchronous activities.
+ *
+ * @since 1.8
+ */
+ public static interface AsynchronousCompletionTask {
+ }
+
+ private static final boolean USE_COMMON_POOL =
+ (ForkJoinPool.getCommonPoolParallelism() > 1);
+
+ /**
+ * Default executor -- ForkJoinPool.commonPool() unless it cannot
+ * support parallelism.
+ */
+ private static final Executor ASYNC_POOL = USE_COMMON_POOL ?
+ ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
+
+ /** Fallback if ForkJoinPool.commonPool() cannot support parallelism */
+ static final class ThreadPerTaskExecutor implements Executor {
+ public void execute(Runnable r) { new Thread(r).start(); }
+ }
+
+ /**
+ * Null-checks user executor argument, and translates uses of
+ * commonPool to ASYNC_POOL in case parallelism disabled.
+ */
+ static Executor screenExecutor(Executor e) {
+ if (!USE_COMMON_POOL && e == ForkJoinPool.commonPool())
+ return ASYNC_POOL;
+ if (e == null) throw new NullPointerException();
+ return e;
+ }
+
+ // Modes for Completion.tryFire. Signedness matters.
+ static final int SYNC = 0;
+ static final int ASYNC = 1;
+ static final int NESTED = -1;
+
+ /**
+ * Spins before blocking in waitingGet
+ */
+ static final int SPINS = (Runtime.getRuntime().availableProcessors() > 1 ?
+ 1 << 8 : 0);
+
+ /* ------------- Base Completion classes and operations -------------- */
+
+ @SuppressWarnings("serial")
+ abstract static class Completion extends ForkJoinTask<Void>
+ implements Runnable, AsynchronousCompletionTask {
+ volatile Completion next; // Treiber stack link
+
+ /**
+ * Performs completion action if triggered, returning a
+ * dependent that may need propagation, if one exists.
+ *
+ * @param mode SYNC, ASYNC, or NESTED
+ */
+ abstract CompletableFuture<?> tryFire(int mode);
+
+ /** Returns true if possibly still triggerable. Used by cleanStack. */
+ abstract boolean isLive();
+
+ public final void run() { tryFire(ASYNC); }
+ public final boolean exec() { tryFire(ASYNC); return false; }
+ public final Void getRawResult() { return null; }
+ public final void setRawResult(Void v) {}
+ }
+
+ static void lazySetNext(Completion c, Completion next) {
+ U.putOrderedObject(c, NEXT, next);
+ }
+
+ /**
+ * Pops and tries to trigger all reachable dependents. Call only
+ * when known to be done.
+ */
+ final void postComplete() {
+ /*
+ * On each step, variable f holds current dependents to pop
+ * and run. It is extended along only one path at a time,
+ * pushing others to avoid unbounded recursion.
+ */
+ CompletableFuture<?> f = this; Completion h;
+ while ((h = f.stack) != null ||
+ (f != this && (h = (f = this).stack) != null)) {
+ CompletableFuture<?> d; Completion t;
+ if (f.casStack(h, t = h.next)) {
+ if (t != null) {
+ if (f != this) {
+ pushStack(h);
+ continue;
+ }
+ h.next = null; // detach
+ }
+ f = (d = h.tryFire(NESTED)) == null ? this : d;
+ }
+ }
+ }
+
+ /** Traverses stack and unlinks dead Completions. */
+ final void cleanStack() {
+ for (Completion p = null, q = stack; q != null;) {
+ Completion s = q.next;
+ if (q.isLive()) {
+ p = q;
+ q = s;
+ }
+ else if (p == null) {
+ casStack(q, s);
+ q = stack;
+ }
+ else {
+ p.next = s;
+ if (p.isLive())
+ q = s;
+ else {
+ p = null; // restart
+ q = stack;
+ }
+ }
+ }
+ }
+
+ /* ------------- One-input Completions -------------- */
+
+ /** A Completion with a source, dependent, and executor. */
+ @SuppressWarnings("serial")
+ abstract static class UniCompletion<T,V> extends Completion {
+ Executor executor; // executor to use (null if none)
+ CompletableFuture<V> dep; // the dependent to complete
+ CompletableFuture<T> src; // source for action
+
+ UniCompletion(Executor executor, CompletableFuture<V> dep,
+ CompletableFuture<T> src) {
+ this.executor = executor; this.dep = dep; this.src = src;
+ }
+
+ /**
+ * Returns true if action can be run. Call only when known to
+ * be triggerable. Uses FJ tag bit to ensure that only one
+ * thread claims ownership. If async, starts as task -- a
+ * later call to tryFire will run action.
+ */
+ final boolean claim() {
+ Executor e = executor;
+ if (compareAndSetForkJoinTaskTag((short)0, (short)1)) {
+ if (e == null)
+ return true;
+ executor = null; // disable
+ e.execute(this);
+ }
+ return false;
+ }
+
+ final boolean isLive() { return dep != null; }
+ }
+
+ /** Pushes the given completion (if it exists) unless done. */
+ final void push(UniCompletion<?,?> c) {
+ if (c != null) {
+ while (result == null && !tryPushStack(c))
+ lazySetNext(c, null); // clear on failure
+ }
+ }
+
+ /**
+ * Post-processing by dependent after successful UniCompletion
+ * tryFire. Tries to clean stack of source a, and then either runs
+ * postComplete or returns this to caller, depending on mode.
+ */
+ final CompletableFuture<T> postFire(CompletableFuture<?> a, int mode) {
+ if (a != null && a.stack != null) {
+ if (mode < 0 || a.result == null)
+ a.cleanStack();
+ else
+ a.postComplete();
+ }
+ if (result != null && stack != null) {
+ if (mode < 0)
+ return this;
+ else
+ postComplete();
+ }
+ return null;
+ }
+
+ @SuppressWarnings("serial")
+ static final class UniApply<T,V> extends UniCompletion<T,V> {
+ Function<? super T,? extends V> fn;
+ UniApply(Executor executor, CompletableFuture<V> dep,
+ CompletableFuture<T> src,
+ Function<? super T,? extends V> fn) {
+ super(executor, dep, src); this.fn = fn;
+ }
+ final CompletableFuture<V> tryFire(int mode) {
+ CompletableFuture<V> d; CompletableFuture<T> a;
+ if ((d = dep) == null ||
+ !d.uniApply(a = src, fn, mode > 0 ? null : this))
+ return null;
+ dep = null; src = null; fn = null;
+ return d.postFire(a, mode);
+ }
+ }
+
+ final <S> boolean uniApply(CompletableFuture<S> a,
+ Function<? super S,? extends T> f,
+ UniApply<S,T> c) {
+ Object r; Throwable x;
+ if (a == null || (r = a.result) == null || f == null)
+ return false;
+ tryComplete: if (result == null) {
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ completeThrowable(x, r);
+ break tryComplete;
+ }
+ r = null;
+ }
+ try {
+ if (c != null && !c.claim())
+ return false;
+ @SuppressWarnings("unchecked") S s = (S) r;
+ completeValue(f.apply(s));
+ } catch (Throwable ex) {
+ completeThrowable(ex);
+ }
+ }
+ return true;
+ }
+
+ private <V> CompletableFuture<V> uniApplyStage(
+ Executor e, Function<? super T,? extends V> f) {
+ if (f == null) throw new NullPointerException();
+ CompletableFuture<V> d = newIncompleteFuture();
+ if (e != null || !d.uniApply(this, f, null)) {
+ UniApply<T,V> c = new UniApply<T,V>(e, d, this, f);
+ push(c);
+ c.tryFire(SYNC);
+ }
+ return d;
+ }
+
+ @SuppressWarnings("serial")
+ static final class UniAccept<T> extends UniCompletion<T,Void> {
+ Consumer<? super T> fn;
+ UniAccept(Executor executor, CompletableFuture<Void> dep,
+ CompletableFuture<T> src, Consumer<? super T> fn) {
+ super(executor, dep, src); this.fn = fn;
+ }
+ final CompletableFuture<Void> tryFire(int mode) {
+ CompletableFuture<Void> d; CompletableFuture<T> a;
+ if ((d = dep) == null ||
+ !d.uniAccept(a = src, fn, mode > 0 ? null : this))
+ return null;
+ dep = null; src = null; fn = null;
+ return d.postFire(a, mode);
+ }
+ }
+
+ final <S> boolean uniAccept(CompletableFuture<S> a,
+ Consumer<? super S> f, UniAccept<S> c) {
+ Object r; Throwable x;
+ if (a == null || (r = a.result) == null || f == null)
+ return false;
+ tryComplete: if (result == null) {
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ completeThrowable(x, r);
+ break tryComplete;
+ }
+ r = null;
+ }
+ try {
+ if (c != null && !c.claim())
+ return false;
+ @SuppressWarnings("unchecked") S s = (S) r;
+ f.accept(s);
+ completeNull();
+ } catch (Throwable ex) {
+ completeThrowable(ex);
+ }
+ }
+ return true;
+ }
+
+ private CompletableFuture<Void> uniAcceptStage(Executor e,
+ Consumer<? super T> f) {
+ if (f == null) throw new NullPointerException();
+ CompletableFuture<Void> d = newIncompleteFuture();
+ if (e != null || !d.uniAccept(this, f, null)) {
+ UniAccept<T> c = new UniAccept<T>(e, d, this, f);
+ push(c);
+ c.tryFire(SYNC);
+ }
+ return d;
+ }
+
+ @SuppressWarnings("serial")
+ static final class UniRun<T> extends UniCompletion<T,Void> {
+ Runnable fn;
+ UniRun(Executor executor, CompletableFuture<Void> dep,
+ CompletableFuture<T> src, Runnable fn) {
+ super(executor, dep, src); this.fn = fn;
+ }
+ final CompletableFuture<Void> tryFire(int mode) {
+ CompletableFuture<Void> d; CompletableFuture<T> a;
+ if ((d = dep) == null ||
+ !d.uniRun(a = src, fn, mode > 0 ? null : this))
+ return null;
+ dep = null; src = null; fn = null;
+ return d.postFire(a, mode);
+ }
+ }
+
+ final boolean uniRun(CompletableFuture<?> a, Runnable f, UniRun<?> c) {
+ Object r; Throwable x;
+ if (a == null || (r = a.result) == null || f == null)
+ return false;
+ if (result == null) {
+ if (r instanceof AltResult && (x = ((AltResult)r).ex) != null)
+ completeThrowable(x, r);
+ else
+ try {
+ if (c != null && !c.claim())
+ return false;
+ f.run();
+ completeNull();
+ } catch (Throwable ex) {
+ completeThrowable(ex);
+ }
+ }
+ return true;
+ }
+
+ private CompletableFuture<Void> uniRunStage(Executor e, Runnable f) {
+ if (f == null) throw new NullPointerException();
+ CompletableFuture<Void> d = newIncompleteFuture();
+ if (e != null || !d.uniRun(this, f, null)) {
+ UniRun<T> c = new UniRun<T>(e, d, this, f);
+ push(c);
+ c.tryFire(SYNC);
+ }
+ return d;
+ }
+
+ @SuppressWarnings("serial")
+ static final class UniWhenComplete<T> extends UniCompletion<T,T> {
+ BiConsumer<? super T, ? super Throwable> fn;
+ UniWhenComplete(Executor executor, CompletableFuture<T> dep,
+ CompletableFuture<T> src,
+ BiConsumer<? super T, ? super Throwable> fn) {
+ super(executor, dep, src); this.fn = fn;
+ }
+ final CompletableFuture<T> tryFire(int mode) {
+ CompletableFuture<T> d; CompletableFuture<T> a;
+ if ((d = dep) == null ||
+ !d.uniWhenComplete(a = src, fn, mode > 0 ? null : this))
+ return null;
+ dep = null; src = null; fn = null;
+ return d.postFire(a, mode);
+ }
+ }
+
+ final boolean uniWhenComplete(CompletableFuture<T> a,
+ BiConsumer<? super T,? super Throwable> f,
+ UniWhenComplete<T> c) {
+ Object r; T t; Throwable x = null;
+ if (a == null || (r = a.result) == null || f == null)
+ return false;
+ if (result == null) {
+ try {
+ if (c != null && !c.claim())
+ return false;
+ if (r instanceof AltResult) {
+ x = ((AltResult)r).ex;
+ t = null;
+ } else {
+ @SuppressWarnings("unchecked") T tr = (T) r;
+ t = tr;
+ }
+ f.accept(t, x);
+ if (x == null) {
+ internalComplete(r);
+ return true;
+ }
+ } catch (Throwable ex) {
+ if (x == null)
+ x = ex;
+ else if (x != ex)
+ x.addSuppressed(ex);
+ }
+ completeThrowable(x, r);
+ }
+ return true;
+ }
+
+ private CompletableFuture<T> uniWhenCompleteStage(
+ Executor e, BiConsumer<? super T, ? super Throwable> f) {
+ if (f == null) throw new NullPointerException();
+ CompletableFuture<T> d = newIncompleteFuture();
+ if (e != null || !d.uniWhenComplete(this, f, null)) {
+ UniWhenComplete<T> c = new UniWhenComplete<T>(e, d, this, f);
+ push(c);
+ c.tryFire(SYNC);
+ }
+ return d;
+ }
+
+ @SuppressWarnings("serial")
+ static final class UniHandle<T,V> extends UniCompletion<T,V> {
+ BiFunction<? super T, Throwable, ? extends V> fn;
+ UniHandle(Executor executor, CompletableFuture<V> dep,
+ CompletableFuture<T> src,
+ BiFunction<? super T, Throwable, ? extends V> fn) {
+ super(executor, dep, src); this.fn = fn;
+ }
+ final CompletableFuture<V> tryFire(int mode) {
+ CompletableFuture<V> d; CompletableFuture<T> a;
+ if ((d = dep) == null ||
+ !d.uniHandle(a = src, fn, mode > 0 ? null : this))
+ return null;
+ dep = null; src = null; fn = null;
+ return d.postFire(a, mode);
+ }
+ }
+
+ final <S> boolean uniHandle(CompletableFuture<S> a,
+ BiFunction<? super S, Throwable, ? extends T> f,
+ UniHandle<S,T> c) {
+ Object r; S s; Throwable x;
+ if (a == null || (r = a.result) == null || f == null)
+ return false;
+ if (result == null) {
+ try {
+ if (c != null && !c.claim())
+ return false;
+ if (r instanceof AltResult) {
+ x = ((AltResult)r).ex;
+ s = null;
+ } else {
+ x = null;
+ @SuppressWarnings("unchecked") S ss = (S) r;
+ s = ss;
+ }
+ completeValue(f.apply(s, x));
+ } catch (Throwable ex) {
+ completeThrowable(ex);
+ }
+ }
+ return true;
+ }
+
+ private <V> CompletableFuture<V> uniHandleStage(
+ Executor e, BiFunction<? super T, Throwable, ? extends V> f) {
+ if (f == null) throw new NullPointerException();
+ CompletableFuture<V> d = newIncompleteFuture();
+ if (e != null || !d.uniHandle(this, f, null)) {
+ UniHandle<T,V> c = new UniHandle<T,V>(e, d, this, f);
+ push(c);
+ c.tryFire(SYNC);
+ }
+ return d;
+ }
+
+ @SuppressWarnings("serial")
+ static final class UniExceptionally<T> extends UniCompletion<T,T> {
+ Function<? super Throwable, ? extends T> fn;
+ UniExceptionally(CompletableFuture<T> dep, CompletableFuture<T> src,
+ Function<? super Throwable, ? extends T> fn) {
+ super(null, dep, src); this.fn = fn;
+ }
+ final CompletableFuture<T> tryFire(int mode) { // never ASYNC
+ // assert mode != ASYNC;
+ CompletableFuture<T> d; CompletableFuture<T> a;
+ if ((d = dep) == null || !d.uniExceptionally(a = src, fn, this))
+ return null;
+ dep = null; src = null; fn = null;
+ return d.postFire(a, mode);
+ }
+ }
+
+ final boolean uniExceptionally(CompletableFuture<T> a,
+ Function<? super Throwable, ? extends T> f,
+ UniExceptionally<T> c) {
+ Object r; Throwable x;
+ if (a == null || (r = a.result) == null || f == null)
+ return false;
+ if (result == null) {
+ try {
+ if (r instanceof AltResult && (x = ((AltResult)r).ex) != null) {
+ if (c != null && !c.claim())
+ return false;
+ completeValue(f.apply(x));
+ } else
+ internalComplete(r);
+ } catch (Throwable ex) {
+ completeThrowable(ex);
+ }
+ }
+ return true;
+ }
+
+ private CompletableFuture<T> uniExceptionallyStage(
+ Function<Throwable, ? extends T> f) {
+ if (f == null) throw new NullPointerException();
+ CompletableFuture<T> d = newIncompleteFuture();
+ if (!d.uniExceptionally(this, f, null)) {
+ UniExceptionally<T> c = new UniExceptionally<T>(d, this, f);
+ push(c);
+ c.tryFire(SYNC);
+ }
+ return d;
+ }
+
+ @SuppressWarnings("serial")
+ static final class UniRelay<T> extends UniCompletion<T,T> { // for Compose
+ UniRelay(CompletableFuture<T> dep, CompletableFuture<T> src) {
+ super(null, dep, src);
+ }
+ final CompletableFuture<T> tryFire(int mode) {
+ CompletableFuture<T> d; CompletableFuture<T> a;
+ if ((d = dep) == null || !d.uniRelay(a = src))
+ return null;
+ src = null; dep = null;
+ return d.postFire(a, mode);
+ }
+ }
+
+ final boolean uniRelay(CompletableFuture<T> a) {
+ Object r;
+ if (a == null || (r = a.result) == null)
+ return false;
+ if (result == null) // no need to claim
+ completeRelay(r);
+ return true;
+ }
+
+ private CompletableFuture<T> uniCopyStage() {
+ Object r;
+ CompletableFuture<T> d = newIncompleteFuture();
+ if ((r = result) != null)
+ d.completeRelay(r);
+ else {
+ UniRelay<T> c = new UniRelay<T>(d, this);
+ push(c);
+ c.tryFire(SYNC);
+ }
+ return d;
+ }
+
+ private MinimalStage<T> uniAsMinimalStage() {
+ Object r;
+ if ((r = result) != null)
+ return new MinimalStage<T>(encodeRelay(r));
+ MinimalStage<T> d = new MinimalStage<T>();
+ UniRelay<T> c = new UniRelay<T>(d, this);
+ push(c);
+ c.tryFire(SYNC);
+ return d;
+ }
+
+ @SuppressWarnings("serial")
+ static final class UniCompose<T,V> extends UniCompletion<T,V> {
+ Function<? super T, ? extends CompletionStage<V>> fn;
+ UniCompose(Executor executor, CompletableFuture<V> dep,
+ CompletableFuture<T> src,
+ Function<? super T, ? extends CompletionStage<V>> fn) {
+ super(executor, dep, src); this.fn = fn;
+ }
+ final CompletableFuture<V> tryFire(int mode) {
+ CompletableFuture<V> d; CompletableFuture<T> a;
+ if ((d = dep) == null ||
+ !d.uniCompose(a = src, fn, mode > 0 ? null : this))
+ return null;
+ dep = null; src = null; fn = null;
+ return d.postFire(a, mode);
+ }
+ }
+
+ final <S> boolean uniCompose(
+ CompletableFuture<S> a,
+ Function<? super S, ? extends CompletionStage<T>> f,
+ UniCompose<S,T> c) {
+ Object r; Throwable x;
+ if (a == null || (r = a.result) == null || f == null)
+ return false;
+ tryComplete: if (result == null) {
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ completeThrowable(x, r);
+ break tryComplete;
+ }
+ r = null;
+ }
+ try {
+ if (c != null && !c.claim())
+ return false;
+ @SuppressWarnings("unchecked") S s = (S) r;
+ CompletableFuture<T> g = f.apply(s).toCompletableFuture();
+ if (g.result == null || !uniRelay(g)) {
+ UniRelay<T> copy = new UniRelay<T>(this, g);
+ g.push(copy);
+ copy.tryFire(SYNC);
+ if (result == null)
+ return false;
+ }
+ } catch (Throwable ex) {
+ completeThrowable(ex);
+ }
+ }
+ return true;
+ }
+
+ private <V> CompletableFuture<V> uniComposeStage(
+ Executor e, Function<? super T, ? extends CompletionStage<V>> f) {
+ if (f == null) throw new NullPointerException();
+ Object r, s; Throwable x;
+ CompletableFuture<V> d = newIncompleteFuture();
+ if (e == null && (r = result) != null) {
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ d.result = encodeThrowable(x, r);
+ return d;
+ }
+ r = null;
+ }
+ try {
+ @SuppressWarnings("unchecked") T t = (T) r;
+ CompletableFuture<V> g = f.apply(t).toCompletableFuture();
+ if ((s = g.result) != null)
+ d.completeRelay(s);
+ else {
+ UniRelay<V> c = new UniRelay<V>(d, g);
+ g.push(c);
+ c.tryFire(SYNC);
+ }
+ return d;
+ } catch (Throwable ex) {
+ d.result = encodeThrowable(ex);
+ return d;
+ }
+ }
+ UniCompose<T,V> c = new UniCompose<T,V>(e, d, this, f);
+ push(c);
+ c.tryFire(SYNC);
+ return d;
+ }
+
+ /* ------------- Two-input Completions -------------- */
+
+ /** A Completion for an action with two sources */
+ @SuppressWarnings("serial")
+ abstract static class BiCompletion<T,U,V> extends UniCompletion<T,V> {
+ CompletableFuture<U> snd; // second source for action
+ BiCompletion(Executor executor, CompletableFuture<V> dep,
+ CompletableFuture<T> src, CompletableFuture<U> snd) {
+ super(executor, dep, src); this.snd = snd;
+ }
+ }
+
+ /** A Completion delegating to a BiCompletion */
+ @SuppressWarnings("serial")
+ static final class CoCompletion extends Completion {
+ BiCompletion<?,?,?> base;
+ CoCompletion(BiCompletion<?,?,?> base) { this.base = base; }
+ final CompletableFuture<?> tryFire(int mode) {
+ BiCompletion<?,?,?> c; CompletableFuture<?> d;
+ if ((c = base) == null || (d = c.tryFire(mode)) == null)
+ return null;
+ base = null; // detach
+ return d;
+ }
+ final boolean isLive() {
+ BiCompletion<?,?,?> c;
+ return (c = base) != null && c.dep != null;
+ }
+ }
+
+ /** Pushes completion to this and b unless both done. */
+ final void bipush(CompletableFuture<?> b, BiCompletion<?,?,?> c) {
+ if (c != null) {
+ Object r;
+ while ((r = result) == null && !tryPushStack(c))
+ lazySetNext(c, null); // clear on failure
+ if (b != null && b != this && b.result == null) {
+ Completion q = (r != null) ? c : new CoCompletion(c);
+ while (b.result == null && !b.tryPushStack(q))
+ lazySetNext(q, null); // clear on failure
+ }
+ }
+ }
+
+ /** Post-processing after successful BiCompletion tryFire. */
+ final CompletableFuture<T> postFire(CompletableFuture<?> a,
+ CompletableFuture<?> b, int mode) {
+ if (b != null && b.stack != null) { // clean second source
+ if (mode < 0 || b.result == null)
+ b.cleanStack();
+ else
+ b.postComplete();
+ }
+ return postFire(a, mode);
+ }
+
+ @SuppressWarnings("serial")
+ static final class BiApply<T,U,V> extends BiCompletion<T,U,V> {
+ BiFunction<? super T,? super U,? extends V> fn;
+ BiApply(Executor executor, CompletableFuture<V> dep,
+ CompletableFuture<T> src, CompletableFuture<U> snd,
+ BiFunction<? super T,? super U,? extends V> fn) {
+ super(executor, dep, src, snd); this.fn = fn;
+ }
+ final CompletableFuture<V> tryFire(int mode) {
+ CompletableFuture<V> d;
+ CompletableFuture<T> a;
+ CompletableFuture<U> b;
+ if ((d = dep) == null ||
+ !d.biApply(a = src, b = snd, fn, mode > 0 ? null : this))
+ return null;
+ dep = null; src = null; snd = null; fn = null;
+ return d.postFire(a, b, mode);
+ }
+ }
+
+ final <R,S> boolean biApply(CompletableFuture<R> a,
+ CompletableFuture<S> b,
+ BiFunction<? super R,? super S,? extends T> f,
+ BiApply<R,S,T> c) {
+ Object r, s; Throwable x;
+ if (a == null || (r = a.result) == null ||
+ b == null || (s = b.result) == null || f == null)
+ return false;
+ tryComplete: if (result == null) {
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ completeThrowable(x, r);
+ break tryComplete;
+ }
+ r = null;
+ }
+ if (s instanceof AltResult) {
+ if ((x = ((AltResult)s).ex) != null) {
+ completeThrowable(x, s);
+ break tryComplete;
+ }
+ s = null;
+ }
+ try {
+ if (c != null && !c.claim())
+ return false;
+ @SuppressWarnings("unchecked") R rr = (R) r;
+ @SuppressWarnings("unchecked") S ss = (S) s;
+ completeValue(f.apply(rr, ss));
+ } catch (Throwable ex) {
+ completeThrowable(ex);
+ }
+ }
+ return true;
+ }
+
+ private <U,V> CompletableFuture<V> biApplyStage(
+ Executor e, CompletionStage<U> o,
+ BiFunction<? super T,? super U,? extends V> f) {
+ CompletableFuture<U> b;
+ if (f == null || (b = o.toCompletableFuture()) == null)
+ throw new NullPointerException();
+ CompletableFuture<V> d = newIncompleteFuture();
+ if (e != null || !d.biApply(this, b, f, null)) {
+ BiApply<T,U,V> c = new BiApply<T,U,V>(e, d, this, b, f);
+ bipush(b, c);
+ c.tryFire(SYNC);
+ }
+ return d;
+ }
+
+ @SuppressWarnings("serial")
+ static final class BiAccept<T,U> extends BiCompletion<T,U,Void> {
+ BiConsumer<? super T,? super U> fn;
+ BiAccept(Executor executor, CompletableFuture<Void> dep,
+ CompletableFuture<T> src, CompletableFuture<U> snd,
+ BiConsumer<? super T,? super U> fn) {
+ super(executor, dep, src, snd); this.fn = fn;
+ }
+ final CompletableFuture<Void> tryFire(int mode) {
+ CompletableFuture<Void> d;
+ CompletableFuture<T> a;
+ CompletableFuture<U> b;
+ if ((d = dep) == null ||
+ !d.biAccept(a = src, b = snd, fn, mode > 0 ? null : this))
+ return null;
+ dep = null; src = null; snd = null; fn = null;
+ return d.postFire(a, b, mode);
+ }
+ }
+
+ final <R,S> boolean biAccept(CompletableFuture<R> a,
+ CompletableFuture<S> b,
+ BiConsumer<? super R,? super S> f,
+ BiAccept<R,S> c) {
+ Object r, s; Throwable x;
+ if (a == null || (r = a.result) == null ||
+ b == null || (s = b.result) == null || f == null)
+ return false;
+ tryComplete: if (result == null) {
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ completeThrowable(x, r);
+ break tryComplete;
+ }
+ r = null;
+ }
+ if (s instanceof AltResult) {
+ if ((x = ((AltResult)s).ex) != null) {
+ completeThrowable(x, s);
+ break tryComplete;
+ }
+ s = null;
+ }
+ try {
+ if (c != null && !c.claim())
+ return false;
+ @SuppressWarnings("unchecked") R rr = (R) r;
+ @SuppressWarnings("unchecked") S ss = (S) s;
+ f.accept(rr, ss);
+ completeNull();
+ } catch (Throwable ex) {
+ completeThrowable(ex);
+ }
+ }
+ return true;
+ }
+
+ private <U> CompletableFuture<Void> biAcceptStage(
+ Executor e, CompletionStage<U> o,
+ BiConsumer<? super T,? super U> f) {
+ CompletableFuture<U> b;
+ if (f == null || (b = o.toCompletableFuture()) == null)
+ throw new NullPointerException();
+ CompletableFuture<Void> d = newIncompleteFuture();
+ if (e != null || !d.biAccept(this, b, f, null)) {
+ BiAccept<T,U> c = new BiAccept<T,U>(e, d, this, b, f);
+ bipush(b, c);
+ c.tryFire(SYNC);
+ }
+ return d;
+ }
+
+ @SuppressWarnings("serial")
+ static final class BiRun<T,U> extends BiCompletion<T,U,Void> {
+ Runnable fn;
+ BiRun(Executor executor, CompletableFuture<Void> dep,
+ CompletableFuture<T> src,
+ CompletableFuture<U> snd,
+ Runnable fn) {
+ super(executor, dep, src, snd); this.fn = fn;
+ }
+ final CompletableFuture<Void> tryFire(int mode) {
+ CompletableFuture<Void> d;
+ CompletableFuture<T> a;
+ CompletableFuture<U> b;
+ if ((d = dep) == null ||
+ !d.biRun(a = src, b = snd, fn, mode > 0 ? null : this))
+ return null;
+ dep = null; src = null; snd = null; fn = null;
+ return d.postFire(a, b, mode);
+ }
+ }
+
+ final boolean biRun(CompletableFuture<?> a, CompletableFuture<?> b,
+ Runnable f, BiRun<?,?> c) {
+ Object r, s; Throwable x;
+ if (a == null || (r = a.result) == null ||
+ b == null || (s = b.result) == null || f == null)
+ return false;
+ if (result == null) {
+ if (r instanceof AltResult && (x = ((AltResult)r).ex) != null)
+ completeThrowable(x, r);
+ else if (s instanceof AltResult && (x = ((AltResult)s).ex) != null)
+ completeThrowable(x, s);
+ else
+ try {
+ if (c != null && !c.claim())
+ return false;
+ f.run();
+ completeNull();
+ } catch (Throwable ex) {
+ completeThrowable(ex);
+ }
+ }
+ return true;
+ }
+
+ private CompletableFuture<Void> biRunStage(Executor e, CompletionStage<?> o,
+ Runnable f) {
+ CompletableFuture<?> b;
+ if (f == null || (b = o.toCompletableFuture()) == null)
+ throw new NullPointerException();
+ CompletableFuture<Void> d = newIncompleteFuture();
+ if (e != null || !d.biRun(this, b, f, null)) {
+ BiRun<T,?> c = new BiRun<>(e, d, this, b, f);
+ bipush(b, c);
+ c.tryFire(SYNC);
+ }
+ return d;
+ }
+
+ @SuppressWarnings("serial")
+ static final class BiRelay<T,U> extends BiCompletion<T,U,Void> { // for And
+ BiRelay(CompletableFuture<Void> dep,
+ CompletableFuture<T> src,
+ CompletableFuture<U> snd) {
+ super(null, dep, src, snd);
+ }
+ final CompletableFuture<Void> tryFire(int mode) {
+ CompletableFuture<Void> d;
+ CompletableFuture<T> a;
+ CompletableFuture<U> b;
+ if ((d = dep) == null || !d.biRelay(a = src, b = snd))
+ return null;
+ src = null; snd = null; dep = null;
+ return d.postFire(a, b, mode);
+ }
+ }
+
+ boolean biRelay(CompletableFuture<?> a, CompletableFuture<?> b) {
+ Object r, s; Throwable x;
+ if (a == null || (r = a.result) == null ||
+ b == null || (s = b.result) == null)
+ return false;
+ if (result == null) {
+ if (r instanceof AltResult && (x = ((AltResult)r).ex) != null)
+ completeThrowable(x, r);
+ else if (s instanceof AltResult && (x = ((AltResult)s).ex) != null)
+ completeThrowable(x, s);
+ else
+ completeNull();
+ }
+ return true;
+ }
+
+ /** Recursively constructs a tree of completions. */
+ static CompletableFuture<Void> andTree(CompletableFuture<?>[] cfs,
+ int lo, int hi) {
+ CompletableFuture<Void> d = new CompletableFuture<Void>();
+ if (lo > hi) // empty
+ d.result = NIL;
+ else {
+ CompletableFuture<?> a, b;
+ int mid = (lo + hi) >>> 1;
+ if ((a = (lo == mid ? cfs[lo] :
+ andTree(cfs, lo, mid))) == null ||
+ (b = (lo == hi ? a : (hi == mid+1) ? cfs[hi] :
+ andTree(cfs, mid+1, hi))) == null)
+ throw new NullPointerException();
+ if (!d.biRelay(a, b)) {
+ BiRelay<?,?> c = new BiRelay<>(d, a, b);
+ a.bipush(b, c);
+ c.tryFire(SYNC);
+ }
+ }
+ return d;
+ }
+
+ /* ------------- Projected (Ored) BiCompletions -------------- */
+
+ /** Pushes completion to this and b unless either done. */
+ final void orpush(CompletableFuture<?> b, BiCompletion<?,?,?> c) {
+ if (c != null) {
+ while ((b == null || b.result == null) && result == null) {
+ if (tryPushStack(c)) {
+ if (b != null && b != this && b.result == null) {
+ Completion q = new CoCompletion(c);
+ while (result == null && b.result == null &&
+ !b.tryPushStack(q))
+ lazySetNext(q, null); // clear on failure
+ }
+ break;
+ }
+ lazySetNext(c, null); // clear on failure
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class OrApply<T,U extends T,V> extends BiCompletion<T,U,V> {
+ Function<? super T,? extends V> fn;
+ OrApply(Executor executor, CompletableFuture<V> dep,
+ CompletableFuture<T> src,
+ CompletableFuture<U> snd,
+ Function<? super T,? extends V> fn) {
+ super(executor, dep, src, snd); this.fn = fn;
+ }
+ final CompletableFuture<V> tryFire(int mode) {
+ CompletableFuture<V> d;
+ CompletableFuture<T> a;
+ CompletableFuture<U> b;
+ if ((d = dep) == null ||
+ !d.orApply(a = src, b = snd, fn, mode > 0 ? null : this))
+ return null;
+ dep = null; src = null; snd = null; fn = null;
+ return d.postFire(a, b, mode);
+ }
+ }
+
+ final <R,S extends R> boolean orApply(CompletableFuture<R> a,
+ CompletableFuture<S> b,
+ Function<? super R, ? extends T> f,
+ OrApply<R,S,T> c) {
+ Object r; Throwable x;
+ if (a == null || b == null ||
+ ((r = a.result) == null && (r = b.result) == null) || f == null)
+ return false;
+ tryComplete: if (result == null) {
+ try {
+ if (c != null && !c.claim())
+ return false;
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ completeThrowable(x, r);
+ break tryComplete;
+ }
+ r = null;
+ }
+ @SuppressWarnings("unchecked") R rr = (R) r;
+ completeValue(f.apply(rr));
+ } catch (Throwable ex) {
+ completeThrowable(ex);
+ }
+ }
+ return true;
+ }
+
+ private <U extends T,V> CompletableFuture<V> orApplyStage(
+ Executor e, CompletionStage<U> o,
+ Function<? super T, ? extends V> f) {
+ CompletableFuture<U> b;
+ if (f == null || (b = o.toCompletableFuture()) == null)
+ throw new NullPointerException();
+ CompletableFuture<V> d = newIncompleteFuture();
+ if (e != null || !d.orApply(this, b, f, null)) {
+ OrApply<T,U,V> c = new OrApply<T,U,V>(e, d, this, b, f);
+ orpush(b, c);
+ c.tryFire(SYNC);
+ }
+ return d;
+ }
+
+ @SuppressWarnings("serial")
+ static final class OrAccept<T,U extends T> extends BiCompletion<T,U,Void> {
+ Consumer<? super T> fn;
+ OrAccept(Executor executor, CompletableFuture<Void> dep,
+ CompletableFuture<T> src,
+ CompletableFuture<U> snd,
+ Consumer<? super T> fn) {
+ super(executor, dep, src, snd); this.fn = fn;
+ }
+ final CompletableFuture<Void> tryFire(int mode) {
+ CompletableFuture<Void> d;
+ CompletableFuture<T> a;
+ CompletableFuture<U> b;
+ if ((d = dep) == null ||
+ !d.orAccept(a = src, b = snd, fn, mode > 0 ? null : this))
+ return null;
+ dep = null; src = null; snd = null; fn = null;
+ return d.postFire(a, b, mode);
+ }
+ }
+
+ final <R,S extends R> boolean orAccept(CompletableFuture<R> a,
+ CompletableFuture<S> b,
+ Consumer<? super R> f,
+ OrAccept<R,S> c) {
+ Object r; Throwable x;
+ if (a == null || b == null ||
+ ((r = a.result) == null && (r = b.result) == null) || f == null)
+ return false;
+ tryComplete: if (result == null) {
+ try {
+ if (c != null && !c.claim())
+ return false;
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ completeThrowable(x, r);
+ break tryComplete;
+ }
+ r = null;
+ }
+ @SuppressWarnings("unchecked") R rr = (R) r;
+ f.accept(rr);
+ completeNull();
+ } catch (Throwable ex) {
+ completeThrowable(ex);
+ }
+ }
+ return true;
+ }
+
+ private <U extends T> CompletableFuture<Void> orAcceptStage(
+ Executor e, CompletionStage<U> o, Consumer<? super T> f) {
+ CompletableFuture<U> b;
+ if (f == null || (b = o.toCompletableFuture()) == null)
+ throw new NullPointerException();
+ CompletableFuture<Void> d = newIncompleteFuture();
+ if (e != null || !d.orAccept(this, b, f, null)) {
+ OrAccept<T,U> c = new OrAccept<T,U>(e, d, this, b, f);
+ orpush(b, c);
+ c.tryFire(SYNC);
+ }
+ return d;
+ }
+
+ @SuppressWarnings("serial")
+ static final class OrRun<T,U> extends BiCompletion<T,U,Void> {
+ Runnable fn;
+ OrRun(Executor executor, CompletableFuture<Void> dep,
+ CompletableFuture<T> src,
+ CompletableFuture<U> snd,
+ Runnable fn) {
+ super(executor, dep, src, snd); this.fn = fn;
+ }
+ final CompletableFuture<Void> tryFire(int mode) {
+ CompletableFuture<Void> d;
+ CompletableFuture<T> a;
+ CompletableFuture<U> b;
+ if ((d = dep) == null ||
+ !d.orRun(a = src, b = snd, fn, mode > 0 ? null : this))
+ return null;
+ dep = null; src = null; snd = null; fn = null;
+ return d.postFire(a, b, mode);
+ }
+ }
+
+ final boolean orRun(CompletableFuture<?> a, CompletableFuture<?> b,
+ Runnable f, OrRun<?,?> c) {
+ Object r; Throwable x;
+ if (a == null || b == null ||
+ ((r = a.result) == null && (r = b.result) == null) || f == null)
+ return false;
+ if (result == null) {
+ try {
+ if (c != null && !c.claim())
+ return false;
+ if (r instanceof AltResult && (x = ((AltResult)r).ex) != null)
+ completeThrowable(x, r);
+ else {
+ f.run();
+ completeNull();
+ }
+ } catch (Throwable ex) {
+ completeThrowable(ex);
+ }
+ }
+ return true;
+ }
+
+ private CompletableFuture<Void> orRunStage(Executor e, CompletionStage<?> o,
+ Runnable f) {
+ CompletableFuture<?> b;
+ if (f == null || (b = o.toCompletableFuture()) == null)
+ throw new NullPointerException();
+ CompletableFuture<Void> d = newIncompleteFuture();
+ if (e != null || !d.orRun(this, b, f, null)) {
+ OrRun<T,?> c = new OrRun<>(e, d, this, b, f);
+ orpush(b, c);
+ c.tryFire(SYNC);
+ }
+ return d;
+ }
+
+ @SuppressWarnings("serial")
+ static final class OrRelay<T,U> extends BiCompletion<T,U,Object> { // for Or
+ OrRelay(CompletableFuture<Object> dep, CompletableFuture<T> src,
+ CompletableFuture<U> snd) {
+ super(null, dep, src, snd);
+ }
+ final CompletableFuture<Object> tryFire(int mode) {
+ CompletableFuture<Object> d;
+ CompletableFuture<T> a;
+ CompletableFuture<U> b;
+ if ((d = dep) == null || !d.orRelay(a = src, b = snd))
+ return null;
+ src = null; snd = null; dep = null;
+ return d.postFire(a, b, mode);
+ }
+ }
+
+ final boolean orRelay(CompletableFuture<?> a, CompletableFuture<?> b) {
+ Object r;
+ if (a == null || b == null ||
+ ((r = a.result) == null && (r = b.result) == null))
+ return false;
+ if (result == null)
+ completeRelay(r);
+ return true;
+ }
+
+ /** Recursively constructs a tree of completions. */
+ static CompletableFuture<Object> orTree(CompletableFuture<?>[] cfs,
+ int lo, int hi) {
+ CompletableFuture<Object> d = new CompletableFuture<Object>();
+ if (lo <= hi) {
+ CompletableFuture<?> a, b;
+ int mid = (lo + hi) >>> 1;
+ if ((a = (lo == mid ? cfs[lo] :
+ orTree(cfs, lo, mid))) == null ||
+ (b = (lo == hi ? a : (hi == mid+1) ? cfs[hi] :
+ orTree(cfs, mid+1, hi))) == null)
+ throw new NullPointerException();
+ if (!d.orRelay(a, b)) {
+ OrRelay<?,?> c = new OrRelay<>(d, a, b);
+ a.orpush(b, c);
+ c.tryFire(SYNC);
+ }
+ }
+ return d;
+ }
+
+ /* ------------- Zero-input Async forms -------------- */
+
+ @SuppressWarnings("serial")
+ static final class AsyncSupply<T> extends ForkJoinTask<Void>
+ implements Runnable, AsynchronousCompletionTask {
+ CompletableFuture<T> dep; Supplier<? extends T> fn;
+ AsyncSupply(CompletableFuture<T> dep, Supplier<? extends T> fn) {
+ this.dep = dep; this.fn = fn;
+ }
+
+ public final Void getRawResult() { return null; }
+ public final void setRawResult(Void v) {}
+ public final boolean exec() { run(); return true; }
+
+ public void run() {
+ CompletableFuture<T> d; Supplier<? extends T> f;
+ if ((d = dep) != null && (f = fn) != null) {
+ dep = null; fn = null;
+ if (d.result == null) {
+ try {
+ d.completeValue(f.get());
+ } catch (Throwable ex) {
+ d.completeThrowable(ex);
+ }
+ }
+ d.postComplete();
+ }
+ }
+ }
+
+ static <U> CompletableFuture<U> asyncSupplyStage(Executor e,
+ Supplier<U> f) {
+ if (f == null) throw new NullPointerException();
+ CompletableFuture<U> d = new CompletableFuture<U>();
+ e.execute(new AsyncSupply<U>(d, f));
+ return d;
+ }
+
+ @SuppressWarnings("serial")
+ static final class AsyncRun extends ForkJoinTask<Void>
+ implements Runnable, AsynchronousCompletionTask {
+ CompletableFuture<Void> dep; Runnable fn;
+ AsyncRun(CompletableFuture<Void> dep, Runnable fn) {
+ this.dep = dep; this.fn = fn;
+ }
+
+ public final Void getRawResult() { return null; }
+ public final void setRawResult(Void v) {}
+ public final boolean exec() { run(); return true; }
+
+ public void run() {
+ CompletableFuture<Void> d; Runnable f;
+ if ((d = dep) != null && (f = fn) != null) {
+ dep = null; fn = null;
+ if (d.result == null) {
+ try {
+ f.run();
+ d.completeNull();
+ } catch (Throwable ex) {
+ d.completeThrowable(ex);
+ }
+ }
+ d.postComplete();
+ }
+ }
+ }
+
+ static CompletableFuture<Void> asyncRunStage(Executor e, Runnable f) {
+ if (f == null) throw new NullPointerException();
+ CompletableFuture<Void> d = new CompletableFuture<Void>();
+ e.execute(new AsyncRun(d, f));
+ return d;
+ }
+
+ /* ------------- Signallers -------------- */
+
+ /**
+ * Completion for recording and releasing a waiting thread. This
+ * class implements ManagedBlocker to avoid starvation when
+ * blocking actions pile up in ForkJoinPools.
+ */
+ @SuppressWarnings("serial")
+ static final class Signaller extends Completion
+ implements ForkJoinPool.ManagedBlocker {
+ long nanos; // remaining wait time if timed
+ final long deadline; // non-zero if timed
+ final boolean interruptible;
+ boolean interrupted;
+ volatile Thread thread;
+
+ Signaller(boolean interruptible, long nanos, long deadline) {
+ this.thread = Thread.currentThread();
+ this.interruptible = interruptible;
+ this.nanos = nanos;
+ this.deadline = deadline;
+ }
+ final CompletableFuture<?> tryFire(int ignore) {
+ Thread w; // no need to atomically claim
+ if ((w = thread) != null) {
+ thread = null;
+ LockSupport.unpark(w);
+ }
+ return null;
+ }
+ public boolean isReleasable() {
+ if (Thread.interrupted())
+ interrupted = true;
+ return ((interrupted && interruptible) ||
+ (deadline != 0L &&
+ (nanos <= 0L ||
+ (nanos = deadline - System.nanoTime()) <= 0L)) ||
+ thread == null);
+ }
+ public boolean block() {
+ while (!isReleasable()) {
+ if (deadline == 0L)
+ LockSupport.park(this);
+ else
+ LockSupport.parkNanos(this, nanos);
+ }
+ return true;
+ }
+ final boolean isLive() { return thread != null; }
+ }
+
+ /**
+ * Returns raw result after waiting, or null if interruptible and
+ * interrupted.
+ */
+ private Object waitingGet(boolean interruptible) {
+ Signaller q = null;
+ boolean queued = false;
+ int spins = SPINS;
+ Object r;
+ while ((r = result) == null) {
+ if (spins > 0) {
+ if (ThreadLocalRandom.nextSecondarySeed() >= 0)
+ --spins;
+ }
+ else if (q == null)
+ q = new Signaller(interruptible, 0L, 0L);
+ else if (!queued)
+ queued = tryPushStack(q);
+ else {
+ try {
+ ForkJoinPool.managedBlock(q);
+ } catch (InterruptedException ie) { // currently cannot happen
+ q.interrupted = true;
+ }
+ if (q.interrupted && interruptible)
+ break;
+ }
+ }
+ if (q != null) {
+ q.thread = null;
+ if (q.interrupted) {
+ if (interruptible)
+ cleanStack();
+ else
+ Thread.currentThread().interrupt();
+ }
+ }
+ if (r != null)
+ postComplete();
+ return r;
+ }
+
+ /**
+ * Returns raw result after waiting, or null if interrupted, or
+ * throws TimeoutException on timeout.
+ */
+ private Object timedGet(long nanos) throws TimeoutException {
+ if (Thread.interrupted())
+ return null;
+ if (nanos > 0L) {
+ long d = System.nanoTime() + nanos;
+ long deadline = (d == 0L) ? 1L : d; // avoid 0
+ Signaller q = null;
+ boolean queued = false;
+ Object r;
+ while ((r = result) == null) { // similar to untimed, without spins
+ if (q == null)
+ q = new Signaller(true, nanos, deadline);
+ else if (!queued)
+ queued = tryPushStack(q);
+ else if (q.nanos <= 0L)
+ break;
+ else {
+ try {
+ ForkJoinPool.managedBlock(q);
+ } catch (InterruptedException ie) {
+ q.interrupted = true;
+ }
+ if (q.interrupted)
+ break;
+ }
+ }
+ if (q != null)
+ q.thread = null;
+ if (r != null)
+ postComplete();
+ else
+ cleanStack();
+ if (r != null || (q != null && q.interrupted))
+ return r;
+ }
+ throw new TimeoutException();
+ }
+
+ /* ------------- public methods -------------- */
+
+ /**
+ * Creates a new incomplete CompletableFuture.
+ */
+ public CompletableFuture() {
+ }
+
+ /**
+ * Creates a new complete CompletableFuture with given encoded result.
+ */
+ CompletableFuture(Object r) {
+ this.result = r;
+ }
+
+ /**
+ * Returns a new CompletableFuture that is asynchronously completed
+ * by a task running in the {@link ForkJoinPool#commonPool()} with
+ * the value obtained by calling the given Supplier.
+ *
+ * @param supplier a function returning the value to be used
+ * to complete the returned CompletableFuture
+ * @param <U> the function's return type
+ * @return the new CompletableFuture
+ */
+ public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
+ return asyncSupplyStage(ASYNC_POOL, supplier);
+ }
+
+ /**
+ * Returns a new CompletableFuture that is asynchronously completed
+ * by a task running in the given executor with the value obtained
+ * by calling the given Supplier.
+ *
+ * @param supplier a function returning the value to be used
+ * to complete the returned CompletableFuture
+ * @param executor the executor to use for asynchronous execution
+ * @param <U> the function's return type
+ * @return the new CompletableFuture
+ */
+ public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
+ Executor executor) {
+ return asyncSupplyStage(screenExecutor(executor), supplier);
+ }
+
+ /**
+ * Returns a new CompletableFuture that is asynchronously completed
+ * by a task running in the {@link ForkJoinPool#commonPool()} after
+ * it runs the given action.
+ *
+ * @param runnable the action to run before completing the
+ * returned CompletableFuture
+ * @return the new CompletableFuture
+ */
+ public static CompletableFuture<Void> runAsync(Runnable runnable) {
+ return asyncRunStage(ASYNC_POOL, runnable);
+ }
+
+ /**
+ * Returns a new CompletableFuture that is asynchronously completed
+ * by a task running in the given executor after it runs the given
+ * action.
+ *
+ * @param runnable the action to run before completing the
+ * returned CompletableFuture
+ * @param executor the executor to use for asynchronous execution
+ * @return the new CompletableFuture
+ */
+ public static CompletableFuture<Void> runAsync(Runnable runnable,
+ Executor executor) {
+ return asyncRunStage(screenExecutor(executor), runnable);
+ }
+
+ /**
+ * Returns a new CompletableFuture that is already completed with
+ * the given value.
+ *
+ * @param value the value
+ * @param <U> the type of the value
+ * @return the completed CompletableFuture
+ */
+ public static <U> CompletableFuture<U> completedFuture(U value) {
+ return new CompletableFuture<U>((value == null) ? NIL : value);
+ }
+
+ /**
+ * Returns {@code true} if completed in any fashion: normally,
+ * exceptionally, or via cancellation.
+ *
+ * @return {@code true} if completed
+ */
+ public boolean isDone() {
+ return result != null;
+ }
+
+ /**
+ * Waits if necessary for this future to complete, and then
+ * returns its result.
+ *
+ * @return the result value
+ * @throws CancellationException if this future was cancelled
+ * @throws ExecutionException if this future completed exceptionally
+ * @throws InterruptedException if the current thread was interrupted
+ * while waiting
+ */
+ public T get() throws InterruptedException, ExecutionException {
+ Object r;
+ return reportGet((r = result) == null ? waitingGet(true) : r);
+ }
+
+ /**
+ * Waits if necessary for at most the given time for this future
+ * to complete, and then returns its result, if available.
+ *
+ * @param timeout the maximum time to wait
+ * @param unit the time unit of the timeout argument
+ * @return the result value
+ * @throws CancellationException if this future was cancelled
+ * @throws ExecutionException if this future completed exceptionally
+ * @throws InterruptedException if the current thread was interrupted
+ * while waiting
+ * @throws TimeoutException if the wait timed out
+ */
+ public T get(long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ Object r;
+ long nanos = unit.toNanos(timeout);
+ return reportGet((r = result) == null ? timedGet(nanos) : r);
+ }
+
+ /**
+ * Returns the result value when complete, or throws an
+ * (unchecked) exception if completed exceptionally. To better
+ * conform with the use of common functional forms, if a
+ * computation involved in the completion of this
+ * CompletableFuture threw an exception, this method throws an
+ * (unchecked) {@link CompletionException} with the underlying
+ * exception as its cause.
+ *
+ * @return the result value
+ * @throws CancellationException if the computation was cancelled
+ * @throws CompletionException if this future completed
+ * exceptionally or a completion computation threw an exception
+ */
+ public T join() {
+ Object r;
+ return reportJoin((r = result) == null ? waitingGet(false) : r);
+ }
+
+ /**
+ * Returns the result value (or throws any encountered exception)
+ * if completed, else returns the given valueIfAbsent.
+ *
+ * @param valueIfAbsent the value to return if not completed
+ * @return the result value, if completed, else the given valueIfAbsent
+ * @throws CancellationException if the computation was cancelled
+ * @throws CompletionException if this future completed
+ * exceptionally or a completion computation threw an exception
+ */
+ public T getNow(T valueIfAbsent) {
+ Object r;
+ return ((r = result) == null) ? valueIfAbsent : reportJoin(r);
+ }
+
+ /**
+ * If not already completed, sets the value returned by {@link
+ * #get()} and related methods to the given value.
+ *
+ * @param value the result value
+ * @return {@code true} if this invocation caused this CompletableFuture
+ * to transition to a completed state, else {@code false}
+ */
+ public boolean complete(T value) {
+ boolean triggered = completeValue(value);
+ postComplete();
+ return triggered;
+ }
+
+ /**
+ * If not already completed, causes invocations of {@link #get()}
+ * and related methods to throw the given exception.
+ *
+ * @param ex the exception
+ * @return {@code true} if this invocation caused this CompletableFuture
+ * to transition to a completed state, else {@code false}
+ */
+ public boolean completeExceptionally(Throwable ex) {
+ if (ex == null) throw new NullPointerException();
+ boolean triggered = internalComplete(new AltResult(ex));
+ postComplete();
+ return triggered;
+ }
+
+ public <U> CompletableFuture<U> thenApply(
+ Function<? super T,? extends U> fn) {
+ return uniApplyStage(null, fn);
+ }
+
+ public <U> CompletableFuture<U> thenApplyAsync(
+ Function<? super T,? extends U> fn) {
+ return uniApplyStage(defaultExecutor(), fn);
+ }
+
+ public <U> CompletableFuture<U> thenApplyAsync(
+ Function<? super T,? extends U> fn, Executor executor) {
+ return uniApplyStage(screenExecutor(executor), fn);
+ }
+
+ public CompletableFuture<Void> thenAccept(Consumer<? super T> action) {
+ return uniAcceptStage(null, action);
+ }
+
+ public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action) {
+ return uniAcceptStage(defaultExecutor(), action);
+ }
+
+ public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,
+ Executor executor) {
+ return uniAcceptStage(screenExecutor(executor), action);
+ }
+
+ public CompletableFuture<Void> thenRun(Runnable action) {
+ return uniRunStage(null, action);
+ }
+
+ public CompletableFuture<Void> thenRunAsync(Runnable action) {
+ return uniRunStage(defaultExecutor(), action);
+ }
+
+ public CompletableFuture<Void> thenRunAsync(Runnable action,
+ Executor executor) {
+ return uniRunStage(screenExecutor(executor), action);
+ }
+
+ public <U,V> CompletableFuture<V> thenCombine(
+ CompletionStage<? extends U> other,
+ BiFunction<? super T,? super U,? extends V> fn) {
+ return biApplyStage(null, other, fn);
+ }
+
+ public <U,V> CompletableFuture<V> thenCombineAsync(
+ CompletionStage<? extends U> other,
+ BiFunction<? super T,? super U,? extends V> fn) {
+ return biApplyStage(defaultExecutor(), other, fn);
+ }
+
+ public <U,V> CompletableFuture<V> thenCombineAsync(
+ CompletionStage<? extends U> other,
+ BiFunction<? super T,? super U,? extends V> fn, Executor executor) {
+ return biApplyStage(screenExecutor(executor), other, fn);
+ }
+
+ public <U> CompletableFuture<Void> thenAcceptBoth(
+ CompletionStage<? extends U> other,
+ BiConsumer<? super T, ? super U> action) {
+ return biAcceptStage(null, other, action);
+ }
+
+ public <U> CompletableFuture<Void> thenAcceptBothAsync(
+ CompletionStage<? extends U> other,
+ BiConsumer<? super T, ? super U> action) {
+ return biAcceptStage(defaultExecutor(), other, action);
+ }
+
+ public <U> CompletableFuture<Void> thenAcceptBothAsync(
+ CompletionStage<? extends U> other,
+ BiConsumer<? super T, ? super U> action, Executor executor) {
+ return biAcceptStage(screenExecutor(executor), other, action);
+ }
+
+ public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other,
+ Runnable action) {
+ return biRunStage(null, other, action);
+ }
+
+ public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,
+ Runnable action) {
+ return biRunStage(defaultExecutor(), other, action);
+ }
+
+ public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,
+ Runnable action,
+ Executor executor) {
+ return biRunStage(screenExecutor(executor), other, action);
+ }
+
+ public <U> CompletableFuture<U> applyToEither(
+ CompletionStage<? extends T> other, Function<? super T, U> fn) {
+ return orApplyStage(null, other, fn);
+ }
+
+ public <U> CompletableFuture<U> applyToEitherAsync(
+ CompletionStage<? extends T> other, Function<? super T, U> fn) {
+ return orApplyStage(defaultExecutor(), other, fn);
+ }
+
+ public <U> CompletableFuture<U> applyToEitherAsync(
+ CompletionStage<? extends T> other, Function<? super T, U> fn,
+ Executor executor) {
+ return orApplyStage(screenExecutor(executor), other, fn);
+ }
+
+ public CompletableFuture<Void> acceptEither(
+ CompletionStage<? extends T> other, Consumer<? super T> action) {
+ return orAcceptStage(null, other, action);
+ }
+
+ public CompletableFuture<Void> acceptEitherAsync(
+ CompletionStage<? extends T> other, Consumer<? super T> action) {
+ return orAcceptStage(defaultExecutor(), other, action);
+ }
+
+ public CompletableFuture<Void> acceptEitherAsync(
+ CompletionStage<? extends T> other, Consumer<? super T> action,
+ Executor executor) {
+ return orAcceptStage(screenExecutor(executor), other, action);
+ }
+
+ public CompletableFuture<Void> runAfterEither(CompletionStage<?> other,
+ Runnable action) {
+ return orRunStage(null, other, action);
+ }
+
+ public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,
+ Runnable action) {
+ return orRunStage(defaultExecutor(), other, action);
+ }
+
+ public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,
+ Runnable action,
+ Executor executor) {
+ return orRunStage(screenExecutor(executor), other, action);
+ }
+
+ public <U> CompletableFuture<U> thenCompose(
+ Function<? super T, ? extends CompletionStage<U>> fn) {
+ return uniComposeStage(null, fn);
+ }
+
+ public <U> CompletableFuture<U> thenComposeAsync(
+ Function<? super T, ? extends CompletionStage<U>> fn) {
+ return uniComposeStage(defaultExecutor(), fn);
+ }
+
+ public <U> CompletableFuture<U> thenComposeAsync(
+ Function<? super T, ? extends CompletionStage<U>> fn,
+ Executor executor) {
+ return uniComposeStage(screenExecutor(executor), fn);
+ }
+
+ public CompletableFuture<T> whenComplete(
+ BiConsumer<? super T, ? super Throwable> action) {
+ return uniWhenCompleteStage(null, action);
+ }
+
+ public CompletableFuture<T> whenCompleteAsync(
+ BiConsumer<? super T, ? super Throwable> action) {
+ return uniWhenCompleteStage(defaultExecutor(), action);
+ }
+
+ public CompletableFuture<T> whenCompleteAsync(
+ BiConsumer<? super T, ? super Throwable> action, Executor executor) {
+ return uniWhenCompleteStage(screenExecutor(executor), action);
+ }
+
+ public <U> CompletableFuture<U> handle(
+ BiFunction<? super T, Throwable, ? extends U> fn) {
+ return uniHandleStage(null, fn);
+ }
+
+ public <U> CompletableFuture<U> handleAsync(
+ BiFunction<? super T, Throwable, ? extends U> fn) {
+ return uniHandleStage(defaultExecutor(), fn);
+ }
+
+ public <U> CompletableFuture<U> handleAsync(
+ BiFunction<? super T, Throwable, ? extends U> fn, Executor executor) {
+ return uniHandleStage(screenExecutor(executor), fn);
+ }
+
+ /**
+ * Returns this CompletableFuture.
+ *
+ * @return this CompletableFuture
+ */
+ public CompletableFuture<T> toCompletableFuture() {
+ return this;
+ }
+
+ // not in interface CompletionStage
+
+ /**
+ * Returns a new CompletableFuture that is completed when this
+ * CompletableFuture completes, with the result of the given
+ * function of the exception triggering this CompletableFuture's
+ * completion when it completes exceptionally; otherwise, if this
+ * CompletableFuture completes normally, then the returned
+ * CompletableFuture also completes normally with the same value.
+ * Note: More flexible versions of this functionality are
+ * available using methods {@code whenComplete} and {@code handle}.
+ *
+ * @param fn the function to use to compute the value of the
+ * returned CompletableFuture if this CompletableFuture completed
+ * exceptionally
+ * @return the new CompletableFuture
+ */
+ public CompletableFuture<T> exceptionally(
+ Function<Throwable, ? extends T> fn) {
+ return uniExceptionallyStage(fn);
+ }
+
+
+ /* ------------- Arbitrary-arity constructions -------------- */
+
+ /**
+ * Returns a new CompletableFuture that is completed when all of
+ * the given CompletableFutures complete. If any of the given
+ * CompletableFutures complete exceptionally, then the returned
+ * CompletableFuture also does so, with a CompletionException
+ * holding this exception as its cause. Otherwise, the results,
+ * if any, of the given CompletableFutures are not reflected in
+ * the returned CompletableFuture, but may be obtained by
+ * inspecting them individually. If no CompletableFutures are
+ * provided, returns a CompletableFuture completed with the value
+ * {@code null}.
+ *
+ * <p>Among the applications of this method is to await completion
+ * of a set of independent CompletableFutures before continuing a
+ * program, as in: {@code CompletableFuture.allOf(c1, c2,
+ * c3).join();}.
+ *
+ * @param cfs the CompletableFutures
+ * @return a new CompletableFuture that is completed when all of the
+ * given CompletableFutures complete
+ * @throws NullPointerException if the array or any of its elements are
+ * {@code null}
+ */
+ public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) {
+ return andTree(cfs, 0, cfs.length - 1);
+ }
+
+ /**
+ * Returns a new CompletableFuture that is completed when any of
+ * the given CompletableFutures complete, with the same result.
+ * Otherwise, if it completed exceptionally, the returned
+ * CompletableFuture also does so, with a CompletionException
+ * holding this exception as its cause. If no CompletableFutures
+ * are provided, returns an incomplete CompletableFuture.
+ *
+ * @param cfs the CompletableFutures
+ * @return a new CompletableFuture that is completed with the
+ * result or exception of any of the given CompletableFutures when
+ * one completes
+ * @throws NullPointerException if the array or any of its elements are
+ * {@code null}
+ */
+ public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) {
+ return orTree(cfs, 0, cfs.length - 1);
+ }
+
+ /* ------------- Control and status methods -------------- */
+
+ /**
+ * If not already completed, completes this CompletableFuture with
+ * a {@link CancellationException}. Dependent CompletableFutures
+ * that have not already completed will also complete
+ * exceptionally, with a {@link CompletionException} caused by
+ * this {@code CancellationException}.
+ *
+ * @param mayInterruptIfRunning this value has no effect in this
+ * implementation because interrupts are not used to control
+ * processing.
+ *
+ * @return {@code true} if this task is now cancelled
+ */
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ boolean cancelled = (result == null) &&
+ internalComplete(new AltResult(new CancellationException()));
+ postComplete();
+ return cancelled || isCancelled();
+ }
+
+ /**
+ * Returns {@code true} if this CompletableFuture was cancelled
+ * before it completed normally.
+ *
+ * @return {@code true} if this CompletableFuture was cancelled
+ * before it completed normally
+ */
+ public boolean isCancelled() {
+ Object r;
+ return ((r = result) instanceof AltResult) &&
+ (((AltResult)r).ex instanceof CancellationException);
+ }
+
+ /**
+ * Returns {@code true} if this CompletableFuture completed
+ * exceptionally, in any way. Possible causes include
+ * cancellation, explicit invocation of {@code
+ * completeExceptionally}, and abrupt termination of a
+ * CompletionStage action.
+ *
+ * @return {@code true} if this CompletableFuture completed
+ * exceptionally
+ */
+ public boolean isCompletedExceptionally() {
+ Object r;
+ return ((r = result) instanceof AltResult) && r != NIL;
+ }
+
+ /**
+ * Forcibly sets or resets the value subsequently returned by
+ * method {@link #get()} and related methods, whether or not
+ * already completed. This method is designed for use only in
+ * error recovery actions, and even in such situations may result
+ * in ongoing dependent completions using established versus
+ * overwritten outcomes.
+ *
+ * @param value the completion value
+ */
+ public void obtrudeValue(T value) {
+ result = (value == null) ? NIL : value;
+ postComplete();
+ }
+
+ /**
+ * Forcibly causes subsequent invocations of method {@link #get()}
+ * and related methods to throw the given exception, whether or
+ * not already completed. This method is designed for use only in
+ * error recovery actions, and even in such situations may result
+ * in ongoing dependent completions using established versus
+ * overwritten outcomes.
+ *
+ * @param ex the exception
+ * @throws NullPointerException if the exception is null
+ */
+ public void obtrudeException(Throwable ex) {
+ if (ex == null) throw new NullPointerException();
+ result = new AltResult(ex);
+ postComplete();
+ }
+
+ /**
+ * Returns the estimated number of CompletableFutures whose
+ * completions are awaiting completion of this CompletableFuture.
+ * This method is designed for use in monitoring system state, not
+ * for synchronization control.
+ *
+ * @return the number of dependent CompletableFutures
+ */
+ public int getNumberOfDependents() {
+ int count = 0;
+ for (Completion p = stack; p != null; p = p.next)
+ ++count;
+ return count;
+ }
+
+ /**
+ * Returns a string identifying this CompletableFuture, as well as
+ * its completion state. The state, in brackets, contains the
+ * String {@code "Completed Normally"} or the String {@code
+ * "Completed Exceptionally"}, or the String {@code "Not
+ * completed"} followed by the number of CompletableFutures
+ * dependent upon its completion, if any.
+ *
+ * @return a string identifying this CompletableFuture, as well as its state
+ */
+ public String toString() {
+ Object r = result;
+ int count = 0; // avoid call to getNumberOfDependents in case disabled
+ for (Completion p = stack; p != null; p = p.next)
+ ++count;
+ return super.toString() +
+ ((r == null) ?
+ ((count == 0) ?
+ "[Not completed]" :
+ "[Not completed, " + count + " dependents]") :
+ (((r instanceof AltResult) && ((AltResult)r).ex != null) ?
+ "[Completed exceptionally]" :
+ "[Completed normally]"));
+ }
+
+ // jdk9 additions
+
+ /**
+ * Returns a new incomplete CompletableFuture of the type to be
+ * returned by a CompletionStage method. Subclasses should
+ * normally override this method to return an instance of the same
+ * class as this CompletableFuture. The default implementation
+ * returns an instance of class CompletableFuture.
+ *
+ * @param <U> the type of the value
+ * @return a new CompletableFuture
+ * @since 9
+ * @hide
+ */
+ // android-changed - hidden
+ public <U> CompletableFuture<U> newIncompleteFuture() {
+ return new CompletableFuture<U>();
+ }
+
+ /**
+ * Returns the default Executor used for async methods that do not
+ * specify an Executor. This class uses the {@link
+ * ForkJoinPool#commonPool()} if it supports more than one
+ * parallel thread, or else an Executor using one thread per async
+ * task. This method may be overridden in subclasses to return
+ * an Executor that provides at least one independent thread.
+ *
+ * @return the executor
+ * @since 9
+ * @hide
+ */
+ // android-changed - hidden
+ public Executor defaultExecutor() {
+ return ASYNC_POOL;
+ }
+
+ /**
+ * Returns a new CompletableFuture that is completed normally with
+ * the same value as this CompletableFuture when it completes
+ * normally. If this CompletableFuture completes exceptionally,
+ * then the returned CompletableFuture completes exceptionally
+ * with a CompletionException with this exception as cause. The
+ * behavior is equivalent to {@code thenApply(x -> x)}. This
+ * method may be useful as a form of "defensive copying", to
+ * prevent clients from completing, while still being able to
+ * arrange dependent actions.
+ *
+ * @return the new CompletableFuture
+ * @since 9
+ * @hide
+ */
+ // android-changed - hidden
+ public CompletableFuture<T> copy() {
+ return uniCopyStage();
+ }
+
+ /**
+ * Returns a new CompletionStage that is completed normally with
+ * the same value as this CompletableFuture when it completes
+ * normally, and cannot be independently completed or otherwise
+ * used in ways not defined by the methods of interface {@link
+ * CompletionStage}. If this CompletableFuture completes
+ * exceptionally, then the returned CompletionStage completes
+ * exceptionally with a CompletionException with this exception as
+ * cause.
+ *
+ * @return the new CompletionStage
+ * @since 9
+ * @hide
+ */
+ // android-changed - hidden
+ public CompletionStage<T> minimalCompletionStage() {
+ return uniAsMinimalStage();
+ }
+
+ /**
+ * Completes this CompletableFuture with the result of
+ * the given Supplier function invoked from an asynchronous
+ * task using the given executor.
+ *
+ * @param supplier a function returning the value to be used
+ * to complete this CompletableFuture
+ * @param executor the executor to use for asynchronous execution
+ * @return this CompletableFuture
+ * @since 9
+ * @hide
+ */
+ // android-changed - hidden
+ public CompletableFuture<T> completeAsync(Supplier<? extends T> supplier,
+ Executor executor) {
+ if (supplier == null || executor == null)
+ throw new NullPointerException();
+ executor.execute(new AsyncSupply<T>(this, supplier));
+ return this;
+ }
+
+ /**
+ * Completes this CompletableFuture with the result of the given
+ * Supplier function invoked from an asynchronous task using the
+ * default executor.
+ *
+ * @param supplier a function returning the value to be used
+ * to complete this CompletableFuture
+ * @return this CompletableFuture
+ * @since 9
+ * @hide
+ */
+ // android-changed - hidden
+ public CompletableFuture<T> completeAsync(Supplier<? extends T> supplier) {
+ return completeAsync(supplier, defaultExecutor());
+ }
+
+ /**
+ * Exceptionally completes this CompletableFuture with
+ * a {@link TimeoutException} if not otherwise completed
+ * before the given timeout.
+ *
+ * @param timeout how long to wait before completing exceptionally
+ * with a TimeoutException, in units of {@code unit}
+ * @param unit a {@code TimeUnit} determining how to interpret the
+ * {@code timeout} parameter
+ * @return this CompletableFuture
+ * @since 9
+ * @hide
+ */
+ // android-changed - hidden
+ public CompletableFuture<T> orTimeout(long timeout, TimeUnit unit) {
+ if (unit == null)
+ throw new NullPointerException();
+ if (result == null)
+ whenComplete(new Canceller(Delayer.delay(new Timeout(this),
+ timeout, unit)));
+ return this;
+ }
+
+ /**
+ * Completes this CompletableFuture with the given value if not
+ * otherwise completed before the given timeout.
+ *
+ * @param value the value to use upon timeout
+ * @param timeout how long to wait before completing normally
+ * with the given value, in units of {@code unit}
+ * @param unit a {@code TimeUnit} determining how to interpret the
+ * {@code timeout} parameter
+ * @return this CompletableFuture
+ * @since 9
+ * @hide
+ */
+ // android-changed - hidden
+ public CompletableFuture<T> completeOnTimeout(T value, long timeout,
+ TimeUnit unit) {
+ if (unit == null)
+ throw new NullPointerException();
+ if (result == null)
+ whenComplete(new Canceller(Delayer.delay(
+ new DelayedCompleter<T>(this, value),
+ timeout, unit)));
+ return this;
+ }
+
+ /**
+ * Returns a new Executor that submits a task to the given base
+ * executor after the given delay (or no delay if non-positive).
+ * Each delay commences upon invocation of the returned executor's
+ * {@code execute} method.
+ *
+ * @param delay how long to delay, in units of {@code unit}
+ * @param unit a {@code TimeUnit} determining how to interpret the
+ * {@code delay} parameter
+ * @param executor the base executor
+ * @return the new delayed executor
+ * @since 9
+ * @hide
+ */
+ // android-changed - hidden
+ public static Executor delayedExecutor(long delay, TimeUnit unit,
+ Executor executor) {
+ if (unit == null || executor == null)
+ throw new NullPointerException();
+ return new DelayedExecutor(delay, unit, executor);
+ }
+
+ /**
+ * Returns a new Executor that submits a task to the default
+ * executor after the given delay (or no delay if non-positive).
+ * Each delay commences upon invocation of the returned executor's
+ * {@code execute} method.
+ *
+ * @param delay how long to delay, in units of {@code unit}
+ * @param unit a {@code TimeUnit} determining how to interpret the
+ * {@code delay} parameter
+ * @return the new delayed executor
+ * @since 9
+ * @hide
+ */
+ // android-changed - hidden
+ public static Executor delayedExecutor(long delay, TimeUnit unit) {
+ if (unit == null)
+ throw new NullPointerException();
+ return new DelayedExecutor(delay, unit, ASYNC_POOL);
+ }
+
+ /**
+ * Returns a new CompletionStage that is already completed with
+ * the given value and supports only those methods in
+ * interface {@link CompletionStage}.
+ *
+ * @param value the value
+ * @param <U> the type of the value
+ * @return the completed CompletionStage
+ * @since 9
+ * @hide
+ */
+ // android-changed - hidden
+ public static <U> CompletionStage<U> completedStage(U value) {
+ return new MinimalStage<U>((value == null) ? NIL : value);
+ }
+
+ /**
+ * Returns a new CompletableFuture that is already completed
+ * exceptionally with the given exception.
+ *
+ * @param ex the exception
+ * @param <U> the type of the value
+ * @return the exceptionally completed CompletableFuture
+ * @since 9
+ * @hide
+ */
+ // android-changed - hidden
+ public static <U> CompletableFuture<U> failedFuture(Throwable ex) {
+ if (ex == null) throw new NullPointerException();
+ return new CompletableFuture<U>(new AltResult(ex));
+ }
+
+ /**
+ * Returns a new CompletionStage that is already completed
+ * exceptionally with the given exception and supports only those
+ * methods in interface {@link CompletionStage}.
+ *
+ * @param ex the exception
+ * @param <U> the type of the value
+ * @return the exceptionally completed CompletionStage
+ * @since 9
+ * @hide
+ */
+ // android-changed - hidden
+ public static <U> CompletionStage<U> failedStage(Throwable ex) {
+ if (ex == null) throw new NullPointerException();
+ return new MinimalStage<U>(new AltResult(ex));
+ }
+
+ /**
+ * Singleton delay scheduler, used only for starting and
+ * cancelling tasks.
+ */
+ static final class Delayer {
+ static ScheduledFuture<?> delay(Runnable command, long delay,
+ TimeUnit unit) {
+ return delayer.schedule(command, delay, unit);
+ }
+
+ static final class DaemonThreadFactory implements ThreadFactory {
+ public Thread newThread(Runnable r) {
+ Thread t = new Thread(r);
+ t.setDaemon(true);
+ t.setName("CompletableFutureDelayScheduler");
+ return t;
+ }
+ }
+
+ static final ScheduledThreadPoolExecutor delayer;
+ static {
+ (delayer = new ScheduledThreadPoolExecutor(
+ 1, new DaemonThreadFactory())).
+ setRemoveOnCancelPolicy(true);
+ }
+ }
+
+ // Little class-ified lambdas to better support monitoring
+
+ static final class DelayedExecutor implements Executor {
+ final long delay;
+ final TimeUnit unit;
+ final Executor executor;
+ DelayedExecutor(long delay, TimeUnit unit, Executor executor) {
+ this.delay = delay; this.unit = unit; this.executor = executor;
+ }
+ public void execute(Runnable r) {
+ Delayer.delay(new TaskSubmitter(executor, r), delay, unit);
+ }
+ }
+
+ /** Action to submit user task */
+ static final class TaskSubmitter implements Runnable {
+ final Executor executor;
+ final Runnable action;
+ TaskSubmitter(Executor executor, Runnable action) {
+ this.executor = executor;
+ this.action = action;
+ }
+ public void run() { executor.execute(action); }
+ }
+
+ /** Action to completeExceptionally on timeout */
+ static final class Timeout implements Runnable {
+ final CompletableFuture<?> f;
+ Timeout(CompletableFuture<?> f) { this.f = f; }
+ public void run() {
+ if (f != null && !f.isDone())
+ f.completeExceptionally(new TimeoutException());
+ }
+ }
+
+ /** Action to complete on timeout */
+ static final class DelayedCompleter<U> implements Runnable {
+ final CompletableFuture<U> f;
+ final U u;
+ DelayedCompleter(CompletableFuture<U> f, U u) { this.f = f; this.u = u; }
+ public void run() {
+ if (f != null)
+ f.complete(u);
+ }
+ }
+
+ /** Action to cancel unneeded timeouts */
+ static final class Canceller implements BiConsumer<Object, Throwable> {
+ final Future<?> f;
+ Canceller(Future<?> f) { this.f = f; }
+ public void accept(Object ignore, Throwable ex) {
+ if (ex == null && f != null && !f.isDone())
+ f.cancel(false);
+ }
+ }
+
+ /**
+ * A subclass that just throws UOE for most non-CompletionStage methods.
+ */
+ static final class MinimalStage<T> extends CompletableFuture<T> {
+ MinimalStage() { }
+ MinimalStage(Object r) { super(r); }
+ @Override public <U> CompletableFuture<U> newIncompleteFuture() {
+ return new MinimalStage<U>(); }
+ @Override public T get() {
+ throw new UnsupportedOperationException(); }
+ @Override public T get(long timeout, TimeUnit unit) {
+ throw new UnsupportedOperationException(); }
+ @Override public T getNow(T valueIfAbsent) {
+ throw new UnsupportedOperationException(); }
+ @Override public T join() {
+ throw new UnsupportedOperationException(); }
+ @Override public boolean complete(T value) {
+ throw new UnsupportedOperationException(); }
+ @Override public boolean completeExceptionally(Throwable ex) {
+ throw new UnsupportedOperationException(); }
+ @Override public boolean cancel(boolean mayInterruptIfRunning) {
+ throw new UnsupportedOperationException(); }
+ @Override public void obtrudeValue(T value) {
+ throw new UnsupportedOperationException(); }
+ @Override public void obtrudeException(Throwable ex) {
+ throw new UnsupportedOperationException(); }
+ @Override public boolean isDone() {
+ throw new UnsupportedOperationException(); }
+ @Override public boolean isCancelled() {
+ throw new UnsupportedOperationException(); }
+ @Override public boolean isCompletedExceptionally() {
+ throw new UnsupportedOperationException(); }
+ @Override public int getNumberOfDependents() {
+ throw new UnsupportedOperationException(); }
+ @Override public CompletableFuture<T> completeAsync
+ (Supplier<? extends T> supplier, Executor executor) {
+ throw new UnsupportedOperationException(); }
+ @Override public CompletableFuture<T> completeAsync
+ (Supplier<? extends T> supplier) {
+ throw new UnsupportedOperationException(); }
+ @Override public CompletableFuture<T> orTimeout
+ (long timeout, TimeUnit unit) {
+ throw new UnsupportedOperationException(); }
+ @Override public CompletableFuture<T> completeOnTimeout
+ (T value, long timeout, TimeUnit unit) {
+ throw new UnsupportedOperationException(); }
+ }
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long RESULT;
+ private static final long STACK;
+ private static final long NEXT;
+ static {
+ try {
+ RESULT = U.objectFieldOffset
+ (CompletableFuture.class.getDeclaredField("result"));
+ STACK = U.objectFieldOffset
+ (CompletableFuture.class.getDeclaredField("stack"));
+ NEXT = U.objectFieldOffset
+ (Completion.class.getDeclaredField("next"));
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
+
+ // Reduce the risk of rare disastrous classloading in first call to
+ // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773
+ Class<?> ensureLoaded = LockSupport.class;
+ }
+}
diff --git a/luni/src/main/java/java/util/concurrent/CompletionException.java b/luni/src/main/java/java/util/concurrent/CompletionException.java
new file mode 100644
index 0000000..9b905d2
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/CompletionException.java
@@ -0,0 +1,61 @@
+/*
+ * 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/
+ */
+
+package java.util.concurrent;
+
+/**
+ * Exception thrown when an error or other exception is encountered
+ * in the course of completing a result or task.
+ *
+ * @since 1.8
+ * @author Doug Lea
+ */
+public class CompletionException extends RuntimeException {
+ private static final long serialVersionUID = 7830266012832686185L;
+
+ /**
+ * Constructs a {@code CompletionException} with no detail message.
+ * The cause is not initialized, and may subsequently be
+ * initialized by a call to {@link #initCause(Throwable) initCause}.
+ */
+ protected CompletionException() { }
+
+ /**
+ * Constructs a {@code CompletionException} with the specified detail
+ * message. The cause is not initialized, and may subsequently be
+ * initialized by a call to {@link #initCause(Throwable) initCause}.
+ *
+ * @param message the detail message
+ */
+ protected CompletionException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a {@code CompletionException} with the specified detail
+ * message and cause.
+ *
+ * @param message the detail message
+ * @param cause the cause (which is saved for later retrieval by the
+ * {@link #getCause()} method)
+ */
+ public CompletionException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Constructs a {@code CompletionException} with the specified cause.
+ * The detail message is set to {@code (cause == null ? null :
+ * cause.toString())} (which typically contains the class and
+ * detail message of {@code cause}).
+ *
+ * @param cause the cause (which is saved for later retrieval by the
+ * {@link #getCause()} method)
+ */
+ public CompletionException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/luni/src/main/java/java/util/concurrent/CompletionStage.java b/luni/src/main/java/java/util/concurrent/CompletionStage.java
new file mode 100644
index 0000000..ccb1aa4
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/CompletionStage.java
@@ -0,0 +1,840 @@
+/*
+ * 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/
+ */
+
+package java.util.concurrent;
+
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * A stage of a possibly asynchronous computation, that performs an
+ * action or computes a value when another CompletionStage completes.
+ * A stage completes upon termination of its computation, but this may
+ * in turn trigger other dependent stages. The functionality defined
+ * in this interface takes only a few basic forms, which expand out to
+ * a larger set of methods to capture a range of usage styles:
+ *
+ * <ul>
+ *
+ * <li>The computation performed by a stage may be expressed as a
+ * Function, Consumer, or Runnable (using methods with names including
+ * <em>apply</em>, <em>accept</em>, or <em>run</em>, respectively)
+ * depending on whether it requires arguments and/or produces results.
+ * For example:
+ * <pre> {@code
+ * stage.thenApply(x -> square(x))
+ * .thenAccept(x -> System.out.print(x))
+ * .thenRun(() -> System.out.println());}</pre>
+ *
+ * An additional form (<em>compose</em>) allows the construction of
+ * computation pipelines from functions returning completion stages.
+ *
+ * <p>Any argument to a stage's computation is the outcome of a
+ * triggering stage's computation.
+ *
+ * <li>One stage's execution may be triggered by completion of a
+ * single stage, or both of two stages, or either of two stages.
+ * Dependencies on a single stage are arranged using methods with
+ * prefix <em>then</em>. Those triggered by completion of
+ * <em>both</em> of two stages may <em>combine</em> their results or
+ * effects, using correspondingly named methods. Those triggered by
+ * <em>either</em> of two stages make no guarantees about which of the
+ * results or effects are used for the dependent stage's computation.
+ *
+ * <li>Dependencies among stages control the triggering of
+ * computations, but do not otherwise guarantee any particular
+ * ordering. Additionally, execution of a new stage's computations may
+ * be arranged in any of three ways: default execution, default
+ * asynchronous execution (using methods with suffix <em>async</em>
+ * that employ the stage's default asynchronous execution facility),
+ * or custom (via a supplied {@link Executor}). The execution
+ * properties of default and async modes are specified by
+ * CompletionStage implementations, not this interface. Methods with
+ * explicit Executor arguments may have arbitrary execution
+ * properties, and might not even support concurrent execution, but
+ * are arranged for processing in a way that accommodates asynchrony.
+ *
+ * <li>Two method forms ({@link #handle handle} and {@link
+ * #whenComplete whenComplete}) support unconditional computation
+ * whether the triggering stage completed normally or exceptionally.
+ * Method {@link #exceptionally exceptionally} supports computation
+ * only when the triggering stage completes exceptionally, computing a
+ * replacement result, similarly to the java {@code catch} keyword.
+ * In all other cases, if a stage's computation terminates abruptly
+ * with an (unchecked) exception or error, then all dependent stages
+ * requiring its completion complete exceptionally as well, with a
+ * {@link CompletionException} holding the exception as its cause. If
+ * a stage is dependent on <em>both</em> of two stages, and both
+ * complete exceptionally, then the CompletionException may correspond
+ * to either one of these exceptions. If a stage is dependent on
+ * <em>either</em> of two others, and only one of them completes
+ * exceptionally, no guarantees are made about whether the dependent
+ * stage completes normally or exceptionally. In the case of method
+ * {@code whenComplete}, when the supplied action itself encounters an
+ * exception, then the stage completes exceptionally with this
+ * exception unless the source stage also completed exceptionally, in
+ * which case the exceptional completion from the source stage is
+ * given preference and propagated to the dependent stage.
+ *
+ * </ul>
+ *
+ * <p>All methods adhere to the above triggering, execution, and
+ * exceptional completion specifications (which are not repeated in
+ * individual method specifications). Additionally, while arguments
+ * used to pass a completion result (that is, for parameters of type
+ * {@code T}) for methods accepting them may be null, passing a null
+ * value for any other parameter will result in a {@link
+ * NullPointerException} being thrown.
+ *
+ * <p>Method form {@link #handle handle} is the most general way of
+ * creating a continuation stage, unconditionally performing a
+ * computation that is given both the result and exception (if any) of
+ * the triggering CompletionStage, and computing an arbitrary result.
+ * Method {@link #whenComplete whenComplete} is similar, but preserves
+ * the result of the triggering stage instead of computing a new one.
+ * Because a stage's normal result may be {@code null}, both methods
+ * should have a computation structured thus:
+ *
+ * <pre>{@code (result, exception) -> {
+ * if (exception == null) {
+ * // triggering stage completed normally
+ * } else {
+ * // triggering stage completed exceptionally
+ * }
+ * }}</pre>
+ *
+ * <p>This interface does not define methods for initially creating,
+ * forcibly completing normally or exceptionally, probing completion
+ * status or results, or awaiting completion of a stage.
+ * Implementations of CompletionStage may provide means of achieving
+ * such effects, as appropriate. Method {@link #toCompletableFuture}
+ * enables interoperability among different implementations of this
+ * interface by providing a common conversion type.
+ *
+ * @author Doug Lea
+ * @since 1.8
+ */
+public interface CompletionStage<T> {
+
+ /**
+ * Returns a new CompletionStage that, when this stage completes
+ * normally, is executed with this stage's result as the argument
+ * to the supplied function.
+ *
+ * <p>This method is analogous to
+ * {@link java.util.Optional#map Optional.map} and
+ * TODO(streams): make a link to java.util.stream.Stream#map Stream.map.
+ *
+ * <p>See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param fn the function to use to compute the value of the
+ * returned CompletionStage
+ * @param <U> the function's return type
+ * @return the new CompletionStage
+ */
+ public <U> CompletionStage<U> thenApply(Function<? super T,? extends U> fn);
+
+ /**
+ * Returns a new CompletionStage that, when this stage completes
+ * normally, is executed using this stage's default asynchronous
+ * execution facility, with this stage's result as the argument to
+ * the supplied function.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param fn the function to use to compute the value of the
+ * returned CompletionStage
+ * @param <U> the function's return type
+ * @return the new CompletionStage
+ */
+ public <U> CompletionStage<U> thenApplyAsync
+ (Function<? super T,? extends U> fn);
+
+ /**
+ * Returns a new CompletionStage that, when this stage completes
+ * normally, is executed using the supplied Executor, with this
+ * stage's result as the argument to the supplied function.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param fn the function to use to compute the value of the
+ * returned CompletionStage
+ * @param executor the executor to use for asynchronous execution
+ * @param <U> the function's return type
+ * @return the new CompletionStage
+ */
+ public <U> CompletionStage<U> thenApplyAsync
+ (Function<? super T,? extends U> fn,
+ Executor executor);
+
+ /**
+ * Returns a new CompletionStage that, when this stage completes
+ * normally, is executed with this stage's result as the argument
+ * to the supplied action.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param action the action to perform before completing the
+ * returned CompletionStage
+ * @return the new CompletionStage
+ */
+ public CompletionStage<Void> thenAccept(Consumer<? super T> action);
+
+ /**
+ * Returns a new CompletionStage that, when this stage completes
+ * normally, is executed using this stage's default asynchronous
+ * execution facility, with this stage's result as the argument to
+ * the supplied action.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param action the action to perform before completing the
+ * returned CompletionStage
+ * @return the new CompletionStage
+ */
+ public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action);
+
+ /**
+ * Returns a new CompletionStage that, when this stage completes
+ * normally, is executed using the supplied Executor, with this
+ * stage's result as the argument to the supplied action.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param action the action to perform before completing the
+ * returned CompletionStage
+ * @param executor the executor to use for asynchronous execution
+ * @return the new CompletionStage
+ */
+ public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action,
+ Executor executor);
+ /**
+ * Returns a new CompletionStage that, when this stage completes
+ * normally, executes the given action.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param action the action to perform before completing the
+ * returned CompletionStage
+ * @return the new CompletionStage
+ */
+ public CompletionStage<Void> thenRun(Runnable action);
+
+ /**
+ * Returns a new CompletionStage that, when this stage completes
+ * normally, executes the given action using this stage's default
+ * asynchronous execution facility.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param action the action to perform before completing the
+ * returned CompletionStage
+ * @return the new CompletionStage
+ */
+ public CompletionStage<Void> thenRunAsync(Runnable action);
+
+ /**
+ * Returns a new CompletionStage that, when this stage completes
+ * normally, executes the given action using the supplied Executor.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param action the action to perform before completing the
+ * returned CompletionStage
+ * @param executor the executor to use for asynchronous execution
+ * @return the new CompletionStage
+ */
+ public CompletionStage<Void> thenRunAsync(Runnable action,
+ Executor executor);
+
+ /**
+ * Returns a new CompletionStage that, when this and the other
+ * given stage both complete normally, is executed with the two
+ * results as arguments to the supplied function.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param other the other CompletionStage
+ * @param fn the function to use to compute the value of the
+ * returned CompletionStage
+ * @param <U> the type of the other CompletionStage's result
+ * @param <V> the function's return type
+ * @return the new CompletionStage
+ */
+ public <U,V> CompletionStage<V> thenCombine
+ (CompletionStage<? extends U> other,
+ BiFunction<? super T,? super U,? extends V> fn);
+
+ /**
+ * Returns a new CompletionStage that, when this and the other
+ * given stage both complete normally, is executed using this
+ * stage's default asynchronous execution facility, with the two
+ * results as arguments to the supplied function.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param other the other CompletionStage
+ * @param fn the function to use to compute the value of the
+ * returned CompletionStage
+ * @param <U> the type of the other CompletionStage's result
+ * @param <V> the function's return type
+ * @return the new CompletionStage
+ */
+ public <U,V> CompletionStage<V> thenCombineAsync
+ (CompletionStage<? extends U> other,
+ BiFunction<? super T,? super U,? extends V> fn);
+
+ /**
+ * Returns a new CompletionStage that, when this and the other
+ * given stage both complete normally, is executed using the
+ * supplied executor, with the two results as arguments to the
+ * supplied function.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param other the other CompletionStage
+ * @param fn the function to use to compute the value of the
+ * returned CompletionStage
+ * @param executor the executor to use for asynchronous execution
+ * @param <U> the type of the other CompletionStage's result
+ * @param <V> the function's return type
+ * @return the new CompletionStage
+ */
+ public <U,V> CompletionStage<V> thenCombineAsync
+ (CompletionStage<? extends U> other,
+ BiFunction<? super T,? super U,? extends V> fn,
+ Executor executor);
+
+ /**
+ * Returns a new CompletionStage that, when this and the other
+ * given stage both complete normally, is executed with the two
+ * results as arguments to the supplied action.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param other the other CompletionStage
+ * @param action the action to perform before completing the
+ * returned CompletionStage
+ * @param <U> the type of the other CompletionStage's result
+ * @return the new CompletionStage
+ */
+ public <U> CompletionStage<Void> thenAcceptBoth
+ (CompletionStage<? extends U> other,
+ BiConsumer<? super T, ? super U> action);
+
+ /**
+ * Returns a new CompletionStage that, when this and the other
+ * given stage both complete normally, is executed using this
+ * stage's default asynchronous execution facility, with the two
+ * results as arguments to the supplied action.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param other the other CompletionStage
+ * @param action the action to perform before completing the
+ * returned CompletionStage
+ * @param <U> the type of the other CompletionStage's result
+ * @return the new CompletionStage
+ */
+ public <U> CompletionStage<Void> thenAcceptBothAsync
+ (CompletionStage<? extends U> other,
+ BiConsumer<? super T, ? super U> action);
+
+ /**
+ * Returns a new CompletionStage that, when this and the other
+ * given stage both complete normally, is executed using the
+ * supplied executor, with the two results as arguments to the
+ * supplied action.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param other the other CompletionStage
+ * @param action the action to perform before completing the
+ * returned CompletionStage
+ * @param executor the executor to use for asynchronous execution
+ * @param <U> the type of the other CompletionStage's result
+ * @return the new CompletionStage
+ */
+ public <U> CompletionStage<Void> thenAcceptBothAsync
+ (CompletionStage<? extends U> other,
+ BiConsumer<? super T, ? super U> action,
+ Executor executor);
+
+ /**
+ * Returns a new CompletionStage that, when this and the other
+ * given stage both complete normally, executes the given action.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param other the other CompletionStage
+ * @param action the action to perform before completing the
+ * returned CompletionStage
+ * @return the new CompletionStage
+ */
+ public CompletionStage<Void> runAfterBoth(CompletionStage<?> other,
+ Runnable action);
+ /**
+ * Returns a new CompletionStage that, when this and the other
+ * given stage both complete normally, executes the given action
+ * using this stage's default asynchronous execution facility.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param other the other CompletionStage
+ * @param action the action to perform before completing the
+ * returned CompletionStage
+ * @return the new CompletionStage
+ */
+ public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other,
+ Runnable action);
+
+ /**
+ * Returns a new CompletionStage that, when this and the other
+ * given stage both complete normally, executes the given action
+ * using the supplied executor.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param other the other CompletionStage
+ * @param action the action to perform before completing the
+ * returned CompletionStage
+ * @param executor the executor to use for asynchronous execution
+ * @return the new CompletionStage
+ */
+ public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other,
+ Runnable action,
+ Executor executor);
+ /**
+ * Returns a new CompletionStage that, when either this or the
+ * other given stage complete normally, is executed with the
+ * corresponding result as argument to the supplied function.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param other the other CompletionStage
+ * @param fn the function to use to compute the value of the
+ * returned CompletionStage
+ * @param <U> the function's return type
+ * @return the new CompletionStage
+ */
+ public <U> CompletionStage<U> applyToEither
+ (CompletionStage<? extends T> other,
+ Function<? super T, U> fn);
+
+ /**
+ * Returns a new CompletionStage that, when either this or the
+ * other given stage complete normally, is executed using this
+ * stage's default asynchronous execution facility, with the
+ * corresponding result as argument to the supplied function.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param other the other CompletionStage
+ * @param fn the function to use to compute the value of the
+ * returned CompletionStage
+ * @param <U> the function's return type
+ * @return the new CompletionStage
+ */
+ public <U> CompletionStage<U> applyToEitherAsync
+ (CompletionStage<? extends T> other,
+ Function<? super T, U> fn);
+
+ /**
+ * Returns a new CompletionStage that, when either this or the
+ * other given stage complete normally, is executed using the
+ * supplied executor, with the corresponding result as argument to
+ * the supplied function.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param other the other CompletionStage
+ * @param fn the function to use to compute the value of the
+ * returned CompletionStage
+ * @param executor the executor to use for asynchronous execution
+ * @param <U> the function's return type
+ * @return the new CompletionStage
+ */
+ public <U> CompletionStage<U> applyToEitherAsync
+ (CompletionStage<? extends T> other,
+ Function<? super T, U> fn,
+ Executor executor);
+
+ /**
+ * Returns a new CompletionStage that, when either this or the
+ * other given stage complete normally, is executed with the
+ * corresponding result as argument to the supplied action.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param other the other CompletionStage
+ * @param action the action to perform before completing the
+ * returned CompletionStage
+ * @return the new CompletionStage
+ */
+ public CompletionStage<Void> acceptEither
+ (CompletionStage<? extends T> other,
+ Consumer<? super T> action);
+
+ /**
+ * Returns a new CompletionStage that, when either this or the
+ * other given stage complete normally, is executed using this
+ * stage's default asynchronous execution facility, with the
+ * corresponding result as argument to the supplied action.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param other the other CompletionStage
+ * @param action the action to perform before completing the
+ * returned CompletionStage
+ * @return the new CompletionStage
+ */
+ public CompletionStage<Void> acceptEitherAsync
+ (CompletionStage<? extends T> other,
+ Consumer<? super T> action);
+
+ /**
+ * Returns a new CompletionStage that, when either this or the
+ * other given stage complete normally, is executed using the
+ * supplied executor, with the corresponding result as argument to
+ * the supplied action.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param other the other CompletionStage
+ * @param action the action to perform before completing the
+ * returned CompletionStage
+ * @param executor the executor to use for asynchronous execution
+ * @return the new CompletionStage
+ */
+ public CompletionStage<Void> acceptEitherAsync
+ (CompletionStage<? extends T> other,
+ Consumer<? super T> action,
+ Executor executor);
+
+ /**
+ * Returns a new CompletionStage that, when either this or the
+ * other given stage complete normally, executes the given action.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param other the other CompletionStage
+ * @param action the action to perform before completing the
+ * returned CompletionStage
+ * @return the new CompletionStage
+ */
+ public CompletionStage<Void> runAfterEither(CompletionStage<?> other,
+ Runnable action);
+
+ /**
+ * Returns a new CompletionStage that, when either this or the
+ * other given stage complete normally, executes the given action
+ * using this stage's default asynchronous execution facility.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param other the other CompletionStage
+ * @param action the action to perform before completing the
+ * returned CompletionStage
+ * @return the new CompletionStage
+ */
+ public CompletionStage<Void> runAfterEitherAsync
+ (CompletionStage<?> other,
+ Runnable action);
+
+ /**
+ * Returns a new CompletionStage that, when either this or the
+ * other given stage complete normally, executes the given action
+ * using the supplied executor.
+ *
+ * See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param other the other CompletionStage
+ * @param action the action to perform before completing the
+ * returned CompletionStage
+ * @param executor the executor to use for asynchronous execution
+ * @return the new CompletionStage
+ */
+ public CompletionStage<Void> runAfterEitherAsync
+ (CompletionStage<?> other,
+ Runnable action,
+ Executor executor);
+
+ /**
+ * Returns a new CompletionStage that is completed with the same
+ * value as the CompletionStage returned by the given function.
+ *
+ * <p>When this stage completes normally, the given function is
+ * invoked with this stage's result as the argument, returning
+ * another CompletionStage. When that stage completes normally,
+ * the CompletionStage returned by this method is completed with
+ * the same value.
+ *
+ * <p>To ensure progress, the supplied function must arrange
+ * eventual completion of its result.
+ *
+ * <p>This method is analogous to
+ * {@link java.util.Optional#flatMap Optional.flatMap} and
+ * TODO(streams): make a link to java.util.stream.Stream#flatMap Stream.flatMap.
+ *
+ * <p>See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param fn the function to use to compute another CompletionStage
+ * @param <U> the type of the returned CompletionStage's result
+ * @return the new CompletionStage
+ */
+ public <U> CompletionStage<U> thenCompose
+ (Function<? super T, ? extends CompletionStage<U>> fn);
+
+ /**
+ * Returns a new CompletionStage that is completed with the same
+ * value as the CompletionStage returned by the given function,
+ * executed using this stage's default asynchronous execution
+ * facility.
+ *
+ * <p>When this stage completes normally, the given function is
+ * invoked with this stage's result as the argument, returning
+ * another CompletionStage. When that stage completes normally,
+ * the CompletionStage returned by this method is completed with
+ * the same value.
+ *
+ * <p>To ensure progress, the supplied function must arrange
+ * eventual completion of its result.
+ *
+ * <p>See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param fn the function to use to compute another CompletionStage
+ * @param <U> the type of the returned CompletionStage's result
+ * @return the new CompletionStage
+ */
+ public <U> CompletionStage<U> thenComposeAsync
+ (Function<? super T, ? extends CompletionStage<U>> fn);
+
+ /**
+ * Returns a new CompletionStage that is completed with the same
+ * value as the CompletionStage returned by the given function,
+ * executed using the supplied Executor.
+ *
+ * <p>When this stage completes normally, the given function is
+ * invoked with this stage's result as the argument, returning
+ * another CompletionStage. When that stage completes normally,
+ * the CompletionStage returned by this method is completed with
+ * the same value.
+ *
+ * <p>To ensure progress, the supplied function must arrange
+ * eventual completion of its result.
+ *
+ * <p>See the {@link CompletionStage} documentation for rules
+ * covering exceptional completion.
+ *
+ * @param fn the function to use to compute another CompletionStage
+ * @param executor the executor to use for asynchronous execution
+ * @param <U> the type of the returned CompletionStage's result
+ * @return the new CompletionStage
+ */
+ public <U> CompletionStage<U> thenComposeAsync
+ (Function<? super T, ? extends CompletionStage<U>> fn,
+ Executor executor);
+
+ /**
+ * Returns a new CompletionStage that, when this stage completes
+ * either normally or exceptionally, is executed with this stage's
+ * result and exception as arguments to the supplied function.
+ *
+ * <p>When this stage is complete, the given function is invoked
+ * with the result (or {@code null} if none) and the exception (or
+ * {@code null} if none) of this stage as arguments, and the
+ * function's result is used to complete the returned stage.
+ *
+ * @param fn the function to use to compute the value of the
+ * returned CompletionStage
+ * @param <U> the function's return type
+ * @return the new CompletionStage
+ */
+ public <U> CompletionStage<U> handle
+ (BiFunction<? super T, Throwable, ? extends U> fn);
+
+ /**
+ * Returns a new CompletionStage that, when this stage completes
+ * either normally or exceptionally, is executed using this stage's
+ * default asynchronous execution facility, with this stage's
+ * result and exception as arguments to the supplied function.
+ *
+ * <p>When this stage is complete, the given function is invoked
+ * with the result (or {@code null} if none) and the exception (or
+ * {@code null} if none) of this stage as arguments, and the
+ * function's result is used to complete the returned stage.
+ *
+ * @param fn the function to use to compute the value of the
+ * returned CompletionStage
+ * @param <U> the function's return type
+ * @return the new CompletionStage
+ */
+ public <U> CompletionStage<U> handleAsync
+ (BiFunction<? super T, Throwable, ? extends U> fn);
+
+ /**
+ * Returns a new CompletionStage that, when this stage completes
+ * either normally or exceptionally, is executed using the
+ * supplied executor, with this stage's result and exception as
+ * arguments to the supplied function.
+ *
+ * <p>When this stage is complete, the given function is invoked
+ * with the result (or {@code null} if none) and the exception (or
+ * {@code null} if none) of this stage as arguments, and the
+ * function's result is used to complete the returned stage.
+ *
+ * @param fn the function to use to compute the value of the
+ * returned CompletionStage
+ * @param executor the executor to use for asynchronous execution
+ * @param <U> the function's return type
+ * @return the new CompletionStage
+ */
+ public <U> CompletionStage<U> handleAsync
+ (BiFunction<? super T, Throwable, ? extends U> fn,
+ Executor executor);
+
+ /**
+ * Returns a new CompletionStage with the same result or exception as
+ * this stage, that executes the given action when this stage completes.
+ *
+ * <p>When this stage is complete, the given action is invoked
+ * with the result (or {@code null} if none) and the exception (or
+ * {@code null} if none) of this stage as arguments. The returned
+ * stage is completed when the action returns.
+ *
+ * <p>Unlike method {@link #handle handle},
+ * this method is not designed to translate completion outcomes,
+ * so the supplied action should not throw an exception. However,
+ * if it does, the following rules apply: if this stage completed
+ * normally but the supplied action throws an exception, then the
+ * returned stage completes exceptionally with the supplied
+ * action's exception. Or, if this stage completed exceptionally
+ * and the supplied action throws an exception, then the returned
+ * stage completes exceptionally with this stage's exception.
+ *
+ * @param action the action to perform
+ * @return the new CompletionStage
+ */
+ public CompletionStage<T> whenComplete
+ (BiConsumer<? super T, ? super Throwable> action);
+
+ /**
+ * Returns a new CompletionStage with the same result or exception as
+ * this stage, that executes the given action using this stage's
+ * default asynchronous execution facility when this stage completes.
+ *
+ * <p>When this stage is complete, the given action is invoked with the
+ * result (or {@code null} if none) and the exception (or {@code null}
+ * if none) of this stage as arguments. The returned stage is completed
+ * when the action returns.
+ *
+ * <p>Unlike method {@link #handleAsync(BiFunction) handleAsync},
+ * this method is not designed to translate completion outcomes,
+ * so the supplied action should not throw an exception. However,
+ * if it does, the following rules apply: If this stage completed
+ * normally but the supplied action throws an exception, then the
+ * returned stage completes exceptionally with the supplied
+ * action's exception. Or, if this stage completed exceptionally
+ * and the supplied action throws an exception, then the returned
+ * stage completes exceptionally with this stage's exception.
+ *
+ * @param action the action to perform
+ * @return the new CompletionStage
+ */
+ public CompletionStage<T> whenCompleteAsync
+ (BiConsumer<? super T, ? super Throwable> action);
+
+ /**
+ * Returns a new CompletionStage with the same result or exception as
+ * this stage, that executes the given action using the supplied
+ * Executor when this stage completes.
+ *
+ * <p>When this stage is complete, the given action is invoked with the
+ * result (or {@code null} if none) and the exception (or {@code null}
+ * if none) of this stage as arguments. The returned stage is completed
+ * when the action returns.
+ *
+ * <p>Unlike method {@link #handleAsync(BiFunction,Executor) handleAsync},
+ * this method is not designed to translate completion outcomes,
+ * so the supplied action should not throw an exception. However,
+ * if it does, the following rules apply: If this stage completed
+ * normally but the supplied action throws an exception, then the
+ * returned stage completes exceptionally with the supplied
+ * action's exception. Or, if this stage completed exceptionally
+ * and the supplied action throws an exception, then the returned
+ * stage completes exceptionally with this stage's exception.
+ *
+ * @param action the action to perform
+ * @param executor the executor to use for asynchronous execution
+ * @return the new CompletionStage
+ */
+ public CompletionStage<T> whenCompleteAsync
+ (BiConsumer<? super T, ? super Throwable> action,
+ Executor executor);
+
+ /**
+ * Returns a new CompletionStage that, when this stage completes
+ * exceptionally, is executed with this stage's exception as the
+ * argument to the supplied function. Otherwise, if this stage
+ * completes normally, then the returned stage also completes
+ * normally with the same value.
+ *
+ * @param fn the function to use to compute the value of the
+ * returned CompletionStage if this CompletionStage completed
+ * exceptionally
+ * @return the new CompletionStage
+ */
+ public CompletionStage<T> exceptionally
+ (Function<Throwable, ? extends T> fn);
+
+ /**
+ * Returns a {@link CompletableFuture} maintaining the same
+ * completion properties as this stage. If this stage is already a
+ * CompletableFuture, this method may return this stage itself.
+ * Otherwise, invocation of this method may be equivalent in
+ * effect to {@code thenApply(x -> x)}, but returning an instance
+ * of type {@code CompletableFuture}. A CompletionStage
+ * implementation that does not choose to interoperate with others
+ * may throw {@code UnsupportedOperationException}.
+ *
+ * @return the CompletableFuture
+ * @throws UnsupportedOperationException if this implementation
+ * does not interoperate with CompletableFuture
+ */
+ public CompletableFuture<T> toCompletableFuture();
+
+}
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentHashMap.java b/luni/src/main/java/java/util/concurrent/ConcurrentHashMap.java
index 3ed54cf..b4fa8aa 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentHashMap.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentHashMap.java
@@ -13,7 +13,6 @@
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.Collection;
-import java.util.ConcurrentModificationException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
@@ -21,14 +20,29 @@
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.Spliterator;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.DoubleBinaryOperator;
+import java.util.function.Function;
+import java.util.function.IntBinaryOperator;
+import java.util.function.LongBinaryOperator;
+import java.util.function.Predicate;
+import java.util.function.ToDoubleBiFunction;
+import java.util.function.ToDoubleFunction;
+import java.util.function.ToIntBiFunction;
+import java.util.function.ToIntFunction;
+import java.util.function.ToLongBiFunction;
+import java.util.function.ToLongFunction;
+// TODO(streams):
+//import java.util.stream.Stream;
// BEGIN android-note
// removed link to collections framework docs
-// removed links to hidden api
// END android-note
/**
@@ -52,14 +66,14 @@
* that key reporting the updated value.) For aggregate operations
* such as {@code putAll} and {@code clear}, concurrent retrievals may
* reflect insertion or removal of only some entries. Similarly,
- * Iterators and Enumerations return elements reflecting the state of
- * the hash table at some point at or since the creation of the
+ * Iterators, Spliterators and Enumerations return elements reflecting the
+ * state of the hash table at some point at or since the creation of the
* iterator/enumeration. They do <em>not</em> throw {@link
- * ConcurrentModificationException}. However, iterators are designed
- * to be used by only one thread at a time. Bear in mind that the
- * results of aggregate status methods including {@code size}, {@code
- * isEmpty}, and {@code containsValue} are typically useful only when
- * a map is not undergoing concurrent updates in other threads.
+ * java.util.ConcurrentModificationException ConcurrentModificationException}.
+ * However, iterators are designed to be used by only one thread at a time.
+ * Bear in mind that the results of aggregate status methods including
+ * {@code size}, {@code isEmpty}, and {@code containsValue} are typically
+ * useful only when a map is not undergoing concurrent updates in other threads.
* Otherwise the results of these methods reflect transient states
* that may be adequate for monitoring or estimation purposes, but not
* for program control.
@@ -86,6 +100,19 @@
* hash table. To ameliorate impact, when keys are {@link Comparable},
* this class may use comparison order among keys to help break ties.
*
+ * <p>A {@link Set} projection of a ConcurrentHashMap may be created
+ * (using {@link #newKeySet()} or {@link #newKeySet(int)}), or viewed
+ * (using {@link #keySet(Object)} when only keys are of interest, and the
+ * mapped values are (perhaps transiently) not used or all take the
+ * same mapping value.
+ *
+ * <p>A ConcurrentHashMap can be used as a scalable frequency map (a
+ * form of histogram or multiset) by using {@link
+ * java.util.concurrent.atomic.LongAdder} values and initializing via
+ * {@link #computeIfAbsent computeIfAbsent}. For example, to add a count
+ * to a {@code ConcurrentHashMap<String,LongAdder> freqs}, you can use
+ * {@code freqs.computeIfAbsent(key, k -> new LongAdder()).increment();}
+ *
* <p>This class and its views and iterators implement all of the
* <em>optional</em> methods of the {@link Map} and {@link Iterator}
* interfaces.
@@ -93,15 +120,121 @@
* <p>Like {@link Hashtable} but unlike {@link HashMap}, this class
* does <em>not</em> allow {@code null} to be used as a key or value.
*
+ * <p>ConcurrentHashMaps support a set of sequential and parallel bulk
+ * operations that, unlike most (TODO(streams): link to Stream) methods, are designed
+ * to be safely, and often sensibly, applied even with maps that are
+ * being concurrently updated by other threads; for example, when
+ * computing a snapshot summary of the values in a shared registry.
+ * There are three kinds of operation, each with four forms, accepting
+ * functions with keys, values, entries, and (key, value) pairs as
+ * arguments and/or return values. Because the elements of a
+ * ConcurrentHashMap are not ordered in any particular way, and may be
+ * processed in different orders in different parallel executions, the
+ * correctness of supplied functions should not depend on any
+ * ordering, or on any other objects or values that may transiently
+ * change while computation is in progress; and except for forEach
+ * actions, should ideally be side-effect-free. Bulk operations on
+ * {@link java.util.Map.Entry} objects do not support method {@code
+ * setValue}.
+ *
+ * <ul>
+ * <li>forEach: Performs a given action on each element.
+ * A variant form applies a given transformation on each element
+ * before performing the action.
+ *
+ * <li>search: Returns the first available non-null result of
+ * applying a given function on each element; skipping further
+ * search when a result is found.
+ *
+ * <li>reduce: Accumulates each element. The supplied reduction
+ * function cannot rely on ordering (more formally, it should be
+ * both associative and commutative). There are five variants:
+ *
+ * <ul>
+ *
+ * <li>Plain reductions. (There is not a form of this method for
+ * (key, value) function arguments since there is no corresponding
+ * return type.)
+ *
+ * <li>Mapped reductions that accumulate the results of a given
+ * function applied to each element.
+ *
+ * <li>Reductions to scalar doubles, longs, and ints, using a
+ * given basis value.
+ *
+ * </ul>
+ * </ul>
+ *
+ * <p>These bulk operations accept a {@code parallelismThreshold}
+ * argument. Methods proceed sequentially if the current map size is
+ * estimated to be less than the given threshold. Using a value of
+ * {@code Long.MAX_VALUE} suppresses all parallelism. Using a value
+ * of {@code 1} results in maximal parallelism by partitioning into
+ * enough subtasks to fully utilize the {@link
+ * ForkJoinPool#commonPool()} that is used for all parallel
+ * computations. Normally, you would initially choose one of these
+ * extreme values, and then measure performance of using in-between
+ * values that trade off overhead versus throughput.
+ *
+ * <p>The concurrency properties of bulk operations follow
+ * from those of ConcurrentHashMap: Any non-null result returned
+ * from {@code get(key)} and related access methods bears a
+ * happens-before relation with the associated insertion or
+ * update. The result of any bulk operation reflects the
+ * composition of these per-element relations (but is not
+ * necessarily atomic with respect to the map as a whole unless it
+ * is somehow known to be quiescent). Conversely, because keys
+ * and values in the map are never null, null serves as a reliable
+ * atomic indicator of the current lack of any result. To
+ * maintain this property, null serves as an implicit basis for
+ * all non-scalar reduction operations. For the double, long, and
+ * int versions, the basis should be one that, when combined with
+ * any other value, returns that other value (more formally, it
+ * should be the identity element for the reduction). Most common
+ * reductions have these properties; for example, computing a sum
+ * with basis 0 or a minimum with basis MAX_VALUE.
+ *
+ * <p>Search and transformation functions provided as arguments
+ * should similarly return null to indicate the lack of any result
+ * (in which case it is not used). In the case of mapped
+ * reductions, this also enables transformations to serve as
+ * filters, returning null (or, in the case of primitive
+ * specializations, the identity basis) if the element should not
+ * be combined. You can create compound transformations and
+ * filterings by composing them yourself under this "null means
+ * there is nothing there now" rule before using them in search or
+ * reduce operations.
+ *
+ * <p>Methods accepting and/or returning Entry arguments maintain
+ * key-value associations. They may be useful for example when
+ * finding the key for the greatest value. Note that "plain" Entry
+ * arguments can be supplied using {@code new
+ * AbstractMap.SimpleEntry(k,v)}.
+ *
+ * <p>Bulk operations may complete abruptly, throwing an
+ * exception encountered in the application of a supplied
+ * function. Bear in mind when handling such exceptions that other
+ * concurrently executing functions could also have thrown
+ * exceptions, or would have done so if the first exception had
+ * not occurred.
+ *
+ * <p>Speedups for parallel compared to sequential forms are common
+ * but not guaranteed. Parallel operations involving brief functions
+ * on small maps may execute more slowly than sequential forms if the
+ * underlying work to parallelize the computation is more expensive
+ * than the computation itself. Similarly, parallelization may not
+ * lead to much actual parallelism if all processors are busy
+ * performing unrelated tasks.
+ *
+ * <p>All arguments to all task methods must be non-null.
+ *
* @since 1.5
* @author Doug Lea
* @param <K> the type of keys maintained by this map
* @param <V> the type of mapped values
*/
-// android-note: removed documentation about hidden newKeySet and newKeySet(int) APIs.
-// android-note: Added "extends AbstractMap<K, V>.
-public class ConcurrentHashMap<K,V> extends AbstractMap<K, V>
- implements ConcurrentMap<K,V>, Serializable {
+public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
+ implements ConcurrentMap<K,V>, Serializable {
private static final long serialVersionUID = 7249069246763182397L;
/*
@@ -316,7 +449,7 @@
*
* Maintaining API and serialization compatibility with previous
* versions of this class introduces several oddities. Mainly: We
- * leave untouched but unused constructor arguments refering to
+ * leave untouched but unused constructor arguments referring to
* concurrencyLevel. We accept a loadFactor constructor argument,
* but apply it only to initial table capacity (which is the only
* time that we can guarantee to honor it.) We also declare an
@@ -335,7 +468,6 @@
* bulk operations.
*/
-
/* ---------------- Constants -------------- */
/**
@@ -412,7 +544,7 @@
* The number of bits used for generation stamp in sizeCtl.
* Must be at least 6 for 32bit arrays.
*/
- private static int RESIZE_STAMP_BITS = 16;
+ private static final int RESIZE_STAMP_BITS = 16;
/**
* The maximum number of threads that can help resize.
@@ -428,19 +560,28 @@
/*
* Encodings for Node hash fields. See above for explanation.
*/
- static final int MOVED = 0x8fffffff; // (-1) hash for forwarding nodes
- static final int TREEBIN = 0x80000000; // hash for roots of trees
- static final int RESERVED = 0x80000001; // hash for transient reservations
+ static final int MOVED = -1; // hash for forwarding nodes
+ static final int TREEBIN = -2; // hash for roots of trees
+ static final int RESERVED = -3; // hash for transient reservations
static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash
/** Number of CPUS, to place bounds on some sizings */
static final int NCPU = Runtime.getRuntime().availableProcessors();
- /** For serialization compatibility. */
+ /**
+ * Serialized pseudo-fields, provided only for jdk7 compatibility.
+ * @serialField segments Segment[]
+ * The segments, each of which is a specialized hash table.
+ * @serialField segmentMask int
+ * Mask value for indexing into segments. The upper bits of a
+ * key's hash code are used to choose the segment.
+ * @serialField segmentShift int
+ * Shift value for indexing within segments.
+ */
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("segments", Segment[].class),
new ObjectStreamField("segmentMask", Integer.TYPE),
- new ObjectStreamField("segmentShift", Integer.TYPE)
+ new ObjectStreamField("segmentShift", Integer.TYPE),
};
/* ---------------- Nodes -------------- */
@@ -457,7 +598,7 @@
final int hash;
final K key;
volatile V val;
- Node<K,V> next;
+ volatile Node<K,V> next;
Node(int hash, K key, V val, Node<K,V> next) {
this.hash = hash;
@@ -466,10 +607,12 @@
this.next = next;
}
- public final K getKey() { return key; }
- public final V getValue() { return val; }
- public final int hashCode() { return key.hashCode() ^ val.hashCode(); }
- public final String toString(){ return key + "=" + val; }
+ public final K getKey() { return key; }
+ public final V getValue() { return val; }
+ public final int hashCode() { return key.hashCode() ^ val.hashCode(); }
+ public final String toString() {
+ return Helpers.mapEntryToString(key, val);
+ }
public final V setValue(V value) {
throw new UnsupportedOperationException();
}
@@ -582,8 +725,9 @@
* errors by users, these checks must operate on local variables,
* which accounts for some odd-looking inline assignments below.
* Note that calls to setTabAt always occur within locked regions,
- * and so do not need full volatile semantics, but still require
- * ordering to maintain concurrent readability.
+ * and so in principle require only release ordering, not
+ * full volatile semantics, but are currently coded as volatile
+ * writes to be conservative.
*/
@SuppressWarnings("unchecked")
@@ -597,7 +741,7 @@
}
static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
- U.putOrderedObject(tab, ((long)i << ASHIFT) + ABASE, v);
+ U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
}
/* ---------------- Fields -------------- */
@@ -892,6 +1036,8 @@
p.val = value;
}
}
+ else if (f instanceof ReservationNode)
+ throw new IllegalStateException("Recursive update");
}
}
if (binCount != 0) {
@@ -994,6 +1140,8 @@
}
}
}
+ else if (f instanceof ReservationNode)
+ throw new IllegalStateException("Recursive update");
}
}
if (validated) {
@@ -1054,16 +1202,15 @@
* operations. It does not support the {@code add} or
* {@code addAll} operations.
*
- * <p>The view's {@code iterator} is a "weakly consistent" iterator
- * that will never throw {@link ConcurrentModificationException},
- * and guarantees to traverse elements as they existed upon
- * construction of the iterator, and may (but is not guaranteed to)
- * reflect any modifications subsequent to construction.
+ * <p>The view's iterators and spliterators are
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
+ *
+ * <p>The view's {@code spliterator} reports {@link Spliterator#CONCURRENT},
+ * {@link Spliterator#DISTINCT}, and {@link Spliterator#NONNULL}.
*
* @return the set view
*/
- // android-note : changed KeySetView<K,V> to Set<K> to maintain API compatibility.
- public Set<K> keySet() {
+ public KeySetView<K,V> keySet() {
KeySetView<K,V> ks;
return (ks = keySet) != null ? ks : (keySet = new KeySetView<K,V>(this, null));
}
@@ -1078,11 +1225,11 @@
* {@code retainAll}, and {@code clear} operations. It does not
* support the {@code add} or {@code addAll} operations.
*
- * <p>The view's {@code iterator} is a "weakly consistent" iterator
- * that will never throw {@link ConcurrentModificationException},
- * and guarantees to traverse elements as they existed upon
- * construction of the iterator, and may (but is not guaranteed to)
- * reflect any modifications subsequent to construction.
+ * <p>The view's iterators and spliterators are
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
+ *
+ * <p>The view's {@code spliterator} reports {@link Spliterator#CONCURRENT}
+ * and {@link Spliterator#NONNULL}.
*
* @return the collection view
*/
@@ -1100,11 +1247,11 @@
* {@code removeAll}, {@code retainAll}, and {@code clear}
* operations.
*
- * <p>The view's {@code iterator} is a "weakly consistent" iterator
- * that will never throw {@link ConcurrentModificationException},
- * and guarantees to traverse elements as they existed upon
- * construction of the iterator, and may (but is not guaranteed to)
- * reflect any modifications subsequent to construction.
+ * <p>The view's iterators and spliterators are
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
+ *
+ * <p>The view's {@code spliterator} reports {@link Spliterator#CONCURRENT},
+ * {@link Spliterator#DISTINCT}, and {@link Spliterator#NONNULL}.
*
* @return the set view
*/
@@ -1202,7 +1349,7 @@
/**
* Stripped-down version of helper class used in previous version,
- * declared for the sake of serialization compatibility
+ * declared for the sake of serialization compatibility.
*/
static class Segment<K,V> extends ReentrantLock implements Serializable {
private static final long serialVersionUID = 2249069246763182397L;
@@ -1214,9 +1361,10 @@
* Saves the state of the {@code ConcurrentHashMap} instance to a
* stream (i.e., serializes it).
* @param s the stream
+ * @throws java.io.IOException if an I/O error occurs
* @serialData
- * the key (Object) and value (Object)
- * for each key-value mapping, followed by a null pair.
+ * the serialized fields, followed by the key (Object) and value
+ * (Object) for each key-value mapping, followed by a null pair.
* The key-value mappings are emitted in no particular order.
*/
private void writeObject(java.io.ObjectOutputStream s)
@@ -1231,7 +1379,8 @@
}
int segmentShift = 32 - sshift;
int segmentMask = ssize - 1;
- @SuppressWarnings("unchecked") Segment<K,V>[] segments = (Segment<K,V>[])
+ @SuppressWarnings("unchecked")
+ Segment<K,V>[] segments = (Segment<K,V>[])
new Segment<?,?>[DEFAULT_CONCURRENCY_LEVEL];
for (int i = 0; i < segments.length; ++i)
segments[i] = new Segment<K,V>(LOAD_FACTOR);
@@ -1251,12 +1400,14 @@
}
s.writeObject(null);
s.writeObject(null);
- segments = null; // throw away
}
/**
* Reconstitutes the instance from a stream (that is, deserializes it).
* @param s the stream
+ * @throws ClassNotFoundException if the class of a serialized object
+ * could not be found
+ * @throws java.io.IOException if an I/O error occurs
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
@@ -1272,8 +1423,10 @@
long size = 0L;
Node<K,V> p = null;
for (;;) {
- @SuppressWarnings("unchecked") K k = (K) s.readObject();
- @SuppressWarnings("unchecked") V v = (V) s.readObject();
+ @SuppressWarnings("unchecked")
+ K k = (K) s.readObject();
+ @SuppressWarnings("unchecked")
+ V v = (V) s.readObject();
if (k != null && v != null) {
p = new Node<K,V>(spread(k.hashCode()), k, v, p);
++size;
@@ -1292,7 +1445,7 @@
n = tableSizeFor(sz + (sz >>> 1) + 1);
}
@SuppressWarnings("unchecked")
- Node<K,V>[] tab = (Node<K,V>[])new Node<?,?>[n];
+ Node<K,V>[] tab = (Node<K,V>[])new Node<?,?>[n];
int mask = n - 1;
long added = 0L;
while (p != null) {
@@ -1400,17 +1553,544 @@
throw new NullPointerException();
return replaceNode(key, value, null);
}
+
+ // Overrides of JDK8+ Map extension method defaults
+
+ /**
+ * Returns the value to which the specified key is mapped, or the
+ * given default value if this map contains no mapping for the
+ * key.
+ *
+ * @param key the key whose associated value is to be returned
+ * @param defaultValue the value to return if this map contains
+ * no mapping for the given key
+ * @return the mapping for the key, if present; else the default value
+ * @throws NullPointerException if the specified key is null
+ */
+ public V getOrDefault(Object key, V defaultValue) {
+ V v;
+ return (v = get(key)) == null ? defaultValue : v;
+ }
+
+ public void forEach(BiConsumer<? super K, ? super V> action) {
+ if (action == null) throw new NullPointerException();
+ Node<K,V>[] t;
+ if ((t = table) != null) {
+ Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
+ for (Node<K,V> p; (p = it.advance()) != null; ) {
+ action.accept(p.key, p.val);
+ }
+ }
+ }
+
+ public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
+ if (function == null) throw new NullPointerException();
+ Node<K,V>[] t;
+ if ((t = table) != null) {
+ Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
+ for (Node<K,V> p; (p = it.advance()) != null; ) {
+ V oldValue = p.val;
+ for (K key = p.key;;) {
+ V newValue = function.apply(key, oldValue);
+ if (newValue == null)
+ throw new NullPointerException();
+ if (replaceNode(key, newValue, oldValue) != null ||
+ (oldValue = get(key)) == null)
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Helper method for EntrySetView.removeIf.
+ */
+ boolean removeEntryIf(Predicate<? super Entry<K,V>> function) {
+ if (function == null) throw new NullPointerException();
+ Node<K,V>[] t;
+ boolean removed = false;
+ if ((t = table) != null) {
+ Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
+ for (Node<K,V> p; (p = it.advance()) != null; ) {
+ K k = p.key;
+ V v = p.val;
+ Map.Entry<K,V> e = new AbstractMap.SimpleImmutableEntry<>(k, v);
+ if (function.test(e) && replaceNode(k, null, v) != null)
+ removed = true;
+ }
+ }
+ return removed;
+ }
+
+ /**
+ * Helper method for ValuesView.removeIf.
+ */
+ boolean removeValueIf(Predicate<? super V> function) {
+ if (function == null) throw new NullPointerException();
+ Node<K,V>[] t;
+ boolean removed = false;
+ if ((t = table) != null) {
+ Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
+ for (Node<K,V> p; (p = it.advance()) != null; ) {
+ K k = p.key;
+ V v = p.val;
+ if (function.test(v) && replaceNode(k, null, v) != null)
+ removed = true;
+ }
+ }
+ return removed;
+ }
+
+ /**
+ * If the specified key is not already associated with a value,
+ * attempts to compute its value using the given mapping function
+ * and enters it into this map unless {@code null}. The entire
+ * method invocation is performed atomically, so the function is
+ * applied at most once per key. Some attempted update operations
+ * on this map by other threads may be blocked while computation
+ * is in progress, so the computation should be short and simple,
+ * and must not attempt to update any other mappings of this map.
+ *
+ * @param key key with which the specified value is to be associated
+ * @param mappingFunction the function to compute a value
+ * @return the current (existing or computed) value associated with
+ * the specified key, or null if the computed value is null
+ * @throws NullPointerException if the specified key or mappingFunction
+ * is null
+ * @throws IllegalStateException if the computation detectably
+ * attempts a recursive update to this map that would
+ * otherwise never complete
+ * @throws RuntimeException or Error if the mappingFunction does so,
+ * in which case the mapping is left unestablished
+ */
+ public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
+ if (key == null || mappingFunction == null)
+ throw new NullPointerException();
+ int h = spread(key.hashCode());
+ V val = null;
+ int binCount = 0;
+ for (Node<K,V>[] tab = table;;) {
+ Node<K,V> f; int n, i, fh;
+ if (tab == null || (n = tab.length) == 0)
+ tab = initTable();
+ else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
+ Node<K,V> r = new ReservationNode<K,V>();
+ synchronized (r) {
+ if (casTabAt(tab, i, null, r)) {
+ binCount = 1;
+ Node<K,V> node = null;
+ try {
+ if ((val = mappingFunction.apply(key)) != null)
+ node = new Node<K,V>(h, key, val, null);
+ } finally {
+ setTabAt(tab, i, node);
+ }
+ }
+ }
+ if (binCount != 0)
+ break;
+ }
+ else if ((fh = f.hash) == MOVED)
+ tab = helpTransfer(tab, f);
+ else {
+ boolean added = false;
+ synchronized (f) {
+ if (tabAt(tab, i) == f) {
+ if (fh >= 0) {
+ binCount = 1;
+ for (Node<K,V> e = f;; ++binCount) {
+ K ek;
+ if (e.hash == h &&
+ ((ek = e.key) == key ||
+ (ek != null && key.equals(ek)))) {
+ val = e.val;
+ break;
+ }
+ Node<K,V> pred = e;
+ if ((e = e.next) == null) {
+ if ((val = mappingFunction.apply(key)) != null) {
+ if (pred.next != null)
+ throw new IllegalStateException("Recursive update");
+ added = true;
+ pred.next = new Node<K,V>(h, key, val, null);
+ }
+ break;
+ }
+ }
+ }
+ else if (f instanceof TreeBin) {
+ binCount = 2;
+ TreeBin<K,V> t = (TreeBin<K,V>)f;
+ TreeNode<K,V> r, p;
+ if ((r = t.root) != null &&
+ (p = r.findTreeNode(h, key, null)) != null)
+ val = p.val;
+ else if ((val = mappingFunction.apply(key)) != null) {
+ added = true;
+ t.putTreeVal(h, key, val);
+ }
+ }
+ else if (f instanceof ReservationNode)
+ throw new IllegalStateException("Recursive update");
+ }
+ }
+ if (binCount != 0) {
+ if (binCount >= TREEIFY_THRESHOLD)
+ treeifyBin(tab, i);
+ if (!added)
+ return val;
+ break;
+ }
+ }
+ }
+ if (val != null)
+ addCount(1L, binCount);
+ return val;
+ }
+
+ /**
+ * If the value for the specified key is present, attempts to
+ * compute a new mapping given the key and its current mapped
+ * value. The entire method invocation is performed atomically.
+ * Some attempted update operations on this map by other threads
+ * may be blocked while computation is in progress, so the
+ * computation should be short and simple, and must not attempt to
+ * update any other mappings of this map.
+ *
+ * @param key key with which a value may be associated
+ * @param remappingFunction the function to compute a value
+ * @return the new value associated with the specified key, or null if none
+ * @throws NullPointerException if the specified key or remappingFunction
+ * is null
+ * @throws IllegalStateException if the computation detectably
+ * attempts a recursive update to this map that would
+ * otherwise never complete
+ * @throws RuntimeException or Error if the remappingFunction does so,
+ * in which case the mapping is unchanged
+ */
+ public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ if (key == null || remappingFunction == null)
+ throw new NullPointerException();
+ int h = spread(key.hashCode());
+ V val = null;
+ int delta = 0;
+ int binCount = 0;
+ for (Node<K,V>[] tab = table;;) {
+ Node<K,V> f; int n, i, fh;
+ if (tab == null || (n = tab.length) == 0)
+ tab = initTable();
+ else if ((f = tabAt(tab, i = (n - 1) & h)) == null)
+ break;
+ else if ((fh = f.hash) == MOVED)
+ tab = helpTransfer(tab, f);
+ else {
+ synchronized (f) {
+ if (tabAt(tab, i) == f) {
+ if (fh >= 0) {
+ binCount = 1;
+ for (Node<K,V> e = f, pred = null;; ++binCount) {
+ K ek;
+ if (e.hash == h &&
+ ((ek = e.key) == key ||
+ (ek != null && key.equals(ek)))) {
+ val = remappingFunction.apply(key, e.val);
+ if (val != null)
+ e.val = val;
+ else {
+ delta = -1;
+ Node<K,V> en = e.next;
+ if (pred != null)
+ pred.next = en;
+ else
+ setTabAt(tab, i, en);
+ }
+ break;
+ }
+ pred = e;
+ if ((e = e.next) == null)
+ break;
+ }
+ }
+ else if (f instanceof TreeBin) {
+ binCount = 2;
+ TreeBin<K,V> t = (TreeBin<K,V>)f;
+ TreeNode<K,V> r, p;
+ if ((r = t.root) != null &&
+ (p = r.findTreeNode(h, key, null)) != null) {
+ val = remappingFunction.apply(key, p.val);
+ if (val != null)
+ p.val = val;
+ else {
+ delta = -1;
+ if (t.removeTreeNode(p))
+ setTabAt(tab, i, untreeify(t.first));
+ }
+ }
+ }
+ else if (f instanceof ReservationNode)
+ throw new IllegalStateException("Recursive update");
+ }
+ }
+ if (binCount != 0)
+ break;
+ }
+ }
+ if (delta != 0)
+ addCount((long)delta, binCount);
+ return val;
+ }
+
+ /**
+ * Attempts to compute a mapping for the specified key and its
+ * current mapped value (or {@code null} if there is no current
+ * mapping). The entire method invocation is performed atomically.
+ * Some attempted update operations on this map by other threads
+ * may be blocked while computation is in progress, so the
+ * computation should be short and simple, and must not attempt to
+ * update any other mappings of this Map.
+ *
+ * @param key key with which the specified value is to be associated
+ * @param remappingFunction the function to compute a value
+ * @return the new value associated with the specified key, or null if none
+ * @throws NullPointerException if the specified key or remappingFunction
+ * is null
+ * @throws IllegalStateException if the computation detectably
+ * attempts a recursive update to this map that would
+ * otherwise never complete
+ * @throws RuntimeException or Error if the remappingFunction does so,
+ * in which case the mapping is unchanged
+ */
+ public V compute(K key,
+ BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ if (key == null || remappingFunction == null)
+ throw new NullPointerException();
+ int h = spread(key.hashCode());
+ V val = null;
+ int delta = 0;
+ int binCount = 0;
+ for (Node<K,V>[] tab = table;;) {
+ Node<K,V> f; int n, i, fh;
+ if (tab == null || (n = tab.length) == 0)
+ tab = initTable();
+ else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
+ Node<K,V> r = new ReservationNode<K,V>();
+ synchronized (r) {
+ if (casTabAt(tab, i, null, r)) {
+ binCount = 1;
+ Node<K,V> node = null;
+ try {
+ if ((val = remappingFunction.apply(key, null)) != null) {
+ delta = 1;
+ node = new Node<K,V>(h, key, val, null);
+ }
+ } finally {
+ setTabAt(tab, i, node);
+ }
+ }
+ }
+ if (binCount != 0)
+ break;
+ }
+ else if ((fh = f.hash) == MOVED)
+ tab = helpTransfer(tab, f);
+ else {
+ synchronized (f) {
+ if (tabAt(tab, i) == f) {
+ if (fh >= 0) {
+ binCount = 1;
+ for (Node<K,V> e = f, pred = null;; ++binCount) {
+ K ek;
+ if (e.hash == h &&
+ ((ek = e.key) == key ||
+ (ek != null && key.equals(ek)))) {
+ val = remappingFunction.apply(key, e.val);
+ if (val != null)
+ e.val = val;
+ else {
+ delta = -1;
+ Node<K,V> en = e.next;
+ if (pred != null)
+ pred.next = en;
+ else
+ setTabAt(tab, i, en);
+ }
+ break;
+ }
+ pred = e;
+ if ((e = e.next) == null) {
+ val = remappingFunction.apply(key, null);
+ if (val != null) {
+ if (pred.next != null)
+ throw new IllegalStateException("Recursive update");
+ delta = 1;
+ pred.next =
+ new Node<K,V>(h, key, val, null);
+ }
+ break;
+ }
+ }
+ }
+ else if (f instanceof TreeBin) {
+ binCount = 1;
+ TreeBin<K,V> t = (TreeBin<K,V>)f;
+ TreeNode<K,V> r, p;
+ if ((r = t.root) != null)
+ p = r.findTreeNode(h, key, null);
+ else
+ p = null;
+ V pv = (p == null) ? null : p.val;
+ val = remappingFunction.apply(key, pv);
+ if (val != null) {
+ if (p != null)
+ p.val = val;
+ else {
+ delta = 1;
+ t.putTreeVal(h, key, val);
+ }
+ }
+ else if (p != null) {
+ delta = -1;
+ if (t.removeTreeNode(p))
+ setTabAt(tab, i, untreeify(t.first));
+ }
+ }
+ else if (f instanceof ReservationNode)
+ throw new IllegalStateException("Recursive update");
+ }
+ }
+ if (binCount != 0) {
+ if (binCount >= TREEIFY_THRESHOLD)
+ treeifyBin(tab, i);
+ break;
+ }
+ }
+ }
+ if (delta != 0)
+ addCount((long)delta, binCount);
+ return val;
+ }
+
+ /**
+ * If the specified key is not already associated with a
+ * (non-null) value, associates it with the given value.
+ * Otherwise, replaces the value with the results of the given
+ * remapping function, or removes if {@code null}. The entire
+ * method invocation is performed atomically. Some attempted
+ * update operations on this map by other threads may be blocked
+ * while computation is in progress, so the computation should be
+ * short and simple, and must not attempt to update any other
+ * mappings of this Map.
+ *
+ * @param key key with which the specified value is to be associated
+ * @param value the value to use if absent
+ * @param remappingFunction the function to recompute a value if present
+ * @return the new value associated with the specified key, or null if none
+ * @throws NullPointerException if the specified key or the
+ * remappingFunction is null
+ * @throws RuntimeException or Error if the remappingFunction does so,
+ * in which case the mapping is unchanged
+ */
+ public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
+ if (key == null || value == null || remappingFunction == null)
+ throw new NullPointerException();
+ int h = spread(key.hashCode());
+ V val = null;
+ int delta = 0;
+ int binCount = 0;
+ for (Node<K,V>[] tab = table;;) {
+ Node<K,V> f; int n, i, fh;
+ if (tab == null || (n = tab.length) == 0)
+ tab = initTable();
+ else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
+ if (casTabAt(tab, i, null, new Node<K,V>(h, key, value, null))) {
+ delta = 1;
+ val = value;
+ break;
+ }
+ }
+ else if ((fh = f.hash) == MOVED)
+ tab = helpTransfer(tab, f);
+ else {
+ synchronized (f) {
+ if (tabAt(tab, i) == f) {
+ if (fh >= 0) {
+ binCount = 1;
+ for (Node<K,V> e = f, pred = null;; ++binCount) {
+ K ek;
+ if (e.hash == h &&
+ ((ek = e.key) == key ||
+ (ek != null && key.equals(ek)))) {
+ val = remappingFunction.apply(e.val, value);
+ if (val != null)
+ e.val = val;
+ else {
+ delta = -1;
+ Node<K,V> en = e.next;
+ if (pred != null)
+ pred.next = en;
+ else
+ setTabAt(tab, i, en);
+ }
+ break;
+ }
+ pred = e;
+ if ((e = e.next) == null) {
+ delta = 1;
+ val = value;
+ pred.next =
+ new Node<K,V>(h, key, val, null);
+ break;
+ }
+ }
+ }
+ else if (f instanceof TreeBin) {
+ binCount = 2;
+ TreeBin<K,V> t = (TreeBin<K,V>)f;
+ TreeNode<K,V> r = t.root;
+ TreeNode<K,V> p = (r == null) ? null :
+ r.findTreeNode(h, key, null);
+ val = (p == null) ? value :
+ remappingFunction.apply(p.val, value);
+ if (val != null) {
+ if (p != null)
+ p.val = val;
+ else {
+ delta = 1;
+ t.putTreeVal(h, key, val);
+ }
+ }
+ else if (p != null) {
+ delta = -1;
+ if (t.removeTreeNode(p))
+ setTabAt(tab, i, untreeify(t.first));
+ }
+ }
+ else if (f instanceof ReservationNode)
+ throw new IllegalStateException("Recursive update");
+ }
+ }
+ if (binCount != 0) {
+ if (binCount >= TREEIFY_THRESHOLD)
+ treeifyBin(tab, i);
+ break;
+ }
+ }
+ }
+ if (delta != 0)
+ addCount((long)delta, binCount);
+ return val;
+ }
+
// Hashtable legacy methods
/**
- * Legacy method testing if some key maps into the specified value
- * in this table.
+ * Tests if some key maps into the specified value in this table.
*
- * This method is identical in functionality to
+ * <p>Note that this method is identical in functionality to
* {@link #containsValue(Object)}, and exists solely to ensure
* full compatibility with class {@link java.util.Hashtable},
* which supported this method prior to introduction of the
- * Java Collections framework.
+ * Java Collections Framework.
*
* @param value a value to search for
* @return {@code true} if and only if some key maps to the
@@ -1419,11 +2099,7 @@
* {@code false} otherwise
* @throws NullPointerException if the specified value is null
*/
- // android-note : removed @deprecated tag from javadoc.
public boolean contains(Object value) {
- // BEGIN android-note
- // removed deprecation
- // END android-note
return containsValue(value);
}
@@ -1462,8 +2138,6 @@
*
* @return the number of mappings
* @since 1.8
- *
- * @hide
*/
public long mappingCount() {
long n = sumCount();
@@ -1477,8 +2151,6 @@
* @param <K> the element type of the returned set
* @return the new set
* @since 1.8
- *
- * @hide
*/
public static <K> KeySetView<K,Boolean> newKeySet() {
return new KeySetView<K,Boolean>
@@ -1496,8 +2168,6 @@
* @throws IllegalArgumentException if the initial capacity of
* elements is negative
* @since 1.8
- *
- * @hide
*/
public static <K> KeySetView<K,Boolean> newKeySet(int initialCapacity) {
return new KeySetView<K,Boolean>
@@ -1514,8 +2184,6 @@
* @param mappedValue the mapped value to use for any additions
* @return the set view
* @throws NullPointerException if the mappedValue is null
- *
- * @hide
*/
public KeySetView<K,V> keySet(V mappedValue) {
if (mappedValue == null)
@@ -1536,25 +2204,34 @@
}
Node<K,V> find(int h, Object k) {
- Node<K,V> e; int n;
- Node<K,V>[] tab = nextTable;
- if (k != null && tab != null && (n = tab.length) > 0 &&
- (e = tabAt(tab, (n - 1) & h)) != null) {
- do {
+ // loop to avoid arbitrarily deep recursion on forwarding nodes
+ outer: for (Node<K,V>[] tab = nextTable;;) {
+ Node<K,V> e; int n;
+ if (k == null || tab == null || (n = tab.length) == 0 ||
+ (e = tabAt(tab, (n - 1) & h)) == null)
+ return null;
+ for (;;) {
int eh; K ek;
if ((eh = e.hash) == h &&
((ek = e.key) == k || (ek != null && k.equals(ek))))
return e;
- if (eh < 0)
- return e.find(h, k);
- } while ((e = e.next) != null);
+ if (eh < 0) {
+ if (e instanceof ForwardingNode) {
+ tab = ((ForwardingNode<K,V>)e).nextTable;
+ continue outer;
+ }
+ else
+ return e.find(h, k);
+ }
+ if ((e = e.next) == null)
+ return null;
+ }
}
- return null;
}
}
/**
- * A place-holder node used in computeIfAbsent and compute
+ * A place-holder node used in computeIfAbsent and compute.
*/
static final class ReservationNode<K,V> extends Node<K,V> {
ReservationNode() {
@@ -1616,14 +2293,13 @@
CounterCell[] as; long b, s;
if ((as = counterCells) != null ||
!U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
- CounterHashCode hc; CounterCell a; long v; int m;
+ CounterCell a; long v; int m;
boolean uncontended = true;
- if ((hc = threadCounterHashCode.get()) == null ||
- as == null || (m = as.length - 1) < 0 ||
- (a = as[m & hc.code]) == null ||
+ if (as == null || (m = as.length - 1) < 0 ||
+ (a = as[ThreadLocalRandom.getProbe() & m]) == null ||
!(uncontended =
U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
- fullAddCount(x, hc, uncontended);
+ fullAddCount(x, uncontended);
return;
}
if (check <= 1)
@@ -1704,17 +2380,8 @@
break;
else if (tab == table) {
int rs = resizeStamp(n);
- if (sc < 0) {
- Node<K,V>[] nt;
- if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
- sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
- transferIndex <= 0)
- break;
- if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
- transfer(tab, nt);
- }
- else if (U.compareAndSwapInt(this, SIZECTL, sc,
- (rs << RESIZE_STAMP_SHIFT) + 2))
+ if (U.compareAndSwapInt(this, SIZECTL, sc,
+ (rs << RESIZE_STAMP_SHIFT) + 2))
transfer(tab, null);
}
}
@@ -1857,6 +2524,112 @@
}
}
+ /* ---------------- Counter support -------------- */
+
+ /**
+ * A padded cell for distributing counts. Adapted from LongAdder
+ * and Striped64. See their internal docs for explanation.
+ */
+ //@jdk.internal.vm.annotation.Contended // android-removed
+ static final class CounterCell {
+ volatile long value;
+ CounterCell(long x) { value = x; }
+ }
+
+ final long sumCount() {
+ CounterCell[] as = counterCells; CounterCell a;
+ long sum = baseCount;
+ if (as != null) {
+ for (int i = 0; i < as.length; ++i) {
+ if ((a = as[i]) != null)
+ sum += a.value;
+ }
+ }
+ return sum;
+ }
+
+ // See LongAdder version for explanation
+ private final void fullAddCount(long x, boolean wasUncontended) {
+ int h;
+ if ((h = ThreadLocalRandom.getProbe()) == 0) {
+ ThreadLocalRandom.localInit(); // force initialization
+ h = ThreadLocalRandom.getProbe();
+ wasUncontended = true;
+ }
+ boolean collide = false; // True if last slot nonempty
+ for (;;) {
+ CounterCell[] as; CounterCell a; int n; long v;
+ if ((as = counterCells) != null && (n = as.length) > 0) {
+ if ((a = as[(n - 1) & h]) == null) {
+ if (cellsBusy == 0) { // Try to attach new Cell
+ CounterCell r = new CounterCell(x); // Optimistic create
+ if (cellsBusy == 0 &&
+ U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
+ boolean created = false;
+ try { // Recheck under lock
+ CounterCell[] rs; int m, j;
+ if ((rs = counterCells) != null &&
+ (m = rs.length) > 0 &&
+ rs[j = (m - 1) & h] == null) {
+ rs[j] = r;
+ created = true;
+ }
+ } finally {
+ cellsBusy = 0;
+ }
+ if (created)
+ break;
+ continue; // Slot is now non-empty
+ }
+ }
+ collide = false;
+ }
+ else if (!wasUncontended) // CAS already known to fail
+ wasUncontended = true; // Continue after rehash
+ else if (U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))
+ break;
+ else if (counterCells != as || n >= NCPU)
+ collide = false; // At max size or stale
+ else if (!collide)
+ collide = true;
+ else if (cellsBusy == 0 &&
+ U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
+ try {
+ if (counterCells == as) {// Expand table unless stale
+ CounterCell[] rs = new CounterCell[n << 1];
+ for (int i = 0; i < n; ++i)
+ rs[i] = as[i];
+ counterCells = rs;
+ }
+ } finally {
+ cellsBusy = 0;
+ }
+ collide = false;
+ continue; // Retry with expanded table
+ }
+ h = ThreadLocalRandom.advanceProbe(h);
+ }
+ else if (cellsBusy == 0 && counterCells == as &&
+ U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
+ boolean init = false;
+ try { // Initialize table
+ if (counterCells == as) {
+ CounterCell[] rs = new CounterCell[2];
+ rs[h & 1] = new CounterCell(x);
+ counterCells = rs;
+ init = true;
+ }
+ } finally {
+ cellsBusy = 0;
+ }
+ if (init)
+ break;
+ }
+ else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x))
+ break; // Fall back on using base
+ }
+ }
+
/* ---------------- Conversion from/to TreeBins -------------- */
/**
@@ -1864,7 +2637,7 @@
* too small, in which case resizes instead.
*/
private final void treeifyBin(Node<K,V>[] tab, int index) {
- Node<K,V> b; int n, sc;
+ Node<K,V> b; int n;
if (tab != null) {
if ((n = tab.length) < MIN_TREEIFY_CAPACITY)
tryPresize(n << 1);
@@ -1908,7 +2681,7 @@
/* ---------------- TreeNodes -------------- */
/**
- * Nodes for use in TreeBins
+ * Nodes for use in TreeBins.
*/
static final class TreeNode<K,V> extends Node<K,V> {
TreeNode<K,V> parent; // red-black tree links
@@ -1961,7 +2734,6 @@
}
}
-
/* ---------------- TreeBins -------------- */
/**
@@ -2028,7 +2800,7 @@
(kc = comparableClassFor(k)) == null) ||
(dir = compareComparables(kc, k, pk)) == 0)
dir = tieBreakOrder(k, pk);
- TreeNode<K,V> xp = p;
+ TreeNode<K,V> xp = p;
if ((p = (dir <= 0) ? p.left : p.right) == null) {
x.parent = xp;
if (dir <= 0)
@@ -2106,13 +2878,9 @@
p = ((r = root) == null ? null :
r.findTreeNode(h, k, null));
} finally {
-
Thread w;
- int ls;
- do {} while (!U.compareAndSwapInt
- (this, LOCKSTATE,
- ls = lockState, ls - READER));
- if (ls == (READER|WAITER) && (w = waiter) != null)
+ if (U.getAndAddInt(this, LOCKSTATE, -READER) ==
+ (READER|WAITER) && (w = waiter) != null)
LockSupport.unpark(w);
}
return p;
@@ -2126,10 +2894,6 @@
* Finds or adds a node.
* @return null if added
*/
- /**
- * Finds or adds a node.
- * @return null if added
- */
final TreeNode<K,V> putTreeVal(int h, K k, V v) {
Class<?> kc = null;
boolean searched = false;
@@ -2480,7 +3244,7 @@
}
/**
- * Recursive invariant check
+ * Checks invariants recursively for the tree of Nodes rooted at t.
*/
static <K,V> boolean checkInvariants(TreeNode<K,V> t) {
TreeNode<K,V> tp = t.parent, tl = t.left, tr = t.right,
@@ -2504,15 +3268,13 @@
return true;
}
- private static final sun.misc.Unsafe U;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
private static final long LOCKSTATE;
static {
try {
- U = sun.misc.Unsafe.getUnsafe();
- Class<?> k = TreeBin.class;
LOCKSTATE = U.objectFieldOffset
- (k.getDeclaredField("lockState"));
- } catch (Exception e) {
+ (TreeBin.class.getDeclaredField("lockState"));
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
@@ -2727,7 +3489,7 @@
}
/**
- * Exported Entry for EntryIterator
+ * Exported Entry for EntryIterator.
*/
static final class MapEntry<K,V> implements Map.Entry<K,V> {
final K key; // non-null
@@ -2741,7 +3503,9 @@
public K getKey() { return key; }
public V getValue() { return val; }
public int hashCode() { return key.hashCode() ^ val.hashCode(); }
- public String toString() { return key + "=" + val; }
+ public String toString() {
+ return Helpers.mapEntryToString(key, val);
+ }
public boolean equals(Object o) {
Object k, v; Map.Entry<?,?> e;
@@ -2769,6 +3533,866 @@
}
}
+ static final class KeySpliterator<K,V> extends Traverser<K,V>
+ implements Spliterator<K> {
+ long est; // size estimate
+ KeySpliterator(Node<K,V>[] tab, int size, int index, int limit,
+ long est) {
+ super(tab, size, index, limit);
+ this.est = est;
+ }
+
+ public KeySpliterator<K,V> trySplit() {
+ int i, f, h;
+ return (h = ((i = baseIndex) + (f = baseLimit)) >>> 1) <= i ? null :
+ new KeySpliterator<K,V>(tab, baseSize, baseLimit = h,
+ f, est >>>= 1);
+ }
+
+ public void forEachRemaining(Consumer<? super K> action) {
+ if (action == null) throw new NullPointerException();
+ for (Node<K,V> p; (p = advance()) != null;)
+ action.accept(p.key);
+ }
+
+ public boolean tryAdvance(Consumer<? super K> action) {
+ if (action == null) throw new NullPointerException();
+ Node<K,V> p;
+ if ((p = advance()) == null)
+ return false;
+ action.accept(p.key);
+ return true;
+ }
+
+ public long estimateSize() { return est; }
+
+ public int characteristics() {
+ return Spliterator.DISTINCT | Spliterator.CONCURRENT |
+ Spliterator.NONNULL;
+ }
+ }
+
+ static final class ValueSpliterator<K,V> extends Traverser<K,V>
+ implements Spliterator<V> {
+ long est; // size estimate
+ ValueSpliterator(Node<K,V>[] tab, int size, int index, int limit,
+ long est) {
+ super(tab, size, index, limit);
+ this.est = est;
+ }
+
+ public ValueSpliterator<K,V> trySplit() {
+ int i, f, h;
+ return (h = ((i = baseIndex) + (f = baseLimit)) >>> 1) <= i ? null :
+ new ValueSpliterator<K,V>(tab, baseSize, baseLimit = h,
+ f, est >>>= 1);
+ }
+
+ public void forEachRemaining(Consumer<? super V> action) {
+ if (action == null) throw new NullPointerException();
+ for (Node<K,V> p; (p = advance()) != null;)
+ action.accept(p.val);
+ }
+
+ public boolean tryAdvance(Consumer<? super V> action) {
+ if (action == null) throw new NullPointerException();
+ Node<K,V> p;
+ if ((p = advance()) == null)
+ return false;
+ action.accept(p.val);
+ return true;
+ }
+
+ public long estimateSize() { return est; }
+
+ public int characteristics() {
+ return Spliterator.CONCURRENT | Spliterator.NONNULL;
+ }
+ }
+
+ static final class EntrySpliterator<K,V> extends Traverser<K,V>
+ implements Spliterator<Map.Entry<K,V>> {
+ final ConcurrentHashMap<K,V> map; // To export MapEntry
+ long est; // size estimate
+ EntrySpliterator(Node<K,V>[] tab, int size, int index, int limit,
+ long est, ConcurrentHashMap<K,V> map) {
+ super(tab, size, index, limit);
+ this.map = map;
+ this.est = est;
+ }
+
+ public EntrySpliterator<K,V> trySplit() {
+ int i, f, h;
+ return (h = ((i = baseIndex) + (f = baseLimit)) >>> 1) <= i ? null :
+ new EntrySpliterator<K,V>(tab, baseSize, baseLimit = h,
+ f, est >>>= 1, map);
+ }
+
+ public void forEachRemaining(Consumer<? super Map.Entry<K,V>> action) {
+ if (action == null) throw new NullPointerException();
+ for (Node<K,V> p; (p = advance()) != null; )
+ action.accept(new MapEntry<K,V>(p.key, p.val, map));
+ }
+
+ public boolean tryAdvance(Consumer<? super Map.Entry<K,V>> action) {
+ if (action == null) throw new NullPointerException();
+ Node<K,V> p;
+ if ((p = advance()) == null)
+ return false;
+ action.accept(new MapEntry<K,V>(p.key, p.val, map));
+ return true;
+ }
+
+ public long estimateSize() { return est; }
+
+ public int characteristics() {
+ return Spliterator.DISTINCT | Spliterator.CONCURRENT |
+ Spliterator.NONNULL;
+ }
+ }
+
+ // Parallel bulk operations
+
+ /**
+ * Computes initial batch value for bulk tasks. The returned value
+ * is approximately exp2 of the number of times (minus one) to
+ * split task by two before executing leaf action. This value is
+ * faster to compute and more convenient to use as a guide to
+ * splitting than is the depth, since it is used while dividing by
+ * two anyway.
+ */
+ final int batchFor(long b) {
+ long n;
+ if (b == Long.MAX_VALUE || (n = sumCount()) <= 1L || n < b)
+ return 0;
+ int sp = ForkJoinPool.getCommonPoolParallelism() << 2; // slack of 4
+ return (b <= 0L || (n /= b) >= sp) ? sp : (int)n;
+ }
+
+ /**
+ * Performs the given action for each (key, value).
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param action the action
+ * @since 1.8
+ */
+ public void forEach(long parallelismThreshold,
+ BiConsumer<? super K,? super V> action) {
+ if (action == null) throw new NullPointerException();
+ new ForEachMappingTask<K,V>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ action).invoke();
+ }
+
+ /**
+ * Performs the given action for each non-null transformation
+ * of each (key, value).
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param transformer a function returning the transformation
+ * for an element, or null if there is no transformation (in
+ * which case the action is not applied)
+ * @param action the action
+ * @param <U> the return type of the transformer
+ * @since 1.8
+ */
+ public <U> void forEach(long parallelismThreshold,
+ BiFunction<? super K, ? super V, ? extends U> transformer,
+ Consumer<? super U> action) {
+ if (transformer == null || action == null)
+ throw new NullPointerException();
+ new ForEachTransformedMappingTask<K,V,U>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ transformer, action).invoke();
+ }
+
+ /**
+ * Returns a non-null result from applying the given search
+ * function on each (key, value), or null if none. Upon
+ * success, further element processing is suppressed and the
+ * results of any other parallel invocations of the search
+ * function are ignored.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param searchFunction a function returning a non-null
+ * result on success, else null
+ * @param <U> the return type of the search function
+ * @return a non-null result from applying the given search
+ * function on each (key, value), or null if none
+ * @since 1.8
+ */
+ public <U> U search(long parallelismThreshold,
+ BiFunction<? super K, ? super V, ? extends U> searchFunction) {
+ if (searchFunction == null) throw new NullPointerException();
+ return new SearchMappingsTask<K,V,U>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ searchFunction, new AtomicReference<U>()).invoke();
+ }
+
+ /**
+ * Returns the result of accumulating the given transformation
+ * of all (key, value) pairs using the given reducer to
+ * combine values, or null if none.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param transformer a function returning the transformation
+ * for an element, or null if there is no transformation (in
+ * which case it is not combined)
+ * @param reducer a commutative associative combining function
+ * @param <U> the return type of the transformer
+ * @return the result of accumulating the given transformation
+ * of all (key, value) pairs
+ * @since 1.8
+ */
+ public <U> U reduce(long parallelismThreshold,
+ BiFunction<? super K, ? super V, ? extends U> transformer,
+ BiFunction<? super U, ? super U, ? extends U> reducer) {
+ if (transformer == null || reducer == null)
+ throw new NullPointerException();
+ return new MapReduceMappingsTask<K,V,U>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ null, transformer, reducer).invoke();
+ }
+
+ /**
+ * Returns the result of accumulating the given transformation
+ * of all (key, value) pairs using the given reducer to
+ * combine values, and the given basis as an identity value.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param transformer a function returning the transformation
+ * for an element
+ * @param basis the identity (initial default value) for the reduction
+ * @param reducer a commutative associative combining function
+ * @return the result of accumulating the given transformation
+ * of all (key, value) pairs
+ * @since 1.8
+ */
+ public double reduceToDouble(long parallelismThreshold,
+ ToDoubleBiFunction<? super K, ? super V> transformer,
+ double basis,
+ DoubleBinaryOperator reducer) {
+ if (transformer == null || reducer == null)
+ throw new NullPointerException();
+ return new MapReduceMappingsToDoubleTask<K,V>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ null, transformer, basis, reducer).invoke();
+ }
+
+ /**
+ * Returns the result of accumulating the given transformation
+ * of all (key, value) pairs using the given reducer to
+ * combine values, and the given basis as an identity value.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param transformer a function returning the transformation
+ * for an element
+ * @param basis the identity (initial default value) for the reduction
+ * @param reducer a commutative associative combining function
+ * @return the result of accumulating the given transformation
+ * of all (key, value) pairs
+ * @since 1.8
+ */
+ public long reduceToLong(long parallelismThreshold,
+ ToLongBiFunction<? super K, ? super V> transformer,
+ long basis,
+ LongBinaryOperator reducer) {
+ if (transformer == null || reducer == null)
+ throw new NullPointerException();
+ return new MapReduceMappingsToLongTask<K,V>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ null, transformer, basis, reducer).invoke();
+ }
+
+ /**
+ * Returns the result of accumulating the given transformation
+ * of all (key, value) pairs using the given reducer to
+ * combine values, and the given basis as an identity value.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param transformer a function returning the transformation
+ * for an element
+ * @param basis the identity (initial default value) for the reduction
+ * @param reducer a commutative associative combining function
+ * @return the result of accumulating the given transformation
+ * of all (key, value) pairs
+ * @since 1.8
+ */
+ public int reduceToInt(long parallelismThreshold,
+ ToIntBiFunction<? super K, ? super V> transformer,
+ int basis,
+ IntBinaryOperator reducer) {
+ if (transformer == null || reducer == null)
+ throw new NullPointerException();
+ return new MapReduceMappingsToIntTask<K,V>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ null, transformer, basis, reducer).invoke();
+ }
+
+ /**
+ * Performs the given action for each key.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param action the action
+ * @since 1.8
+ */
+ public void forEachKey(long parallelismThreshold,
+ Consumer<? super K> action) {
+ if (action == null) throw new NullPointerException();
+ new ForEachKeyTask<K,V>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ action).invoke();
+ }
+
+ /**
+ * Performs the given action for each non-null transformation
+ * of each key.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param transformer a function returning the transformation
+ * for an element, or null if there is no transformation (in
+ * which case the action is not applied)
+ * @param action the action
+ * @param <U> the return type of the transformer
+ * @since 1.8
+ */
+ public <U> void forEachKey(long parallelismThreshold,
+ Function<? super K, ? extends U> transformer,
+ Consumer<? super U> action) {
+ if (transformer == null || action == null)
+ throw new NullPointerException();
+ new ForEachTransformedKeyTask<K,V,U>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ transformer, action).invoke();
+ }
+
+ /**
+ * Returns a non-null result from applying the given search
+ * function on each key, or null if none. Upon success,
+ * further element processing is suppressed and the results of
+ * any other parallel invocations of the search function are
+ * ignored.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param searchFunction a function returning a non-null
+ * result on success, else null
+ * @param <U> the return type of the search function
+ * @return a non-null result from applying the given search
+ * function on each key, or null if none
+ * @since 1.8
+ */
+ public <U> U searchKeys(long parallelismThreshold,
+ Function<? super K, ? extends U> searchFunction) {
+ if (searchFunction == null) throw new NullPointerException();
+ return new SearchKeysTask<K,V,U>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ searchFunction, new AtomicReference<U>()).invoke();
+ }
+
+ /**
+ * Returns the result of accumulating all keys using the given
+ * reducer to combine values, or null if none.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param reducer a commutative associative combining function
+ * @return the result of accumulating all keys using the given
+ * reducer to combine values, or null if none
+ * @since 1.8
+ */
+ public K reduceKeys(long parallelismThreshold,
+ BiFunction<? super K, ? super K, ? extends K> reducer) {
+ if (reducer == null) throw new NullPointerException();
+ return new ReduceKeysTask<K,V>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ null, reducer).invoke();
+ }
+
+ /**
+ * Returns the result of accumulating the given transformation
+ * of all keys using the given reducer to combine values, or
+ * null if none.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param transformer a function returning the transformation
+ * for an element, or null if there is no transformation (in
+ * which case it is not combined)
+ * @param reducer a commutative associative combining function
+ * @param <U> the return type of the transformer
+ * @return the result of accumulating the given transformation
+ * of all keys
+ * @since 1.8
+ */
+ public <U> U reduceKeys(long parallelismThreshold,
+ Function<? super K, ? extends U> transformer,
+ BiFunction<? super U, ? super U, ? extends U> reducer) {
+ if (transformer == null || reducer == null)
+ throw new NullPointerException();
+ return new MapReduceKeysTask<K,V,U>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ null, transformer, reducer).invoke();
+ }
+
+ /**
+ * Returns the result of accumulating the given transformation
+ * of all keys using the given reducer to combine values, and
+ * the given basis as an identity value.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param transformer a function returning the transformation
+ * for an element
+ * @param basis the identity (initial default value) for the reduction
+ * @param reducer a commutative associative combining function
+ * @return the result of accumulating the given transformation
+ * of all keys
+ * @since 1.8
+ */
+ public double reduceKeysToDouble(long parallelismThreshold,
+ ToDoubleFunction<? super K> transformer,
+ double basis,
+ DoubleBinaryOperator reducer) {
+ if (transformer == null || reducer == null)
+ throw new NullPointerException();
+ return new MapReduceKeysToDoubleTask<K,V>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ null, transformer, basis, reducer).invoke();
+ }
+
+ /**
+ * Returns the result of accumulating the given transformation
+ * of all keys using the given reducer to combine values, and
+ * the given basis as an identity value.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param transformer a function returning the transformation
+ * for an element
+ * @param basis the identity (initial default value) for the reduction
+ * @param reducer a commutative associative combining function
+ * @return the result of accumulating the given transformation
+ * of all keys
+ * @since 1.8
+ */
+ public long reduceKeysToLong(long parallelismThreshold,
+ ToLongFunction<? super K> transformer,
+ long basis,
+ LongBinaryOperator reducer) {
+ if (transformer == null || reducer == null)
+ throw new NullPointerException();
+ return new MapReduceKeysToLongTask<K,V>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ null, transformer, basis, reducer).invoke();
+ }
+
+ /**
+ * Returns the result of accumulating the given transformation
+ * of all keys using the given reducer to combine values, and
+ * the given basis as an identity value.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param transformer a function returning the transformation
+ * for an element
+ * @param basis the identity (initial default value) for the reduction
+ * @param reducer a commutative associative combining function
+ * @return the result of accumulating the given transformation
+ * of all keys
+ * @since 1.8
+ */
+ public int reduceKeysToInt(long parallelismThreshold,
+ ToIntFunction<? super K> transformer,
+ int basis,
+ IntBinaryOperator reducer) {
+ if (transformer == null || reducer == null)
+ throw new NullPointerException();
+ return new MapReduceKeysToIntTask<K,V>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ null, transformer, basis, reducer).invoke();
+ }
+
+ /**
+ * Performs the given action for each value.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param action the action
+ * @since 1.8
+ */
+ public void forEachValue(long parallelismThreshold,
+ Consumer<? super V> action) {
+ if (action == null)
+ throw new NullPointerException();
+ new ForEachValueTask<K,V>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ action).invoke();
+ }
+
+ /**
+ * Performs the given action for each non-null transformation
+ * of each value.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param transformer a function returning the transformation
+ * for an element, or null if there is no transformation (in
+ * which case the action is not applied)
+ * @param action the action
+ * @param <U> the return type of the transformer
+ * @since 1.8
+ */
+ public <U> void forEachValue(long parallelismThreshold,
+ Function<? super V, ? extends U> transformer,
+ Consumer<? super U> action) {
+ if (transformer == null || action == null)
+ throw new NullPointerException();
+ new ForEachTransformedValueTask<K,V,U>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ transformer, action).invoke();
+ }
+
+ /**
+ * Returns a non-null result from applying the given search
+ * function on each value, or null if none. Upon success,
+ * further element processing is suppressed and the results of
+ * any other parallel invocations of the search function are
+ * ignored.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param searchFunction a function returning a non-null
+ * result on success, else null
+ * @param <U> the return type of the search function
+ * @return a non-null result from applying the given search
+ * function on each value, or null if none
+ * @since 1.8
+ */
+ public <U> U searchValues(long parallelismThreshold,
+ Function<? super V, ? extends U> searchFunction) {
+ if (searchFunction == null) throw new NullPointerException();
+ return new SearchValuesTask<K,V,U>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ searchFunction, new AtomicReference<U>()).invoke();
+ }
+
+ /**
+ * Returns the result of accumulating all values using the
+ * given reducer to combine values, or null if none.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param reducer a commutative associative combining function
+ * @return the result of accumulating all values
+ * @since 1.8
+ */
+ public V reduceValues(long parallelismThreshold,
+ BiFunction<? super V, ? super V, ? extends V> reducer) {
+ if (reducer == null) throw new NullPointerException();
+ return new ReduceValuesTask<K,V>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ null, reducer).invoke();
+ }
+
+ /**
+ * Returns the result of accumulating the given transformation
+ * of all values using the given reducer to combine values, or
+ * null if none.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param transformer a function returning the transformation
+ * for an element, or null if there is no transformation (in
+ * which case it is not combined)
+ * @param reducer a commutative associative combining function
+ * @param <U> the return type of the transformer
+ * @return the result of accumulating the given transformation
+ * of all values
+ * @since 1.8
+ */
+ public <U> U reduceValues(long parallelismThreshold,
+ Function<? super V, ? extends U> transformer,
+ BiFunction<? super U, ? super U, ? extends U> reducer) {
+ if (transformer == null || reducer == null)
+ throw new NullPointerException();
+ return new MapReduceValuesTask<K,V,U>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ null, transformer, reducer).invoke();
+ }
+
+ /**
+ * Returns the result of accumulating the given transformation
+ * of all values using the given reducer to combine values,
+ * and the given basis as an identity value.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param transformer a function returning the transformation
+ * for an element
+ * @param basis the identity (initial default value) for the reduction
+ * @param reducer a commutative associative combining function
+ * @return the result of accumulating the given transformation
+ * of all values
+ * @since 1.8
+ */
+ public double reduceValuesToDouble(long parallelismThreshold,
+ ToDoubleFunction<? super V> transformer,
+ double basis,
+ DoubleBinaryOperator reducer) {
+ if (transformer == null || reducer == null)
+ throw new NullPointerException();
+ return new MapReduceValuesToDoubleTask<K,V>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ null, transformer, basis, reducer).invoke();
+ }
+
+ /**
+ * Returns the result of accumulating the given transformation
+ * of all values using the given reducer to combine values,
+ * and the given basis as an identity value.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param transformer a function returning the transformation
+ * for an element
+ * @param basis the identity (initial default value) for the reduction
+ * @param reducer a commutative associative combining function
+ * @return the result of accumulating the given transformation
+ * of all values
+ * @since 1.8
+ */
+ public long reduceValuesToLong(long parallelismThreshold,
+ ToLongFunction<? super V> transformer,
+ long basis,
+ LongBinaryOperator reducer) {
+ if (transformer == null || reducer == null)
+ throw new NullPointerException();
+ return new MapReduceValuesToLongTask<K,V>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ null, transformer, basis, reducer).invoke();
+ }
+
+ /**
+ * Returns the result of accumulating the given transformation
+ * of all values using the given reducer to combine values,
+ * and the given basis as an identity value.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param transformer a function returning the transformation
+ * for an element
+ * @param basis the identity (initial default value) for the reduction
+ * @param reducer a commutative associative combining function
+ * @return the result of accumulating the given transformation
+ * of all values
+ * @since 1.8
+ */
+ public int reduceValuesToInt(long parallelismThreshold,
+ ToIntFunction<? super V> transformer,
+ int basis,
+ IntBinaryOperator reducer) {
+ if (transformer == null || reducer == null)
+ throw new NullPointerException();
+ return new MapReduceValuesToIntTask<K,V>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ null, transformer, basis, reducer).invoke();
+ }
+
+ /**
+ * Performs the given action for each entry.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param action the action
+ * @since 1.8
+ */
+ public void forEachEntry(long parallelismThreshold,
+ Consumer<? super Map.Entry<K,V>> action) {
+ if (action == null) throw new NullPointerException();
+ new ForEachEntryTask<K,V>(null, batchFor(parallelismThreshold), 0, 0, table,
+ action).invoke();
+ }
+
+ /**
+ * Performs the given action for each non-null transformation
+ * of each entry.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param transformer a function returning the transformation
+ * for an element, or null if there is no transformation (in
+ * which case the action is not applied)
+ * @param action the action
+ * @param <U> the return type of the transformer
+ * @since 1.8
+ */
+ public <U> void forEachEntry(long parallelismThreshold,
+ Function<Map.Entry<K,V>, ? extends U> transformer,
+ Consumer<? super U> action) {
+ if (transformer == null || action == null)
+ throw new NullPointerException();
+ new ForEachTransformedEntryTask<K,V,U>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ transformer, action).invoke();
+ }
+
+ /**
+ * Returns a non-null result from applying the given search
+ * function on each entry, or null if none. Upon success,
+ * further element processing is suppressed and the results of
+ * any other parallel invocations of the search function are
+ * ignored.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param searchFunction a function returning a non-null
+ * result on success, else null
+ * @param <U> the return type of the search function
+ * @return a non-null result from applying the given search
+ * function on each entry, or null if none
+ * @since 1.8
+ */
+ public <U> U searchEntries(long parallelismThreshold,
+ Function<Map.Entry<K,V>, ? extends U> searchFunction) {
+ if (searchFunction == null) throw new NullPointerException();
+ return new SearchEntriesTask<K,V,U>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ searchFunction, new AtomicReference<U>()).invoke();
+ }
+
+ /**
+ * Returns the result of accumulating all entries using the
+ * given reducer to combine values, or null if none.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param reducer a commutative associative combining function
+ * @return the result of accumulating all entries
+ * @since 1.8
+ */
+ public Map.Entry<K,V> reduceEntries(long parallelismThreshold,
+ BiFunction<Map.Entry<K,V>, Map.Entry<K,V>, ? extends Map.Entry<K,V>> reducer) {
+ if (reducer == null) throw new NullPointerException();
+ return new ReduceEntriesTask<K,V>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ null, reducer).invoke();
+ }
+
+ /**
+ * Returns the result of accumulating the given transformation
+ * of all entries using the given reducer to combine values,
+ * or null if none.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param transformer a function returning the transformation
+ * for an element, or null if there is no transformation (in
+ * which case it is not combined)
+ * @param reducer a commutative associative combining function
+ * @param <U> the return type of the transformer
+ * @return the result of accumulating the given transformation
+ * of all entries
+ * @since 1.8
+ */
+ public <U> U reduceEntries(long parallelismThreshold,
+ Function<Map.Entry<K,V>, ? extends U> transformer,
+ BiFunction<? super U, ? super U, ? extends U> reducer) {
+ if (transformer == null || reducer == null)
+ throw new NullPointerException();
+ return new MapReduceEntriesTask<K,V,U>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ null, transformer, reducer).invoke();
+ }
+
+ /**
+ * Returns the result of accumulating the given transformation
+ * of all entries using the given reducer to combine values,
+ * and the given basis as an identity value.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param transformer a function returning the transformation
+ * for an element
+ * @param basis the identity (initial default value) for the reduction
+ * @param reducer a commutative associative combining function
+ * @return the result of accumulating the given transformation
+ * of all entries
+ * @since 1.8
+ */
+ public double reduceEntriesToDouble(long parallelismThreshold,
+ ToDoubleFunction<Map.Entry<K,V>> transformer,
+ double basis,
+ DoubleBinaryOperator reducer) {
+ if (transformer == null || reducer == null)
+ throw new NullPointerException();
+ return new MapReduceEntriesToDoubleTask<K,V>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ null, transformer, basis, reducer).invoke();
+ }
+
+ /**
+ * Returns the result of accumulating the given transformation
+ * of all entries using the given reducer to combine values,
+ * and the given basis as an identity value.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param transformer a function returning the transformation
+ * for an element
+ * @param basis the identity (initial default value) for the reduction
+ * @param reducer a commutative associative combining function
+ * @return the result of accumulating the given transformation
+ * of all entries
+ * @since 1.8
+ */
+ public long reduceEntriesToLong(long parallelismThreshold,
+ ToLongFunction<Map.Entry<K,V>> transformer,
+ long basis,
+ LongBinaryOperator reducer) {
+ if (transformer == null || reducer == null)
+ throw new NullPointerException();
+ return new MapReduceEntriesToLongTask<K,V>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ null, transformer, basis, reducer).invoke();
+ }
+
+ /**
+ * Returns the result of accumulating the given transformation
+ * of all entries using the given reducer to combine values,
+ * and the given basis as an identity value.
+ *
+ * @param parallelismThreshold the (estimated) number of elements
+ * needed for this operation to be executed in parallel
+ * @param transformer a function returning the transformation
+ * for an element
+ * @param basis the identity (initial default value) for the reduction
+ * @param reducer a commutative associative combining function
+ * @return the result of accumulating the given transformation
+ * of all entries
+ * @since 1.8
+ */
+ public int reduceEntriesToInt(long parallelismThreshold,
+ ToIntFunction<Map.Entry<K,V>> transformer,
+ int basis,
+ IntBinaryOperator reducer) {
+ if (transformer == null || reducer == null)
+ throw new NullPointerException();
+ return new MapReduceEntriesToIntTask<K,V>
+ (null, batchFor(parallelismThreshold), 0, 0, table,
+ null, transformer, basis, reducer).invoke();
+ }
+
+
/* ----------------Views -------------- */
/**
@@ -2798,30 +4422,30 @@
// implementations below rely on concrete classes supplying these
// abstract methods
/**
- * Returns a "weakly consistent" iterator that will never
- * throw {@link ConcurrentModificationException}, and
- * guarantees to traverse elements as they existed upon
- * construction of the iterator, and may (but is not
- * guaranteed to) reflect any modifications subsequent to
- * construction.
+ * Returns an iterator over the elements in this collection.
+ *
+ * <p>The returned iterator is
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
+ *
+ * @return an iterator over the elements in this collection
*/
public abstract Iterator<E> iterator();
public abstract boolean contains(Object o);
public abstract boolean remove(Object o);
- private static final String oomeMsg = "Required array size too large";
+ private static final String OOME_MSG = "Required array size too large";
public final Object[] toArray() {
long sz = map.mappingCount();
if (sz > MAX_ARRAY_SIZE)
- throw new OutOfMemoryError(oomeMsg);
+ throw new OutOfMemoryError(OOME_MSG);
int n = (int)sz;
Object[] r = new Object[n];
int i = 0;
for (E e : this) {
if (i == n) {
if (n >= MAX_ARRAY_SIZE)
- throw new OutOfMemoryError(oomeMsg);
+ throw new OutOfMemoryError(OOME_MSG);
if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1)
n = MAX_ARRAY_SIZE;
else
@@ -2837,7 +4461,7 @@
public final <T> T[] toArray(T[] a) {
long sz = map.mappingCount();
if (sz > MAX_ARRAY_SIZE)
- throw new OutOfMemoryError(oomeMsg);
+ throw new OutOfMemoryError(OOME_MSG);
int m = (int)sz;
T[] r = (a.length >= m) ? a :
(T[])java.lang.reflect.Array
@@ -2847,7 +4471,7 @@
for (E e : this) {
if (i == n) {
if (n >= MAX_ARRAY_SIZE)
- throw new OutOfMemoryError(oomeMsg);
+ throw new OutOfMemoryError(OOME_MSG);
if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1)
n = MAX_ARRAY_SIZE;
else
@@ -2901,6 +4525,7 @@
}
public final boolean removeAll(Collection<?> c) {
+ if (c == null) throw new NullPointerException();
boolean modified = false;
for (Iterator<E> it = iterator(); it.hasNext();) {
if (c.contains(it.next())) {
@@ -2912,6 +4537,7 @@
}
public final boolean retainAll(Collection<?> c) {
+ if (c == null) throw new NullPointerException();
boolean modified = false;
for (Iterator<E> it = iterator(); it.hasNext();) {
if (!c.contains(it.next())) {
@@ -2930,12 +4556,11 @@
* common value. This class cannot be directly instantiated.
* See {@link #keySet() keySet()},
* {@link #keySet(Object) keySet(V)},
+ * {@link #newKeySet() newKeySet()},
+ * {@link #newKeySet(int) newKeySet(int)}.
*
* @since 1.8
- *
- * @hide
*/
- // android-note: removed references to hidden APIs.
public static class KeySetView<K,V> extends CollectionView<K,V,K>
implements Set<K>, java.io.Serializable {
private static final long serialVersionUID = 7249069246763182397L;
@@ -3035,6 +4660,23 @@
(containsAll(c) && c.containsAll(this))));
}
+ public Spliterator<K> spliterator() {
+ Node<K,V>[] t;
+ ConcurrentHashMap<K,V> m = map;
+ long n = m.sumCount();
+ int f = (t = m.table) == null ? 0 : t.length;
+ return new KeySpliterator<K,V>(t, f, 0, f, n < 0L ? 0L : n);
+ }
+
+ public void forEach(Consumer<? super K> action) {
+ if (action == null) throw new NullPointerException();
+ Node<K,V>[] t;
+ if ((t = map.table) != null) {
+ Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
+ for (Node<K,V> p; (p = it.advance()) != null; )
+ action.accept(p.key);
+ }
+ }
}
/**
@@ -3076,6 +4718,27 @@
throw new UnsupportedOperationException();
}
+ public boolean removeIf(Predicate<? super V> filter) {
+ return map.removeValueIf(filter);
+ }
+
+ public Spliterator<V> spliterator() {
+ Node<K,V>[] t;
+ ConcurrentHashMap<K,V> m = map;
+ long n = m.sumCount();
+ int f = (t = m.table) == null ? 0 : t.length;
+ return new ValueSpliterator<K,V>(t, f, 0, f, n < 0L ? 0L : n);
+ }
+
+ public void forEach(Consumer<? super V> action) {
+ if (action == null) throw new NullPointerException();
+ Node<K,V>[] t;
+ if ((t = map.table) != null) {
+ Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
+ for (Node<K,V> p; (p = it.advance()) != null; )
+ action.accept(p.val);
+ }
+ }
}
/**
@@ -3128,6 +4791,10 @@
return added;
}
+ public boolean removeIf(Predicate<? super Entry<K,V>> filter) {
+ return map.removeEntryIf(filter);
+ }
+
public final int hashCode() {
int h = 0;
Node<K,V>[] t;
@@ -3147,181 +4814,1532 @@
(containsAll(c) && c.containsAll(this))));
}
- }
+ public Spliterator<Map.Entry<K,V>> spliterator() {
+ Node<K,V>[] t;
+ ConcurrentHashMap<K,V> m = map;
+ long n = m.sumCount();
+ int f = (t = m.table) == null ? 0 : t.length;
+ return new EntrySpliterator<K,V>(t, f, 0, f, n < 0L ? 0L : n, m);
+ }
-
- /* ---------------- Counters -------------- */
-
- // Adapted from LongAdder and Striped64.
- // See their internal docs for explanation.
-
- // A padded cell for distributing counts
- static final class CounterCell {
- volatile long p0, p1, p2, p3, p4, p5, p6;
- volatile long value;
- volatile long q0, q1, q2, q3, q4, q5, q6;
- CounterCell(long x) { value = x; }
- }
-
- /**
- * Holder for the thread-local hash code determining which
- * CounterCell to use. The code is initialized via the
- * counterHashCodeGenerator, but may be moved upon collisions.
- */
- static final class CounterHashCode {
- int code;
- }
-
- /**
- * Generates initial value for per-thread CounterHashCodes.
- */
- static final AtomicInteger counterHashCodeGenerator = new AtomicInteger();
-
- /**
- * Increment for counterHashCodeGenerator. See class ThreadLocal
- * for explanation.
- */
- static final int SEED_INCREMENT = 0x61c88647;
-
- /**
- * Per-thread counter hash codes. Shared across all instances.
- */
- static final ThreadLocal<CounterHashCode> threadCounterHashCode =
- new ThreadLocal<CounterHashCode>();
-
- final long sumCount() {
- CounterCell[] as = counterCells; CounterCell a;
- long sum = baseCount;
- if (as != null) {
- for (int i = 0; i < as.length; ++i) {
- if ((a = as[i]) != null)
- sum += a.value;
+ public void forEach(Consumer<? super Map.Entry<K,V>> action) {
+ if (action == null) throw new NullPointerException();
+ Node<K,V>[] t;
+ if ((t = map.table) != null) {
+ Traverser<K,V> it = new Traverser<K,V>(t, t.length, 0, t.length);
+ for (Node<K,V> p; (p = it.advance()) != null; )
+ action.accept(new MapEntry<K,V>(p.key, p.val, map));
}
}
- return sum;
+
}
- // See LongAdder version for explanation
- private final void fullAddCount(long x, CounterHashCode hc,
- boolean wasUncontended) {
- int h;
- if (hc == null) {
- hc = new CounterHashCode();
- int s = counterHashCodeGenerator.addAndGet(SEED_INCREMENT);
- h = hc.code = (s == 0) ? 1 : s; // Avoid zero
- threadCounterHashCode.set(hc);
- }
- else
- h = hc.code;
- boolean collide = false; // True if last slot nonempty
- for (;;) {
- CounterCell[] as; CounterCell a; int n; long v;
- if ((as = counterCells) != null && (n = as.length) > 0) {
- if ((a = as[(n - 1) & h]) == null) {
- if (cellsBusy == 0) { // Try to attach new Cell
- CounterCell r = new CounterCell(x); // Optimistic create
- if (cellsBusy == 0 &&
- U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
- boolean created = false;
- try { // Recheck under lock
- CounterCell[] rs; int m, j;
- if ((rs = counterCells) != null &&
- (m = rs.length) > 0 &&
- rs[j = (m - 1) & h] == null) {
- rs[j] = r;
- created = true;
- }
- } finally {
- cellsBusy = 0;
- }
- if (created)
- break;
- continue; // Slot is now non-empty
- }
- }
- collide = false;
- }
- else if (!wasUncontended) // CAS already known to fail
- wasUncontended = true; // Continue after rehash
- else if (U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))
- break;
- else if (counterCells != as || n >= NCPU)
- collide = false; // At max size or stale
- else if (!collide)
- collide = true;
- else if (cellsBusy == 0 &&
- U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
- try {
- if (counterCells == as) {// Expand table unless stale
- CounterCell[] rs = new CounterCell[n << 1];
- for (int i = 0; i < n; ++i)
- rs[i] = as[i];
- counterCells = rs;
- }
- } finally {
- cellsBusy = 0;
- }
- collide = false;
- continue; // Retry with expanded table
- }
- h ^= h << 13; // Rehash
- h ^= h >>> 17;
- h ^= h << 5;
+ // -------------------------------------------------------
+
+ /**
+ * Base class for bulk tasks. Repeats some fields and code from
+ * class Traverser, because we need to subclass CountedCompleter.
+ */
+ @SuppressWarnings("serial")
+ abstract static class BulkTask<K,V,R> extends CountedCompleter<R> {
+ Node<K,V>[] tab; // same as Traverser
+ Node<K,V> next;
+ TableStack<K,V> stack, spare;
+ int index;
+ int baseIndex;
+ int baseLimit;
+ final int baseSize;
+ int batch; // split control
+
+ BulkTask(BulkTask<K,V,?> par, int b, int i, int f, Node<K,V>[] t) {
+ super(par);
+ this.batch = b;
+ this.index = this.baseIndex = i;
+ if ((this.tab = t) == null)
+ this.baseSize = this.baseLimit = 0;
+ else if (par == null)
+ this.baseSize = this.baseLimit = t.length;
+ else {
+ this.baseLimit = f;
+ this.baseSize = par.baseSize;
}
- else if (cellsBusy == 0 && counterCells == as &&
- U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
- boolean init = false;
- try { // Initialize table
- if (counterCells == as) {
- CounterCell[] rs = new CounterCell[2];
- rs[h & 1] = new CounterCell(x);
- counterCells = rs;
- init = true;
- }
- } finally {
- cellsBusy = 0;
- }
- if (init)
- break;
- }
- else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x))
- break; // Fall back on using base
}
- hc.code = h; // Record index for next time
+
+ /**
+ * Same as Traverser version.
+ */
+ final Node<K,V> advance() {
+ Node<K,V> e;
+ if ((e = next) != null)
+ e = e.next;
+ for (;;) {
+ Node<K,V>[] t; int i, n;
+ if (e != null)
+ return next = e;
+ if (baseIndex >= baseLimit || (t = tab) == null ||
+ (n = t.length) <= (i = index) || i < 0)
+ return next = null;
+ if ((e = tabAt(t, i)) != null && e.hash < 0) {
+ if (e instanceof ForwardingNode) {
+ tab = ((ForwardingNode<K,V>)e).nextTable;
+ e = null;
+ pushState(t, i, n);
+ continue;
+ }
+ else if (e instanceof TreeBin)
+ e = ((TreeBin<K,V>)e).first;
+ else
+ e = null;
+ }
+ if (stack != null)
+ recoverState(n);
+ else if ((index = i + baseSize) >= n)
+ index = ++baseIndex;
+ }
+ }
+
+ private void pushState(Node<K,V>[] t, int i, int n) {
+ TableStack<K,V> s = spare;
+ if (s != null)
+ spare = s.next;
+ else
+ s = new TableStack<K,V>();
+ s.tab = t;
+ s.length = n;
+ s.index = i;
+ s.next = stack;
+ stack = s;
+ }
+
+ private void recoverState(int n) {
+ TableStack<K,V> s; int len;
+ while ((s = stack) != null && (index += (len = s.length)) >= n) {
+ n = len;
+ index = s.index;
+ tab = s.tab;
+ s.tab = null;
+ TableStack<K,V> next = s.next;
+ s.next = spare; // save for reuse
+ stack = next;
+ spare = s;
+ }
+ if (s == null && (index += baseSize) >= n)
+ index = ++baseIndex;
+ }
+ }
+
+ /*
+ * Task classes. Coded in a regular but ugly format/style to
+ * simplify checks that each variant differs in the right way from
+ * others. The null screenings exist because compilers cannot tell
+ * that we've already null-checked task arguments, so we force
+ * simplest hoisted bypass to help avoid convoluted traps.
+ */
+ @SuppressWarnings("serial")
+ static final class ForEachKeyTask<K,V>
+ extends BulkTask<K,V,Void> {
+ final Consumer<? super K> action;
+ ForEachKeyTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ Consumer<? super K> action) {
+ super(p, b, i, f, t);
+ this.action = action;
+ }
+ public final void compute() {
+ final Consumer<? super K> action;
+ if ((action = this.action) != null) {
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ new ForEachKeyTask<K,V>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ action).fork();
+ }
+ for (Node<K,V> p; (p = advance()) != null;)
+ action.accept(p.key);
+ propagateCompletion();
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class ForEachValueTask<K,V>
+ extends BulkTask<K,V,Void> {
+ final Consumer<? super V> action;
+ ForEachValueTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ Consumer<? super V> action) {
+ super(p, b, i, f, t);
+ this.action = action;
+ }
+ public final void compute() {
+ final Consumer<? super V> action;
+ if ((action = this.action) != null) {
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ new ForEachValueTask<K,V>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ action).fork();
+ }
+ for (Node<K,V> p; (p = advance()) != null;)
+ action.accept(p.val);
+ propagateCompletion();
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class ForEachEntryTask<K,V>
+ extends BulkTask<K,V,Void> {
+ final Consumer<? super Entry<K,V>> action;
+ ForEachEntryTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ Consumer<? super Entry<K,V>> action) {
+ super(p, b, i, f, t);
+ this.action = action;
+ }
+ public final void compute() {
+ final Consumer<? super Entry<K,V>> action;
+ if ((action = this.action) != null) {
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ new ForEachEntryTask<K,V>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ action).fork();
+ }
+ for (Node<K,V> p; (p = advance()) != null; )
+ action.accept(p);
+ propagateCompletion();
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class ForEachMappingTask<K,V>
+ extends BulkTask<K,V,Void> {
+ final BiConsumer<? super K, ? super V> action;
+ ForEachMappingTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ BiConsumer<? super K,? super V> action) {
+ super(p, b, i, f, t);
+ this.action = action;
+ }
+ public final void compute() {
+ final BiConsumer<? super K, ? super V> action;
+ if ((action = this.action) != null) {
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ new ForEachMappingTask<K,V>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ action).fork();
+ }
+ for (Node<K,V> p; (p = advance()) != null; )
+ action.accept(p.key, p.val);
+ propagateCompletion();
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class ForEachTransformedKeyTask<K,V,U>
+ extends BulkTask<K,V,Void> {
+ final Function<? super K, ? extends U> transformer;
+ final Consumer<? super U> action;
+ ForEachTransformedKeyTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ Function<? super K, ? extends U> transformer, Consumer<? super U> action) {
+ super(p, b, i, f, t);
+ this.transformer = transformer; this.action = action;
+ }
+ public final void compute() {
+ final Function<? super K, ? extends U> transformer;
+ final Consumer<? super U> action;
+ if ((transformer = this.transformer) != null &&
+ (action = this.action) != null) {
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ new ForEachTransformedKeyTask<K,V,U>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ transformer, action).fork();
+ }
+ for (Node<K,V> p; (p = advance()) != null; ) {
+ U u;
+ if ((u = transformer.apply(p.key)) != null)
+ action.accept(u);
+ }
+ propagateCompletion();
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class ForEachTransformedValueTask<K,V,U>
+ extends BulkTask<K,V,Void> {
+ final Function<? super V, ? extends U> transformer;
+ final Consumer<? super U> action;
+ ForEachTransformedValueTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ Function<? super V, ? extends U> transformer, Consumer<? super U> action) {
+ super(p, b, i, f, t);
+ this.transformer = transformer; this.action = action;
+ }
+ public final void compute() {
+ final Function<? super V, ? extends U> transformer;
+ final Consumer<? super U> action;
+ if ((transformer = this.transformer) != null &&
+ (action = this.action) != null) {
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ new ForEachTransformedValueTask<K,V,U>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ transformer, action).fork();
+ }
+ for (Node<K,V> p; (p = advance()) != null; ) {
+ U u;
+ if ((u = transformer.apply(p.val)) != null)
+ action.accept(u);
+ }
+ propagateCompletion();
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class ForEachTransformedEntryTask<K,V,U>
+ extends BulkTask<K,V,Void> {
+ final Function<Map.Entry<K,V>, ? extends U> transformer;
+ final Consumer<? super U> action;
+ ForEachTransformedEntryTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ Function<Map.Entry<K,V>, ? extends U> transformer, Consumer<? super U> action) {
+ super(p, b, i, f, t);
+ this.transformer = transformer; this.action = action;
+ }
+ public final void compute() {
+ final Function<Map.Entry<K,V>, ? extends U> transformer;
+ final Consumer<? super U> action;
+ if ((transformer = this.transformer) != null &&
+ (action = this.action) != null) {
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ new ForEachTransformedEntryTask<K,V,U>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ transformer, action).fork();
+ }
+ for (Node<K,V> p; (p = advance()) != null; ) {
+ U u;
+ if ((u = transformer.apply(p)) != null)
+ action.accept(u);
+ }
+ propagateCompletion();
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class ForEachTransformedMappingTask<K,V,U>
+ extends BulkTask<K,V,Void> {
+ final BiFunction<? super K, ? super V, ? extends U> transformer;
+ final Consumer<? super U> action;
+ ForEachTransformedMappingTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ BiFunction<? super K, ? super V, ? extends U> transformer,
+ Consumer<? super U> action) {
+ super(p, b, i, f, t);
+ this.transformer = transformer; this.action = action;
+ }
+ public final void compute() {
+ final BiFunction<? super K, ? super V, ? extends U> transformer;
+ final Consumer<? super U> action;
+ if ((transformer = this.transformer) != null &&
+ (action = this.action) != null) {
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ new ForEachTransformedMappingTask<K,V,U>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ transformer, action).fork();
+ }
+ for (Node<K,V> p; (p = advance()) != null; ) {
+ U u;
+ if ((u = transformer.apply(p.key, p.val)) != null)
+ action.accept(u);
+ }
+ propagateCompletion();
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class SearchKeysTask<K,V,U>
+ extends BulkTask<K,V,U> {
+ final Function<? super K, ? extends U> searchFunction;
+ final AtomicReference<U> result;
+ SearchKeysTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ Function<? super K, ? extends U> searchFunction,
+ AtomicReference<U> result) {
+ super(p, b, i, f, t);
+ this.searchFunction = searchFunction; this.result = result;
+ }
+ public final U getRawResult() { return result.get(); }
+ public final void compute() {
+ final Function<? super K, ? extends U> searchFunction;
+ final AtomicReference<U> result;
+ if ((searchFunction = this.searchFunction) != null &&
+ (result = this.result) != null) {
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ if (result.get() != null)
+ return;
+ addToPendingCount(1);
+ new SearchKeysTask<K,V,U>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ searchFunction, result).fork();
+ }
+ while (result.get() == null) {
+ U u;
+ Node<K,V> p;
+ if ((p = advance()) == null) {
+ propagateCompletion();
+ break;
+ }
+ if ((u = searchFunction.apply(p.key)) != null) {
+ if (result.compareAndSet(null, u))
+ quietlyCompleteRoot();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class SearchValuesTask<K,V,U>
+ extends BulkTask<K,V,U> {
+ final Function<? super V, ? extends U> searchFunction;
+ final AtomicReference<U> result;
+ SearchValuesTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ Function<? super V, ? extends U> searchFunction,
+ AtomicReference<U> result) {
+ super(p, b, i, f, t);
+ this.searchFunction = searchFunction; this.result = result;
+ }
+ public final U getRawResult() { return result.get(); }
+ public final void compute() {
+ final Function<? super V, ? extends U> searchFunction;
+ final AtomicReference<U> result;
+ if ((searchFunction = this.searchFunction) != null &&
+ (result = this.result) != null) {
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ if (result.get() != null)
+ return;
+ addToPendingCount(1);
+ new SearchValuesTask<K,V,U>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ searchFunction, result).fork();
+ }
+ while (result.get() == null) {
+ U u;
+ Node<K,V> p;
+ if ((p = advance()) == null) {
+ propagateCompletion();
+ break;
+ }
+ if ((u = searchFunction.apply(p.val)) != null) {
+ if (result.compareAndSet(null, u))
+ quietlyCompleteRoot();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class SearchEntriesTask<K,V,U>
+ extends BulkTask<K,V,U> {
+ final Function<Entry<K,V>, ? extends U> searchFunction;
+ final AtomicReference<U> result;
+ SearchEntriesTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ Function<Entry<K,V>, ? extends U> searchFunction,
+ AtomicReference<U> result) {
+ super(p, b, i, f, t);
+ this.searchFunction = searchFunction; this.result = result;
+ }
+ public final U getRawResult() { return result.get(); }
+ public final void compute() {
+ final Function<Entry<K,V>, ? extends U> searchFunction;
+ final AtomicReference<U> result;
+ if ((searchFunction = this.searchFunction) != null &&
+ (result = this.result) != null) {
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ if (result.get() != null)
+ return;
+ addToPendingCount(1);
+ new SearchEntriesTask<K,V,U>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ searchFunction, result).fork();
+ }
+ while (result.get() == null) {
+ U u;
+ Node<K,V> p;
+ if ((p = advance()) == null) {
+ propagateCompletion();
+ break;
+ }
+ if ((u = searchFunction.apply(p)) != null) {
+ if (result.compareAndSet(null, u))
+ quietlyCompleteRoot();
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class SearchMappingsTask<K,V,U>
+ extends BulkTask<K,V,U> {
+ final BiFunction<? super K, ? super V, ? extends U> searchFunction;
+ final AtomicReference<U> result;
+ SearchMappingsTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ BiFunction<? super K, ? super V, ? extends U> searchFunction,
+ AtomicReference<U> result) {
+ super(p, b, i, f, t);
+ this.searchFunction = searchFunction; this.result = result;
+ }
+ public final U getRawResult() { return result.get(); }
+ public final void compute() {
+ final BiFunction<? super K, ? super V, ? extends U> searchFunction;
+ final AtomicReference<U> result;
+ if ((searchFunction = this.searchFunction) != null &&
+ (result = this.result) != null) {
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ if (result.get() != null)
+ return;
+ addToPendingCount(1);
+ new SearchMappingsTask<K,V,U>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ searchFunction, result).fork();
+ }
+ while (result.get() == null) {
+ U u;
+ Node<K,V> p;
+ if ((p = advance()) == null) {
+ propagateCompletion();
+ break;
+ }
+ if ((u = searchFunction.apply(p.key, p.val)) != null) {
+ if (result.compareAndSet(null, u))
+ quietlyCompleteRoot();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class ReduceKeysTask<K,V>
+ extends BulkTask<K,V,K> {
+ final BiFunction<? super K, ? super K, ? extends K> reducer;
+ K result;
+ ReduceKeysTask<K,V> rights, nextRight;
+ ReduceKeysTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ ReduceKeysTask<K,V> nextRight,
+ BiFunction<? super K, ? super K, ? extends K> reducer) {
+ super(p, b, i, f, t); this.nextRight = nextRight;
+ this.reducer = reducer;
+ }
+ public final K getRawResult() { return result; }
+ public final void compute() {
+ final BiFunction<? super K, ? super K, ? extends K> reducer;
+ if ((reducer = this.reducer) != null) {
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ (rights = new ReduceKeysTask<K,V>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ rights, reducer)).fork();
+ }
+ K r = null;
+ for (Node<K,V> p; (p = advance()) != null; ) {
+ K u = p.key;
+ r = (r == null) ? u : u == null ? r : reducer.apply(r, u);
+ }
+ result = r;
+ CountedCompleter<?> c;
+ for (c = firstComplete(); c != null; c = c.nextComplete()) {
+ @SuppressWarnings("unchecked")
+ ReduceKeysTask<K,V>
+ t = (ReduceKeysTask<K,V>)c,
+ s = t.rights;
+ while (s != null) {
+ K tr, sr;
+ if ((sr = s.result) != null)
+ t.result = (((tr = t.result) == null) ? sr :
+ reducer.apply(tr, sr));
+ s = t.rights = s.nextRight;
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class ReduceValuesTask<K,V>
+ extends BulkTask<K,V,V> {
+ final BiFunction<? super V, ? super V, ? extends V> reducer;
+ V result;
+ ReduceValuesTask<K,V> rights, nextRight;
+ ReduceValuesTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ ReduceValuesTask<K,V> nextRight,
+ BiFunction<? super V, ? super V, ? extends V> reducer) {
+ super(p, b, i, f, t); this.nextRight = nextRight;
+ this.reducer = reducer;
+ }
+ public final V getRawResult() { return result; }
+ public final void compute() {
+ final BiFunction<? super V, ? super V, ? extends V> reducer;
+ if ((reducer = this.reducer) != null) {
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ (rights = new ReduceValuesTask<K,V>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ rights, reducer)).fork();
+ }
+ V r = null;
+ for (Node<K,V> p; (p = advance()) != null; ) {
+ V v = p.val;
+ r = (r == null) ? v : reducer.apply(r, v);
+ }
+ result = r;
+ CountedCompleter<?> c;
+ for (c = firstComplete(); c != null; c = c.nextComplete()) {
+ @SuppressWarnings("unchecked")
+ ReduceValuesTask<K,V>
+ t = (ReduceValuesTask<K,V>)c,
+ s = t.rights;
+ while (s != null) {
+ V tr, sr;
+ if ((sr = s.result) != null)
+ t.result = (((tr = t.result) == null) ? sr :
+ reducer.apply(tr, sr));
+ s = t.rights = s.nextRight;
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class ReduceEntriesTask<K,V>
+ extends BulkTask<K,V,Map.Entry<K,V>> {
+ final BiFunction<Map.Entry<K,V>, Map.Entry<K,V>, ? extends Map.Entry<K,V>> reducer;
+ Map.Entry<K,V> result;
+ ReduceEntriesTask<K,V> rights, nextRight;
+ ReduceEntriesTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ ReduceEntriesTask<K,V> nextRight,
+ BiFunction<Entry<K,V>, Map.Entry<K,V>, ? extends Map.Entry<K,V>> reducer) {
+ super(p, b, i, f, t); this.nextRight = nextRight;
+ this.reducer = reducer;
+ }
+ public final Map.Entry<K,V> getRawResult() { return result; }
+ public final void compute() {
+ final BiFunction<Map.Entry<K,V>, Map.Entry<K,V>, ? extends Map.Entry<K,V>> reducer;
+ if ((reducer = this.reducer) != null) {
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ (rights = new ReduceEntriesTask<K,V>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ rights, reducer)).fork();
+ }
+ Map.Entry<K,V> r = null;
+ for (Node<K,V> p; (p = advance()) != null; )
+ r = (r == null) ? p : reducer.apply(r, p);
+ result = r;
+ CountedCompleter<?> c;
+ for (c = firstComplete(); c != null; c = c.nextComplete()) {
+ @SuppressWarnings("unchecked")
+ ReduceEntriesTask<K,V>
+ t = (ReduceEntriesTask<K,V>)c,
+ s = t.rights;
+ while (s != null) {
+ Map.Entry<K,V> tr, sr;
+ if ((sr = s.result) != null)
+ t.result = (((tr = t.result) == null) ? sr :
+ reducer.apply(tr, sr));
+ s = t.rights = s.nextRight;
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class MapReduceKeysTask<K,V,U>
+ extends BulkTask<K,V,U> {
+ final Function<? super K, ? extends U> transformer;
+ final BiFunction<? super U, ? super U, ? extends U> reducer;
+ U result;
+ MapReduceKeysTask<K,V,U> rights, nextRight;
+ MapReduceKeysTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ MapReduceKeysTask<K,V,U> nextRight,
+ Function<? super K, ? extends U> transformer,
+ BiFunction<? super U, ? super U, ? extends U> reducer) {
+ super(p, b, i, f, t); this.nextRight = nextRight;
+ this.transformer = transformer;
+ this.reducer = reducer;
+ }
+ public final U getRawResult() { return result; }
+ public final void compute() {
+ final Function<? super K, ? extends U> transformer;
+ final BiFunction<? super U, ? super U, ? extends U> reducer;
+ if ((transformer = this.transformer) != null &&
+ (reducer = this.reducer) != null) {
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ (rights = new MapReduceKeysTask<K,V,U>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ rights, transformer, reducer)).fork();
+ }
+ U r = null;
+ for (Node<K,V> p; (p = advance()) != null; ) {
+ U u;
+ if ((u = transformer.apply(p.key)) != null)
+ r = (r == null) ? u : reducer.apply(r, u);
+ }
+ result = r;
+ CountedCompleter<?> c;
+ for (c = firstComplete(); c != null; c = c.nextComplete()) {
+ @SuppressWarnings("unchecked")
+ MapReduceKeysTask<K,V,U>
+ t = (MapReduceKeysTask<K,V,U>)c,
+ s = t.rights;
+ while (s != null) {
+ U tr, sr;
+ if ((sr = s.result) != null)
+ t.result = (((tr = t.result) == null) ? sr :
+ reducer.apply(tr, sr));
+ s = t.rights = s.nextRight;
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class MapReduceValuesTask<K,V,U>
+ extends BulkTask<K,V,U> {
+ final Function<? super V, ? extends U> transformer;
+ final BiFunction<? super U, ? super U, ? extends U> reducer;
+ U result;
+ MapReduceValuesTask<K,V,U> rights, nextRight;
+ MapReduceValuesTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ MapReduceValuesTask<K,V,U> nextRight,
+ Function<? super V, ? extends U> transformer,
+ BiFunction<? super U, ? super U, ? extends U> reducer) {
+ super(p, b, i, f, t); this.nextRight = nextRight;
+ this.transformer = transformer;
+ this.reducer = reducer;
+ }
+ public final U getRawResult() { return result; }
+ public final void compute() {
+ final Function<? super V, ? extends U> transformer;
+ final BiFunction<? super U, ? super U, ? extends U> reducer;
+ if ((transformer = this.transformer) != null &&
+ (reducer = this.reducer) != null) {
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ (rights = new MapReduceValuesTask<K,V,U>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ rights, transformer, reducer)).fork();
+ }
+ U r = null;
+ for (Node<K,V> p; (p = advance()) != null; ) {
+ U u;
+ if ((u = transformer.apply(p.val)) != null)
+ r = (r == null) ? u : reducer.apply(r, u);
+ }
+ result = r;
+ CountedCompleter<?> c;
+ for (c = firstComplete(); c != null; c = c.nextComplete()) {
+ @SuppressWarnings("unchecked")
+ MapReduceValuesTask<K,V,U>
+ t = (MapReduceValuesTask<K,V,U>)c,
+ s = t.rights;
+ while (s != null) {
+ U tr, sr;
+ if ((sr = s.result) != null)
+ t.result = (((tr = t.result) == null) ? sr :
+ reducer.apply(tr, sr));
+ s = t.rights = s.nextRight;
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class MapReduceEntriesTask<K,V,U>
+ extends BulkTask<K,V,U> {
+ final Function<Map.Entry<K,V>, ? extends U> transformer;
+ final BiFunction<? super U, ? super U, ? extends U> reducer;
+ U result;
+ MapReduceEntriesTask<K,V,U> rights, nextRight;
+ MapReduceEntriesTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ MapReduceEntriesTask<K,V,U> nextRight,
+ Function<Map.Entry<K,V>, ? extends U> transformer,
+ BiFunction<? super U, ? super U, ? extends U> reducer) {
+ super(p, b, i, f, t); this.nextRight = nextRight;
+ this.transformer = transformer;
+ this.reducer = reducer;
+ }
+ public final U getRawResult() { return result; }
+ public final void compute() {
+ final Function<Map.Entry<K,V>, ? extends U> transformer;
+ final BiFunction<? super U, ? super U, ? extends U> reducer;
+ if ((transformer = this.transformer) != null &&
+ (reducer = this.reducer) != null) {
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ (rights = new MapReduceEntriesTask<K,V,U>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ rights, transformer, reducer)).fork();
+ }
+ U r = null;
+ for (Node<K,V> p; (p = advance()) != null; ) {
+ U u;
+ if ((u = transformer.apply(p)) != null)
+ r = (r == null) ? u : reducer.apply(r, u);
+ }
+ result = r;
+ CountedCompleter<?> c;
+ for (c = firstComplete(); c != null; c = c.nextComplete()) {
+ @SuppressWarnings("unchecked")
+ MapReduceEntriesTask<K,V,U>
+ t = (MapReduceEntriesTask<K,V,U>)c,
+ s = t.rights;
+ while (s != null) {
+ U tr, sr;
+ if ((sr = s.result) != null)
+ t.result = (((tr = t.result) == null) ? sr :
+ reducer.apply(tr, sr));
+ s = t.rights = s.nextRight;
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class MapReduceMappingsTask<K,V,U>
+ extends BulkTask<K,V,U> {
+ final BiFunction<? super K, ? super V, ? extends U> transformer;
+ final BiFunction<? super U, ? super U, ? extends U> reducer;
+ U result;
+ MapReduceMappingsTask<K,V,U> rights, nextRight;
+ MapReduceMappingsTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ MapReduceMappingsTask<K,V,U> nextRight,
+ BiFunction<? super K, ? super V, ? extends U> transformer,
+ BiFunction<? super U, ? super U, ? extends U> reducer) {
+ super(p, b, i, f, t); this.nextRight = nextRight;
+ this.transformer = transformer;
+ this.reducer = reducer;
+ }
+ public final U getRawResult() { return result; }
+ public final void compute() {
+ final BiFunction<? super K, ? super V, ? extends U> transformer;
+ final BiFunction<? super U, ? super U, ? extends U> reducer;
+ if ((transformer = this.transformer) != null &&
+ (reducer = this.reducer) != null) {
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ (rights = new MapReduceMappingsTask<K,V,U>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ rights, transformer, reducer)).fork();
+ }
+ U r = null;
+ for (Node<K,V> p; (p = advance()) != null; ) {
+ U u;
+ if ((u = transformer.apply(p.key, p.val)) != null)
+ r = (r == null) ? u : reducer.apply(r, u);
+ }
+ result = r;
+ CountedCompleter<?> c;
+ for (c = firstComplete(); c != null; c = c.nextComplete()) {
+ @SuppressWarnings("unchecked")
+ MapReduceMappingsTask<K,V,U>
+ t = (MapReduceMappingsTask<K,V,U>)c,
+ s = t.rights;
+ while (s != null) {
+ U tr, sr;
+ if ((sr = s.result) != null)
+ t.result = (((tr = t.result) == null) ? sr :
+ reducer.apply(tr, sr));
+ s = t.rights = s.nextRight;
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class MapReduceKeysToDoubleTask<K,V>
+ extends BulkTask<K,V,Double> {
+ final ToDoubleFunction<? super K> transformer;
+ final DoubleBinaryOperator reducer;
+ final double basis;
+ double result;
+ MapReduceKeysToDoubleTask<K,V> rights, nextRight;
+ MapReduceKeysToDoubleTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ MapReduceKeysToDoubleTask<K,V> nextRight,
+ ToDoubleFunction<? super K> transformer,
+ double basis,
+ DoubleBinaryOperator reducer) {
+ super(p, b, i, f, t); this.nextRight = nextRight;
+ this.transformer = transformer;
+ this.basis = basis; this.reducer = reducer;
+ }
+ public final Double getRawResult() { return result; }
+ public final void compute() {
+ final ToDoubleFunction<? super K> transformer;
+ final DoubleBinaryOperator reducer;
+ if ((transformer = this.transformer) != null &&
+ (reducer = this.reducer) != null) {
+ double r = this.basis;
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ (rights = new MapReduceKeysToDoubleTask<K,V>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ rights, transformer, r, reducer)).fork();
+ }
+ for (Node<K,V> p; (p = advance()) != null; )
+ r = reducer.applyAsDouble(r, transformer.applyAsDouble(p.key));
+ result = r;
+ CountedCompleter<?> c;
+ for (c = firstComplete(); c != null; c = c.nextComplete()) {
+ @SuppressWarnings("unchecked")
+ MapReduceKeysToDoubleTask<K,V>
+ t = (MapReduceKeysToDoubleTask<K,V>)c,
+ s = t.rights;
+ while (s != null) {
+ t.result = reducer.applyAsDouble(t.result, s.result);
+ s = t.rights = s.nextRight;
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class MapReduceValuesToDoubleTask<K,V>
+ extends BulkTask<K,V,Double> {
+ final ToDoubleFunction<? super V> transformer;
+ final DoubleBinaryOperator reducer;
+ final double basis;
+ double result;
+ MapReduceValuesToDoubleTask<K,V> rights, nextRight;
+ MapReduceValuesToDoubleTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ MapReduceValuesToDoubleTask<K,V> nextRight,
+ ToDoubleFunction<? super V> transformer,
+ double basis,
+ DoubleBinaryOperator reducer) {
+ super(p, b, i, f, t); this.nextRight = nextRight;
+ this.transformer = transformer;
+ this.basis = basis; this.reducer = reducer;
+ }
+ public final Double getRawResult() { return result; }
+ public final void compute() {
+ final ToDoubleFunction<? super V> transformer;
+ final DoubleBinaryOperator reducer;
+ if ((transformer = this.transformer) != null &&
+ (reducer = this.reducer) != null) {
+ double r = this.basis;
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ (rights = new MapReduceValuesToDoubleTask<K,V>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ rights, transformer, r, reducer)).fork();
+ }
+ for (Node<K,V> p; (p = advance()) != null; )
+ r = reducer.applyAsDouble(r, transformer.applyAsDouble(p.val));
+ result = r;
+ CountedCompleter<?> c;
+ for (c = firstComplete(); c != null; c = c.nextComplete()) {
+ @SuppressWarnings("unchecked")
+ MapReduceValuesToDoubleTask<K,V>
+ t = (MapReduceValuesToDoubleTask<K,V>)c,
+ s = t.rights;
+ while (s != null) {
+ t.result = reducer.applyAsDouble(t.result, s.result);
+ s = t.rights = s.nextRight;
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class MapReduceEntriesToDoubleTask<K,V>
+ extends BulkTask<K,V,Double> {
+ final ToDoubleFunction<Map.Entry<K,V>> transformer;
+ final DoubleBinaryOperator reducer;
+ final double basis;
+ double result;
+ MapReduceEntriesToDoubleTask<K,V> rights, nextRight;
+ MapReduceEntriesToDoubleTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ MapReduceEntriesToDoubleTask<K,V> nextRight,
+ ToDoubleFunction<Map.Entry<K,V>> transformer,
+ double basis,
+ DoubleBinaryOperator reducer) {
+ super(p, b, i, f, t); this.nextRight = nextRight;
+ this.transformer = transformer;
+ this.basis = basis; this.reducer = reducer;
+ }
+ public final Double getRawResult() { return result; }
+ public final void compute() {
+ final ToDoubleFunction<Map.Entry<K,V>> transformer;
+ final DoubleBinaryOperator reducer;
+ if ((transformer = this.transformer) != null &&
+ (reducer = this.reducer) != null) {
+ double r = this.basis;
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ (rights = new MapReduceEntriesToDoubleTask<K,V>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ rights, transformer, r, reducer)).fork();
+ }
+ for (Node<K,V> p; (p = advance()) != null; )
+ r = reducer.applyAsDouble(r, transformer.applyAsDouble(p));
+ result = r;
+ CountedCompleter<?> c;
+ for (c = firstComplete(); c != null; c = c.nextComplete()) {
+ @SuppressWarnings("unchecked")
+ MapReduceEntriesToDoubleTask<K,V>
+ t = (MapReduceEntriesToDoubleTask<K,V>)c,
+ s = t.rights;
+ while (s != null) {
+ t.result = reducer.applyAsDouble(t.result, s.result);
+ s = t.rights = s.nextRight;
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class MapReduceMappingsToDoubleTask<K,V>
+ extends BulkTask<K,V,Double> {
+ final ToDoubleBiFunction<? super K, ? super V> transformer;
+ final DoubleBinaryOperator reducer;
+ final double basis;
+ double result;
+ MapReduceMappingsToDoubleTask<K,V> rights, nextRight;
+ MapReduceMappingsToDoubleTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ MapReduceMappingsToDoubleTask<K,V> nextRight,
+ ToDoubleBiFunction<? super K, ? super V> transformer,
+ double basis,
+ DoubleBinaryOperator reducer) {
+ super(p, b, i, f, t); this.nextRight = nextRight;
+ this.transformer = transformer;
+ this.basis = basis; this.reducer = reducer;
+ }
+ public final Double getRawResult() { return result; }
+ public final void compute() {
+ final ToDoubleBiFunction<? super K, ? super V> transformer;
+ final DoubleBinaryOperator reducer;
+ if ((transformer = this.transformer) != null &&
+ (reducer = this.reducer) != null) {
+ double r = this.basis;
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ (rights = new MapReduceMappingsToDoubleTask<K,V>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ rights, transformer, r, reducer)).fork();
+ }
+ for (Node<K,V> p; (p = advance()) != null; )
+ r = reducer.applyAsDouble(r, transformer.applyAsDouble(p.key, p.val));
+ result = r;
+ CountedCompleter<?> c;
+ for (c = firstComplete(); c != null; c = c.nextComplete()) {
+ @SuppressWarnings("unchecked")
+ MapReduceMappingsToDoubleTask<K,V>
+ t = (MapReduceMappingsToDoubleTask<K,V>)c,
+ s = t.rights;
+ while (s != null) {
+ t.result = reducer.applyAsDouble(t.result, s.result);
+ s = t.rights = s.nextRight;
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class MapReduceKeysToLongTask<K,V>
+ extends BulkTask<K,V,Long> {
+ final ToLongFunction<? super K> transformer;
+ final LongBinaryOperator reducer;
+ final long basis;
+ long result;
+ MapReduceKeysToLongTask<K,V> rights, nextRight;
+ MapReduceKeysToLongTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ MapReduceKeysToLongTask<K,V> nextRight,
+ ToLongFunction<? super K> transformer,
+ long basis,
+ LongBinaryOperator reducer) {
+ super(p, b, i, f, t); this.nextRight = nextRight;
+ this.transformer = transformer;
+ this.basis = basis; this.reducer = reducer;
+ }
+ public final Long getRawResult() { return result; }
+ public final void compute() {
+ final ToLongFunction<? super K> transformer;
+ final LongBinaryOperator reducer;
+ if ((transformer = this.transformer) != null &&
+ (reducer = this.reducer) != null) {
+ long r = this.basis;
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ (rights = new MapReduceKeysToLongTask<K,V>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ rights, transformer, r, reducer)).fork();
+ }
+ for (Node<K,V> p; (p = advance()) != null; )
+ r = reducer.applyAsLong(r, transformer.applyAsLong(p.key));
+ result = r;
+ CountedCompleter<?> c;
+ for (c = firstComplete(); c != null; c = c.nextComplete()) {
+ @SuppressWarnings("unchecked")
+ MapReduceKeysToLongTask<K,V>
+ t = (MapReduceKeysToLongTask<K,V>)c,
+ s = t.rights;
+ while (s != null) {
+ t.result = reducer.applyAsLong(t.result, s.result);
+ s = t.rights = s.nextRight;
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class MapReduceValuesToLongTask<K,V>
+ extends BulkTask<K,V,Long> {
+ final ToLongFunction<? super V> transformer;
+ final LongBinaryOperator reducer;
+ final long basis;
+ long result;
+ MapReduceValuesToLongTask<K,V> rights, nextRight;
+ MapReduceValuesToLongTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ MapReduceValuesToLongTask<K,V> nextRight,
+ ToLongFunction<? super V> transformer,
+ long basis,
+ LongBinaryOperator reducer) {
+ super(p, b, i, f, t); this.nextRight = nextRight;
+ this.transformer = transformer;
+ this.basis = basis; this.reducer = reducer;
+ }
+ public final Long getRawResult() { return result; }
+ public final void compute() {
+ final ToLongFunction<? super V> transformer;
+ final LongBinaryOperator reducer;
+ if ((transformer = this.transformer) != null &&
+ (reducer = this.reducer) != null) {
+ long r = this.basis;
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ (rights = new MapReduceValuesToLongTask<K,V>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ rights, transformer, r, reducer)).fork();
+ }
+ for (Node<K,V> p; (p = advance()) != null; )
+ r = reducer.applyAsLong(r, transformer.applyAsLong(p.val));
+ result = r;
+ CountedCompleter<?> c;
+ for (c = firstComplete(); c != null; c = c.nextComplete()) {
+ @SuppressWarnings("unchecked")
+ MapReduceValuesToLongTask<K,V>
+ t = (MapReduceValuesToLongTask<K,V>)c,
+ s = t.rights;
+ while (s != null) {
+ t.result = reducer.applyAsLong(t.result, s.result);
+ s = t.rights = s.nextRight;
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class MapReduceEntriesToLongTask<K,V>
+ extends BulkTask<K,V,Long> {
+ final ToLongFunction<Map.Entry<K,V>> transformer;
+ final LongBinaryOperator reducer;
+ final long basis;
+ long result;
+ MapReduceEntriesToLongTask<K,V> rights, nextRight;
+ MapReduceEntriesToLongTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ MapReduceEntriesToLongTask<K,V> nextRight,
+ ToLongFunction<Map.Entry<K,V>> transformer,
+ long basis,
+ LongBinaryOperator reducer) {
+ super(p, b, i, f, t); this.nextRight = nextRight;
+ this.transformer = transformer;
+ this.basis = basis; this.reducer = reducer;
+ }
+ public final Long getRawResult() { return result; }
+ public final void compute() {
+ final ToLongFunction<Map.Entry<K,V>> transformer;
+ final LongBinaryOperator reducer;
+ if ((transformer = this.transformer) != null &&
+ (reducer = this.reducer) != null) {
+ long r = this.basis;
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ (rights = new MapReduceEntriesToLongTask<K,V>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ rights, transformer, r, reducer)).fork();
+ }
+ for (Node<K,V> p; (p = advance()) != null; )
+ r = reducer.applyAsLong(r, transformer.applyAsLong(p));
+ result = r;
+ CountedCompleter<?> c;
+ for (c = firstComplete(); c != null; c = c.nextComplete()) {
+ @SuppressWarnings("unchecked")
+ MapReduceEntriesToLongTask<K,V>
+ t = (MapReduceEntriesToLongTask<K,V>)c,
+ s = t.rights;
+ while (s != null) {
+ t.result = reducer.applyAsLong(t.result, s.result);
+ s = t.rights = s.nextRight;
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class MapReduceMappingsToLongTask<K,V>
+ extends BulkTask<K,V,Long> {
+ final ToLongBiFunction<? super K, ? super V> transformer;
+ final LongBinaryOperator reducer;
+ final long basis;
+ long result;
+ MapReduceMappingsToLongTask<K,V> rights, nextRight;
+ MapReduceMappingsToLongTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ MapReduceMappingsToLongTask<K,V> nextRight,
+ ToLongBiFunction<? super K, ? super V> transformer,
+ long basis,
+ LongBinaryOperator reducer) {
+ super(p, b, i, f, t); this.nextRight = nextRight;
+ this.transformer = transformer;
+ this.basis = basis; this.reducer = reducer;
+ }
+ public final Long getRawResult() { return result; }
+ public final void compute() {
+ final ToLongBiFunction<? super K, ? super V> transformer;
+ final LongBinaryOperator reducer;
+ if ((transformer = this.transformer) != null &&
+ (reducer = this.reducer) != null) {
+ long r = this.basis;
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ (rights = new MapReduceMappingsToLongTask<K,V>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ rights, transformer, r, reducer)).fork();
+ }
+ for (Node<K,V> p; (p = advance()) != null; )
+ r = reducer.applyAsLong(r, transformer.applyAsLong(p.key, p.val));
+ result = r;
+ CountedCompleter<?> c;
+ for (c = firstComplete(); c != null; c = c.nextComplete()) {
+ @SuppressWarnings("unchecked")
+ MapReduceMappingsToLongTask<K,V>
+ t = (MapReduceMappingsToLongTask<K,V>)c,
+ s = t.rights;
+ while (s != null) {
+ t.result = reducer.applyAsLong(t.result, s.result);
+ s = t.rights = s.nextRight;
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class MapReduceKeysToIntTask<K,V>
+ extends BulkTask<K,V,Integer> {
+ final ToIntFunction<? super K> transformer;
+ final IntBinaryOperator reducer;
+ final int basis;
+ int result;
+ MapReduceKeysToIntTask<K,V> rights, nextRight;
+ MapReduceKeysToIntTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ MapReduceKeysToIntTask<K,V> nextRight,
+ ToIntFunction<? super K> transformer,
+ int basis,
+ IntBinaryOperator reducer) {
+ super(p, b, i, f, t); this.nextRight = nextRight;
+ this.transformer = transformer;
+ this.basis = basis; this.reducer = reducer;
+ }
+ public final Integer getRawResult() { return result; }
+ public final void compute() {
+ final ToIntFunction<? super K> transformer;
+ final IntBinaryOperator reducer;
+ if ((transformer = this.transformer) != null &&
+ (reducer = this.reducer) != null) {
+ int r = this.basis;
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ (rights = new MapReduceKeysToIntTask<K,V>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ rights, transformer, r, reducer)).fork();
+ }
+ for (Node<K,V> p; (p = advance()) != null; )
+ r = reducer.applyAsInt(r, transformer.applyAsInt(p.key));
+ result = r;
+ CountedCompleter<?> c;
+ for (c = firstComplete(); c != null; c = c.nextComplete()) {
+ @SuppressWarnings("unchecked")
+ MapReduceKeysToIntTask<K,V>
+ t = (MapReduceKeysToIntTask<K,V>)c,
+ s = t.rights;
+ while (s != null) {
+ t.result = reducer.applyAsInt(t.result, s.result);
+ s = t.rights = s.nextRight;
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class MapReduceValuesToIntTask<K,V>
+ extends BulkTask<K,V,Integer> {
+ final ToIntFunction<? super V> transformer;
+ final IntBinaryOperator reducer;
+ final int basis;
+ int result;
+ MapReduceValuesToIntTask<K,V> rights, nextRight;
+ MapReduceValuesToIntTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ MapReduceValuesToIntTask<K,V> nextRight,
+ ToIntFunction<? super V> transformer,
+ int basis,
+ IntBinaryOperator reducer) {
+ super(p, b, i, f, t); this.nextRight = nextRight;
+ this.transformer = transformer;
+ this.basis = basis; this.reducer = reducer;
+ }
+ public final Integer getRawResult() { return result; }
+ public final void compute() {
+ final ToIntFunction<? super V> transformer;
+ final IntBinaryOperator reducer;
+ if ((transformer = this.transformer) != null &&
+ (reducer = this.reducer) != null) {
+ int r = this.basis;
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ (rights = new MapReduceValuesToIntTask<K,V>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ rights, transformer, r, reducer)).fork();
+ }
+ for (Node<K,V> p; (p = advance()) != null; )
+ r = reducer.applyAsInt(r, transformer.applyAsInt(p.val));
+ result = r;
+ CountedCompleter<?> c;
+ for (c = firstComplete(); c != null; c = c.nextComplete()) {
+ @SuppressWarnings("unchecked")
+ MapReduceValuesToIntTask<K,V>
+ t = (MapReduceValuesToIntTask<K,V>)c,
+ s = t.rights;
+ while (s != null) {
+ t.result = reducer.applyAsInt(t.result, s.result);
+ s = t.rights = s.nextRight;
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class MapReduceEntriesToIntTask<K,V>
+ extends BulkTask<K,V,Integer> {
+ final ToIntFunction<Map.Entry<K,V>> transformer;
+ final IntBinaryOperator reducer;
+ final int basis;
+ int result;
+ MapReduceEntriesToIntTask<K,V> rights, nextRight;
+ MapReduceEntriesToIntTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ MapReduceEntriesToIntTask<K,V> nextRight,
+ ToIntFunction<Map.Entry<K,V>> transformer,
+ int basis,
+ IntBinaryOperator reducer) {
+ super(p, b, i, f, t); this.nextRight = nextRight;
+ this.transformer = transformer;
+ this.basis = basis; this.reducer = reducer;
+ }
+ public final Integer getRawResult() { return result; }
+ public final void compute() {
+ final ToIntFunction<Map.Entry<K,V>> transformer;
+ final IntBinaryOperator reducer;
+ if ((transformer = this.transformer) != null &&
+ (reducer = this.reducer) != null) {
+ int r = this.basis;
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ (rights = new MapReduceEntriesToIntTask<K,V>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ rights, transformer, r, reducer)).fork();
+ }
+ for (Node<K,V> p; (p = advance()) != null; )
+ r = reducer.applyAsInt(r, transformer.applyAsInt(p));
+ result = r;
+ CountedCompleter<?> c;
+ for (c = firstComplete(); c != null; c = c.nextComplete()) {
+ @SuppressWarnings("unchecked")
+ MapReduceEntriesToIntTask<K,V>
+ t = (MapReduceEntriesToIntTask<K,V>)c,
+ s = t.rights;
+ while (s != null) {
+ t.result = reducer.applyAsInt(t.result, s.result);
+ s = t.rights = s.nextRight;
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("serial")
+ static final class MapReduceMappingsToIntTask<K,V>
+ extends BulkTask<K,V,Integer> {
+ final ToIntBiFunction<? super K, ? super V> transformer;
+ final IntBinaryOperator reducer;
+ final int basis;
+ int result;
+ MapReduceMappingsToIntTask<K,V> rights, nextRight;
+ MapReduceMappingsToIntTask
+ (BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
+ MapReduceMappingsToIntTask<K,V> nextRight,
+ ToIntBiFunction<? super K, ? super V> transformer,
+ int basis,
+ IntBinaryOperator reducer) {
+ super(p, b, i, f, t); this.nextRight = nextRight;
+ this.transformer = transformer;
+ this.basis = basis; this.reducer = reducer;
+ }
+ public final Integer getRawResult() { return result; }
+ public final void compute() {
+ final ToIntBiFunction<? super K, ? super V> transformer;
+ final IntBinaryOperator reducer;
+ if ((transformer = this.transformer) != null &&
+ (reducer = this.reducer) != null) {
+ int r = this.basis;
+ for (int i = baseIndex, f, h; batch > 0 &&
+ (h = ((f = baseLimit) + i) >>> 1) > i;) {
+ addToPendingCount(1);
+ (rights = new MapReduceMappingsToIntTask<K,V>
+ (this, batch >>>= 1, baseLimit = h, f, tab,
+ rights, transformer, r, reducer)).fork();
+ }
+ for (Node<K,V> p; (p = advance()) != null; )
+ r = reducer.applyAsInt(r, transformer.applyAsInt(p.key, p.val));
+ result = r;
+ CountedCompleter<?> c;
+ for (c = firstComplete(); c != null; c = c.nextComplete()) {
+ @SuppressWarnings("unchecked")
+ MapReduceMappingsToIntTask<K,V>
+ t = (MapReduceMappingsToIntTask<K,V>)c,
+ s = t.rights;
+ while (s != null) {
+ t.result = reducer.applyAsInt(t.result, s.result);
+ s = t.rights = s.nextRight;
+ }
+ }
+ }
+ }
}
// Unsafe mechanics
- private static final sun.misc.Unsafe U;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
private static final long SIZECTL;
private static final long TRANSFERINDEX;
private static final long BASECOUNT;
private static final long CELLSBUSY;
private static final long CELLVALUE;
- private static final long ABASE;
+ private static final int ABASE;
private static final int ASHIFT;
static {
try {
- U = sun.misc.Unsafe.getUnsafe();
- Class<?> k = ConcurrentHashMap.class;
SIZECTL = U.objectFieldOffset
- (k.getDeclaredField("sizeCtl"));
+ (ConcurrentHashMap.class.getDeclaredField("sizeCtl"));
TRANSFERINDEX = U.objectFieldOffset
- (k.getDeclaredField("transferIndex"));
+ (ConcurrentHashMap.class.getDeclaredField("transferIndex"));
BASECOUNT = U.objectFieldOffset
- (k.getDeclaredField("baseCount"));
+ (ConcurrentHashMap.class.getDeclaredField("baseCount"));
CELLSBUSY = U.objectFieldOffset
- (k.getDeclaredField("cellsBusy"));
- Class<?> ck = CounterCell.class;
+ (ConcurrentHashMap.class.getDeclaredField("cellsBusy"));
+
CELLVALUE = U.objectFieldOffset
- (ck.getDeclaredField("value"));
- Class<?> ak = Node[].class;
- ABASE = U.arrayBaseOffset(ak);
- int scale = U.arrayIndexScale(ak);
+ (CounterCell.class.getDeclaredField("value"));
+
+ ABASE = U.arrayBaseOffset(Node[].class);
+ int scale = U.arrayIndexScale(Node[].class);
if ((scale & (scale - 1)) != 0)
- throw new Error("data type scale not a power of two");
+ throw new Error("array index scale not a power of two");
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
- } catch (Exception e) {
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
@@ -3329,5 +6347,4 @@
// LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773
Class<?> ensureLoaded = LockSupport.class;
}
-
}
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java b/luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java
index b38d6a5..7084d14 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java
@@ -7,12 +7,16 @@
package java.util.concurrent;
import java.util.AbstractCollection;
-import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Deque;
import java.util.Iterator;
import java.util.NoSuchElementException;
+import java.util.Objects;
import java.util.Queue;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.function.Consumer;
// BEGIN android-note
// removed link to collections framework docs
@@ -27,12 +31,8 @@
* Like most other concurrent collection implementations, this class
* does not permit the use of {@code null} elements.
*
- * <p>Iterators are <i>weakly consistent</i>, returning elements
- * reflecting the state of the deque at some point at or since the
- * creation of the iterator. They do <em>not</em> throw {@link
- * java.util.ConcurrentModificationException
- * ConcurrentModificationException}, and may proceed concurrently with
- * other operations.
+ * <p>Iterators and spliterators are
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
*
* <p>Beware that, unlike in most collections, the {@code size} method
* is <em>NOT</em> a constant-time operation. Because of the
@@ -59,7 +59,7 @@
* @since 1.7
* @author Doug Lea
* @author Martin Buchholz
- * @param <E> the type of elements held in this collection
+ * @param <E> the type of elements held in this deque
*/
public class ConcurrentLinkedDeque<E>
extends AbstractCollection<E>
@@ -272,47 +272,45 @@
* only be seen after publication via casNext or casPrev.
*/
Node(E item) {
- UNSAFE.putObject(this, itemOffset, item);
+ U.putObject(this, ITEM, item);
}
boolean casItem(E cmp, E val) {
- return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
+ return U.compareAndSwapObject(this, ITEM, cmp, val);
}
void lazySetNext(Node<E> val) {
- UNSAFE.putOrderedObject(this, nextOffset, val);
+ U.putOrderedObject(this, NEXT, val);
}
boolean casNext(Node<E> cmp, Node<E> val) {
- return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
+ return U.compareAndSwapObject(this, NEXT, cmp, val);
}
void lazySetPrev(Node<E> val) {
- UNSAFE.putOrderedObject(this, prevOffset, val);
+ U.putOrderedObject(this, PREV, val);
}
boolean casPrev(Node<E> cmp, Node<E> val) {
- return UNSAFE.compareAndSwapObject(this, prevOffset, cmp, val);
+ return U.compareAndSwapObject(this, PREV, cmp, val);
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE;
- private static final long prevOffset;
- private static final long itemOffset;
- private static final long nextOffset;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long PREV;
+ private static final long ITEM;
+ private static final long NEXT;
static {
try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- Class<?> k = Node.class;
- prevOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("prev"));
- itemOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("item"));
- nextOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("next"));
- } catch (Exception e) {
+ PREV = U.objectFieldOffset
+ (Node.class.getDeclaredField("prev"));
+ ITEM = U.objectFieldOffset
+ (Node.class.getDeclaredField("item"));
+ NEXT = U.objectFieldOffset
+ (Node.class.getDeclaredField("next"));
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
@@ -322,8 +320,7 @@
* Links e as first element.
*/
private void linkFirst(E e) {
- checkNotNull(e);
- final Node<E> newNode = new Node<E>(e);
+ final Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
restartFromHead:
for (;;)
@@ -355,8 +352,7 @@
* Links e as last element.
*/
private void linkLast(E e) {
- checkNotNull(e);
- final Node<E> newNode = new Node<E>(e);
+ final Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
restartFromTail:
for (;;)
@@ -761,16 +757,6 @@
// Minor convenience utilities
/**
- * Throws NullPointerException if argument is null.
- *
- * @param v the element
- */
- private static void checkNotNull(Object v) {
- if (v == null)
- throw new NullPointerException();
- }
-
- /**
* Returns element unless it is null, in which case throws
* NoSuchElementException.
*
@@ -784,22 +770,6 @@
}
/**
- * Creates an array list and fills it with elements of this list.
- * Used by toArray.
- *
- * @return the array list
- */
- private ArrayList<E> toArrayList() {
- ArrayList<E> list = new ArrayList<E>();
- for (Node<E> p = first(); p != null; p = succ(p)) {
- E item = p.item;
- if (item != null)
- list.add(item);
- }
- return list;
- }
-
- /**
* Constructs an empty deque.
*/
public ConcurrentLinkedDeque() {
@@ -819,8 +789,7 @@
// Copy c into a private chain of Nodes
Node<E> h = null, t = null;
for (E e : c) {
- checkNotNull(e);
- Node<E> newNode = new Node<E>(e);
+ Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
if (h == null)
h = t = newNode;
else {
@@ -995,23 +964,42 @@
}
public E poll() { return pollFirst(); }
- public E remove() { return removeFirst(); }
public E peek() { return peekFirst(); }
- public E element() { return getFirst(); }
- public void push(E e) { addFirst(e); }
+
+ /**
+ * @throws NoSuchElementException {@inheritDoc}
+ */
+ public E remove() { return removeFirst(); }
+
+ /**
+ * @throws NoSuchElementException {@inheritDoc}
+ */
public E pop() { return removeFirst(); }
/**
- * Removes the first element {@code e} such that
- * {@code o.equals(e)}, if such an element exists in this deque.
+ * @throws NoSuchElementException {@inheritDoc}
+ */
+ public E element() { return getFirst(); }
+
+ /**
+ * @throws NullPointerException {@inheritDoc}
+ */
+ public void push(E e) { addFirst(e); }
+
+ /**
+ * Removes the first occurrence of the specified element from this deque.
* If the deque does not contain the element, it is unchanged.
+ * More formally, removes the first element {@code e} such that
+ * {@code o.equals(e)} (if such an element exists).
+ * Returns {@code true} if this deque contained the specified element
+ * (or equivalently, if this deque changed as a result of the call).
*
* @param o element to be removed from this deque, if present
* @return {@code true} if the deque contained the specified element
* @throws NullPointerException if the specified element is null
*/
public boolean removeFirstOccurrence(Object o) {
- checkNotNull(o);
+ Objects.requireNonNull(o);
for (Node<E> p = first(); p != null; p = succ(p)) {
E item = p.item;
if (item != null && o.equals(item) && p.casItem(item, null)) {
@@ -1023,16 +1011,19 @@
}
/**
- * Removes the last element {@code e} such that
- * {@code o.equals(e)}, if such an element exists in this deque.
+ * Removes the last occurrence of the specified element from this deque.
* If the deque does not contain the element, it is unchanged.
+ * More formally, removes the last element {@code e} such that
+ * {@code o.equals(e)} (if such an element exists).
+ * Returns {@code true} if this deque contained the specified element
+ * (or equivalently, if this deque changed as a result of the call).
*
* @param o element to be removed from this deque, if present
* @return {@code true} if the deque contained the specified element
* @throws NullPointerException if the specified element is null
*/
public boolean removeLastOccurrence(Object o) {
- checkNotNull(o);
+ Objects.requireNonNull(o);
for (Node<E> p = last(); p != null; p = pred(p)) {
E item = p.item;
if (item != null && o.equals(item) && p.casItem(item, null)) {
@@ -1044,18 +1035,20 @@
}
/**
- * Returns {@code true} if this deque contains at least one
- * element {@code e} such that {@code o.equals(e)}.
+ * Returns {@code true} if this deque contains the specified element.
+ * More formally, returns {@code true} if and only if this deque contains
+ * at least one element {@code e} such that {@code o.equals(e)}.
*
* @param o element whose presence in this deque is to be tested
* @return {@code true} if this deque contains the specified element
*/
public boolean contains(Object o) {
- if (o == null) return false;
- for (Node<E> p = first(); p != null; p = succ(p)) {
- E item = p.item;
- if (item != null && o.equals(item))
- return true;
+ if (o != null) {
+ for (Node<E> p = first(); p != null; p = succ(p)) {
+ E item = p.item;
+ if (item != null && o.equals(item))
+ return true;
+ }
}
return false;
}
@@ -1086,19 +1079,28 @@
* @return the number of elements in this deque
*/
public int size() {
- int count = 0;
- for (Node<E> p = first(); p != null; p = succ(p))
- if (p.item != null)
- // Collection.size() spec says to max out
- if (++count == Integer.MAX_VALUE)
- break;
- return count;
+ restartFromHead: for (;;) {
+ int count = 0;
+ for (Node<E> p = first(); p != null;) {
+ if (p.item != null)
+ if (++count == Integer.MAX_VALUE)
+ break; // @see Collection.size()
+ if (p == (p = p.next))
+ continue restartFromHead;
+ }
+ return count;
+ }
}
/**
- * Removes the first element {@code e} such that
- * {@code o.equals(e)}, if such an element exists in this deque.
+ * Removes the first occurrence of the specified element from this deque.
* If the deque does not contain the element, it is unchanged.
+ * More formally, removes the first element {@code e} such that
+ * {@code o.equals(e)} (if such an element exists).
+ * Returns {@code true} if this deque contained the specified element
+ * (or equivalently, if this deque changed as a result of the call).
+ *
+ * <p>This method is equivalent to {@link #removeFirstOccurrence(Object)}.
*
* @param o element to be removed from this deque, if present
* @return {@code true} if the deque contained the specified element
@@ -1128,8 +1130,7 @@
// Copy c into a private chain of Nodes
Node<E> beginningOfTheEnd = null, last = null;
for (E e : c) {
- checkNotNull(e);
- Node<E> newNode = new Node<E>(e);
+ Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
if (beginningOfTheEnd == null)
beginningOfTheEnd = last = newNode;
else {
@@ -1180,6 +1181,62 @@
;
}
+ public String toString() {
+ String[] a = null;
+ restartFromHead: for (;;) {
+ int charLength = 0;
+ int size = 0;
+ for (Node<E> p = first(); p != null;) {
+ E item = p.item;
+ if (item != null) {
+ if (a == null)
+ a = new String[4];
+ else if (size == a.length)
+ a = Arrays.copyOf(a, 2 * size);
+ String s = item.toString();
+ a[size++] = s;
+ charLength += s.length();
+ }
+ if (p == (p = p.next))
+ continue restartFromHead;
+ }
+
+ if (size == 0)
+ return "[]";
+
+ return Helpers.toString(a, size, charLength);
+ }
+ }
+
+ private Object[] toArrayInternal(Object[] a) {
+ Object[] x = a;
+ restartFromHead: for (;;) {
+ int size = 0;
+ for (Node<E> p = first(); p != null;) {
+ E item = p.item;
+ if (item != null) {
+ if (x == null)
+ x = new Object[4];
+ else if (size == x.length)
+ x = Arrays.copyOf(x, 2 * (size + 4));
+ x[size++] = item;
+ }
+ if (p == (p = p.next))
+ continue restartFromHead;
+ }
+ if (x == null)
+ return new Object[0];
+ else if (a != null && size <= a.length) {
+ if (a != x)
+ System.arraycopy(x, 0, a, 0, size);
+ if (size < a.length)
+ a[size] = null;
+ return a;
+ }
+ return (size == x.length) ? x : Arrays.copyOf(x, size);
+ }
+ }
+
/**
* Returns an array containing all of the elements in this deque, in
* proper sequence (from first to last element).
@@ -1194,7 +1251,7 @@
* @return an array containing all of the elements in this deque
*/
public Object[] toArray() {
- return toArrayList().toArray();
+ return toArrayInternal(null);
}
/**
@@ -1220,7 +1277,7 @@
* The following code can be used to dump the deque into a newly
* allocated array of {@code String}:
*
- * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
@@ -1234,20 +1291,18 @@
* this deque
* @throws NullPointerException if the specified array is null
*/
+ @SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
- return toArrayList().toArray(a);
+ if (a == null) throw new NullPointerException();
+ return (T[]) toArrayInternal(a);
}
/**
* Returns an iterator over the elements in this deque in proper sequence.
* The elements will be returned in order from first (head) to last (tail).
*
- * <p>The returned iterator is a "weakly consistent" iterator that
- * will never throw {@link java.util.ConcurrentModificationException
- * ConcurrentModificationException}, and guarantees to traverse
- * elements as they existed upon construction of the iterator, and
- * may (but is not guaranteed to) reflect any modifications
- * subsequent to construction.
+ * <p>The returned iterator is
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
*
* @return an iterator over the elements in this deque in proper sequence
*/
@@ -1260,12 +1315,8 @@
* sequential order. The elements will be returned in order from
* last (tail) to first (head).
*
- * <p>The returned iterator is a "weakly consistent" iterator that
- * will never throw {@link java.util.ConcurrentModificationException
- * ConcurrentModificationException}, and guarantees to traverse
- * elements as they existed upon construction of the iterator, and
- * may (but is not guaranteed to) reflect any modifications
- * subsequent to construction.
+ * <p>The returned iterator is
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
*
* @return an iterator over the elements in this deque in reverse order
*/
@@ -1310,7 +1361,7 @@
Node<E> p = (nextNode == null) ? startNode() : nextNode(nextNode);
for (;; p = nextNode(p)) {
if (p == null) {
- // p might be active end or TERMINATOR node; both are OK
+ // might be at active end or TERMINATOR node; both are OK
nextNode = null;
nextItem = null;
break;
@@ -1356,9 +1407,121 @@
Node<E> nextNode(Node<E> p) { return pred(p); }
}
+ /** A customized variant of Spliterators.IteratorSpliterator */
+ static final class CLDSpliterator<E> implements Spliterator<E> {
+ static final int MAX_BATCH = 1 << 25; // max batch array size;
+ final ConcurrentLinkedDeque<E> queue;
+ Node<E> current; // current node; null until initialized
+ int batch; // batch size for splits
+ boolean exhausted; // true when no more nodes
+ CLDSpliterator(ConcurrentLinkedDeque<E> queue) {
+ this.queue = queue;
+ }
+
+ public Spliterator<E> trySplit() {
+ Node<E> p;
+ final ConcurrentLinkedDeque<E> q = this.queue;
+ int b = batch;
+ int n = (b <= 0) ? 1 : (b >= MAX_BATCH) ? MAX_BATCH : b + 1;
+ if (!exhausted &&
+ ((p = current) != null || (p = q.first()) != null)) {
+ if (p.item == null && p == (p = p.next))
+ current = p = q.first();
+ if (p != null && p.next != null) {
+ Object[] a = new Object[n];
+ int i = 0;
+ do {
+ if ((a[i] = p.item) != null)
+ ++i;
+ if (p == (p = p.next))
+ p = q.first();
+ } while (p != null && i < n);
+ if ((current = p) == null)
+ exhausted = true;
+ if (i > 0) {
+ batch = i;
+ return Spliterators.spliterator
+ (a, 0, i, (Spliterator.ORDERED |
+ Spliterator.NONNULL |
+ Spliterator.CONCURRENT));
+ }
+ }
+ }
+ return null;
+ }
+
+ public void forEachRemaining(Consumer<? super E> action) {
+ Node<E> p;
+ if (action == null) throw new NullPointerException();
+ final ConcurrentLinkedDeque<E> q = this.queue;
+ if (!exhausted &&
+ ((p = current) != null || (p = q.first()) != null)) {
+ exhausted = true;
+ do {
+ E e = p.item;
+ if (p == (p = p.next))
+ p = q.first();
+ if (e != null)
+ action.accept(e);
+ } while (p != null);
+ }
+ }
+
+ public boolean tryAdvance(Consumer<? super E> action) {
+ Node<E> p;
+ if (action == null) throw new NullPointerException();
+ final ConcurrentLinkedDeque<E> q = this.queue;
+ if (!exhausted &&
+ ((p = current) != null || (p = q.first()) != null)) {
+ E e;
+ do {
+ e = p.item;
+ if (p == (p = p.next))
+ p = q.first();
+ } while (e == null && p != null);
+ if ((current = p) == null)
+ exhausted = true;
+ if (e != null) {
+ action.accept(e);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public long estimateSize() { return Long.MAX_VALUE; }
+
+ public int characteristics() {
+ return Spliterator.ORDERED | Spliterator.NONNULL |
+ Spliterator.CONCURRENT;
+ }
+ }
+
+ /**
+ * Returns a {@link Spliterator} over the elements in this deque.
+ *
+ * <p>The returned spliterator is
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
+ *
+ * <p>The {@code Spliterator} reports {@link Spliterator#CONCURRENT},
+ * {@link Spliterator#ORDERED}, and {@link Spliterator#NONNULL}.
+ *
+ * @implNote
+ * The {@code Spliterator} implements {@code trySplit} to permit limited
+ * parallelism.
+ *
+ * @return a {@code Spliterator} over the elements in this deque
+ * @since 1.8
+ */
+ public Spliterator<E> spliterator() {
+ return new CLDSpliterator<E>(this);
+ }
+
/**
* Saves this deque to a stream (that is, serializes it).
*
+ * @param s the stream
+ * @throws java.io.IOException if an I/O error occurs
* @serialData All of the elements (each an {@code E}) in
* the proper order, followed by a null
*/
@@ -1381,6 +1544,10 @@
/**
* Reconstitutes this deque from a stream (that is, deserializes it).
+ * @param s the stream
+ * @throws ClassNotFoundException if the class of a serialized object
+ * could not be found
+ * @throws java.io.IOException if an I/O error occurs
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
@@ -1388,8 +1555,7 @@
// Read in elements until trailing null sentinel found
Node<E> h = null, t = null;
- Object item;
- while ((item = s.readObject()) != null) {
+ for (Object item; (item = s.readObject()) != null; ) {
@SuppressWarnings("unchecked")
Node<E> newNode = new Node<E>((E) item);
if (h == null)
@@ -1404,31 +1570,29 @@
}
private boolean casHead(Node<E> cmp, Node<E> val) {
- return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val);
+ return U.compareAndSwapObject(this, HEAD, cmp, val);
}
private boolean casTail(Node<E> cmp, Node<E> val) {
- return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val);
+ return U.compareAndSwapObject(this, TAIL, cmp, val);
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE;
- private static final long headOffset;
- private static final long tailOffset;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long HEAD;
+ private static final long TAIL;
static {
PREV_TERMINATOR = new Node<Object>();
PREV_TERMINATOR.next = PREV_TERMINATOR;
NEXT_TERMINATOR = new Node<Object>();
NEXT_TERMINATOR.prev = NEXT_TERMINATOR;
try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- Class<?> k = ConcurrentLinkedDeque.class;
- headOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("head"));
- tailOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("tail"));
- } catch (Exception e) {
+ HEAD = U.objectFieldOffset
+ (ConcurrentLinkedDeque.class.getDeclaredField("head"));
+ TAIL = U.objectFieldOffset
+ (ConcurrentLinkedDeque.class.getDeclaredField("tail"));
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java b/luni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java
index 9010cbe..e96b1b8 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java
@@ -7,11 +7,15 @@
package java.util.concurrent;
import java.util.AbstractQueue;
-import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
+import java.util.Objects;
import java.util.Queue;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.function.Consumer;
// BEGIN android-note
// removed link to collections framework docs
@@ -68,7 +72,7 @@
*
* @since 1.5
* @author Doug Lea
- * @param <E> the type of elements held in this collection
+ * @param <E> the type of elements held in this queue
*/
public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
implements Queue<E>, java.io.Serializable {
@@ -148,45 +152,28 @@
private static class Node<E> {
volatile E item;
volatile Node<E> next;
+ }
- /**
- * Constructs a new node. Uses relaxed write because item can
- * only be seen after publication via casNext.
- */
- Node(E item) {
- UNSAFE.putObject(this, itemOffset, item);
- }
+ /**
+ * Returns a new node holding item. Uses relaxed write because item
+ * can only be seen after piggy-backing publication via casNext.
+ */
+ static <E> Node<E> newNode(E item) {
+ Node<E> node = new Node<E>();
+ U.putObject(node, ITEM, item);
+ return node;
+ }
- boolean casItem(E cmp, E val) {
- return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
- }
+ static <E> boolean casItem(Node<E> node, E cmp, E val) {
+ return U.compareAndSwapObject(node, ITEM, cmp, val);
+ }
- void lazySetNext(Node<E> val) {
- UNSAFE.putOrderedObject(this, nextOffset, val);
- }
+ static <E> void lazySetNext(Node<E> node, Node<E> val) {
+ U.putOrderedObject(node, NEXT, val);
+ }
- boolean casNext(Node<E> cmp, Node<E> val) {
- return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
- }
-
- // Unsafe mechanics
-
- private static final sun.misc.Unsafe UNSAFE;
- private static final long itemOffset;
- private static final long nextOffset;
-
- static {
- try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- Class<?> k = Node.class;
- itemOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("item"));
- nextOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("next"));
- } catch (Exception e) {
- throw new Error(e);
- }
- }
+ static <E> boolean casNext(Node<E> node, Node<E> cmp, Node<E> val) {
+ return U.compareAndSwapObject(node, NEXT, cmp, val);
}
/**
@@ -201,7 +188,7 @@
* - it is permitted for tail to lag behind head, that is, for tail
* to not be reachable from head!
*/
- private transient volatile Node<E> head;
+ transient volatile Node<E> head;
/**
* A node from which the last node on list (that is, the unique
@@ -221,7 +208,7 @@
* Creates a {@code ConcurrentLinkedQueue} that is initially empty.
*/
public ConcurrentLinkedQueue() {
- head = tail = new Node<E>(null);
+ head = tail = newNode(null);
}
/**
@@ -236,17 +223,16 @@
public ConcurrentLinkedQueue(Collection<? extends E> c) {
Node<E> h = null, t = null;
for (E e : c) {
- checkNotNull(e);
- Node<E> newNode = new Node<E>(e);
+ Node<E> newNode = newNode(Objects.requireNonNull(e));
if (h == null)
h = t = newNode;
else {
- t.lazySetNext(newNode);
+ lazySetNext(t, newNode);
t = newNode;
}
}
if (h == null)
- h = t = new Node<E>(null);
+ h = t = newNode(null);
head = h;
tail = t;
}
@@ -270,8 +256,9 @@
* as sentinel for succ(), below.
*/
final void updateHead(Node<E> h, Node<E> p) {
+ // assert h != null && p != null && (h == p || h.item == null);
if (h != p && casHead(h, p))
- h.lazySetNext(h);
+ lazySetNext(h, h);
}
/**
@@ -292,14 +279,13 @@
* @throws NullPointerException if the specified element is null
*/
public boolean offer(E e) {
- checkNotNull(e);
- final Node<E> newNode = new Node<E>(e);
+ final Node<E> newNode = newNode(Objects.requireNonNull(e));
for (Node<E> t = tail, p = t;;) {
Node<E> q = p.next;
if (q == null) {
// p is last node
- if (p.casNext(null, newNode)) {
+ if (casNext(p, null, newNode)) {
// Successful CAS is the linearization point
// for e to become an element of this queue,
// and for newNode to become "live".
@@ -327,7 +313,7 @@
for (Node<E> h = head, p = h, q;;) {
E item = p.item;
- if (item != null && p.casItem(item, null)) {
+ if (item != null && casItem(p, item, null)) {
// Successful CAS is the linearization point
// for item to be removed from this queue.
if (p != h) // hop two nodes at a time
@@ -414,13 +400,17 @@
* @return the number of elements in this queue
*/
public int size() {
- int count = 0;
- for (Node<E> p = first(); p != null; p = succ(p))
- if (p.item != null)
- // Collection.size() spec says to max out
- if (++count == Integer.MAX_VALUE)
- break;
- return count;
+ restartFromHead: for (;;) {
+ int count = 0;
+ for (Node<E> p = first(); p != null;) {
+ if (p.item != null)
+ if (++count == Integer.MAX_VALUE)
+ break; // @see Collection.size()
+ if (p == (p = p.next))
+ continue restartFromHead;
+ }
+ return count;
+ }
}
/**
@@ -432,11 +422,12 @@
* @return {@code true} if this queue contains the specified element
*/
public boolean contains(Object o) {
- if (o == null) return false;
- for (Node<E> p = first(); p != null; p = succ(p)) {
- E item = p.item;
- if (item != null && o.equals(item))
- return true;
+ if (o != null) {
+ for (Node<E> p = first(); p != null; p = succ(p)) {
+ E item = p.item;
+ if (item != null && o.equals(item))
+ return true;
+ }
}
return false;
}
@@ -453,19 +444,25 @@
* @return {@code true} if this queue changed as a result of the call
*/
public boolean remove(Object o) {
- if (o == null) return false;
- Node<E> pred = null;
- for (Node<E> p = first(); p != null; p = succ(p)) {
- E item = p.item;
- if (item != null &&
- o.equals(item) &&
- p.casItem(item, null)) {
- Node<E> next = succ(p);
- if (pred != null && next != null)
- pred.casNext(p, next);
- return true;
+ if (o != null) {
+ Node<E> next, pred = null;
+ for (Node<E> p = first(); p != null; pred = p, p = next) {
+ boolean removed = false;
+ E item = p.item;
+ if (item != null) {
+ if (!o.equals(item)) {
+ next = succ(p);
+ continue;
+ }
+ removed = casItem(p, item, null);
+ }
+
+ next = succ(p);
+ if (pred != null && next != null) // unlink
+ casNext(pred, p, next);
+ if (removed)
+ return true;
}
- pred = p;
}
return false;
}
@@ -490,12 +487,11 @@
// Copy c into a private chain of Nodes
Node<E> beginningOfTheEnd = null, last = null;
for (E e : c) {
- checkNotNull(e);
- Node<E> newNode = new Node<E>(e);
+ Node<E> newNode = newNode(Objects.requireNonNull(e));
if (beginningOfTheEnd == null)
beginningOfTheEnd = last = newNode;
else {
- last.lazySetNext(newNode);
+ lazySetNext(last, newNode);
last = newNode;
}
}
@@ -507,7 +503,7 @@
Node<E> q = p.next;
if (q == null) {
// p is last node
- if (p.casNext(null, beginningOfTheEnd)) {
+ if (casNext(p, null, beginningOfTheEnd)) {
// Successful CAS is the linearization point
// for all elements to be added to this queue.
if (!casTail(t, last)) {
@@ -533,6 +529,62 @@
}
}
+ public String toString() {
+ String[] a = null;
+ restartFromHead: for (;;) {
+ int charLength = 0;
+ int size = 0;
+ for (Node<E> p = first(); p != null;) {
+ E item = p.item;
+ if (item != null) {
+ if (a == null)
+ a = new String[4];
+ else if (size == a.length)
+ a = Arrays.copyOf(a, 2 * size);
+ String s = item.toString();
+ a[size++] = s;
+ charLength += s.length();
+ }
+ if (p == (p = p.next))
+ continue restartFromHead;
+ }
+
+ if (size == 0)
+ return "[]";
+
+ return Helpers.toString(a, size, charLength);
+ }
+ }
+
+ private Object[] toArrayInternal(Object[] a) {
+ Object[] x = a;
+ restartFromHead: for (;;) {
+ int size = 0;
+ for (Node<E> p = first(); p != null;) {
+ E item = p.item;
+ if (item != null) {
+ if (x == null)
+ x = new Object[4];
+ else if (size == x.length)
+ x = Arrays.copyOf(x, 2 * (size + 4));
+ x[size++] = item;
+ }
+ if (p == (p = p.next))
+ continue restartFromHead;
+ }
+ if (x == null)
+ return new Object[0];
+ else if (a != null && size <= a.length) {
+ if (a != x)
+ System.arraycopy(x, 0, a, 0, size);
+ if (size < a.length)
+ a[size] = null;
+ return a;
+ }
+ return (size == x.length) ? x : Arrays.copyOf(x, size);
+ }
+ }
+
/**
* Returns an array containing all of the elements in this queue, in
* proper sequence.
@@ -547,14 +599,7 @@
* @return an array containing all of the elements in this queue
*/
public Object[] toArray() {
- // Use ArrayList to deal with resizing.
- ArrayList<E> al = new ArrayList<E>();
- for (Node<E> p = first(); p != null; p = succ(p)) {
- E item = p.item;
- if (item != null)
- al.add(item);
- }
- return al.toArray();
+ return toArrayInternal(null);
}
/**
@@ -578,7 +623,7 @@
* The following code can be used to dump the queue into a newly
* allocated array of {@code String}:
*
- * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
@@ -594,40 +639,16 @@
*/
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
- // try to use sent-in array
- int k = 0;
- Node<E> p;
- for (p = first(); p != null && k < a.length; p = succ(p)) {
- E item = p.item;
- if (item != null)
- a[k++] = (T)item;
- }
- if (p == null) {
- if (k < a.length)
- a[k] = null;
- return a;
- }
-
- // If won't fit, use ArrayList version
- ArrayList<E> al = new ArrayList<E>();
- for (Node<E> q = first(); q != null; q = succ(q)) {
- E item = q.item;
- if (item != null)
- al.add(item);
- }
- return al.toArray(a);
+ if (a == null) throw new NullPointerException();
+ return (T[]) toArrayInternal(a);
}
/**
* Returns an iterator over the elements in this queue in proper sequence.
* The elements will be returned in order from first (head) to last (tail).
*
- * <p>The returned iterator is a "weakly consistent" iterator that
- * will never throw {@link java.util.ConcurrentModificationException
- * ConcurrentModificationException}, and guarantees to traverse
- * elements as they existed upon construction of the iterator, and
- * may (but is not guaranteed to) reflect any modifications
- * subsequent to construction.
+ * <p>The returned iterator is
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
*
* @return an iterator over the elements in this queue in proper sequence
*/
@@ -655,54 +676,47 @@
private Node<E> lastRet;
Itr() {
- advance();
- }
-
- /**
- * Moves to next valid node and returns item to return for
- * next(), or null if no such.
- */
- private E advance() {
- lastRet = nextNode;
- E x = nextItem;
-
- Node<E> pred, p;
- if (nextNode == null) {
- p = first();
- pred = null;
- } else {
- pred = nextNode;
- p = succ(nextNode);
- }
-
- for (;;) {
- if (p == null) {
- nextNode = null;
- nextItem = null;
- return x;
+ restartFromHead: for (;;) {
+ Node<E> h, p, q;
+ for (p = h = head;; p = q) {
+ E item;
+ if ((item = p.item) != null) {
+ nextNode = p;
+ nextItem = item;
+ break;
+ }
+ else if ((q = p.next) == null)
+ break;
+ else if (p == q)
+ continue restartFromHead;
}
- E item = p.item;
- if (item != null) {
- nextNode = p;
- nextItem = item;
- return x;
- } else {
- // skip over nulls
- Node<E> next = succ(p);
- if (pred != null && next != null)
- pred.casNext(p, next);
- p = next;
- }
+ updateHead(h, p);
+ return;
}
}
public boolean hasNext() {
- return nextNode != null;
+ return nextItem != null;
}
public E next() {
- if (nextNode == null) throw new NoSuchElementException();
- return advance();
+ final Node<E> pred = nextNode;
+ if (pred == null) throw new NoSuchElementException();
+ // assert nextItem != null;
+ lastRet = pred;
+ E item = null;
+
+ for (Node<E> p = succ(pred), q;; p = q) {
+ if (p == null || (item = p.item) != null) {
+ nextNode = p;
+ E x = nextItem;
+ nextItem = item;
+ return x;
+ }
+ // unlink deleted nodes
+ if ((q = succ(p)) != null)
+ casNext(pred, p, q);
+ }
}
public void remove() {
@@ -717,6 +731,8 @@
/**
* Saves this queue to a stream (that is, serializes it).
*
+ * @param s the stream
+ * @throws java.io.IOException if an I/O error occurs
* @serialData All of the elements (each an {@code E}) in
* the proper order, followed by a null
*/
@@ -739,6 +755,10 @@
/**
* Reconstitutes this queue from a stream (that is, deserializes it).
+ * @param s the stream
+ * @throws ClassNotFoundException if the class of a serialized object
+ * could not be found
+ * @throws java.io.IOException if an I/O error occurs
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
@@ -746,55 +766,156 @@
// Read in elements until trailing null sentinel found
Node<E> h = null, t = null;
- Object item;
- while ((item = s.readObject()) != null) {
+ for (Object item; (item = s.readObject()) != null; ) {
@SuppressWarnings("unchecked")
- Node<E> newNode = new Node<E>((E) item);
+ Node<E> newNode = newNode((E) item);
if (h == null)
h = t = newNode;
else {
- t.lazySetNext(newNode);
+ lazySetNext(t, newNode);
t = newNode;
}
}
if (h == null)
- h = t = new Node<E>(null);
+ h = t = newNode(null);
head = h;
tail = t;
}
+ /** A customized variant of Spliterators.IteratorSpliterator */
+ static final class CLQSpliterator<E> implements Spliterator<E> {
+ static final int MAX_BATCH = 1 << 25; // max batch array size;
+ final ConcurrentLinkedQueue<E> queue;
+ Node<E> current; // current node; null until initialized
+ int batch; // batch size for splits
+ boolean exhausted; // true when no more nodes
+ CLQSpliterator(ConcurrentLinkedQueue<E> queue) {
+ this.queue = queue;
+ }
+
+ public Spliterator<E> trySplit() {
+ Node<E> p;
+ final ConcurrentLinkedQueue<E> q = this.queue;
+ int b = batch;
+ int n = (b <= 0) ? 1 : (b >= MAX_BATCH) ? MAX_BATCH : b + 1;
+ if (!exhausted &&
+ ((p = current) != null || (p = q.first()) != null) &&
+ p.next != null) {
+ Object[] a = new Object[n];
+ int i = 0;
+ do {
+ if ((a[i] = p.item) != null)
+ ++i;
+ if (p == (p = p.next))
+ p = q.first();
+ } while (p != null && i < n);
+ if ((current = p) == null)
+ exhausted = true;
+ if (i > 0) {
+ batch = i;
+ return Spliterators.spliterator
+ (a, 0, i, (Spliterator.ORDERED |
+ Spliterator.NONNULL |
+ Spliterator.CONCURRENT));
+ }
+ }
+ return null;
+ }
+
+ public void forEachRemaining(Consumer<? super E> action) {
+ Node<E> p;
+ if (action == null) throw new NullPointerException();
+ final ConcurrentLinkedQueue<E> q = this.queue;
+ if (!exhausted &&
+ ((p = current) != null || (p = q.first()) != null)) {
+ exhausted = true;
+ do {
+ E e = p.item;
+ if (p == (p = p.next))
+ p = q.first();
+ if (e != null)
+ action.accept(e);
+ } while (p != null);
+ }
+ }
+
+ public boolean tryAdvance(Consumer<? super E> action) {
+ Node<E> p;
+ if (action == null) throw new NullPointerException();
+ final ConcurrentLinkedQueue<E> q = this.queue;
+ if (!exhausted &&
+ ((p = current) != null || (p = q.first()) != null)) {
+ E e;
+ do {
+ e = p.item;
+ if (p == (p = p.next))
+ p = q.first();
+ } while (e == null && p != null);
+ if ((current = p) == null)
+ exhausted = true;
+ if (e != null) {
+ action.accept(e);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public long estimateSize() { return Long.MAX_VALUE; }
+
+ public int characteristics() {
+ return Spliterator.ORDERED | Spliterator.NONNULL |
+ Spliterator.CONCURRENT;
+ }
+ }
+
/**
- * Throws NullPointerException if argument is null.
+ * Returns a {@link Spliterator} over the elements in this queue.
*
- * @param v the element
+ * <p>The returned spliterator is
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
+ *
+ * <p>The {@code Spliterator} reports {@link Spliterator#CONCURRENT},
+ * {@link Spliterator#ORDERED}, and {@link Spliterator#NONNULL}.
+ *
+ * @implNote
+ * The {@code Spliterator} implements {@code trySplit} to permit limited
+ * parallelism.
+ *
+ * @return a {@code Spliterator} over the elements in this queue
+ * @since 1.8
*/
- private static void checkNotNull(Object v) {
- if (v == null)
- throw new NullPointerException();
+ @Override
+ public Spliterator<E> spliterator() {
+ return new CLQSpliterator<E>(this);
}
private boolean casTail(Node<E> cmp, Node<E> val) {
- return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val);
+ return U.compareAndSwapObject(this, TAIL, cmp, val);
}
private boolean casHead(Node<E> cmp, Node<E> val) {
- return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val);
+ return U.compareAndSwapObject(this, HEAD, cmp, val);
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE;
- private static final long headOffset;
- private static final long tailOffset;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long HEAD;
+ private static final long TAIL;
+ private static final long ITEM;
+ private static final long NEXT;
static {
try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- Class<?> k = ConcurrentLinkedQueue.class;
- headOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("head"));
- tailOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("tail"));
- } catch (Exception e) {
+ HEAD = U.objectFieldOffset
+ (ConcurrentLinkedQueue.class.getDeclaredField("head"));
+ TAIL = U.objectFieldOffset
+ (ConcurrentLinkedQueue.class.getDeclaredField("tail"));
+ ITEM = U.objectFieldOffset
+ (Node.class.getDeclaredField("item"));
+ NEXT = U.objectFieldOffset
+ (Node.class.getDeclaredField("next"));
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentMap.java b/luni/src/main/java/java/util/concurrent/ConcurrentMap.java
index 1391f04..ae4d221 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentMap.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentMap.java
@@ -7,14 +7,27 @@
package java.util.concurrent;
import java.util.Map;
+import java.util.Objects;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Function;
// BEGIN android-note
// removed link to collections framework docs
+// fixed framework docs link to "Collection#optional"
// END android-note
/**
- * A {@link java.util.Map} providing additional atomic
- * {@code putIfAbsent}, {@code remove}, and {@code replace} methods.
+ * A {@link java.util.Map} providing thread safety and atomicity
+ * guarantees.
+ *
+ * <p>To maintain the specified guarantees, default implementations of
+ * methods including {@link #putIfAbsent} inherited from {@link Map}
+ * must be overridden by implementations of this interface. Similarly,
+ * implementations of the collections returned by methods {@link
+ * #keySet}, {@link #values}, and {@link #entrySet} must override
+ * methods such as {@code removeIf} when necessary to
+ * preserve atomicity guarantees.
*
* <p>Memory consistency effects: As with other concurrent
* collections, actions in a thread prior to placing an object into a
@@ -29,11 +42,65 @@
* @param <V> the type of mapped values
*/
public interface ConcurrentMap<K,V> extends Map<K,V> {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @implNote This implementation assumes that the ConcurrentMap cannot
+ * contain null values and {@code get()} returning null unambiguously means
+ * the key is absent. Implementations which support null values
+ * <strong>must</strong> override this default implementation.
+ *
+ * @throws ClassCastException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ * @since 1.8
+ */
+ @Override
+ default V getOrDefault(Object key, V defaultValue) {
+ V v;
+ return ((v = get(key)) != null) ? v : defaultValue;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @implSpec The default implementation is equivalent to, for this
+ * {@code map}:
+ * <pre> {@code
+ * for (Map.Entry<K,V> entry : map.entrySet()) {
+ * action.accept(entry.getKey(), entry.getValue());
+ * }}</pre>
+ *
+ * @implNote The default implementation assumes that
+ * {@code IllegalStateException} thrown by {@code getKey()} or
+ * {@code getValue()} indicates that the entry has been removed and cannot
+ * be processed. Operation continues for subsequent entries.
+ *
+ * @throws NullPointerException {@inheritDoc}
+ * @since 1.8
+ */
+ @Override
+ default void forEach(BiConsumer<? super K, ? super V> action) {
+ Objects.requireNonNull(action);
+ for (Map.Entry<K,V> entry : entrySet()) {
+ K k;
+ V v;
+ try {
+ k = entry.getKey();
+ v = entry.getValue();
+ } catch (IllegalStateException ise) {
+ // this usually means the entry is no longer in the map.
+ continue;
+ }
+ action.accept(k, v);
+ }
+ }
+
/**
* If the specified key is not already associated
- * with a value, associate it with the given value.
- * This is equivalent to
- * <pre> {@code
+ * with a value, associates it with the given value.
+ * This is equivalent to, for this {@code map}:
+ * <pre> {@code
* if (!map.containsKey(key))
* return map.put(key, value);
* else
@@ -41,6 +108,9 @@
*
* except that the action is performed atomically.
*
+ * @implNote This implementation intentionally re-abstracts the
+ * inappropriate default provided in {@code Map}.
+ *
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with the specified key, or
@@ -61,16 +131,21 @@
/**
* Removes the entry for a key only if currently mapped to a given value.
- * This is equivalent to
- * <pre> {@code
- * if (map.containsKey(key) && map.get(key).equals(value)) {
+ * This is equivalent to, for this {@code map}:
+ * <pre> {@code
+ * if (map.containsKey(key)
+ * && Objects.equals(map.get(key), value)) {
* map.remove(key);
* return true;
- * } else
- * return false;}</pre>
+ * } else {
+ * return false;
+ * }}</pre>
*
* except that the action is performed atomically.
*
+ * @implNote This implementation intentionally re-abstracts the
+ * inappropriate default provided in {@code Map}.
+ *
* @param key key with which the specified value is associated
* @param value value expected to be associated with the specified key
* @return {@code true} if the value was removed
@@ -78,25 +153,30 @@
* is not supported by this map
* @throws ClassCastException if the key or value is of an inappropriate
* type for this map
- * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified key or value is null,
* and this map does not permit null keys or values
- * (<a href="../Collection.html#optional-restrictions">optional</a>)
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean remove(Object key, Object value);
/**
* Replaces the entry for a key only if currently mapped to a given value.
- * This is equivalent to
- * <pre> {@code
- * if (map.containsKey(key) && map.get(key).equals(oldValue)) {
+ * This is equivalent to, for this {@code map}:
+ * <pre> {@code
+ * if (map.containsKey(key)
+ * && Objects.equals(map.get(key), oldValue)) {
* map.put(key, newValue);
* return true;
- * } else
- * return false;}</pre>
+ * } else {
+ * return false;
+ * }}</pre>
*
* except that the action is performed atomically.
*
+ * @implNote This implementation intentionally re-abstracts the
+ * inappropriate default provided in {@code Map}.
+ *
* @param key key with which the specified value is associated
* @param oldValue value expected to be associated with the specified key
* @param newValue value to be associated with the specified key
@@ -114,15 +194,18 @@
/**
* Replaces the entry for a key only if currently mapped to some value.
- * This is equivalent to
- * <pre> {@code
- * if (map.containsKey(key)) {
+ * This is equivalent to, for this {@code map}:
+ * <pre> {@code
+ * if (map.containsKey(key))
* return map.put(key, value);
- * } else
+ * else
* return null;}</pre>
*
* except that the action is performed atomically.
*
+ * @implNote This implementation intentionally re-abstracts the
+ * inappropriate default provided in {@code Map}.
+ *
* @param key key with which the specified value is associated
* @param value value to be associated with the specified key
* @return the previous value associated with the specified key, or
@@ -140,4 +223,251 @@
* or value prevents it from being stored in this map
*/
V replace(K key, V value);
+
+ /**
+ * {@inheritDoc}
+ *
+ * @implSpec
+ * <p>The default implementation is equivalent to, for this {@code map}:
+ * <pre> {@code
+ * for (Map.Entry<K,V> entry : map.entrySet()) {
+ * K k;
+ * V v;
+ * do {
+ * k = entry.getKey();
+ * v = entry.getValue();
+ * } while (!map.replace(k, v, function.apply(k, v)));
+ * }}</pre>
+ *
+ * The default implementation may retry these steps when multiple
+ * threads attempt updates including potentially calling the function
+ * repeatedly for a given key.
+ *
+ * <p>This implementation assumes that the ConcurrentMap cannot contain null
+ * values and {@code get()} returning null unambiguously means the key is
+ * absent. Implementations which support null values <strong>must</strong>
+ * override this default implementation.
+ *
+ * @throws UnsupportedOperationException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ * @throws ClassCastException {@inheritDoc}
+ * @throws IllegalArgumentException {@inheritDoc}
+ * @since 1.8
+ */
+ @Override
+ default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
+ Objects.requireNonNull(function);
+ forEach((k,v) -> {
+ while (!replace(k, v, function.apply(k, v))) {
+ // v changed or k is gone
+ if ( (v = get(k)) == null) {
+ // k is no longer in the map.
+ break;
+ }
+ }
+ });
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @implSpec
+ * The default implementation is equivalent to the following steps for this
+ * {@code map}:
+ *
+ * <pre> {@code
+ * V oldValue, newValue;
+ * return ((oldValue = map.get(key)) == null
+ * && (newValue = mappingFunction.apply(key)) != null
+ * && (oldValue = map.putIfAbsent(key, newValue)) == null)
+ * ? newValue
+ * : oldValue;}</pre>
+ *
+ * <p>This implementation assumes that the ConcurrentMap cannot contain null
+ * values and {@code get()} returning null unambiguously means the key is
+ * absent. Implementations which support null values <strong>must</strong>
+ * override this default implementation.
+ *
+ * @throws UnsupportedOperationException {@inheritDoc}
+ * @throws ClassCastException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ * @throws IllegalArgumentException {@inheritDoc}
+ * @since 1.8
+ */
+ @Override
+ default V computeIfAbsent(K key,
+ Function<? super K, ? extends V> mappingFunction) {
+ Objects.requireNonNull(mappingFunction);
+ V oldValue, newValue;
+ return ((oldValue = get(key)) == null
+ && (newValue = mappingFunction.apply(key)) != null
+ && (oldValue = putIfAbsent(key, newValue)) == null)
+ ? newValue
+ : oldValue;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @implSpec
+ * The default implementation is equivalent to performing the following
+ * steps for this {@code map}:
+ *
+ * <pre> {@code
+ * for (V oldValue; (oldValue = map.get(key)) != null; ) {
+ * V newValue = remappingFunction.apply(key, oldValue);
+ * if ((newValue == null)
+ * ? map.remove(key, oldValue)
+ * : map.replace(key, oldValue, newValue))
+ * return newValue;
+ * }
+ * return null;}</pre>
+ * When multiple threads attempt updates, map operations and the
+ * remapping function may be called multiple times.
+ *
+ * <p>This implementation assumes that the ConcurrentMap cannot contain null
+ * values and {@code get()} returning null unambiguously means the key is
+ * absent. Implementations which support null values <strong>must</strong>
+ * override this default implementation.
+ *
+ * @throws UnsupportedOperationException {@inheritDoc}
+ * @throws ClassCastException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ * @throws IllegalArgumentException {@inheritDoc}
+ * @since 1.8
+ */
+ @Override
+ default V computeIfPresent(K key,
+ BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ Objects.requireNonNull(remappingFunction);
+ for (V oldValue; (oldValue = get(key)) != null; ) {
+ V newValue = remappingFunction.apply(key, oldValue);
+ if ((newValue == null)
+ ? remove(key, oldValue)
+ : replace(key, oldValue, newValue))
+ return newValue;
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @implSpec
+ * The default implementation is equivalent to performing the following
+ * steps for this {@code map}:
+ *
+ * <pre> {@code
+ * for (;;) {
+ * V oldValue = map.get(key);
+ * V newValue = remappingFunction.apply(key, oldValue);
+ * if (newValue != null) {
+ * if ((oldValue != null)
+ * ? map.replace(key, oldValue, newValue)
+ * : map.putIfAbsent(key, newValue) == null)
+ * return newValue;
+ * } else if (oldValue == null || map.remove(key, oldValue)) {
+ * return null;
+ * }
+ * }}</pre>
+ * When multiple threads attempt updates, map operations and the
+ * remapping function may be called multiple times.
+ *
+ * <p>This implementation assumes that the ConcurrentMap cannot contain null
+ * values and {@code get()} returning null unambiguously means the key is
+ * absent. Implementations which support null values <strong>must</strong>
+ * override this default implementation.
+ *
+ * @throws UnsupportedOperationException {@inheritDoc}
+ * @throws ClassCastException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ * @throws IllegalArgumentException {@inheritDoc}
+ * @since 1.8
+ */
+ @Override
+ default V compute(K key,
+ BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ retry: for (;;) {
+ V oldValue = get(key);
+ // if putIfAbsent fails, opportunistically use its return value
+ haveOldValue: for (;;) {
+ V newValue = remappingFunction.apply(key, oldValue);
+ if (newValue != null) {
+ if (oldValue != null) {
+ if (replace(key, oldValue, newValue))
+ return newValue;
+ }
+ else if ((oldValue = putIfAbsent(key, newValue)) == null)
+ return newValue;
+ else continue haveOldValue;
+ } else if (oldValue == null || remove(key, oldValue)) {
+ return null;
+ }
+ continue retry;
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @implSpec
+ * The default implementation is equivalent to performing the following
+ * steps for this {@code map}:
+ *
+ * <pre> {@code
+ * for (;;) {
+ * V oldValue = map.get(key);
+ * if (oldValue != null) {
+ * V newValue = remappingFunction.apply(oldValue, value);
+ * if (newValue != null) {
+ * if (map.replace(key, oldValue, newValue))
+ * return newValue;
+ * } else if (map.remove(key, oldValue)) {
+ * return null;
+ * }
+ * } else if (map.putIfAbsent(key, value) == null) {
+ * return value;
+ * }
+ * }}</pre>
+ * When multiple threads attempt updates, map operations and the
+ * remapping function may be called multiple times.
+ *
+ * <p>This implementation assumes that the ConcurrentMap cannot contain null
+ * values and {@code get()} returning null unambiguously means the key is
+ * absent. Implementations which support null values <strong>must</strong>
+ * override this default implementation.
+ *
+ * @throws UnsupportedOperationException {@inheritDoc}
+ * @throws ClassCastException {@inheritDoc}
+ * @throws NullPointerException {@inheritDoc}
+ * @throws IllegalArgumentException {@inheritDoc}
+ * @since 1.8
+ */
+ @Override
+ default V merge(K key, V value,
+ BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
+ Objects.requireNonNull(remappingFunction);
+ Objects.requireNonNull(value);
+ retry: for (;;) {
+ V oldValue = get(key);
+ // if putIfAbsent fails, opportunistically use its return value
+ haveOldValue: for (;;) {
+ if (oldValue != null) {
+ V newValue = remappingFunction.apply(oldValue, value);
+ if (newValue != null) {
+ if (replace(key, oldValue, newValue))
+ return newValue;
+ } else if (remove(key, oldValue)) {
+ return null;
+ }
+ continue retry;
+ } else {
+ if ((oldValue = putIfAbsent(key, value)) == null)
+ return value;
+ continue haveOldValue;
+ }
+ }
+ }
+ }
}
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java b/luni/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java
index 17890ff..0d795b4 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentNavigableMap.java
@@ -6,7 +6,8 @@
package java.util.concurrent;
-import java.util.*;
+import java.util.NavigableMap;
+import java.util.NavigableSet;
// BEGIN android-note
// removed link to collections framework docs
@@ -73,7 +74,7 @@
* reflected in the descending map, and vice-versa.
*
* <p>The returned map has an ordering equivalent to
- * {@link Collections#reverseOrder(Comparator) Collections.reverseOrder}{@code (comparator())}.
+ * {@link java.util.Collections#reverseOrder(Comparator) Collections.reverseOrder}{@code (comparator())}.
* The expression {@code m.descendingMap().descendingMap()} returns a
* view of {@code m} essentially equivalent to {@code m}.
*
@@ -92,15 +93,12 @@
* operations. It does not support the {@code add} or {@code addAll}
* operations.
*
- * <p>The view's {@code iterator} is a "weakly consistent" iterator
- * that will never throw {@link ConcurrentModificationException},
- * and guarantees to traverse elements as they existed upon
- * construction of the iterator, and may (but is not guaranteed to)
- * reflect any modifications subsequent to construction.
+ * <p>The view's iterators and spliterators are
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
*
* @return a navigable set view of the keys in this map
*/
- public NavigableSet<K> navigableKeySet();
+ NavigableSet<K> navigableKeySet();
/**
* Returns a {@link NavigableSet} view of the keys contained in this map.
@@ -113,11 +111,8 @@
* operations. It does not support the {@code add} or {@code addAll}
* operations.
*
- * <p>The view's {@code iterator} is a "weakly consistent" iterator
- * that will never throw {@link ConcurrentModificationException},
- * and guarantees to traverse elements as they existed upon
- * construction of the iterator, and may (but is not guaranteed to)
- * reflect any modifications subsequent to construction.
+ * <p>The view's iterators and spliterators are
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
*
* <p>This method is equivalent to method {@code navigableKeySet}.
*
@@ -136,13 +131,10 @@
* operations. It does not support the {@code add} or {@code addAll}
* operations.
*
- * <p>The view's {@code iterator} is a "weakly consistent" iterator
- * that will never throw {@link ConcurrentModificationException},
- * and guarantees to traverse elements as they existed upon
- * construction of the iterator, and may (but is not guaranteed to)
- * reflect any modifications subsequent to construction.
+ * <p>The view's iterators and spliterators are
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
*
* @return a reverse order navigable set view of the keys in this map
*/
- public NavigableSet<K> descendingKeySet();
+ NavigableSet<K> descendingKeySet();
}
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java b/luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java
index 0e8b64a..359d4f1 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentSkipListMap.java
@@ -6,7 +6,28 @@
package java.util.concurrent;
-import java.util.*;
+import java.io.Serializable;
+import java.util.AbstractCollection;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.NavigableSet;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.Spliterator;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
// BEGIN android-note
// removed link to collections framework docs
@@ -24,12 +45,13 @@
* {@code containsKey}, {@code get}, {@code put} and
* {@code remove} operations and their variants. Insertion, removal,
* update, and access operations safely execute concurrently by
- * multiple threads. Iterators are <i>weakly consistent</i>, returning
- * elements reflecting the state of the map at some point at or since
- * the creation of the iterator. They do <em>not</em> throw {@link
- * ConcurrentModificationException}, and may proceed concurrently with
- * other operations. Ascending key ordered views and their iterators
- * are faster than descending ones.
+ * multiple threads.
+ *
+ * <p>Iterators and spliterators are
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
+ *
+ * <p>Ascending key ordered views and their iterators are faster than
+ * descending ones.
*
* <p>All {@code Map.Entry} pairs returned by methods in this class
* and its views represent snapshots of mappings at the time they were
@@ -61,11 +83,8 @@
* @param <V> the type of mapped values
* @since 1.6
*/
-@SuppressWarnings("unchecked")
public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
- implements ConcurrentNavigableMap<K,V>,
- Cloneable,
- java.io.Serializable {
+ implements ConcurrentNavigableMap<K,V>, Cloneable, Serializable {
/*
* This class implements a tree-like two-dimensionally linked skip
* list in which the index levels are represented in separate
@@ -229,7 +248,7 @@
*
* Indexing uses skip list parameters that maintain good search
* performance while using sparser-than-usual indices: The
- * hardwired parameters k=1, p=0.5 (see method randomLevel) mean
+ * hardwired parameters k=1, p=0.5 (see method doPut) mean
* that about one-quarter of the nodes have indices. Of those that
* do, half have one level, a quarter have two, and so on (see
* Pugh's Skip List Cookbook, sec 3.4). The expected total space
@@ -267,6 +286,20 @@
* there is a fair amount of near-duplication of code to handle
* variants.
*
+ * To produce random values without interference across threads,
+ * we use within-JDK thread local random support (via the
+ * "secondary seed", to avoid interference with user-level
+ * ThreadLocalRandom.)
+ *
+ * A previous version of this class wrapped non-comparable keys
+ * with their comparators to emulate Comparables when using
+ * comparators vs Comparables. However, JVMs now appear to better
+ * handle infusing comparator-vs-comparable choice into search
+ * loops. Static method cpr(comparator, x, y) is used for all
+ * comparisons, which works well as long as the comparator
+ * argument is set up outside of loops (thus sometimes passed as
+ * an argument to internal methods) to avoid field re-reads.
+ *
* For explanation of algorithms sharing at least a couple of
* features with this one, see Mikhail Fomitchev's thesis
* (http://www.cs.yorku.ca/~mikhail/), Keir Fraser's thesis
@@ -294,18 +327,10 @@
private static final long serialVersionUID = -8627078645895051609L;
-// BEGIN android-removed
-// /**
-// * Generates the initial random seed for the cheaper per-instance
-// * random number generators used in randomLevel.
-// */
-// private static final Random seedGenerator = new Random();
-// END android-removed
-
/**
- * Special value used to identify base-level header
+ * Special value used to identify base-level header.
*/
- private static final Object BASE_HEADER = new Object();
+ static final Object BASE_HEADER = new Object();
/**
* The topmost head index of the skiplist.
@@ -313,24 +338,19 @@
private transient volatile HeadIndex<K,V> head;
/**
- * The comparator used to maintain order in this map, or null
- * if using natural ordering.
+ * The comparator used to maintain order in this map, or null if
+ * using natural ordering. (Non-private to simplify access in
+ * nested classes.)
* @serial
*/
- private final Comparator<? super K> comparator;
-
- /**
- * Seed for simple random number generator. Not volatile since it
- * doesn't matter too much if different threads don't see updates.
- */
- private transient int randomSeed;
+ final Comparator<? super K> comparator;
/** Lazily initialized key set */
- private transient KeySet<K> keySet;
+ private transient KeySet<K,V> keySet;
/** Lazily initialized entry set */
private transient EntrySet<K,V> entrySet;
/** Lazily initialized values collection */
- private transient Values<V> values;
+ private transient Values<K,V> values;
/** Lazily initialized descending key set */
private transient ConcurrentNavigableMap<K,V> descendingMap;
@@ -339,27 +359,20 @@
* clear, readObject. and ConcurrentSkipListSet.clone.
* (Note that comparator must be separately initialized.)
*/
- final void initialize() {
+ private void initialize() {
keySet = null;
entrySet = null;
values = null;
descendingMap = null;
- // BEGIN android-changed
- //
- // Most processes are forked from the zygote, so they'll end up
- // with the same random seed unless we take additional post fork
- // measures.
- randomSeed = Math.randomIntInternal() | 0x0100; // ensure nonzero
- // END android-changed
head = new HeadIndex<K,V>(new Node<K,V>(null, BASE_HEADER, null),
null, null, 1);
}
/**
- * compareAndSet head node
+ * compareAndSet head node.
*/
private boolean casHead(HeadIndex<K,V> cmp, HeadIndex<K,V> val) {
- return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val);
+ return U.compareAndSwapObject(this, HEAD, cmp, val);
}
/* ---------------- Nodes -------------- */
@@ -399,17 +412,17 @@
}
/**
- * compareAndSet value field
+ * compareAndSet value field.
*/
boolean casValue(Object cmp, Object val) {
- return UNSAFE.compareAndSwapObject(this, valueOffset, cmp, val);
+ return U.compareAndSwapObject(this, VALUE, cmp, val);
}
/**
- * compareAndSet next field
+ * compareAndSet next field.
*/
boolean casNext(Node<K,V> cmp, Node<K,V> val) {
- return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
+ return U.compareAndSwapObject(this, NEXT, cmp, val);
}
/**
@@ -457,7 +470,7 @@
*/
if (f == next && this == b.next) {
if (f == null || f.value != f) // not already marked
- appendMarker(f);
+ casNext(f, new Node<K,V>(f));
else
b.casNext(this, f.next);
}
@@ -473,7 +486,8 @@
Object v = value;
if (v == this || v == BASE_HEADER)
return null;
- return (V)v;
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ return vv;
}
/**
@@ -482,27 +496,26 @@
* @return new entry or null
*/
AbstractMap.SimpleImmutableEntry<K,V> createSnapshot() {
- V v = getValidValue();
- if (v == null)
+ Object v = value;
+ if (v == null || v == this || v == BASE_HEADER)
return null;
- return new AbstractMap.SimpleImmutableEntry<K,V>(key, v);
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ return new AbstractMap.SimpleImmutableEntry<K,V>(key, vv);
}
- // UNSAFE mechanics
+ // Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE;
- private static final long valueOffset;
- private static final long nextOffset;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long VALUE;
+ private static final long NEXT;
static {
try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- Class<?> k = Node.class;
- valueOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("value"));
- nextOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("next"));
- } catch (Exception e) {
+ VALUE = U.objectFieldOffset
+ (Node.class.getDeclaredField("value"));
+ NEXT = U.objectFieldOffset
+ (Node.class.getDeclaredField("next"));
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
@@ -532,10 +545,10 @@
}
/**
- * compareAndSet right field
+ * compareAndSet right field.
*/
final boolean casRight(Index<K,V> cmp, Index<K,V> val) {
- return UNSAFE.compareAndSwapObject(this, rightOffset, cmp, val);
+ return U.compareAndSwapObject(this, RIGHT, cmp, val);
}
/**
@@ -568,19 +581,17 @@
* @return true if successful
*/
final boolean unlink(Index<K,V> succ) {
- return !indexesDeletedNode() && casRight(succ, succ.right);
+ return node.value != null && casRight(succ, succ.right);
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE;
- private static final long rightOffset;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long RIGHT;
static {
try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- Class<?> k = Index.class;
- rightOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("right"));
- } catch (Exception e) {
+ RIGHT = U.objectFieldOffset
+ (Index.class.getDeclaredField("right"));
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
@@ -602,80 +613,12 @@
/* ---------------- Comparison utilities -------------- */
/**
- * Represents a key with a comparator as a Comparable.
- *
- * Because most sorted collections seem to use natural ordering on
- * Comparables (Strings, Integers, etc), most internal methods are
- * geared to use them. This is generally faster than checking
- * per-comparison whether to use comparator or comparable because
- * it doesn't require a (Comparable) cast for each comparison.
- * (Optimizers can only sometimes remove such redundant checks
- * themselves.) When Comparators are used,
- * ComparableUsingComparators are created so that they act in the
- * same way as natural orderings. This penalizes use of
- * Comparators vs Comparables, which seems like the right
- * tradeoff.
+ * Compares using comparator or natural ordering if null.
+ * Called only by methods that have performed required type checks.
*/
- static final class ComparableUsingComparator<K> implements Comparable<K> {
- final K actualKey;
- final Comparator<? super K> cmp;
- ComparableUsingComparator(K key, Comparator<? super K> cmp) {
- this.actualKey = key;
- this.cmp = cmp;
- }
- public int compareTo(K k2) {
- return cmp.compare(actualKey, k2);
- }
- }
-
- /**
- * If using comparator, return a ComparableUsingComparator, else
- * cast key as Comparable, which may cause ClassCastException,
- * which is propagated back to caller.
- */
- private Comparable<? super K> comparable(Object key)
- throws ClassCastException {
- if (key == null)
- throw new NullPointerException();
- if (comparator != null)
- return new ComparableUsingComparator<K>((K)key, comparator);
- else
- return (Comparable<? super K>)key;
- }
-
- /**
- * Compares using comparator or natural ordering. Used when the
- * ComparableUsingComparator approach doesn't apply.
- */
- int compare(K k1, K k2) throws ClassCastException {
- Comparator<? super K> cmp = comparator;
- if (cmp != null)
- return cmp.compare(k1, k2);
- else
- return ((Comparable<? super K>)k1).compareTo(k2);
- }
-
- /**
- * Returns true if given key greater than or equal to least and
- * strictly less than fence, bypassing either test if least or
- * fence are null. Needed mainly in submap operations.
- */
- boolean inHalfOpenRange(K key, K least, K fence) {
- if (key == null)
- throw new NullPointerException();
- return ((least == null || compare(key, least) >= 0) &&
- (fence == null || compare(key, fence) < 0));
- }
-
- /**
- * Returns true if given key greater than or equal to least and less
- * or equal to fence. Needed mainly in submap operations.
- */
- boolean inOpenRange(K key, K least, K fence) {
- if (key == null)
- throw new NullPointerException();
- return ((least == null || compare(key, least) >= 0) &&
- (fence == null || compare(key, fence) <= 0));
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ static final int cpr(Comparator c, Object x, Object y) {
+ return (c != null) ? c.compare(x, y) : ((Comparable)x).compareTo(y);
}
/* ---------------- Traversal -------------- */
@@ -688,13 +631,11 @@
* @param key the key
* @return a predecessor of key
*/
- private Node<K,V> findPredecessor(Comparable<? super K> key) {
+ private Node<K,V> findPredecessor(Object key, Comparator<? super K> cmp) {
if (key == null)
throw new NullPointerException(); // don't postpone errors
for (;;) {
- Index<K,V> q = head;
- Index<K,V> r = q.right;
- for (;;) {
+ for (Index<K,V> q = head, r = q.right, d;;) {
if (r != null) {
Node<K,V> n = r.node;
K k = n.key;
@@ -704,18 +645,16 @@
r = q.right; // reread r
continue;
}
- if (key.compareTo(k) > 0) {
+ if (cpr(cmp, key, k) > 0) {
q = r;
r = r.right;
continue;
}
}
- Index<K,V> d = q.down;
- if (d != null) {
- q = d;
- r = d.right;
- } else
+ if ((d = q.down) == null)
return q.node;
+ q = d;
+ r = d.right;
}
}
}
@@ -756,62 +695,79 @@
*
* The traversal loops in doPut, doRemove, and findNear all
* include the same three kinds of checks. And specialized
- * versions appear in findFirst, and findLast and their
- * variants. They can't easily share code because each uses the
- * reads of fields held in locals occurring in the orders they
- * were performed.
+ * versions appear in findFirst, and findLast and their variants.
+ * They can't easily share code because each uses the reads of
+ * fields held in locals occurring in the orders they were
+ * performed.
*
* @param key the key
* @return node holding key, or null if no such
*/
- private Node<K,V> findNode(Comparable<? super K> key) {
- for (;;) {
- Node<K,V> b = findPredecessor(key);
- Node<K,V> n = b.next;
- for (;;) {
+ private Node<K,V> findNode(Object key) {
+ if (key == null)
+ throw new NullPointerException(); // don't postpone errors
+ Comparator<? super K> cmp = comparator;
+ outer: for (;;) {
+ for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
+ Object v; int c;
if (n == null)
- return null;
+ break outer;
Node<K,V> f = n.next;
if (n != b.next) // inconsistent read
break;
- Object v = n.value;
- if (v == null) { // n is deleted
+ if ((v = n.value) == null) { // n is deleted
n.helpDelete(b, f);
break;
}
- if (v == n || b.value == null) // b is deleted
+ if (b.value == null || v == n) // b is deleted
break;
- int c = key.compareTo(n.key);
- if (c == 0)
+ if ((c = cpr(cmp, key, n.key)) == 0)
return n;
if (c < 0)
- return null;
+ break outer;
b = n;
n = f;
}
}
+ return null;
}
/**
- * Gets value for key using findNode.
- * @param okey the key
+ * Gets value for key. Almost the same as findNode, but returns
+ * the found value (to avoid retries during re-reads)
+ *
+ * @param key the key
* @return the value, or null if absent
*/
- private V doGet(Object okey) {
- Comparable<? super K> key = comparable(okey);
- /*
- * Loop needed here and elsewhere in case value field goes
- * null just as it is about to be returned, in which case we
- * lost a race with a deletion, so must retry.
- */
- for (;;) {
- Node<K,V> n = findNode(key);
- if (n == null)
- return null;
- Object v = n.value;
- if (v != null)
- return (V)v;
+ private V doGet(Object key) {
+ if (key == null)
+ throw new NullPointerException();
+ Comparator<? super K> cmp = comparator;
+ outer: for (;;) {
+ for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
+ Object v; int c;
+ if (n == null)
+ break outer;
+ Node<K,V> f = n.next;
+ if (n != b.next) // inconsistent read
+ break;
+ if ((v = n.value) == null) { // n is deleted
+ n.helpDelete(b, f);
+ break;
+ }
+ if (b.value == null || v == n) // b is deleted
+ break;
+ if ((c = cpr(cmp, key, n.key)) == 0) {
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ return vv;
+ }
+ if (c < 0)
+ break outer;
+ b = n;
+ n = f;
+ }
}
+ return null;
}
/* ---------------- Insertion -------------- */
@@ -819,187 +775,126 @@
/**
* Main insertion method. Adds element if not present, or
* replaces value if present and onlyIfAbsent is false.
- * @param kkey the key
+ * @param key the key
* @param value the value that must be associated with key
* @param onlyIfAbsent if should not insert if already present
* @return the old value, or null if newly inserted
*/
- private V doPut(K kkey, V value, boolean onlyIfAbsent) {
- Comparable<? super K> key = comparable(kkey);
- for (;;) {
- Node<K,V> b = findPredecessor(key);
- Node<K,V> n = b.next;
- for (;;) {
+ private V doPut(K key, V value, boolean onlyIfAbsent) {
+ Node<K,V> z; // added node
+ if (key == null)
+ throw new NullPointerException();
+ Comparator<? super K> cmp = comparator;
+ outer: for (;;) {
+ for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
if (n != null) {
+ Object v; int c;
Node<K,V> f = n.next;
if (n != b.next) // inconsistent read
break;
- Object v = n.value;
- if (v == null) { // n is deleted
+ if ((v = n.value) == null) { // n is deleted
n.helpDelete(b, f);
break;
}
- if (v == n || b.value == null) // b is deleted
+ if (b.value == null || v == n) // b is deleted
break;
- int c = key.compareTo(n.key);
- if (c > 0) {
+ if ((c = cpr(cmp, key, n.key)) > 0) {
b = n;
n = f;
continue;
}
if (c == 0) {
- if (onlyIfAbsent || n.casValue(v, value))
- return (V)v;
- else
- break; // restart if lost race to replace value
+ if (onlyIfAbsent || n.casValue(v, value)) {
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ return vv;
+ }
+ break; // restart if lost race to replace value
}
// else c < 0; fall through
}
- Node<K,V> z = new Node<K,V>(kkey, value, n);
+ z = new Node<K,V>(key, value, n);
if (!b.casNext(n, z))
break; // restart if lost race to append to b
- int level = randomLevel();
- if (level > 0)
- insertIndex(z, level);
- return null;
+ break outer;
}
}
- }
- /**
- * Returns a random level for inserting a new node.
- * Hardwired to k=1, p=0.5, max 31 (see above and
- * Pugh's "Skip List Cookbook", sec 3.4).
- *
- * This uses the simplest of the generators described in George
- * Marsaglia's "Xorshift RNGs" paper. This is not a high-quality
- * generator but is acceptable here.
- */
- private int randomLevel() {
- int x = randomSeed;
- x ^= x << 13;
- x ^= x >>> 17;
- randomSeed = x ^= x << 5;
- if ((x & 0x80000001) != 0) // test highest and lowest bits
- return 0;
- int level = 1;
- while (((x >>>= 1) & 1) != 0) ++level;
- return level;
- }
-
- /**
- * Creates and adds index nodes for the given node.
- * @param z the node
- * @param level the level of the index
- */
- private void insertIndex(Node<K,V> z, int level) {
- HeadIndex<K,V> h = head;
- int max = h.level;
-
- if (level <= max) {
+ int rnd = ThreadLocalRandom.nextSecondarySeed();
+ if ((rnd & 0x80000001) == 0) { // test highest and lowest bits
+ int level = 1, max;
+ while (((rnd >>>= 1) & 1) != 0)
+ ++level;
Index<K,V> idx = null;
- for (int i = 1; i <= level; ++i)
- idx = new Index<K,V>(z, idx, null);
- addIndex(idx, h, level);
-
- } else { // Add a new level
- /*
- * To reduce interference by other threads checking for
- * empty levels in tryReduceLevel, new levels are added
- * with initialized right pointers. Which in turn requires
- * keeping levels in an array to access them while
- * creating new head index nodes from the opposite
- * direction.
- */
- level = max + 1;
- Index<K,V>[] idxs = (Index<K,V>[])new Index<?,?>[level+1];
- Index<K,V> idx = null;
- for (int i = 1; i <= level; ++i)
- idxs[i] = idx = new Index<K,V>(z, idx, null);
-
- HeadIndex<K,V> oldh;
- int k;
- for (;;) {
- oldh = head;
- int oldLevel = oldh.level;
- if (level <= oldLevel) { // lost race to add level
- k = level;
- break;
- }
- HeadIndex<K,V> newh = oldh;
- Node<K,V> oldbase = oldh.node;
- for (int j = oldLevel+1; j <= level; ++j)
- newh = new HeadIndex<K,V>(oldbase, newh, idxs[j], j);
- if (casHead(oldh, newh)) {
- k = oldLevel;
- break;
+ HeadIndex<K,V> h = head;
+ if (level <= (max = h.level)) {
+ for (int i = 1; i <= level; ++i)
+ idx = new Index<K,V>(z, idx, null);
+ }
+ else { // try to grow by one level
+ level = max + 1; // hold in array and later pick the one to use
+ @SuppressWarnings("unchecked")Index<K,V>[] idxs =
+ (Index<K,V>[])new Index<?,?>[level+1];
+ for (int i = 1; i <= level; ++i)
+ idxs[i] = idx = new Index<K,V>(z, idx, null);
+ for (;;) {
+ h = head;
+ int oldLevel = h.level;
+ if (level <= oldLevel) // lost race to add level
+ break;
+ HeadIndex<K,V> newh = h;
+ Node<K,V> oldbase = h.node;
+ for (int j = oldLevel+1; j <= level; ++j)
+ newh = new HeadIndex<K,V>(oldbase, newh, idxs[j], j);
+ if (casHead(h, newh)) {
+ h = newh;
+ idx = idxs[level = oldLevel];
+ break;
+ }
}
}
- addIndex(idxs[k], oldh, k);
- }
- }
-
- /**
- * Adds given index nodes from given level down to 1.
- * @param idx the topmost index node being inserted
- * @param h the value of head to use to insert. This must be
- * snapshotted by callers to provide correct insertion level.
- * @param indexLevel the level of the index
- */
- private void addIndex(Index<K,V> idx, HeadIndex<K,V> h, int indexLevel) {
- // Track next level to insert in case of retries
- int insertionLevel = indexLevel;
- Comparable<? super K> key = comparable(idx.node.key);
- if (key == null) throw new NullPointerException();
-
- // Similar to findPredecessor, but adding index nodes along
- // path to key.
- for (;;) {
- int j = h.level;
- Index<K,V> q = h;
- Index<K,V> r = q.right;
- Index<K,V> t = idx;
- for (;;) {
- if (r != null) {
- Node<K,V> n = r.node;
- // compare before deletion check avoids needing recheck
- int c = key.compareTo(n.key);
- if (n.value == null) {
- if (!q.unlink(r))
- break;
- r = q.right;
- continue;
+ // find insertion points and splice in
+ splice: for (int insertionLevel = level;;) {
+ int j = h.level;
+ for (Index<K,V> q = h, r = q.right, t = idx;;) {
+ if (q == null || t == null)
+ break splice;
+ if (r != null) {
+ Node<K,V> n = r.node;
+ // compare before deletion check avoids needing recheck
+ int c = cpr(cmp, key, n.key);
+ if (n.value == null) {
+ if (!q.unlink(r))
+ break;
+ r = q.right;
+ continue;
+ }
+ if (c > 0) {
+ q = r;
+ r = r.right;
+ continue;
+ }
}
- if (c > 0) {
- q = r;
- r = r.right;
- continue;
- }
- }
- if (j == insertionLevel) {
- // Don't insert index if node already deleted
- if (t.indexesDeletedNode()) {
- findNode(key); // cleans up
- return;
- }
- if (!q.link(r, t))
- break; // restart
- if (--insertionLevel == 0) {
- // need final deletion check before return
- if (t.indexesDeletedNode())
+ if (j == insertionLevel) {
+ if (!q.link(r, t))
+ break; // restart
+ if (t.node.value == null) {
findNode(key);
- return;
+ break splice;
+ }
+ if (--insertionLevel == 0)
+ break splice;
}
- }
- if (--j >= insertionLevel && j < indexLevel)
- t = t.down;
- q = q.down;
- r = q.right;
+ if (--j >= insertionLevel && j < level)
+ t = t.down;
+ q = q.down;
+ r = q.right;
+ }
}
}
+ return null;
}
/* ---------------- Deletion -------------- */
@@ -1018,51 +913,52 @@
* search for it, and we'd like to ensure lack of garbage
* retention, so must call to be sure.
*
- * @param okey the key
+ * @param key the key
* @param value if non-null, the value that must be
* associated with key
* @return the node, or null if not found
*/
- final V doRemove(Object okey, Object value) {
- Comparable<? super K> key = comparable(okey);
- for (;;) {
- Node<K,V> b = findPredecessor(key);
- Node<K,V> n = b.next;
- for (;;) {
+ final V doRemove(Object key, Object value) {
+ if (key == null)
+ throw new NullPointerException();
+ Comparator<? super K> cmp = comparator;
+ outer: for (;;) {
+ for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
+ Object v; int c;
if (n == null)
- return null;
+ break outer;
Node<K,V> f = n.next;
if (n != b.next) // inconsistent read
break;
- Object v = n.value;
- if (v == null) { // n is deleted
+ if ((v = n.value) == null) { // n is deleted
n.helpDelete(b, f);
break;
}
- if (v == n || b.value == null) // b is deleted
+ if (b.value == null || v == n) // b is deleted
break;
- int c = key.compareTo(n.key);
- if (c < 0)
- return null;
+ if ((c = cpr(cmp, key, n.key)) < 0)
+ break outer;
if (c > 0) {
b = n;
n = f;
continue;
}
if (value != null && !value.equals(v))
- return null;
+ break outer;
if (!n.casValue(v, null))
break;
if (!n.appendMarker(f) || !b.casNext(n, f))
- findNode(key); // Retry via findNode
+ findNode(key); // retry via findNode
else {
- findPredecessor(key); // Clean index
+ findPredecessor(key, cmp); // clean index
if (head.right == null)
tryReduceLevel();
}
- return (V)v;
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ return vv;
}
}
+ return null;
}
/**
@@ -1106,11 +1002,9 @@
* Specialized variant of findNode to get first valid node.
* @return first node or null if empty
*/
- Node<K,V> findFirst() {
- for (;;) {
- Node<K,V> b = head.node;
- Node<K,V> n = b.next;
- if (n == null)
+ final Node<K,V> findFirst() {
+ for (Node<K,V> b, n;;) {
+ if ((n = (b = head.node).next) == null)
return null;
if (n.value != null)
return n;
@@ -1122,11 +1016,9 @@
* Removes first entry; returns its snapshot.
* @return null if empty, else snapshot of first entry
*/
- Map.Entry<K,V> doRemoveFirstEntry() {
- for (;;) {
- Node<K,V> b = head.node;
- Node<K,V> n = b.next;
- if (n == null)
+ private Map.Entry<K,V> doRemoveFirstEntry() {
+ for (Node<K,V> b, n;;) {
+ if ((n = (b = head.node).next) == null)
return null;
Node<K,V> f = n.next;
if (n != b.next)
@@ -1141,7 +1033,8 @@
if (!n.appendMarker(f) || !b.casNext(n, f))
findFirst(); // retry
clearIndexToFirst();
- return new AbstractMap.SimpleImmutableEntry<K,V>(n.key, (V)v);
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ return new AbstractMap.SimpleImmutableEntry<K,V>(n.key, vv);
}
}
@@ -1150,8 +1043,7 @@
*/
private void clearIndexToFirst() {
for (;;) {
- Index<K,V> q = head;
- for (;;) {
+ for (Index<K,V> q = head;;) {
Index<K,V> r = q.right;
if (r != null && r.indexesDeletedNode() && !q.unlink(r))
break;
@@ -1164,6 +1056,52 @@
}
}
+ /**
+ * Removes last entry; returns its snapshot.
+ * Specialized variant of doRemove.
+ * @return null if empty, else snapshot of last entry
+ */
+ private Map.Entry<K,V> doRemoveLastEntry() {
+ for (;;) {
+ Node<K,V> b = findPredecessorOfLast();
+ Node<K,V> n = b.next;
+ if (n == null) {
+ if (b.isBaseHeader()) // empty
+ return null;
+ else
+ continue; // all b's successors are deleted; retry
+ }
+ for (;;) {
+ Node<K,V> f = n.next;
+ if (n != b.next) // inconsistent read
+ break;
+ Object v = n.value;
+ if (v == null) { // n is deleted
+ n.helpDelete(b, f);
+ break;
+ }
+ if (b.value == null || v == n) // b is deleted
+ break;
+ if (f != null) {
+ b = n;
+ n = f;
+ continue;
+ }
+ if (!n.casValue(v, null))
+ break;
+ K key = n.key;
+ if (!n.appendMarker(f) || !b.casNext(n, f))
+ findNode(key); // retry via findNode
+ else { // clean index
+ findPredecessor(key, comparator);
+ if (head.right == null)
+ tryReduceLevel();
+ }
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ return new AbstractMap.SimpleImmutableEntry<K,V>(key, vv);
+ }
+ }
+ }
/* ---------------- Finding and removing last element -------------- */
@@ -1171,7 +1109,7 @@
* Specialized version of find to get last valid node.
* @return last node or null if empty
*/
- Node<K,V> findLast() {
+ final Node<K,V> findLast() {
/*
* findPredecessor can't be used to traverse index level
* because this doesn't use comparisons. So traversals of
@@ -1190,9 +1128,7 @@
} else if ((d = q.down) != null) {
q = d;
} else {
- Node<K,V> b = q.node;
- Node<K,V> n = b.next;
- for (;;) {
+ for (Node<K,V> b = q.node, n = b.next;;) {
if (n == null)
return b.isBaseHeader() ? null : b;
Node<K,V> f = n.next; // inconsistent read
@@ -1203,7 +1139,7 @@
n.helpDelete(b, f);
break;
}
- if (v == n || b.value == null) // b is deleted
+ if (b.value == null || v == n) // b is deleted
break;
b = n;
n = f;
@@ -1222,8 +1158,7 @@
*/
private Node<K,V> findPredecessorOfLast() {
for (;;) {
- Index<K,V> q = head;
- for (;;) {
+ for (Index<K,V> q = head;;) {
Index<K,V> d, r;
if ((r = q.right) != null) {
if (r.indexesDeletedNode()) {
@@ -1244,53 +1179,6 @@
}
}
- /**
- * Removes last entry; returns its snapshot.
- * Specialized variant of doRemove.
- * @return null if empty, else snapshot of last entry
- */
- Map.Entry<K,V> doRemoveLastEntry() {
- for (;;) {
- Node<K,V> b = findPredecessorOfLast();
- Node<K,V> n = b.next;
- if (n == null) {
- if (b.isBaseHeader()) // empty
- return null;
- else
- continue; // all b's successors are deleted; retry
- }
- for (;;) {
- Node<K,V> f = n.next;
- if (n != b.next) // inconsistent read
- break;
- Object v = n.value;
- if (v == null) { // n is deleted
- n.helpDelete(b, f);
- break;
- }
- if (v == n || b.value == null) // b is deleted
- break;
- if (f != null) {
- b = n;
- n = f;
- continue;
- }
- if (!n.casValue(v, null))
- break;
- K key = n.key;
- Comparable<? super K> ck = comparable(key);
- if (!n.appendMarker(f) || !b.casNext(n, f))
- findNode(ck); // Retry via findNode
- else {
- findPredecessor(ck); // Clean index
- if (head.right == null)
- tryReduceLevel();
- }
- return new AbstractMap.SimpleImmutableEntry<K,V>(key, (V)v);
- }
- }
- }
-
/* ---------------- Relational operations -------------- */
// Control values OR'ed as arguments to findNear
@@ -1301,29 +1189,28 @@
/**
* Utility for ceiling, floor, lower, higher methods.
- * @param kkey the key
+ * @param key the key
* @param rel the relation -- OR'ed combination of EQ, LT, GT
* @return nearest node fitting relation, or null if no such
*/
- Node<K,V> findNear(K kkey, int rel) {
- Comparable<? super K> key = comparable(kkey);
+ final Node<K,V> findNear(K key, int rel, Comparator<? super K> cmp) {
+ if (key == null)
+ throw new NullPointerException();
for (;;) {
- Node<K,V> b = findPredecessor(key);
- Node<K,V> n = b.next;
- for (;;) {
+ for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
+ Object v;
if (n == null)
return ((rel & LT) == 0 || b.isBaseHeader()) ? null : b;
Node<K,V> f = n.next;
if (n != b.next) // inconsistent read
break;
- Object v = n.value;
- if (v == null) { // n is deleted
+ if ((v = n.value) == null) { // n is deleted
n.helpDelete(b, f);
break;
}
- if (v == n || b.value == null) // b is deleted
+ if (b.value == null || v == n) // b is deleted
break;
- int c = key.compareTo(n.key);
+ int c = cpr(cmp, key, n.key);
if ((c == 0 && (rel & EQ) != 0) ||
(c < 0 && (rel & LT) == 0))
return n;
@@ -1341,9 +1228,10 @@
* @param rel the relation -- OR'ed combination of EQ, LT, GT
* @return Entry fitting relation, or null if no such
*/
- AbstractMap.SimpleImmutableEntry<K,V> getNear(K key, int rel) {
+ final AbstractMap.SimpleImmutableEntry<K,V> getNear(K key, int rel) {
+ Comparator<? super K> cmp = comparator;
for (;;) {
- Node<K,V> n = findNear(key, rel);
+ Node<K,V> n = findNear(key, rel, cmp);
if (n == null)
return null;
AbstractMap.SimpleImmutableEntry<K,V> e = n.createSnapshot();
@@ -1352,7 +1240,6 @@
}
}
-
/* ---------------- Constructors -------------- */
/**
@@ -1442,7 +1329,7 @@
// Track the current rightmost node at each level. Uses an
// ArrayList to avoid committing to initial or maximum level.
- ArrayList<Index<K,V>> preds = new ArrayList<Index<K,V>>();
+ ArrayList<Index<K,V>> preds = new ArrayList<>();
// initialize
for (int i = 0; i <= h.level; ++i)
@@ -1457,8 +1344,14 @@
map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<? extends K, ? extends V> e = it.next();
- int j = randomLevel();
- if (j > h.level) j = h.level + 1;
+ int rnd = ThreadLocalRandom.current().nextInt();
+ int j = 0;
+ if ((rnd & 0x80000001) == 0) {
+ do {
+ ++j;
+ } while (((rnd >>>= 1) & 1) != 0);
+ if (j > h.level) j = h.level + 1;
+ }
K k = e.getKey();
V v = e.getValue();
if (k == null || v == null)
@@ -1489,6 +1382,8 @@
/**
* Saves this map to a stream (that is, serializes it).
*
+ * @param s the stream
+ * @throws java.io.IOException if an I/O error occurs
* @serialData The key (Object) and value (Object) for each
* key-value mapping represented by the map, followed by
* {@code null}. The key-value mappings are emitted in key-order
@@ -1513,7 +1408,12 @@
/**
* Reconstitutes this map from a stream (that is, deserializes it).
+ * @param s the stream
+ * @throws ClassNotFoundException if the class of a serialized object
+ * could not be found
+ * @throws java.io.IOException if an I/O error occurs
*/
+ @SuppressWarnings("unchecked")
private void readObject(final java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in the Comparator and any hidden stuff
@@ -1526,12 +1426,12 @@
* distinct because readObject calls can't be nicely adapted
* as the kind of iterator needed by buildFromSorted. (They
* can be, but doing so requires type cheats and/or creation
- * of adaptor classes.) It is simpler to just adapt the code.
+ * of adapter classes.) It is simpler to just adapt the code.
*/
HeadIndex<K,V> h = head;
Node<K,V> basepred = h.node;
- ArrayList<Index<K,V>> preds = new ArrayList<Index<K,V>>();
+ ArrayList<Index<K,V>> preds = new ArrayList<>();
for (int i = 0; i <= h.level; ++i)
preds.add(null);
Index<K,V> q = h;
@@ -1549,8 +1449,14 @@
throw new NullPointerException();
K key = (K) k;
V val = (V) v;
- int j = randomLevel();
- if (j > h.level) j = h.level + 1;
+ int rnd = ThreadLocalRandom.current().nextInt();
+ int j = 0;
+ if ((rnd & 0x80000001) == 0) {
+ do {
+ ++j;
+ } while (((rnd >>>= 1) & 1) != 0);
+ if (j > h.level) j = h.level + 1;
+ }
Node<K,V> z = new Node<K,V>(key, val, null);
basepred.next = z;
basepred = z;
@@ -1607,6 +1513,22 @@
}
/**
+ * Returns the value to which the specified key is mapped,
+ * or the given defaultValue if this map contains no mapping for the key.
+ *
+ * @param key the key
+ * @param defaultValue the value to return if this map contains
+ * no mapping for the given key
+ * @return the mapping for the key, if present; else the defaultValue
+ * @throws NullPointerException if the specified key is null
+ * @since 1.8
+ */
+ public V getOrDefault(Object key, V defaultValue) {
+ V v;
+ return (v = doGet(key)) == null ? defaultValue : v;
+ }
+
+ /**
* Associates the specified value with the specified key in this map.
* If the map previously contained a mapping for the key, the old
* value is replaced.
@@ -1702,6 +1624,140 @@
initialize();
}
+ /**
+ * If the specified key is not already associated with a value,
+ * attempts to compute its value using the given mapping function
+ * and enters it into this map unless {@code null}. The function
+ * is <em>NOT</em> guaranteed to be applied once atomically only
+ * if the value is not present.
+ *
+ * @param key key with which the specified value is to be associated
+ * @param mappingFunction the function to compute a value
+ * @return the current (existing or computed) value associated with
+ * the specified key, or null if the computed value is null
+ * @throws NullPointerException if the specified key is null
+ * or the mappingFunction is null
+ * @since 1.8
+ */
+ public V computeIfAbsent(K key,
+ Function<? super K, ? extends V> mappingFunction) {
+ if (key == null || mappingFunction == null)
+ throw new NullPointerException();
+ V v, p, r;
+ if ((v = doGet(key)) == null &&
+ (r = mappingFunction.apply(key)) != null)
+ v = (p = doPut(key, r, true)) == null ? r : p;
+ return v;
+ }
+
+ /**
+ * If the value for the specified key is present, attempts to
+ * compute a new mapping given the key and its current mapped
+ * value. The function is <em>NOT</em> guaranteed to be applied
+ * once atomically.
+ *
+ * @param key key with which a value may be associated
+ * @param remappingFunction the function to compute a value
+ * @return the new value associated with the specified key, or null if none
+ * @throws NullPointerException if the specified key is null
+ * or the remappingFunction is null
+ * @since 1.8
+ */
+ public V computeIfPresent(K key,
+ BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ if (key == null || remappingFunction == null)
+ throw new NullPointerException();
+ Node<K,V> n; Object v;
+ while ((n = findNode(key)) != null) {
+ if ((v = n.value) != null) {
+ @SuppressWarnings("unchecked") V vv = (V) v;
+ V r = remappingFunction.apply(key, vv);
+ if (r != null) {
+ if (n.casValue(vv, r))
+ return r;
+ }
+ else if (doRemove(key, vv) != null)
+ break;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Attempts to compute a mapping for the specified key and its
+ * current mapped value (or {@code null} if there is no current
+ * mapping). The function is <em>NOT</em> guaranteed to be applied
+ * once atomically.
+ *
+ * @param key key with which the specified value is to be associated
+ * @param remappingFunction the function to compute a value
+ * @return the new value associated with the specified key, or null if none
+ * @throws NullPointerException if the specified key is null
+ * or the remappingFunction is null
+ * @since 1.8
+ */
+ public V compute(K key,
+ BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ if (key == null || remappingFunction == null)
+ throw new NullPointerException();
+ for (;;) {
+ Node<K,V> n; Object v; V r;
+ if ((n = findNode(key)) == null) {
+ if ((r = remappingFunction.apply(key, null)) == null)
+ break;
+ if (doPut(key, r, true) == null)
+ return r;
+ }
+ else if ((v = n.value) != null) {
+ @SuppressWarnings("unchecked") V vv = (V) v;
+ if ((r = remappingFunction.apply(key, vv)) != null) {
+ if (n.casValue(vv, r))
+ return r;
+ }
+ else if (doRemove(key, vv) != null)
+ break;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * If the specified key is not already associated with a value,
+ * associates it with the given value. Otherwise, replaces the
+ * value with the results of the given remapping function, or
+ * removes if {@code null}. The function is <em>NOT</em>
+ * guaranteed to be applied once atomically.
+ *
+ * @param key key with which the specified value is to be associated
+ * @param value the value to use if absent
+ * @param remappingFunction the function to recompute a value if present
+ * @return the new value associated with the specified key, or null if none
+ * @throws NullPointerException if the specified key or value is null
+ * or the remappingFunction is null
+ * @since 1.8
+ */
+ public V merge(K key, V value,
+ BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
+ if (key == null || value == null || remappingFunction == null)
+ throw new NullPointerException();
+ for (;;) {
+ Node<K,V> n; Object v; V r;
+ if ((n = findNode(key)) == null) {
+ if (doPut(key, value, true) == null)
+ return value;
+ }
+ else if ((v = n.value) != null) {
+ @SuppressWarnings("unchecked") V vv = (V) v;
+ if ((r = remappingFunction.apply(vv, value)) != null) {
+ if (n.casValue(vv, r))
+ return r;
+ }
+ else if (doRemove(key, vv) != null)
+ return null;
+ }
+ }
+ }
+
/* ---------------- View methods -------------- */
/*
@@ -1715,8 +1771,18 @@
/**
* Returns a {@link NavigableSet} view of the keys contained in this map.
- * The set's iterator returns the keys in ascending order.
- * The set is backed by the map, so changes to the map are
+ *
+ * <p>The set's iterator returns the keys in ascending order.
+ * The set's spliterator additionally reports {@link Spliterator#CONCURRENT},
+ * {@link Spliterator#NONNULL}, {@link Spliterator#SORTED} and
+ * {@link Spliterator#ORDERED}, with an encounter order that is ascending
+ * key order. The spliterator's comparator (see
+ * {@link java.util.Spliterator#getComparator()}) is {@code null} if
+ * the map's comparator (see {@link #comparator()}) is {@code null}.
+ * Otherwise, the spliterator's comparator is the same as or imposes the
+ * same total ordering as the map's comparator.
+ *
+ * <p>The set is backed by the map, so changes to the map are
* reflected in the set, and vice-versa. The set supports element
* removal, which removes the corresponding mapping from the map,
* via the {@code Iterator.remove}, {@code Set.remove},
@@ -1724,31 +1790,32 @@
* operations. It does not support the {@code add} or {@code addAll}
* operations.
*
- * <p>The view's {@code iterator} is a "weakly consistent" iterator
- * that will never throw {@link ConcurrentModificationException},
- * and guarantees to traverse elements as they existed upon
- * construction of the iterator, and may (but is not guaranteed to)
- * reflect any modifications subsequent to construction.
+ * <p>The view's iterators and spliterators are
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
*
* <p>This method is equivalent to method {@code navigableKeySet}.
*
* @return a navigable set view of the keys in this map
*/
public NavigableSet<K> keySet() {
- KeySet<K> ks = keySet;
- return (ks != null) ? ks : (keySet = new KeySet<K>(this));
+ KeySet<K,V> ks = keySet;
+ return (ks != null) ? ks : (keySet = new KeySet<>(this));
}
public NavigableSet<K> navigableKeySet() {
- KeySet<K> ks = keySet;
- return (ks != null) ? ks : (keySet = new KeySet<K>(this));
+ KeySet<K,V> ks = keySet;
+ return (ks != null) ? ks : (keySet = new KeySet<>(this));
}
/**
* Returns a {@link Collection} view of the values contained in this map.
- * The collection's iterator returns the values in ascending order
- * of the corresponding keys.
- * The collection is backed by the map, so changes to the map are
+ * <p>The collection's iterator returns the values in ascending order
+ * of the corresponding keys. The collections's spliterator additionally
+ * reports {@link Spliterator#CONCURRENT}, {@link Spliterator#NONNULL} and
+ * {@link Spliterator#ORDERED}, with an encounter order that is ascending
+ * order of the corresponding keys.
+ *
+ * <p>The collection is backed by the map, so changes to the map are
* reflected in the collection, and vice-versa. The collection
* supports element removal, which removes the corresponding
* mapping from the map, via the {@code Iterator.remove},
@@ -1756,21 +1823,24 @@
* {@code retainAll} and {@code clear} operations. It does not
* support the {@code add} or {@code addAll} operations.
*
- * <p>The view's {@code iterator} is a "weakly consistent" iterator
- * that will never throw {@link ConcurrentModificationException},
- * and guarantees to traverse elements as they existed upon
- * construction of the iterator, and may (but is not guaranteed to)
- * reflect any modifications subsequent to construction.
+ * <p>The view's iterators and spliterators are
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
*/
public Collection<V> values() {
- Values<V> vs = values;
- return (vs != null) ? vs : (values = new Values<V>(this));
+ Values<K,V> vs = values;
+ return (vs != null) ? vs : (values = new Values<>(this));
}
/**
* Returns a {@link Set} view of the mappings contained in this map.
- * The set's iterator returns the entries in ascending key order.
- * The set is backed by the map, so changes to the map are
+ *
+ * <p>The set's iterator returns the entries in ascending key order. The
+ * set's spliterator additionally reports {@link Spliterator#CONCURRENT},
+ * {@link Spliterator#NONNULL}, {@link Spliterator#SORTED} and
+ * {@link Spliterator#ORDERED}, with an encounter order that is ascending
+ * key order.
+ *
+ * <p>The set is backed by the map, so changes to the map are
* reflected in the set, and vice-versa. The set supports element
* removal, which removes the corresponding mapping from the map,
* via the {@code Iterator.remove}, {@code Set.remove},
@@ -1778,15 +1848,12 @@
* operations. It does not support the {@code add} or
* {@code addAll} operations.
*
- * <p>The view's {@code iterator} is a "weakly consistent" iterator
- * that will never throw {@link ConcurrentModificationException},
- * and guarantees to traverse elements as they existed upon
- * construction of the iterator, and may (but is not guaranteed to)
- * reflect any modifications subsequent to construction.
+ * <p>The view's iterators and spliterators are
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
*
- * <p>The {@code Map.Entry} elements returned by
- * {@code iterator.next()} do <em>not</em> support the
- * {@code setValue} operation.
+ * <p>The {@code Map.Entry} elements traversed by the {@code iterator}
+ * or {@code spliterator} do <em>not</em> support the {@code setValue}
+ * operation.
*
* @return a set view of the mappings contained in this map,
* sorted in ascending key order
@@ -1871,9 +1938,7 @@
public boolean remove(Object key, Object value) {
if (key == null)
throw new NullPointerException();
- if (value == null)
- return false;
- return doRemove(key, value) != null;
+ return value != null && doRemove(key, value) != null;
}
/**
@@ -1884,15 +1949,13 @@
* @throws NullPointerException if any of the arguments are null
*/
public boolean replace(K key, V oldValue, V newValue) {
- if (oldValue == null || newValue == null)
+ if (key == null || oldValue == null || newValue == null)
throw new NullPointerException();
- Comparable<? super K> k = comparable(key);
for (;;) {
- Node<K,V> n = findNode(k);
- if (n == null)
+ Node<K,V> n; Object v;
+ if ((n = findNode(key)) == null)
return false;
- Object v = n.value;
- if (v != null) {
+ if ((v = n.value) != null) {
if (!oldValue.equals(v))
return false;
if (n.casValue(v, newValue))
@@ -1911,16 +1974,16 @@
* @throws NullPointerException if the specified key or value is null
*/
public V replace(K key, V value) {
- if (value == null)
+ if (key == null || value == null)
throw new NullPointerException();
- Comparable<? super K> k = comparable(key);
for (;;) {
- Node<K,V> n = findNode(k);
- if (n == null)
+ Node<K,V> n; Object v;
+ if ((n = findNode(key)) == null)
return null;
- Object v = n.value;
- if (v != null && n.casValue(v, value))
- return (V)v;
+ if ((v = n.value) != null && n.casValue(v, value)) {
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ return vv;
+ }
}
}
@@ -2038,7 +2101,7 @@
* @throws NullPointerException if the specified key is null
*/
public K lowerKey(K key) {
- Node<K,V> n = findNear(key, LT);
+ Node<K,V> n = findNear(key, LT, comparator);
return (n == null) ? null : n.key;
}
@@ -2062,7 +2125,7 @@
* @throws NullPointerException if the specified key is null
*/
public K floorKey(K key) {
- Node<K,V> n = findNear(key, LT|EQ);
+ Node<K,V> n = findNear(key, LT|EQ, comparator);
return (n == null) ? null : n.key;
}
@@ -2084,7 +2147,7 @@
* @throws NullPointerException if the specified key is null
*/
public K ceilingKey(K key) {
- Node<K,V> n = findNear(key, GT|EQ);
+ Node<K,V> n = findNear(key, GT|EQ, comparator);
return (n == null) ? null : n.key;
}
@@ -2108,7 +2171,7 @@
* @throws NullPointerException if the specified key is null
*/
public K higherKey(K key) {
- Node<K,V> n = findNear(key, GT);
+ Node<K,V> n = findNear(key, GT, comparator);
return (n == null) ? null : n.key;
}
@@ -2182,13 +2245,11 @@
/** Initializes ascending iterator for entire range. */
Iter() {
- for (;;) {
- next = findFirst();
- if (next == null)
- break;
+ while ((next = findFirst()) != null) {
Object x = next.value;
if (x != null && x != next) {
- nextValue = (V) x;
+ @SuppressWarnings("unchecked") V vv = (V)x;
+ nextValue = vv;
break;
}
}
@@ -2203,13 +2264,11 @@
if (next == null)
throw new NoSuchElementException();
lastReturned = next;
- for (;;) {
- next = next.next;
- if (next == null)
- break;
+ while ((next = next.next) != null) {
Object x = next.value;
if (x != null && x != next) {
- nextValue = (V) x;
+ @SuppressWarnings("unchecked") V vv = (V)x;
+ nextValue = vv;
break;
}
}
@@ -2252,20 +2311,6 @@
}
}
- // Factory methods for iterators needed by ConcurrentSkipListSet etc
-
- Iterator<K> keyIterator() {
- return new KeyIterator();
- }
-
- Iterator<V> valueIterator() {
- return new ValueIterator();
- }
-
- Iterator<Map.Entry<K,V>> entryIterator() {
- return new EntryIterator();
- }
-
/* ---------------- View Classes -------------- */
/*
@@ -2282,35 +2327,34 @@
return list;
}
- static final class KeySet<E>
- extends AbstractSet<E> implements NavigableSet<E> {
- private final ConcurrentNavigableMap<E,?> m;
- KeySet(ConcurrentNavigableMap<E,?> map) { m = map; }
+ static final class KeySet<K,V>
+ extends AbstractSet<K> implements NavigableSet<K> {
+ final ConcurrentNavigableMap<K,V> m;
+ KeySet(ConcurrentNavigableMap<K,V> map) { m = map; }
public int size() { return m.size(); }
public boolean isEmpty() { return m.isEmpty(); }
public boolean contains(Object o) { return m.containsKey(o); }
public boolean remove(Object o) { return m.remove(o) != null; }
public void clear() { m.clear(); }
- public E lower(E e) { return m.lowerKey(e); }
- public E floor(E e) { return m.floorKey(e); }
- public E ceiling(E e) { return m.ceilingKey(e); }
- public E higher(E e) { return m.higherKey(e); }
- public Comparator<? super E> comparator() { return m.comparator(); }
- public E first() { return m.firstKey(); }
- public E last() { return m.lastKey(); }
- public E pollFirst() {
- Map.Entry<E,?> e = m.pollFirstEntry();
+ public K lower(K e) { return m.lowerKey(e); }
+ public K floor(K e) { return m.floorKey(e); }
+ public K ceiling(K e) { return m.ceilingKey(e); }
+ public K higher(K e) { return m.higherKey(e); }
+ public Comparator<? super K> comparator() { return m.comparator(); }
+ public K first() { return m.firstKey(); }
+ public K last() { return m.lastKey(); }
+ public K pollFirst() {
+ Map.Entry<K,V> e = m.pollFirstEntry();
return (e == null) ? null : e.getKey();
}
- public E pollLast() {
- Map.Entry<E,?> e = m.pollLastEntry();
+ public K pollLast() {
+ Map.Entry<K,V> e = m.pollLastEntry();
return (e == null) ? null : e.getKey();
}
- public Iterator<E> iterator() {
- if (m instanceof ConcurrentSkipListMap)
- return ((ConcurrentSkipListMap<E,Object>)m).keyIterator();
- else
- return ((ConcurrentSkipListMap.SubMap<E,Object>)m).keyIterator();
+ public Iterator<K> iterator() {
+ return (m instanceof ConcurrentSkipListMap)
+ ? ((ConcurrentSkipListMap<K,V>)m).new KeyIterator()
+ : ((SubMap<K,V>)m).new SubMapKeyIterator();
}
public boolean equals(Object o) {
if (o == this)
@@ -2328,81 +2372,99 @@
}
public Object[] toArray() { return toList(this).toArray(); }
public <T> T[] toArray(T[] a) { return toList(this).toArray(a); }
- public Iterator<E> descendingIterator() {
+ public Iterator<K> descendingIterator() {
return descendingSet().iterator();
}
- public NavigableSet<E> subSet(E fromElement,
+ public NavigableSet<K> subSet(K fromElement,
boolean fromInclusive,
- E toElement,
+ K toElement,
boolean toInclusive) {
- return new KeySet<E>(m.subMap(fromElement, fromInclusive,
- toElement, toInclusive));
+ return new KeySet<>(m.subMap(fromElement, fromInclusive,
+ toElement, toInclusive));
}
- public NavigableSet<E> headSet(E toElement, boolean inclusive) {
- return new KeySet<E>(m.headMap(toElement, inclusive));
+ public NavigableSet<K> headSet(K toElement, boolean inclusive) {
+ return new KeySet<>(m.headMap(toElement, inclusive));
}
- public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
- return new KeySet<E>(m.tailMap(fromElement, inclusive));
+ public NavigableSet<K> tailSet(K fromElement, boolean inclusive) {
+ return new KeySet<>(m.tailMap(fromElement, inclusive));
}
- public NavigableSet<E> subSet(E fromElement, E toElement) {
+ public NavigableSet<K> subSet(K fromElement, K toElement) {
return subSet(fromElement, true, toElement, false);
}
- public NavigableSet<E> headSet(E toElement) {
+ public NavigableSet<K> headSet(K toElement) {
return headSet(toElement, false);
}
- public NavigableSet<E> tailSet(E fromElement) {
+ public NavigableSet<K> tailSet(K fromElement) {
return tailSet(fromElement, true);
}
- public NavigableSet<E> descendingSet() {
- return new KeySet<E>(m.descendingMap());
+ public NavigableSet<K> descendingSet() {
+ return new KeySet<>(m.descendingMap());
+ }
+
+ public Spliterator<K> spliterator() {
+ return (m instanceof ConcurrentSkipListMap)
+ ? ((ConcurrentSkipListMap<K,V>)m).keySpliterator()
+ : ((SubMap<K,V>)m).new SubMapKeyIterator();
}
}
- static final class Values<E> extends AbstractCollection<E> {
- private final ConcurrentNavigableMap<?,E> m;
- Values(ConcurrentNavigableMap<?,E> map) {
+ static final class Values<K,V> extends AbstractCollection<V> {
+ final ConcurrentNavigableMap<K,V> m;
+ Values(ConcurrentNavigableMap<K,V> map) {
m = map;
}
- public Iterator<E> iterator() {
- if (m instanceof ConcurrentSkipListMap)
- return ((ConcurrentSkipListMap<?,E>)m).valueIterator();
- else
- return ((SubMap<?,E>)m).valueIterator();
+ public Iterator<V> iterator() {
+ return (m instanceof ConcurrentSkipListMap)
+ ? ((ConcurrentSkipListMap<K,V>)m).new ValueIterator()
+ : ((SubMap<K,V>)m).new SubMapValueIterator();
}
- public boolean isEmpty() {
- return m.isEmpty();
- }
- public int size() {
- return m.size();
- }
- public boolean contains(Object o) {
- return m.containsValue(o);
- }
- public void clear() {
- m.clear();
- }
+ public int size() { return m.size(); }
+ public boolean isEmpty() { return m.isEmpty(); }
+ public boolean contains(Object o) { return m.containsValue(o); }
+ public void clear() { m.clear(); }
public Object[] toArray() { return toList(this).toArray(); }
public <T> T[] toArray(T[] a) { return toList(this).toArray(a); }
- }
- static final class EntrySet<K1,V1> extends AbstractSet<Map.Entry<K1,V1>> {
- private final ConcurrentNavigableMap<K1, V1> m;
- EntrySet(ConcurrentNavigableMap<K1, V1> map) {
- m = map;
+ public Spliterator<V> spliterator() {
+ return (m instanceof ConcurrentSkipListMap)
+ ? ((ConcurrentSkipListMap<K,V>)m).valueSpliterator()
+ : ((SubMap<K,V>)m).new SubMapValueIterator();
}
- public Iterator<Map.Entry<K1,V1>> iterator() {
+ public boolean removeIf(Predicate<? super V> filter) {
+ if (filter == null) throw new NullPointerException();
if (m instanceof ConcurrentSkipListMap)
- return ((ConcurrentSkipListMap<K1,V1>)m).entryIterator();
- else
- return ((SubMap<K1,V1>)m).entryIterator();
+ return ((ConcurrentSkipListMap<K,V>)m).removeValueIf(filter);
+ // else use iterator
+ Iterator<Map.Entry<K,V>> it =
+ ((SubMap<K,V>)m).new SubMapEntryIterator();
+ boolean removed = false;
+ while (it.hasNext()) {
+ Map.Entry<K,V> e = it.next();
+ V v = e.getValue();
+ if (filter.test(v) && m.remove(e.getKey(), v))
+ removed = true;
+ }
+ return removed;
+ }
+ }
+
+ static final class EntrySet<K,V> extends AbstractSet<Map.Entry<K,V>> {
+ final ConcurrentNavigableMap<K,V> m;
+ EntrySet(ConcurrentNavigableMap<K,V> map) {
+ m = map;
+ }
+ public Iterator<Map.Entry<K,V>> iterator() {
+ return (m instanceof ConcurrentSkipListMap)
+ ? ((ConcurrentSkipListMap<K,V>)m).new EntryIterator()
+ : ((SubMap<K,V>)m).new SubMapEntryIterator();
}
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
- V1 v = m.get(e.getKey());
+ V v = m.get(e.getKey());
return v != null && v.equals(e.getValue());
}
public boolean remove(Object o) {
@@ -2437,27 +2499,47 @@
}
public Object[] toArray() { return toList(this).toArray(); }
public <T> T[] toArray(T[] a) { return toList(this).toArray(a); }
+
+ public Spliterator<Map.Entry<K,V>> spliterator() {
+ return (m instanceof ConcurrentSkipListMap)
+ ? ((ConcurrentSkipListMap<K,V>)m).entrySpliterator()
+ : ((SubMap<K,V>)m).new SubMapEntryIterator();
+ }
+ public boolean removeIf(Predicate<? super Entry<K,V>> filter) {
+ if (filter == null) throw new NullPointerException();
+ if (m instanceof ConcurrentSkipListMap)
+ return ((ConcurrentSkipListMap<K,V>)m).removeEntryIf(filter);
+ // else use iterator
+ Iterator<Map.Entry<K,V>> it =
+ ((SubMap<K,V>)m).new SubMapEntryIterator();
+ boolean removed = false;
+ while (it.hasNext()) {
+ Map.Entry<K,V> e = it.next();
+ if (filter.test(e) && m.remove(e.getKey(), e.getValue()))
+ removed = true;
+ }
+ return removed;
+ }
}
/**
* Submaps returned by {@link ConcurrentSkipListMap} submap operations
- * represent a subrange of mappings of their underlying
- * maps. Instances of this class support all methods of their
- * underlying maps, differing in that mappings outside their range are
- * ignored, and attempts to add mappings outside their ranges result
- * in {@link IllegalArgumentException}. Instances of this class are
- * constructed only using the {@code subMap}, {@code headMap}, and
- * {@code tailMap} methods of their underlying maps.
+ * represent a subrange of mappings of their underlying maps.
+ * Instances of this class support all methods of their underlying
+ * maps, differing in that mappings outside their range are ignored,
+ * and attempts to add mappings outside their ranges result in {@link
+ * IllegalArgumentException}. Instances of this class are constructed
+ * only using the {@code subMap}, {@code headMap}, and {@code tailMap}
+ * methods of their underlying maps.
*
* @serial include
*/
static final class SubMap<K,V> extends AbstractMap<K,V>
- implements ConcurrentNavigableMap<K,V>, Cloneable,
- java.io.Serializable {
+ implements ConcurrentNavigableMap<K,V>, Cloneable, Serializable {
private static final long serialVersionUID = -7647078645895051609L;
/** Underlying map */
- private final ConcurrentSkipListMap<K,V> m;
+ final ConcurrentSkipListMap<K,V> m;
/** lower bound key, or null if from start */
private final K lo;
/** upper bound key, or null if to end */
@@ -2467,10 +2549,10 @@
/** inclusion flag for hi */
private final boolean hiInclusive;
/** direction */
- private final boolean isDescending;
+ final boolean isDescending;
// Lazily initialized view holders
- private transient KeySet<K> keySetView;
+ private transient KeySet<K,V> keySetView;
private transient Set<Map.Entry<K,V>> entrySetView;
private transient Collection<V> valuesView;
@@ -2481,8 +2563,9 @@
K fromKey, boolean fromInclusive,
K toKey, boolean toInclusive,
boolean isDescending) {
+ Comparator<? super K> cmp = map.comparator;
if (fromKey != null && toKey != null &&
- map.compare(fromKey, toKey) > 0)
+ cpr(cmp, fromKey, toKey) > 0)
throw new IllegalArgumentException("inconsistent range");
this.m = map;
this.lo = fromKey;
@@ -2494,39 +2577,34 @@
/* ---------------- Utilities -------------- */
- private boolean tooLow(K key) {
- if (lo != null) {
- int c = m.compare(key, lo);
- if (c < 0 || (c == 0 && !loInclusive))
- return true;
- }
- return false;
+ boolean tooLow(Object key, Comparator<? super K> cmp) {
+ int c;
+ return (lo != null && ((c = cpr(cmp, key, lo)) < 0 ||
+ (c == 0 && !loInclusive)));
}
- private boolean tooHigh(K key) {
- if (hi != null) {
- int c = m.compare(key, hi);
- if (c > 0 || (c == 0 && !hiInclusive))
- return true;
- }
- return false;
+ boolean tooHigh(Object key, Comparator<? super K> cmp) {
+ int c;
+ return (hi != null && ((c = cpr(cmp, key, hi)) > 0 ||
+ (c == 0 && !hiInclusive)));
}
- private boolean inBounds(K key) {
- return !tooLow(key) && !tooHigh(key);
+ boolean inBounds(Object key, Comparator<? super K> cmp) {
+ return !tooLow(key, cmp) && !tooHigh(key, cmp);
}
- private void checkKeyBounds(K key) throws IllegalArgumentException {
+ void checkKeyBounds(K key, Comparator<? super K> cmp) {
if (key == null)
throw new NullPointerException();
- if (!inBounds(key))
+ if (!inBounds(key, cmp))
throw new IllegalArgumentException("key out of range");
}
/**
* Returns true if node key is less than upper bound of range.
*/
- private boolean isBeforeEnd(ConcurrentSkipListMap.Node<K,V> n) {
+ boolean isBeforeEnd(ConcurrentSkipListMap.Node<K,V> n,
+ Comparator<? super K> cmp) {
if (n == null)
return false;
if (hi == null)
@@ -2534,7 +2612,7 @@
K k = n.key;
if (k == null) // pass by markers and headers
return true;
- int c = m.compare(k, hi);
+ int c = cpr(cmp, k, hi);
if (c > 0 || (c == 0 && !hiInclusive))
return false;
return true;
@@ -2544,34 +2622,35 @@
* Returns lowest node. This node might not be in range, so
* most usages need to check bounds.
*/
- private ConcurrentSkipListMap.Node<K,V> loNode() {
+ ConcurrentSkipListMap.Node<K,V> loNode(Comparator<? super K> cmp) {
if (lo == null)
return m.findFirst();
else if (loInclusive)
- return m.findNear(lo, GT|EQ);
+ return m.findNear(lo, GT|EQ, cmp);
else
- return m.findNear(lo, GT);
+ return m.findNear(lo, GT, cmp);
}
/**
* Returns highest node. This node might not be in range, so
* most usages need to check bounds.
*/
- private ConcurrentSkipListMap.Node<K,V> hiNode() {
+ ConcurrentSkipListMap.Node<K,V> hiNode(Comparator<? super K> cmp) {
if (hi == null)
return m.findLast();
else if (hiInclusive)
- return m.findNear(hi, LT|EQ);
+ return m.findNear(hi, LT|EQ, cmp);
else
- return m.findNear(hi, LT);
+ return m.findNear(hi, LT, cmp);
}
/**
* Returns lowest absolute key (ignoring directionality).
*/
- private K lowestKey() {
- ConcurrentSkipListMap.Node<K,V> n = loNode();
- if (isBeforeEnd(n))
+ K lowestKey() {
+ Comparator<? super K> cmp = m.comparator;
+ ConcurrentSkipListMap.Node<K,V> n = loNode(cmp);
+ if (isBeforeEnd(n, cmp))
return n.key;
else
throw new NoSuchElementException();
@@ -2580,20 +2659,22 @@
/**
* Returns highest absolute key (ignoring directionality).
*/
- private K highestKey() {
- ConcurrentSkipListMap.Node<K,V> n = hiNode();
+ K highestKey() {
+ Comparator<? super K> cmp = m.comparator;
+ ConcurrentSkipListMap.Node<K,V> n = hiNode(cmp);
if (n != null) {
K last = n.key;
- if (inBounds(last))
+ if (inBounds(last, cmp))
return last;
}
throw new NoSuchElementException();
}
- private Map.Entry<K,V> lowestEntry() {
+ Map.Entry<K,V> lowestEntry() {
+ Comparator<? super K> cmp = m.comparator;
for (;;) {
- ConcurrentSkipListMap.Node<K,V> n = loNode();
- if (!isBeforeEnd(n))
+ ConcurrentSkipListMap.Node<K,V> n = loNode(cmp);
+ if (!isBeforeEnd(n, cmp))
return null;
Map.Entry<K,V> e = n.createSnapshot();
if (e != null)
@@ -2601,10 +2682,11 @@
}
}
- private Map.Entry<K,V> highestEntry() {
+ Map.Entry<K,V> highestEntry() {
+ Comparator<? super K> cmp = m.comparator;
for (;;) {
- ConcurrentSkipListMap.Node<K,V> n = hiNode();
- if (n == null || !inBounds(n.key))
+ ConcurrentSkipListMap.Node<K,V> n = hiNode(cmp);
+ if (n == null || !inBounds(n.key, cmp))
return null;
Map.Entry<K,V> e = n.createSnapshot();
if (e != null)
@@ -2612,13 +2694,14 @@
}
}
- private Map.Entry<K,V> removeLowest() {
+ Map.Entry<K,V> removeLowest() {
+ Comparator<? super K> cmp = m.comparator;
for (;;) {
- Node<K,V> n = loNode();
+ Node<K,V> n = loNode(cmp);
if (n == null)
return null;
K k = n.key;
- if (!inBounds(k))
+ if (!inBounds(k, cmp))
return null;
V v = m.doRemove(k, null);
if (v != null)
@@ -2626,13 +2709,14 @@
}
}
- private Map.Entry<K,V> removeHighest() {
+ Map.Entry<K,V> removeHighest() {
+ Comparator<? super K> cmp = m.comparator;
for (;;) {
- Node<K,V> n = hiNode();
+ Node<K,V> n = hiNode(cmp);
if (n == null)
return null;
K k = n.key;
- if (!inBounds(k))
+ if (!inBounds(k, cmp))
return null;
V v = m.doRemove(k, null);
if (v != null)
@@ -2641,22 +2725,23 @@
}
/**
- * Submap version of ConcurrentSkipListMap.getNearEntry
+ * Submap version of ConcurrentSkipListMap.getNearEntry.
*/
- private Map.Entry<K,V> getNearEntry(K key, int rel) {
+ Map.Entry<K,V> getNearEntry(K key, int rel) {
+ Comparator<? super K> cmp = m.comparator;
if (isDescending) { // adjust relation for direction
if ((rel & LT) == 0)
rel |= LT;
else
rel &= ~LT;
}
- if (tooLow(key))
+ if (tooLow(key, cmp))
return ((rel & LT) != 0) ? null : lowestEntry();
- if (tooHigh(key))
+ if (tooHigh(key, cmp))
return ((rel & LT) != 0) ? highestEntry() : null;
for (;;) {
- Node<K,V> n = m.findNear(key, rel);
- if (n == null || !inBounds(n.key))
+ Node<K,V> n = m.findNear(key, rel, cmp);
+ if (n == null || !inBounds(n.key, cmp))
return null;
K k = n.key;
V v = n.getValidValue();
@@ -2666,35 +2751,36 @@
}
// Almost the same as getNearEntry, except for keys
- private K getNearKey(K key, int rel) {
+ K getNearKey(K key, int rel) {
+ Comparator<? super K> cmp = m.comparator;
if (isDescending) { // adjust relation for direction
if ((rel & LT) == 0)
rel |= LT;
else
rel &= ~LT;
}
- if (tooLow(key)) {
+ if (tooLow(key, cmp)) {
if ((rel & LT) == 0) {
- ConcurrentSkipListMap.Node<K,V> n = loNode();
- if (isBeforeEnd(n))
+ ConcurrentSkipListMap.Node<K,V> n = loNode(cmp);
+ if (isBeforeEnd(n, cmp))
return n.key;
}
return null;
}
- if (tooHigh(key)) {
+ if (tooHigh(key, cmp)) {
if ((rel & LT) != 0) {
- ConcurrentSkipListMap.Node<K,V> n = hiNode();
+ ConcurrentSkipListMap.Node<K,V> n = hiNode(cmp);
if (n != null) {
K last = n.key;
- if (inBounds(last))
+ if (inBounds(last, cmp))
return last;
}
}
return null;
}
for (;;) {
- Node<K,V> n = m.findNear(key, rel);
- if (n == null || !inBounds(n.key))
+ Node<K,V> n = m.findNear(key, rel, cmp);
+ if (n == null || !inBounds(n.key, cmp))
return null;
K k = n.key;
V v = n.getValidValue();
@@ -2707,30 +2793,28 @@
public boolean containsKey(Object key) {
if (key == null) throw new NullPointerException();
- K k = (K)key;
- return inBounds(k) && m.containsKey(k);
+ return inBounds(key, m.comparator) && m.containsKey(key);
}
public V get(Object key) {
if (key == null) throw new NullPointerException();
- K k = (K)key;
- return (!inBounds(k)) ? null : m.get(k);
+ return (!inBounds(key, m.comparator)) ? null : m.get(key);
}
public V put(K key, V value) {
- checkKeyBounds(key);
+ checkKeyBounds(key, m.comparator);
return m.put(key, value);
}
public V remove(Object key) {
- K k = (K)key;
- return (!inBounds(k)) ? null : m.remove(k);
+ return (!inBounds(key, m.comparator)) ? null : m.remove(key);
}
public int size() {
+ Comparator<? super K> cmp = m.comparator;
long count = 0;
- for (ConcurrentSkipListMap.Node<K,V> n = loNode();
- isBeforeEnd(n);
+ for (ConcurrentSkipListMap.Node<K,V> n = loNode(cmp);
+ isBeforeEnd(n, cmp);
n = n.next) {
if (n.getValidValue() != null)
++count;
@@ -2739,14 +2823,16 @@
}
public boolean isEmpty() {
- return !isBeforeEnd(loNode());
+ Comparator<? super K> cmp = m.comparator;
+ return !isBeforeEnd(loNode(cmp), cmp);
}
public boolean containsValue(Object value) {
if (value == null)
throw new NullPointerException();
- for (ConcurrentSkipListMap.Node<K,V> n = loNode();
- isBeforeEnd(n);
+ Comparator<? super K> cmp = m.comparator;
+ for (ConcurrentSkipListMap.Node<K,V> n = loNode(cmp);
+ isBeforeEnd(n, cmp);
n = n.next) {
V v = n.getValidValue();
if (v != null && value.equals(v))
@@ -2756,8 +2842,9 @@
}
public void clear() {
- for (ConcurrentSkipListMap.Node<K,V> n = loNode();
- isBeforeEnd(n);
+ Comparator<? super K> cmp = m.comparator;
+ for (ConcurrentSkipListMap.Node<K,V> n = loNode(cmp);
+ isBeforeEnd(n, cmp);
n = n.next) {
if (n.getValidValue() != null)
m.remove(n.key);
@@ -2767,22 +2854,21 @@
/* ---------------- ConcurrentMap API methods -------------- */
public V putIfAbsent(K key, V value) {
- checkKeyBounds(key);
+ checkKeyBounds(key, m.comparator);
return m.putIfAbsent(key, value);
}
public boolean remove(Object key, Object value) {
- K k = (K)key;
- return inBounds(k) && m.remove(k, value);
+ return inBounds(key, m.comparator) && m.remove(key, value);
}
public boolean replace(K key, V oldValue, V newValue) {
- checkKeyBounds(key);
+ checkKeyBounds(key, m.comparator);
return m.replace(key, oldValue, newValue);
}
public V replace(K key, V value) {
- checkKeyBounds(key);
+ checkKeyBounds(key, m.comparator);
return m.replace(key, value);
}
@@ -2800,10 +2886,9 @@
* Utility to create submaps, where given bounds override
* unbounded(null) ones and/or are checked against bounded ones.
*/
- private SubMap<K,V> newSubMap(K fromKey,
- boolean fromInclusive,
- K toKey,
- boolean toInclusive) {
+ SubMap<K,V> newSubMap(K fromKey, boolean fromInclusive,
+ K toKey, boolean toInclusive) {
+ Comparator<? super K> cmp = m.comparator;
if (isDescending) { // flip senses
K tk = fromKey;
fromKey = toKey;
@@ -2818,7 +2903,7 @@
fromInclusive = loInclusive;
}
else {
- int c = m.compare(fromKey, lo);
+ int c = cpr(cmp, fromKey, lo);
if (c < 0 || (c == 0 && !loInclusive && fromInclusive))
throw new IllegalArgumentException("key out of range");
}
@@ -2829,7 +2914,7 @@
toInclusive = hiInclusive;
}
else {
- int c = m.compare(toKey, hi);
+ int c = cpr(cmp, toKey, hi);
if (c > 0 || (c == 0 && !hiInclusive && toInclusive))
throw new IllegalArgumentException("key out of range");
}
@@ -2838,24 +2923,20 @@
toKey, toInclusive, isDescending);
}
- public SubMap<K,V> subMap(K fromKey,
- boolean fromInclusive,
- K toKey,
- boolean toInclusive) {
+ public SubMap<K,V> subMap(K fromKey, boolean fromInclusive,
+ K toKey, boolean toInclusive) {
if (fromKey == null || toKey == null)
throw new NullPointerException();
return newSubMap(fromKey, fromInclusive, toKey, toInclusive);
}
- public SubMap<K,V> headMap(K toKey,
- boolean inclusive) {
+ public SubMap<K,V> headMap(K toKey, boolean inclusive) {
if (toKey == null)
throw new NullPointerException();
return newSubMap(null, false, toKey, inclusive);
}
- public SubMap<K,V> tailMap(K fromKey,
- boolean inclusive) {
+ public SubMap<K,V> tailMap(K fromKey, boolean inclusive) {
if (fromKey == null)
throw new NullPointerException();
return newSubMap(fromKey, inclusive, null, false);
@@ -2939,18 +3020,18 @@
/* ---------------- Submap Views -------------- */
public NavigableSet<K> keySet() {
- KeySet<K> ks = keySetView;
- return (ks != null) ? ks : (keySetView = new KeySet<K>(this));
+ KeySet<K,V> ks = keySetView;
+ return (ks != null) ? ks : (keySetView = new KeySet<>(this));
}
public NavigableSet<K> navigableKeySet() {
- KeySet<K> ks = keySetView;
- return (ks != null) ? ks : (keySetView = new KeySet<K>(this));
+ KeySet<K,V> ks = keySetView;
+ return (ks != null) ? ks : (keySetView = new KeySet<>(this));
}
public Collection<V> values() {
Collection<V> vs = valuesView;
- return (vs != null) ? vs : (valuesView = new Values<V>(this));
+ return (vs != null) ? vs : (valuesView = new Values<>(this));
}
public Set<Map.Entry<K,V>> entrySet() {
@@ -2962,22 +3043,11 @@
return descendingMap().navigableKeySet();
}
- Iterator<K> keyIterator() {
- return new SubMapKeyIterator();
- }
-
- Iterator<V> valueIterator() {
- return new SubMapValueIterator();
- }
-
- Iterator<Map.Entry<K,V>> entryIterator() {
- return new SubMapEntryIterator();
- }
-
/**
* Variant of main Iter class to traverse through submaps.
+ * Also serves as back-up Spliterator for views.
*/
- abstract class SubMapIter<T> implements Iterator<T> {
+ abstract class SubMapIter<T> implements Iterator<T>, Spliterator<T> {
/** the last node returned by next() */
Node<K,V> lastReturned;
/** the next node to return from next(); */
@@ -2986,16 +3056,19 @@
V nextValue;
SubMapIter() {
+ Comparator<? super K> cmp = m.comparator;
for (;;) {
- next = isDescending ? hiNode() : loNode();
+ next = isDescending ? hiNode(cmp) : loNode(cmp);
if (next == null)
break;
Object x = next.value;
if (x != null && x != next) {
- if (! inBounds(next.key))
+ if (! inBounds(next.key, cmp))
next = null;
- else
- nextValue = (V) x;
+ else {
+ @SuppressWarnings("unchecked") V vv = (V)x;
+ nextValue = vv;
+ }
break;
}
}
@@ -3016,32 +3089,38 @@
}
private void ascend() {
+ Comparator<? super K> cmp = m.comparator;
for (;;) {
next = next.next;
if (next == null)
break;
Object x = next.value;
if (x != null && x != next) {
- if (tooHigh(next.key))
+ if (tooHigh(next.key, cmp))
next = null;
- else
- nextValue = (V) x;
+ else {
+ @SuppressWarnings("unchecked") V vv = (V)x;
+ nextValue = vv;
+ }
break;
}
}
}
private void descend() {
+ Comparator<? super K> cmp = m.comparator;
for (;;) {
- next = m.findNear(lastReturned.key, LT);
+ next = m.findNear(lastReturned.key, LT, cmp);
if (next == null)
break;
Object x = next.value;
if (x != null && x != next) {
- if (tooLow(next.key))
+ if (tooLow(next.key, cmp))
next = null;
- else
- nextValue = (V) x;
+ else {
+ @SuppressWarnings("unchecked") V vv = (V)x;
+ nextValue = vv;
+ }
break;
}
}
@@ -3055,6 +3134,27 @@
lastReturned = null;
}
+ public Spliterator<T> trySplit() {
+ return null;
+ }
+
+ public boolean tryAdvance(Consumer<? super T> action) {
+ if (hasNext()) {
+ action.accept(next());
+ return true;
+ }
+ return false;
+ }
+
+ public void forEachRemaining(Consumer<? super T> action) {
+ while (hasNext())
+ action.accept(next());
+ }
+
+ public long estimateSize() {
+ return Long.MAX_VALUE;
+ }
+
}
final class SubMapValueIterator extends SubMapIter<V> {
@@ -3063,6 +3163,9 @@
advance();
return v;
}
+ public int characteristics() {
+ return 0;
+ }
}
final class SubMapKeyIterator extends SubMapIter<K> {
@@ -3071,6 +3174,13 @@
advance();
return n.key;
}
+ public int characteristics() {
+ return Spliterator.DISTINCT | Spliterator.ORDERED |
+ Spliterator.SORTED;
+ }
+ public final Comparator<? super K> getComparator() {
+ return SubMap.this.comparator();
+ }
}
final class SubMapEntryIterator extends SubMapIter<Map.Entry<K,V>> {
@@ -3080,19 +3190,390 @@
advance();
return new AbstractMap.SimpleImmutableEntry<K,V>(n.key, v);
}
+ public int characteristics() {
+ return Spliterator.DISTINCT;
+ }
+ }
+ }
+
+ // default Map method overrides
+
+ public void forEach(BiConsumer<? super K, ? super V> action) {
+ if (action == null) throw new NullPointerException();
+ V v;
+ for (Node<K,V> n = findFirst(); n != null; n = n.next) {
+ if ((v = n.getValidValue()) != null)
+ action.accept(n.key, v);
+ }
+ }
+
+ public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
+ if (function == null) throw new NullPointerException();
+ V v;
+ for (Node<K,V> n = findFirst(); n != null; n = n.next) {
+ while ((v = n.getValidValue()) != null) {
+ V r = function.apply(n.key, v);
+ if (r == null) throw new NullPointerException();
+ if (n.casValue(v, r))
+ break;
+ }
+ }
+ }
+
+ /**
+ * Helper method for EntrySet.removeIf.
+ */
+ boolean removeEntryIf(Predicate<? super Entry<K,V>> function) {
+ if (function == null) throw new NullPointerException();
+ boolean removed = false;
+ for (Node<K,V> n = findFirst(); n != null; n = n.next) {
+ V v;
+ if ((v = n.getValidValue()) != null) {
+ K k = n.key;
+ Map.Entry<K,V> e = new AbstractMap.SimpleImmutableEntry<>(k, v);
+ if (function.test(e) && remove(k, v))
+ removed = true;
+ }
+ }
+ return removed;
+ }
+
+ /**
+ * Helper method for Values.removeIf.
+ */
+ boolean removeValueIf(Predicate<? super V> function) {
+ if (function == null) throw new NullPointerException();
+ boolean removed = false;
+ for (Node<K,V> n = findFirst(); n != null; n = n.next) {
+ V v;
+ if ((v = n.getValidValue()) != null) {
+ K k = n.key;
+ if (function.test(v) && remove(k, v))
+ removed = true;
+ }
+ }
+ return removed;
+ }
+
+ /**
+ * Base class providing common structure for Spliterators.
+ * (Although not all that much common functionality; as usual for
+ * view classes, details annoyingly vary in key, value, and entry
+ * subclasses in ways that are not worth abstracting out for
+ * internal classes.)
+ *
+ * The basic split strategy is to recursively descend from top
+ * level, row by row, descending to next row when either split
+ * off, or the end of row is encountered. Control of the number of
+ * splits relies on some statistical estimation: The expected
+ * remaining number of elements of a skip list when advancing
+ * either across or down decreases by about 25%. To make this
+ * observation useful, we need to know initial size, which we
+ * don't. But we can just use Integer.MAX_VALUE so that we
+ * don't prematurely zero out while splitting.
+ */
+ abstract static class CSLMSpliterator<K,V> {
+ final Comparator<? super K> comparator;
+ final K fence; // exclusive upper bound for keys, or null if to end
+ Index<K,V> row; // the level to split out
+ Node<K,V> current; // current traversal node; initialize at origin
+ int est; // pseudo-size estimate
+ CSLMSpliterator(Comparator<? super K> comparator, Index<K,V> row,
+ Node<K,V> origin, K fence, int est) {
+ this.comparator = comparator; this.row = row;
+ this.current = origin; this.fence = fence; this.est = est;
+ }
+
+ public final long estimateSize() { return (long)est; }
+ }
+
+ static final class KeySpliterator<K,V> extends CSLMSpliterator<K,V>
+ implements Spliterator<K> {
+ KeySpliterator(Comparator<? super K> comparator, Index<K,V> row,
+ Node<K,V> origin, K fence, int est) {
+ super(comparator, row, origin, fence, est);
+ }
+
+ public KeySpliterator<K,V> trySplit() {
+ Node<K,V> e; K ek;
+ Comparator<? super K> cmp = comparator;
+ K f = fence;
+ if ((e = current) != null && (ek = e.key) != null) {
+ for (Index<K,V> q = row; q != null; q = row = q.down) {
+ Index<K,V> s; Node<K,V> b, n; K sk;
+ if ((s = q.right) != null && (b = s.node) != null &&
+ (n = b.next) != null && n.value != null &&
+ (sk = n.key) != null && cpr(cmp, sk, ek) > 0 &&
+ (f == null || cpr(cmp, sk, f) < 0)) {
+ current = n;
+ Index<K,V> r = q.down;
+ row = (s.right != null) ? s : s.down;
+ est -= est >>> 2;
+ return new KeySpliterator<K,V>(cmp, r, e, sk, est);
+ }
+ }
+ }
+ return null;
+ }
+
+ public void forEachRemaining(Consumer<? super K> action) {
+ if (action == null) throw new NullPointerException();
+ Comparator<? super K> cmp = comparator;
+ K f = fence;
+ Node<K,V> e = current;
+ current = null;
+ for (; e != null; e = e.next) {
+ K k; Object v;
+ if ((k = e.key) != null && f != null && cpr(cmp, f, k) <= 0)
+ break;
+ if ((v = e.value) != null && v != e)
+ action.accept(k);
+ }
+ }
+
+ public boolean tryAdvance(Consumer<? super K> action) {
+ if (action == null) throw new NullPointerException();
+ Comparator<? super K> cmp = comparator;
+ K f = fence;
+ Node<K,V> e = current;
+ for (; e != null; e = e.next) {
+ K k; Object v;
+ if ((k = e.key) != null && f != null && cpr(cmp, f, k) <= 0) {
+ e = null;
+ break;
+ }
+ if ((v = e.value) != null && v != e) {
+ current = e.next;
+ action.accept(k);
+ return true;
+ }
+ }
+ current = e;
+ return false;
+ }
+
+ public int characteristics() {
+ return Spliterator.DISTINCT | Spliterator.SORTED |
+ Spliterator.ORDERED | Spliterator.CONCURRENT |
+ Spliterator.NONNULL;
+ }
+
+ public final Comparator<? super K> getComparator() {
+ return comparator;
+ }
+ }
+ // factory method for KeySpliterator
+ final KeySpliterator<K,V> keySpliterator() {
+ Comparator<? super K> cmp = comparator;
+ for (;;) { // ensure h corresponds to origin p
+ HeadIndex<K,V> h; Node<K,V> p;
+ Node<K,V> b = (h = head).node;
+ if ((p = b.next) == null || p.value != null)
+ return new KeySpliterator<K,V>(cmp, h, p, null, (p == null) ?
+ 0 : Integer.MAX_VALUE);
+ p.helpDelete(b, p.next);
+ }
+ }
+
+ static final class ValueSpliterator<K,V> extends CSLMSpliterator<K,V>
+ implements Spliterator<V> {
+ ValueSpliterator(Comparator<? super K> comparator, Index<K,V> row,
+ Node<K,V> origin, K fence, int est) {
+ super(comparator, row, origin, fence, est);
+ }
+
+ public ValueSpliterator<K,V> trySplit() {
+ Node<K,V> e; K ek;
+ Comparator<? super K> cmp = comparator;
+ K f = fence;
+ if ((e = current) != null && (ek = e.key) != null) {
+ for (Index<K,V> q = row; q != null; q = row = q.down) {
+ Index<K,V> s; Node<K,V> b, n; K sk;
+ if ((s = q.right) != null && (b = s.node) != null &&
+ (n = b.next) != null && n.value != null &&
+ (sk = n.key) != null && cpr(cmp, sk, ek) > 0 &&
+ (f == null || cpr(cmp, sk, f) < 0)) {
+ current = n;
+ Index<K,V> r = q.down;
+ row = (s.right != null) ? s : s.down;
+ est -= est >>> 2;
+ return new ValueSpliterator<K,V>(cmp, r, e, sk, est);
+ }
+ }
+ }
+ return null;
+ }
+
+ public void forEachRemaining(Consumer<? super V> action) {
+ if (action == null) throw new NullPointerException();
+ Comparator<? super K> cmp = comparator;
+ K f = fence;
+ Node<K,V> e = current;
+ current = null;
+ for (; e != null; e = e.next) {
+ K k; Object v;
+ if ((k = e.key) != null && f != null && cpr(cmp, f, k) <= 0)
+ break;
+ if ((v = e.value) != null && v != e) {
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ action.accept(vv);
+ }
+ }
+ }
+
+ public boolean tryAdvance(Consumer<? super V> action) {
+ if (action == null) throw new NullPointerException();
+ Comparator<? super K> cmp = comparator;
+ K f = fence;
+ Node<K,V> e = current;
+ for (; e != null; e = e.next) {
+ K k; Object v;
+ if ((k = e.key) != null && f != null && cpr(cmp, f, k) <= 0) {
+ e = null;
+ break;
+ }
+ if ((v = e.value) != null && v != e) {
+ current = e.next;
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ action.accept(vv);
+ return true;
+ }
+ }
+ current = e;
+ return false;
+ }
+
+ public int characteristics() {
+ return Spliterator.CONCURRENT | Spliterator.ORDERED |
+ Spliterator.NONNULL;
+ }
+ }
+
+ // Almost the same as keySpliterator()
+ final ValueSpliterator<K,V> valueSpliterator() {
+ Comparator<? super K> cmp = comparator;
+ for (;;) {
+ HeadIndex<K,V> h; Node<K,V> p;
+ Node<K,V> b = (h = head).node;
+ if ((p = b.next) == null || p.value != null)
+ return new ValueSpliterator<K,V>(cmp, h, p, null, (p == null) ?
+ 0 : Integer.MAX_VALUE);
+ p.helpDelete(b, p.next);
+ }
+ }
+
+ static final class EntrySpliterator<K,V> extends CSLMSpliterator<K,V>
+ implements Spliterator<Map.Entry<K,V>> {
+ EntrySpliterator(Comparator<? super K> comparator, Index<K,V> row,
+ Node<K,V> origin, K fence, int est) {
+ super(comparator, row, origin, fence, est);
+ }
+
+ public EntrySpliterator<K,V> trySplit() {
+ Node<K,V> e; K ek;
+ Comparator<? super K> cmp = comparator;
+ K f = fence;
+ if ((e = current) != null && (ek = e.key) != null) {
+ for (Index<K,V> q = row; q != null; q = row = q.down) {
+ Index<K,V> s; Node<K,V> b, n; K sk;
+ if ((s = q.right) != null && (b = s.node) != null &&
+ (n = b.next) != null && n.value != null &&
+ (sk = n.key) != null && cpr(cmp, sk, ek) > 0 &&
+ (f == null || cpr(cmp, sk, f) < 0)) {
+ current = n;
+ Index<K,V> r = q.down;
+ row = (s.right != null) ? s : s.down;
+ est -= est >>> 2;
+ return new EntrySpliterator<K,V>(cmp, r, e, sk, est);
+ }
+ }
+ }
+ return null;
+ }
+
+ public void forEachRemaining(Consumer<? super Map.Entry<K,V>> action) {
+ if (action == null) throw new NullPointerException();
+ Comparator<? super K> cmp = comparator;
+ K f = fence;
+ Node<K,V> e = current;
+ current = null;
+ for (; e != null; e = e.next) {
+ K k; Object v;
+ if ((k = e.key) != null && f != null && cpr(cmp, f, k) <= 0)
+ break;
+ if ((v = e.value) != null && v != e) {
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ action.accept
+ (new AbstractMap.SimpleImmutableEntry<K,V>(k, vv));
+ }
+ }
+ }
+
+ public boolean tryAdvance(Consumer<? super Map.Entry<K,V>> action) {
+ if (action == null) throw new NullPointerException();
+ Comparator<? super K> cmp = comparator;
+ K f = fence;
+ Node<K,V> e = current;
+ for (; e != null; e = e.next) {
+ K k; Object v;
+ if ((k = e.key) != null && f != null && cpr(cmp, f, k) <= 0) {
+ e = null;
+ break;
+ }
+ if ((v = e.value) != null && v != e) {
+ current = e.next;
+ @SuppressWarnings("unchecked") V vv = (V)v;
+ action.accept
+ (new AbstractMap.SimpleImmutableEntry<K,V>(k, vv));
+ return true;
+ }
+ }
+ current = e;
+ return false;
+ }
+
+ public int characteristics() {
+ return Spliterator.DISTINCT | Spliterator.SORTED |
+ Spliterator.ORDERED | Spliterator.CONCURRENT |
+ Spliterator.NONNULL;
+ }
+
+ public final Comparator<Map.Entry<K,V>> getComparator() {
+ // Adapt or create a key-based comparator
+ if (comparator != null) {
+ return Map.Entry.comparingByKey(comparator);
+ }
+ else {
+ return (Comparator<Map.Entry<K,V>> & Serializable) (e1, e2) -> {
+ @SuppressWarnings("unchecked")
+ Comparable<? super K> k1 = (Comparable<? super K>) e1.getKey();
+ return k1.compareTo(e2.getKey());
+ };
+ }
+ }
+ }
+
+ // Almost the same as keySpliterator()
+ final EntrySpliterator<K,V> entrySpliterator() {
+ Comparator<? super K> cmp = comparator;
+ for (;;) { // almost same as key version
+ HeadIndex<K,V> h; Node<K,V> p;
+ Node<K,V> b = (h = head).node;
+ if ((p = b.next) == null || p.value != null)
+ return new EntrySpliterator<K,V>(cmp, h, p, null, (p == null) ?
+ 0 : Integer.MAX_VALUE);
+ p.helpDelete(b, p.next);
}
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE;
- private static final long headOffset;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long HEAD;
static {
try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- Class<?> k = ConcurrentSkipListMap.class;
- headOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("head"));
- } catch (Exception e) {
+ HEAD = U.objectFieldOffset
+ (ConcurrentSkipListMap.class.getDeclaredField("head"));
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
diff --git a/luni/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java b/luni/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java
index 13f1a43..1719822 100644
--- a/luni/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java
+++ b/luni/src/main/java/java/util/concurrent/ConcurrentSkipListSet.java
@@ -6,10 +6,21 @@
package java.util.concurrent;
-import java.util.*;
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.NavigableSet;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.Spliterator;
// BEGIN android-note
// removed link to collections framework docs
+// fixed framework docs link to "Collection#optional"
// END android-note
/**
@@ -23,12 +34,12 @@
* cost for the {@code contains}, {@code add}, and {@code remove}
* operations and their variants. Insertion, removal, and access
* operations safely execute concurrently by multiple threads.
- * Iterators are <i>weakly consistent</i>, returning elements
- * reflecting the state of the set at some point at or since the
- * creation of the iterator. They do <em>not</em> throw {@link
- * ConcurrentModificationException}, and may proceed concurrently with
- * other operations. Ascending ordered views and their iterators are
- * faster than descending ones.
+ *
+ * <p>Iterators and spliterators are
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
+ *
+ * <p>Ascending ordered views and their iterators are faster than
+ * descending ones.
*
* <p>Beware that, unlike in most collections, the {@code size}
* method is <em>not</em> a constant-time operation. Because of the
@@ -285,8 +296,9 @@
*
* @param c collection containing elements to be removed from this set
* @return {@code true} if this set changed as a result of the call
- * @throws ClassCastException if the types of one or more elements in this
- * set are incompatible with the specified collection
+ * @throws ClassCastException if the class of an element of this set
+ * is incompatible with the specified collection
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified collection or any
* of its elements are null
*/
@@ -346,20 +358,19 @@
/* ---------------- SortedSet operations -------------- */
-
public Comparator<? super E> comparator() {
return m.comparator();
}
/**
- * @throws NoSuchElementException {@inheritDoc}
+ * @throws java.util.NoSuchElementException {@inheritDoc}
*/
public E first() {
return m.firstKey();
}
/**
- * @throws NoSuchElementException {@inheritDoc}
+ * @throws java.util.NoSuchElementException {@inheritDoc}
*/
public E last() {
return m.lastKey();
@@ -442,20 +453,42 @@
return new ConcurrentSkipListSet<E>(m.descendingMap());
}
- // Support for resetting map in clone
- private void setMap(ConcurrentNavigableMap<E,Object> map) {
- UNSAFE.putObjectVolatile(this, mapOffset, map);
+ /**
+ * Returns a {@link Spliterator} over the elements in this set.
+ *
+ * <p>The {@code Spliterator} reports {@link Spliterator#CONCURRENT},
+ * {@link Spliterator#NONNULL}, {@link Spliterator#DISTINCT},
+ * {@link Spliterator#SORTED} and {@link Spliterator#ORDERED}, with an
+ * encounter order that is ascending order. Overriding implementations
+ * should document the reporting of additional characteristic values.
+ *
+ * <p>The spliterator's comparator (see
+ * {@link java.util.Spliterator#getComparator()}) is {@code null} if
+ * the set's comparator (see {@link #comparator()}) is {@code null}.
+ * Otherwise, the spliterator's comparator is the same as or imposes the
+ * same total ordering as the set's comparator.
+ *
+ * @return a {@code Spliterator} over the elements in this set
+ * @since 1.8
+ */
+ public Spliterator<E> spliterator() {
+ return (m instanceof ConcurrentSkipListMap)
+ ? ((ConcurrentSkipListMap<E,?>)m).keySpliterator()
+ : ((ConcurrentSkipListMap.SubMap<E,?>)m).new SubMapKeyIterator();
}
- private static final sun.misc.Unsafe UNSAFE;
- private static final long mapOffset;
+ // Support for resetting map in clone
+ private void setMap(ConcurrentNavigableMap<E,Object> map) {
+ U.putObjectVolatile(this, MAP, map);
+ }
+
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long MAP;
static {
try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- Class<?> k = ConcurrentSkipListSet.class;
- mapOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("m"));
- } catch (Exception e) {
+ MAP = U.objectFieldOffset
+ (ConcurrentSkipListSet.class.getDeclaredField("m"));
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
diff --git a/luni/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java b/luni/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java
index 798eaaf..c8e342e 100644
--- a/luni/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java
+++ b/luni/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java
@@ -1,783 +1,1542 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group. Adapted and released, under explicit permission,
+ * from JDK ArrayList.java which carries the following copyright:
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Copyright 1997 by Sun Microsystems, Inc.,
+ * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
+ * All rights reserved.
*
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * This software is the confidential and proprietary information
+ * of Sun Microsystems, Inc. ("Confidential Information"). You
+ * shall not disclose such Confidential Information and shall use
+ * it only in accordance with the terms of the license agreement
+ * you entered into with Sun.
*/
package java.util.concurrent;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
+
import java.util.AbstractList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Comparator;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
+import java.util.Objects;
import java.util.RandomAccess;
+import java.util.Spliterator;
+import java.util.Spliterators;
import java.util.function.Consumer;
-
+import java.util.function.Predicate;
+import java.util.function.UnaryOperator;
import libcore.util.EmptyArray;
-import libcore.util.Objects;
+
+// BEGIN android-note
+// removed link to collections framework docs from header
+// END android-note
/**
- * A thread-safe random-access list.
+ * A thread-safe variant of {@link java.util.ArrayList} in which all mutative
+ * operations ({@code add}, {@code set}, and so on) are implemented by
+ * making a fresh copy of the underlying array.
*
- * <p>Read operations (including {@link #get}) do not block and may overlap with
- * update operations. Reads reflect the results of the most recently completed
- * operations. Aggregate operations like {@link #addAll} and {@link #clear} are
- * atomic; they never expose an intermediate state.
+ * <p>This is ordinarily too costly, but may be <em>more</em> efficient
+ * than alternatives when traversal operations vastly outnumber
+ * mutations, and is useful when you cannot or don't want to
+ * synchronize traversals, yet need to preclude interference among
+ * concurrent threads. The "snapshot" style iterator method uses a
+ * reference to the state of the array at the point that the iterator
+ * was created. This array never changes during the lifetime of the
+ * iterator, so interference is impossible and the iterator is
+ * guaranteed not to throw {@code ConcurrentModificationException}.
+ * The iterator will not reflect additions, removals, or changes to
+ * the list since the iterator was created. Element-changing
+ * operations on iterators themselves ({@code remove}, {@code set}, and
+ * {@code add}) are not supported. These methods throw
+ * {@code UnsupportedOperationException}.
*
- * <p>Iterators of this list never throw {@link
- * ConcurrentModificationException}. When an iterator is created, it keeps a
- * copy of the list's contents. It is always safe to iterate this list, but
- * iterations may not reflect the latest state of the list.
+ * <p>All elements are permitted, including {@code null}.
*
- * <p>Iterators returned by this list and its sub lists cannot modify the
- * underlying list. In particular, {@link Iterator#remove}, {@link
- * ListIterator#add} and {@link ListIterator#set} all throw {@link
- * UnsupportedOperationException}.
+ * <p>Memory consistency effects: As with other concurrent
+ * collections, actions in a thread prior to placing an object into a
+ * {@code CopyOnWriteArrayList}
+ * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
+ * actions subsequent to the access or removal of that element from
+ * the {@code CopyOnWriteArrayList} in another thread.
*
- * <p>This class offers extended API beyond the {@link List} interface. It
- * includes additional overloads for indexed search ({@link #indexOf} and {@link
- * #lastIndexOf}) and methods for conditional adds ({@link #addIfAbsent} and
- * {@link #addAllAbsent}).
+ * @since 1.5
+ * @author Doug Lea
+ * @param <E> the type of elements held in this list
*/
-public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, Serializable {
-
+public class CopyOnWriteArrayList<E>
+ implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
private static final long serialVersionUID = 8673264195747942595L;
/**
- * Holds the latest snapshot of the list's data. This field is volatile so
- * that data can be read without synchronization. As a consequence, all
- * writes to this field must be atomic; it is an error to modify the
- * contents of an array after it has been assigned to this field.
- *
- * Synchronization is required by all update operations. This defends
- * against one update clobbering the result of another operation. For
- * example, 100 threads simultaneously calling add() will grow the list's
- * size by 100 when they have completed. No update operations are lost!
- *
- * Maintainers should be careful to read this field only once in
- * non-blocking read methods. Write methods must be synchronized to avoid
- * clobbering concurrent writes.
+ * The lock protecting all mutators. (We have a mild preference
+ * for builtin monitors over ReentrantLock when either will do.)
*/
- private transient volatile Object[] elements;
+ final transient Object lock = new Object();
+
+ /** The array, accessed only via getArray/setArray. */
+ private transient volatile Object[] array;
/**
- * Creates a new empty instance.
+ * Gets the array. Non-private so as to also be accessible
+ * from CopyOnWriteArraySet class.
+ */
+ final Object[] getArray() {
+ return array;
+ }
+
+ /**
+ * Sets the array.
+ */
+ final void setArray(Object[] a) {
+ array = a;
+ }
+
+ /**
+ * Creates an empty list.
*/
public CopyOnWriteArrayList() {
- elements = EmptyArray.OBJECT;
+ setArray(new Object[0]);
}
/**
- * Creates a new instance containing the elements of {@code collection}.
+ * Creates a list containing the elements of the specified
+ * collection, in the order they are returned by the collection's
+ * iterator.
+ *
+ * @param c the collection of initially held elements
+ * @throws NullPointerException if the specified collection is null
*/
- @SuppressWarnings("unchecked")
- public CopyOnWriteArrayList(Collection<? extends E> collection) {
- this((E[]) collection.toArray());
+ public CopyOnWriteArrayList(Collection<? extends E> c) {
+ Object[] elements;
+ if (c.getClass() == CopyOnWriteArrayList.class)
+ elements = ((CopyOnWriteArrayList<?>)c).getArray();
+ else {
+ elements = c.toArray();
+ // defend against c.toArray (incorrectly) not returning Object[]
+ // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)
+ if (elements.getClass() != Object[].class)
+ elements = Arrays.copyOf(elements, elements.length, Object[].class);
+ }
+ setArray(elements);
}
/**
- * Creates a new instance containing the elements of {@code array}.
+ * Creates a list holding a copy of the given array.
+ *
+ * @param toCopyIn the array (a copy of this array is used as the
+ * internal array)
+ * @throws NullPointerException if the specified array is null
*/
- public CopyOnWriteArrayList(E[] array) {
- this.elements = Arrays.copyOf(array, array.length, Object[].class);
+ public CopyOnWriteArrayList(E[] toCopyIn) {
+ setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
}
- @Override public Object clone() {
+ /**
+ * Returns the number of elements in this list.
+ *
+ * @return the number of elements in this list
+ */
+ public int size() {
+ return getArray().length;
+ }
+
+ /**
+ * Returns {@code true} if this list contains no elements.
+ *
+ * @return {@code true} if this list contains no elements
+ */
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ /**
+ * static version of indexOf, to allow repeated calls without
+ * needing to re-acquire array each time.
+ * @param o element to search for
+ * @param elements the array
+ * @param index first index to search
+ * @param fence one past last index to search
+ * @return index of element, or -1 if absent
+ */
+ private static int indexOf(Object o, Object[] elements,
+ int index, int fence) {
+ if (o == null) {
+ for (int i = index; i < fence; i++)
+ if (elements[i] == null)
+ return i;
+ } else {
+ for (int i = index; i < fence; i++)
+ if (o.equals(elements[i]))
+ return i;
+ }
+ return -1;
+ }
+
+ /**
+ * static version of lastIndexOf.
+ * @param o element to search for
+ * @param elements the array
+ * @param index first index to search
+ * @return index of element, or -1 if absent
+ */
+ private static int lastIndexOf(Object o, Object[] elements, int index) {
+ if (o == null) {
+ for (int i = index; i >= 0; i--)
+ if (elements[i] == null)
+ return i;
+ } else {
+ for (int i = index; i >= 0; i--)
+ if (o.equals(elements[i]))
+ return i;
+ }
+ return -1;
+ }
+
+ /**
+ * Returns {@code true} if this list contains the specified element.
+ * More formally, returns {@code true} if and only if this list contains
+ * at least one element {@code e} such that {@code Objects.equals(o, e)}.
+ *
+ * @param o element whose presence in this list is to be tested
+ * @return {@code true} if this list contains the specified element
+ */
+ public boolean contains(Object o) {
+ Object[] elements = getArray();
+ return indexOf(o, elements, 0, elements.length) >= 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int indexOf(Object o) {
+ Object[] elements = getArray();
+ return indexOf(o, elements, 0, elements.length);
+ }
+
+ /**
+ * Returns the index of the first occurrence of the specified element in
+ * this list, searching forwards from {@code index}, or returns -1 if
+ * the element is not found.
+ * More formally, returns the lowest index {@code i} such that
+ * {@code i >= index && Objects.equals(get(i), e)},
+ * or -1 if there is no such index.
+ *
+ * @param e element to search for
+ * @param index index to start searching from
+ * @return the index of the first occurrence of the element in
+ * this list at position {@code index} or later in the list;
+ * {@code -1} if the element is not found.
+ * @throws IndexOutOfBoundsException if the specified index is negative
+ */
+ public int indexOf(E e, int index) {
+ Object[] elements = getArray();
+ return indexOf(e, elements, index, elements.length);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int lastIndexOf(Object o) {
+ Object[] elements = getArray();
+ return lastIndexOf(o, elements, elements.length - 1);
+ }
+
+ /**
+ * Returns the index of the last occurrence of the specified element in
+ * this list, searching backwards from {@code index}, or returns -1 if
+ * the element is not found.
+ * More formally, returns the highest index {@code i} such that
+ * {@code i <= index && Objects.equals(get(i), e)},
+ * or -1 if there is no such index.
+ *
+ * @param e element to search for
+ * @param index index to start searching backwards from
+ * @return the index of the last occurrence of the element at position
+ * less than or equal to {@code index} in this list;
+ * -1 if the element is not found.
+ * @throws IndexOutOfBoundsException if the specified index is greater
+ * than or equal to the current size of this list
+ */
+ public int lastIndexOf(E e, int index) {
+ Object[] elements = getArray();
+ return lastIndexOf(e, elements, index);
+ }
+
+ /**
+ * Returns a shallow copy of this list. (The elements themselves
+ * are not copied.)
+ *
+ * @return a clone of this list
+ */
+ public Object clone() {
try {
- CopyOnWriteArrayList result = (CopyOnWriteArrayList) super.clone();
- result.elements = result.elements.clone();
- return result;
+ @SuppressWarnings("unchecked")
+ CopyOnWriteArrayList<E> clone =
+ (CopyOnWriteArrayList<E>) super.clone();
+ clone.resetLock();
+ return clone;
} catch (CloneNotSupportedException e) {
- throw new AssertionError(e);
+ // this shouldn't happen, since we are Cloneable
+ throw new InternalError();
}
}
- public int size() {
- return elements.length;
+ /**
+ * Returns an array containing all of the elements in this list
+ * in proper sequence (from first to last element).
+ *
+ * <p>The returned array will be "safe" in that no references to it are
+ * maintained by this list. (In other words, this method must allocate
+ * a new array). The caller is thus free to modify the returned array.
+ *
+ * <p>This method acts as bridge between array-based and collection-based
+ * APIs.
+ *
+ * @return an array containing all the elements in this list
+ */
+ public Object[] toArray() {
+ Object[] elements = getArray();
+ return Arrays.copyOf(elements, elements.length);
}
+ /**
+ * Returns an array containing all of the elements in this list in
+ * proper sequence (from first to last element); the runtime type of
+ * the returned array is that of the specified array. If the list fits
+ * in the specified array, it is returned therein. Otherwise, a new
+ * array is allocated with the runtime type of the specified array and
+ * the size of this list.
+ *
+ * <p>If this list fits in the specified array with room to spare
+ * (i.e., the array has more elements than this list), the element in
+ * the array immediately following the end of the list is set to
+ * {@code null}. (This is useful in determining the length of this
+ * list <i>only</i> if the caller knows that this list does not contain
+ * any null elements.)
+ *
+ * <p>Like the {@link #toArray()} method, this method acts as bridge between
+ * array-based and collection-based APIs. Further, this method allows
+ * precise control over the runtime type of the output array, and may,
+ * under certain circumstances, be used to save allocation costs.
+ *
+ * <p>Suppose {@code x} is a list known to contain only strings.
+ * The following code can be used to dump the list into a newly
+ * allocated array of {@code String}:
+ *
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
+ *
+ * Note that {@code toArray(new Object[0])} is identical in function to
+ * {@code toArray()}.
+ *
+ * @param a the array into which the elements of the list are to
+ * be stored, if it is big enough; otherwise, a new array of the
+ * same runtime type is allocated for this purpose.
+ * @return an array containing all the elements in this list
+ * @throws ArrayStoreException if the runtime type of the specified array
+ * is not a supertype of the runtime type of every element in
+ * this list
+ * @throws NullPointerException if the specified array is null
+ */
+ @SuppressWarnings("unchecked")
+ public <T> T[] toArray(T[] a) {
+ Object[] elements = getArray();
+ int len = elements.length;
+ if (a.length < len)
+ return (T[]) Arrays.copyOf(elements, len, a.getClass());
+ else {
+ System.arraycopy(elements, 0, a, 0, len);
+ if (a.length > len)
+ a[len] = null;
+ return a;
+ }
+ }
+
+ // Positional Access Operations
+
@SuppressWarnings("unchecked")
+ private E get(Object[] a, int index) {
+ return (E) a[index];
+ }
+
+ static String outOfBounds(int index, int size) {
+ return "Index: " + index + ", Size: " + size;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws IndexOutOfBoundsException {@inheritDoc}
+ */
public E get(int index) {
- return (E) elements[index];
+ return get(getArray(), index);
}
- public boolean contains(Object o) {
- return indexOf(o) != -1;
- }
+ /**
+ * Replaces the element at the specified position in this list with the
+ * specified element.
+ *
+ * @throws IndexOutOfBoundsException {@inheritDoc}
+ */
+ public E set(int index, E element) {
+ synchronized (lock) {
+ Object[] elements = getArray();
+ E oldValue = get(elements, index);
- public boolean containsAll(Collection<?> collection) {
- Object[] snapshot = elements;
- return containsAll(collection, snapshot, 0, snapshot.length);
- }
-
- static boolean containsAll(Collection<?> collection, Object[] snapshot, int from, int to) {
- for (Object o : collection) {
- if (indexOf(o, snapshot, from, to) == -1) {
- return false;
+ if (oldValue != element) {
+ int len = elements.length;
+ Object[] newElements = Arrays.copyOf(elements, len);
+ newElements[index] = element;
+ setArray(newElements);
+ } else {
+ // Not quite a no-op; ensures volatile write semantics
+ setArray(elements);
}
+ return oldValue;
+ }
+ }
+
+ /**
+ * Appends the specified element to the end of this list.
+ *
+ * @param e element to be appended to this list
+ * @return {@code true} (as specified by {@link Collection#add})
+ */
+ public boolean add(E e) {
+ synchronized (lock) {
+ Object[] elements = getArray();
+ int len = elements.length;
+ Object[] newElements = Arrays.copyOf(elements, len + 1);
+ newElements[len] = e;
+ setArray(newElements);
+ return true;
+ }
+ }
+
+ /**
+ * Inserts the specified element at the specified position in this
+ * list. Shifts the element currently at that position (if any) and
+ * any subsequent elements to the right (adds one to their indices).
+ *
+ * @throws IndexOutOfBoundsException {@inheritDoc}
+ */
+ public void add(int index, E element) {
+ synchronized (lock) {
+ Object[] elements = getArray();
+ int len = elements.length;
+ if (index > len || index < 0)
+ throw new IndexOutOfBoundsException(outOfBounds(index, len));
+ Object[] newElements;
+ int numMoved = len - index;
+ if (numMoved == 0)
+ newElements = Arrays.copyOf(elements, len + 1);
+ else {
+ newElements = new Object[len + 1];
+ System.arraycopy(elements, 0, newElements, 0, index);
+ System.arraycopy(elements, index, newElements, index + 1,
+ numMoved);
+ }
+ newElements[index] = element;
+ setArray(newElements);
+ }
+ }
+
+ /**
+ * Removes the element at the specified position in this list.
+ * Shifts any subsequent elements to the left (subtracts one from their
+ * indices). Returns the element that was removed from the list.
+ *
+ * @throws IndexOutOfBoundsException {@inheritDoc}
+ */
+ public E remove(int index) {
+ synchronized (lock) {
+ Object[] elements = getArray();
+ int len = elements.length;
+ E oldValue = get(elements, index);
+ int numMoved = len - index - 1;
+ if (numMoved == 0)
+ setArray(Arrays.copyOf(elements, len - 1));
+ else {
+ Object[] newElements = new Object[len - 1];
+ System.arraycopy(elements, 0, newElements, 0, index);
+ System.arraycopy(elements, index + 1, newElements, index,
+ numMoved);
+ setArray(newElements);
+ }
+ return oldValue;
+ }
+ }
+
+ /**
+ * Removes the first occurrence of the specified element from this list,
+ * if it is present. If this list does not contain the element, it is
+ * unchanged. More formally, removes the element with the lowest index
+ * {@code i} such that {@code Objects.equals(o, get(i))}
+ * (if such an element exists). Returns {@code true} if this list
+ * contained the specified element (or equivalently, if this list
+ * changed as a result of the call).
+ *
+ * @param o element to be removed from this list, if present
+ * @return {@code true} if this list contained the specified element
+ */
+ public boolean remove(Object o) {
+ Object[] snapshot = getArray();
+ int index = indexOf(o, snapshot, 0, snapshot.length);
+ return (index < 0) ? false : remove(o, snapshot, index);
+ }
+
+ /**
+ * A version of remove(Object) using the strong hint that given
+ * recent snapshot contains o at the given index.
+ */
+ private boolean remove(Object o, Object[] snapshot, int index) {
+ synchronized (lock) {
+ Object[] current = getArray();
+ int len = current.length;
+ if (snapshot != current) findIndex: {
+ int prefix = Math.min(index, len);
+ for (int i = 0; i < prefix; i++) {
+ if (current[i] != snapshot[i]
+ && Objects.equals(o, current[i])) {
+ index = i;
+ break findIndex;
+ }
+ }
+ if (index >= len)
+ return false;
+ if (current[index] == o)
+ break findIndex;
+ index = indexOf(o, current, index, len);
+ if (index < 0)
+ return false;
+ }
+ Object[] newElements = new Object[len - 1];
+ System.arraycopy(current, 0, newElements, 0, index);
+ System.arraycopy(current, index + 1,
+ newElements, index,
+ len - index - 1);
+ setArray(newElements);
+ return true;
+ }
+ }
+
+ /**
+ * Removes from this list all of the elements whose index is between
+ * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.
+ * Shifts any succeeding elements to the left (reduces their index).
+ * This call shortens the list by {@code (toIndex - fromIndex)} elements.
+ * (If {@code toIndex==fromIndex}, this operation has no effect.)
+ *
+ * @param fromIndex index of first element to be removed
+ * @param toIndex index after last element to be removed
+ * @throws IndexOutOfBoundsException if fromIndex or toIndex out of range
+ * ({@code fromIndex < 0 || toIndex > size() || toIndex < fromIndex})
+ */
+ void removeRange(int fromIndex, int toIndex) {
+ synchronized (lock) {
+ Object[] elements = getArray();
+ int len = elements.length;
+
+ if (fromIndex < 0 || toIndex > len || toIndex < fromIndex)
+ throw new IndexOutOfBoundsException();
+ int newlen = len - (toIndex - fromIndex);
+ int numMoved = len - toIndex;
+ if (numMoved == 0)
+ setArray(Arrays.copyOf(elements, newlen));
+ else {
+ Object[] newElements = new Object[newlen];
+ System.arraycopy(elements, 0, newElements, 0, fromIndex);
+ System.arraycopy(elements, toIndex, newElements,
+ fromIndex, numMoved);
+ setArray(newElements);
+ }
+ }
+ }
+
+ /**
+ * Appends the element, if not present.
+ *
+ * @param e element to be added to this list, if absent
+ * @return {@code true} if the element was added
+ */
+ public boolean addIfAbsent(E e) {
+ Object[] snapshot = getArray();
+ return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
+ addIfAbsent(e, snapshot);
+ }
+
+ /**
+ * A version of addIfAbsent using the strong hint that given
+ * recent snapshot does not contain e.
+ */
+ private boolean addIfAbsent(E e, Object[] snapshot) {
+ synchronized (lock) {
+ Object[] current = getArray();
+ int len = current.length;
+ if (snapshot != current) {
+ // Optimize for lost race to another addXXX operation
+ int common = Math.min(snapshot.length, len);
+ for (int i = 0; i < common; i++)
+ if (current[i] != snapshot[i]
+ && Objects.equals(e, current[i]))
+ return false;
+ if (indexOf(e, current, common, len) >= 0)
+ return false;
+ }
+ Object[] newElements = Arrays.copyOf(current, len + 1);
+ newElements[len] = e;
+ setArray(newElements);
+ return true;
+ }
+ }
+
+ /**
+ * Returns {@code true} if this list contains all of the elements of the
+ * specified collection.
+ *
+ * @param c collection to be checked for containment in this list
+ * @return {@code true} if this list contains all of the elements of the
+ * specified collection
+ * @throws NullPointerException if the specified collection is null
+ * @see #contains(Object)
+ */
+ public boolean containsAll(Collection<?> c) {
+ Object[] elements = getArray();
+ int len = elements.length;
+ for (Object e : c) {
+ if (indexOf(e, elements, 0, len) < 0)
+ return false;
}
return true;
}
/**
- * Searches this list for {@code object} and returns the index of the first
- * occurrence that is at or after {@code from}.
+ * Removes from this list all of its elements that are contained in
+ * the specified collection. This is a particularly expensive operation
+ * in this class because of the need for an internal temporary array.
*
- * @return the index or -1 if the object was not found.
+ * @param c collection containing elements to be removed from this list
+ * @return {@code true} if this list changed as a result of the call
+ * @throws ClassCastException if the class of an element of this list
+ * is incompatible with the specified collection
+ * (<a href="{@docRoot}/../api/java/util/Collection.html#optional-restrictions">optional</a>)
+ * @throws NullPointerException if this list contains a null element and the
+ * specified collection does not permit null elements
+ * (<a href="{@docRoot}/../api/java/util/Collection.html#optional-restrictions">optional</a>),
+ * or if the specified collection is null
+ * @see #remove(Object)
*/
- public int indexOf(E object, int from) {
- Object[] snapshot = elements;
- return indexOf(object, snapshot, from, snapshot.length);
- }
-
- public int indexOf(Object object) {
- Object[] snapshot = elements;
- return indexOf(object, snapshot, 0, snapshot.length);
+ public boolean removeAll(Collection<?> c) {
+ if (c == null) throw new NullPointerException();
+ synchronized (lock) {
+ Object[] elements = getArray();
+ int len = elements.length;
+ if (len != 0) {
+ // temp array holds those elements we know we want to keep
+ int newlen = 0;
+ Object[] temp = new Object[len];
+ for (int i = 0; i < len; ++i) {
+ Object element = elements[i];
+ if (!c.contains(element))
+ temp[newlen++] = element;
+ }
+ if (newlen != len) {
+ setArray(Arrays.copyOf(temp, newlen));
+ return true;
+ }
+ }
+ return false;
+ }
}
/**
- * Searches this list for {@code object} and returns the index of the last
- * occurrence that is before {@code to}.
+ * Retains only the elements in this list that are contained in the
+ * specified collection. In other words, removes from this list all of
+ * its elements that are not contained in the specified collection.
*
- * @return the index or -1 if the object was not found.
+ * @param c collection containing elements to be retained in this list
+ * @return {@code true} if this list changed as a result of the call
+ * @throws ClassCastException if the class of an element of this list
+ * is incompatible with the specified collection
+ * (<a href="{@docRoot}/../api/java/util/Collection.html#optional-restrictions">optional</a>)
+ * @throws NullPointerException if this list contains a null element and the
+ * specified collection does not permit null elements
+ * (<a href="{@docRoot}/../api/java/util/Collection.html#optional-restrictions">optional</a>),
+ * or if the specified collection is null
+ * @see #remove(Object)
*/
- public int lastIndexOf(E object, int to) {
- Object[] snapshot = elements;
- return lastIndexOf(object, snapshot, 0, to);
- }
-
- public int lastIndexOf(Object object) {
- Object[] snapshot = elements;
- return lastIndexOf(object, snapshot, 0, snapshot.length);
- }
-
- public boolean isEmpty() {
- return elements.length == 0;
+ public boolean retainAll(Collection<?> c) {
+ if (c == null) throw new NullPointerException();
+ synchronized (lock) {
+ Object[] elements = getArray();
+ int len = elements.length;
+ if (len != 0) {
+ // temp array holds those elements we know we want to keep
+ int newlen = 0;
+ Object[] temp = new Object[len];
+ for (int i = 0; i < len; ++i) {
+ Object element = elements[i];
+ if (c.contains(element))
+ temp[newlen++] = element;
+ }
+ if (newlen != len) {
+ setArray(Arrays.copyOf(temp, newlen));
+ return true;
+ }
+ }
+ return false;
+ }
}
/**
- * Returns an {@link Iterator} that iterates over the elements of this list
- * as they were at the time of this method call. Changes to the list made
- * after this method call will not be reflected by the iterator, nor will
- * they trigger a {@link ConcurrentModificationException}.
+ * Appends all of the elements in the specified collection that
+ * are not already contained in this list, to the end of
+ * this list, in the order that they are returned by the
+ * specified collection's iterator.
*
- * <p>The returned iterator does not support {@link Iterator#remove()}.
+ * @param c collection containing elements to be added to this list
+ * @return the number of elements added
+ * @throws NullPointerException if the specified collection is null
+ * @see #addIfAbsent(Object)
+ */
+ public int addAllAbsent(Collection<? extends E> c) {
+ Object[] cs = c.toArray();
+ if (cs.length == 0)
+ return 0;
+ synchronized (lock) {
+ Object[] elements = getArray();
+ int len = elements.length;
+ int added = 0;
+ // uniquify and compact elements in cs
+ for (int i = 0; i < cs.length; ++i) {
+ Object e = cs[i];
+ if (indexOf(e, elements, 0, len) < 0 &&
+ indexOf(e, cs, 0, added) < 0)
+ cs[added++] = e;
+ }
+ if (added > 0) {
+ Object[] newElements = Arrays.copyOf(elements, len + added);
+ System.arraycopy(cs, 0, newElements, len, added);
+ setArray(newElements);
+ }
+ return added;
+ }
+ }
+
+ /**
+ * Removes all of the elements from this list.
+ * The list will be empty after this call returns.
+ */
+ public void clear() {
+ synchronized (lock) {
+ setArray(new Object[0]);
+ }
+ }
+
+ /**
+ * Appends all of the elements in the specified collection to the end
+ * of this list, in the order that they are returned by the specified
+ * collection's iterator.
+ *
+ * @param c collection containing elements to be added to this list
+ * @return {@code true} if this list changed as a result of the call
+ * @throws NullPointerException if the specified collection is null
+ * @see #add(Object)
+ */
+ public boolean addAll(Collection<? extends E> c) {
+ Object[] cs = (c.getClass() == CopyOnWriteArrayList.class) ?
+ ((CopyOnWriteArrayList<?>)c).getArray() : c.toArray();
+ if (cs.length == 0)
+ return false;
+ synchronized (lock) {
+ Object[] elements = getArray();
+ int len = elements.length;
+ if (len == 0 && cs.getClass() == Object[].class)
+ setArray(cs);
+ else {
+ Object[] newElements = Arrays.copyOf(elements, len + cs.length);
+ System.arraycopy(cs, 0, newElements, len, cs.length);
+ setArray(newElements);
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Inserts all of the elements in the specified collection into this
+ * list, starting at the specified position. Shifts the element
+ * currently at that position (if any) and any subsequent elements to
+ * the right (increases their indices). The new elements will appear
+ * in this list in the order that they are returned by the
+ * specified collection's iterator.
+ *
+ * @param index index at which to insert the first element
+ * from the specified collection
+ * @param c collection containing elements to be added to this list
+ * @return {@code true} if this list changed as a result of the call
+ * @throws IndexOutOfBoundsException {@inheritDoc}
+ * @throws NullPointerException if the specified collection is null
+ * @see #add(int,Object)
+ */
+ public boolean addAll(int index, Collection<? extends E> c) {
+ Object[] cs = c.toArray();
+ synchronized (lock) {
+ Object[] elements = getArray();
+ int len = elements.length;
+ if (index > len || index < 0)
+ throw new IndexOutOfBoundsException(outOfBounds(index, len));
+ if (cs.length == 0)
+ return false;
+ int numMoved = len - index;
+ Object[] newElements;
+ if (numMoved == 0)
+ newElements = Arrays.copyOf(elements, len + cs.length);
+ else {
+ newElements = new Object[len + cs.length];
+ System.arraycopy(elements, 0, newElements, 0, index);
+ System.arraycopy(elements, index,
+ newElements, index + cs.length,
+ numMoved);
+ }
+ System.arraycopy(cs, 0, newElements, index, cs.length);
+ setArray(newElements);
+ return true;
+ }
+ }
+
+ public void forEach(Consumer<? super E> action) {
+ if (action == null) throw new NullPointerException();
+ for (Object x : getArray()) {
+ @SuppressWarnings("unchecked") E e = (E) x;
+ action.accept(e);
+ }
+ }
+
+ public boolean removeIf(Predicate<? super E> filter) {
+ if (filter == null) throw new NullPointerException();
+ synchronized (lock) {
+ final Object[] elements = getArray();
+ final int len = elements.length;
+ int i;
+ for (i = 0; i < len; i++) {
+ @SuppressWarnings("unchecked") E e = (E) elements[i];
+ if (filter.test(e)) {
+ int newlen = i;
+ final Object[] newElements = new Object[len - 1];
+ System.arraycopy(elements, 0, newElements, 0, newlen);
+ for (i++; i < len; i++) {
+ @SuppressWarnings("unchecked") E x = (E) elements[i];
+ if (!filter.test(x))
+ newElements[newlen++] = x;
+ }
+ setArray((newlen == len - 1)
+ ? newElements // one match => one copy
+ : Arrays.copyOf(newElements, newlen));
+ return true;
+ }
+ }
+ return false; // zero matches => zero copies
+ }
+ }
+
+ public void replaceAll(UnaryOperator<E> operator) {
+ if (operator == null) throw new NullPointerException();
+ synchronized (lock) {
+ Object[] elements = getArray();
+ int len = elements.length;
+ Object[] newElements = Arrays.copyOf(elements, len);
+ for (int i = 0; i < len; ++i) {
+ @SuppressWarnings("unchecked") E e = (E) elements[i];
+ newElements[i] = operator.apply(e);
+ }
+ setArray(newElements);
+ }
+ }
+
+ public void sort(Comparator<? super E> c) {
+ synchronized (lock) {
+ Object[] elements = getArray();
+ Object[] newElements = Arrays.copyOf(elements, elements.length);
+ @SuppressWarnings("unchecked") E[] es = (E[])newElements;
+ Arrays.sort(es, c);
+ setArray(newElements);
+ }
+ }
+
+ /**
+ * Saves this list to a stream (that is, serializes it).
+ *
+ * @param s the stream
+ * @throws java.io.IOException if an I/O error occurs
+ * @serialData The length of the array backing the list is emitted
+ * (int), followed by all of its elements (each an Object)
+ * in the proper order.
+ */
+ private void writeObject(java.io.ObjectOutputStream s)
+ throws java.io.IOException {
+
+ s.defaultWriteObject();
+
+ Object[] elements = getArray();
+ // Write out array length
+ s.writeInt(elements.length);
+
+ // Write out all elements in the proper order.
+ for (Object element : elements)
+ s.writeObject(element);
+ }
+
+ /**
+ * Reconstitutes this list from a stream (that is, deserializes it).
+ * @param s the stream
+ * @throws ClassNotFoundException if the class of a serialized object
+ * could not be found
+ * @throws java.io.IOException if an I/O error occurs
+ */
+ private void readObject(java.io.ObjectInputStream s)
+ throws java.io.IOException, ClassNotFoundException {
+
+ s.defaultReadObject();
+
+ // bind to new lock
+ resetLock();
+
+ // Read in array length and allocate array
+ int len = s.readInt();
+ Object[] elements = new Object[len];
+
+ // Read in all elements in the proper order.
+ for (int i = 0; i < len; i++)
+ elements[i] = s.readObject();
+ setArray(elements);
+ }
+
+ /**
+ * Returns a string representation of this list. The string
+ * representation consists of the string representations of the list's
+ * elements in the order they are returned by its iterator, enclosed in
+ * square brackets ({@code "[]"}). Adjacent elements are separated by
+ * the characters {@code ", "} (comma and space). Elements are
+ * converted to strings as by {@link String#valueOf(Object)}.
+ *
+ * @return a string representation of this list
+ */
+ public String toString() {
+ return Arrays.toString(getArray());
+ }
+
+ /**
+ * Compares the specified object with this list for equality.
+ * Returns {@code true} if the specified object is the same object
+ * as this object, or if it is also a {@link List} and the sequence
+ * of elements returned by an {@linkplain List#iterator() iterator}
+ * over the specified list is the same as the sequence returned by
+ * an iterator over this list. The two sequences are considered to
+ * be the same if they have the same length and corresponding
+ * elements at the same position in the sequence are <em>equal</em>.
+ * Two elements {@code e1} and {@code e2} are considered
+ * <em>equal</em> if {@code Objects.equals(e1, e2)}.
+ *
+ * @param o the object to be compared for equality with this list
+ * @return {@code true} if the specified object is equal to this list
+ */
+ public boolean equals(Object o) {
+ if (o == this)
+ return true;
+ if (!(o instanceof List))
+ return false;
+
+ List<?> list = (List<?>)o;
+ Iterator<?> it = list.iterator();
+ Object[] elements = getArray();
+ for (int i = 0, len = elements.length; i < len; i++)
+ if (!it.hasNext() || !Objects.equals(elements[i], it.next()))
+ return false;
+ if (it.hasNext())
+ return false;
+ return true;
+ }
+
+ /**
+ * Returns the hash code value for this list.
+ *
+ * <p>This implementation uses the definition in {@link List#hashCode}.
+ *
+ * @return the hash code value for this list
+ */
+ public int hashCode() {
+ int hashCode = 1;
+ for (Object x : getArray())
+ hashCode = 31 * hashCode + (x == null ? 0 : x.hashCode());
+ return hashCode;
+ }
+
+ /**
+ * Returns an iterator over the elements in this list in proper sequence.
+ *
+ * <p>The returned iterator provides a snapshot of the state of the list
+ * when the iterator was constructed. No synchronization is needed while
+ * traversing the iterator. The iterator does <em>NOT</em> support the
+ * {@code remove} method.
+ *
+ * @return an iterator over the elements in this list in proper sequence
*/
public Iterator<E> iterator() {
- Object[] snapshot = elements;
- return new CowIterator<E>(snapshot, 0, snapshot.length);
+ return new COWIterator<E>(getArray(), 0);
}
/**
- * Returns a {@link ListIterator} that iterates over the elements of this
- * list as they were at the time of this method call. Changes to the list
- * made after this method call will not be reflected by the iterator, nor
- * will they trigger a {@link ConcurrentModificationException}.
+ * {@inheritDoc}
*
- * <p>The returned iterator does not support {@link ListIterator#add},
- * {@link ListIterator#set} or {@link Iterator#remove()},
- */
- public ListIterator<E> listIterator(int index) {
- Object[] snapshot = elements;
- if (index < 0 || index > snapshot.length) {
- throw new IndexOutOfBoundsException("index=" + index + ", length=" + snapshot.length);
- }
- CowIterator<E> result = new CowIterator<E>(snapshot, 0, snapshot.length);
- result.index = index;
- return result;
- }
-
- /**
- * Equivalent to {@code listIterator(0)}.
+ * <p>The returned iterator provides a snapshot of the state of the list
+ * when the iterator was constructed. No synchronization is needed while
+ * traversing the iterator. The iterator does <em>NOT</em> support the
+ * {@code remove}, {@code set} or {@code add} methods.
*/
public ListIterator<E> listIterator() {
- Object[] snapshot = elements;
- return new CowIterator<E>(snapshot, 0, snapshot.length);
- }
-
- public List<E> subList(int from, int to) {
- Object[] snapshot = elements;
- if (from < 0 || from > to || to > snapshot.length) {
- throw new IndexOutOfBoundsException("from=" + from + ", to=" + to +
- ", list size=" + snapshot.length);
- }
- return new CowSubList(snapshot, from, to);
- }
-
- public Object[] toArray() {
- return elements.clone();
- }
-
- @SuppressWarnings({"unchecked","SuspiciousSystemArraycopy"})
- public <T> T[] toArray(T[] contents) {
- Object[] snapshot = elements;
- if (snapshot.length > contents.length) {
- return (T[]) Arrays.copyOf(snapshot, snapshot.length, contents.getClass());
- }
- System.arraycopy(snapshot, 0, contents, 0, snapshot.length);
- if (snapshot.length < contents.length) {
- contents[snapshot.length] = null;
- }
- return contents;
- }
-
- @Override public boolean equals(Object other) {
- if (other instanceof CopyOnWriteArrayList) {
- return this == other
- || Arrays.equals(elements, ((CopyOnWriteArrayList<?>) other).elements);
- } else if (other instanceof List) {
- Object[] snapshot = elements;
- Iterator<?> i = ((List<?>) other).iterator();
- for (Object o : snapshot) {
- if (!i.hasNext() || !Objects.equal(o, i.next())) {
- return false;
- }
- }
- return !i.hasNext();
- } else {
- return false;
- }
- }
-
- @Override public int hashCode() {
- return Arrays.hashCode(elements);
- }
-
- @Override public String toString() {
- return Arrays.toString(elements);
- }
-
- public synchronized boolean add(E e) {
- Object[] newElements = new Object[elements.length + 1];
- System.arraycopy(elements, 0, newElements, 0, elements.length);
- newElements[elements.length] = e;
- elements = newElements;
- return true;
- }
-
- public synchronized void add(int index, E e) {
- Object[] newElements = new Object[elements.length + 1];
- System.arraycopy(elements, 0, newElements, 0, index);
- newElements[index] = e;
- System.arraycopy(elements, index, newElements, index + 1, elements.length - index);
- elements = newElements;
- }
-
- public synchronized boolean addAll(Collection<? extends E> collection) {
- return addAll(elements.length, collection);
- }
-
- public synchronized boolean addAll(int index, Collection<? extends E> collection) {
- Object[] toAdd = collection.toArray();
- Object[] newElements = new Object[elements.length + toAdd.length];
- System.arraycopy(elements, 0, newElements, 0, index);
- System.arraycopy(toAdd, 0, newElements, index, toAdd.length);
- System.arraycopy(elements, index,
- newElements, index + toAdd.length, elements.length - index);
- elements = newElements;
- return toAdd.length > 0;
+ return new COWIterator<E>(getArray(), 0);
}
/**
- * Adds the elements of {@code collection} that are not already present in
- * this list. If {@code collection} includes a repeated value, at most one
- * occurrence of that value will be added to this list. Elements are added
- * at the end of this list.
+ * {@inheritDoc}
*
- * <p>Callers of this method may prefer {@link CopyOnWriteArraySet}, whose
- * API is more appropriate for set operations.
- */
- public synchronized int addAllAbsent(Collection<? extends E> collection) {
- Object[] toAdd = collection.toArray();
- Object[] newElements = new Object[elements.length + toAdd.length];
- System.arraycopy(elements, 0, newElements, 0, elements.length);
- int addedCount = 0;
- for (Object o : toAdd) {
- if (indexOf(o, newElements, 0, elements.length + addedCount) == -1) {
- newElements[elements.length + addedCount++] = o;
- }
- }
- if (addedCount < toAdd.length) {
- newElements = Arrays.copyOfRange(
- newElements, 0, elements.length + addedCount); // trim to size
- }
- elements = newElements;
- return addedCount;
- }
-
- /**
- * Adds {@code object} to the end of this list if it is not already present.
+ * <p>The returned iterator provides a snapshot of the state of the list
+ * when the iterator was constructed. No synchronization is needed while
+ * traversing the iterator. The iterator does <em>NOT</em> support the
+ * {@code remove}, {@code set} or {@code add} methods.
*
- * <p>Callers of this method may prefer {@link CopyOnWriteArraySet}, whose
- * API is more appropriate for set operations.
+ * @throws IndexOutOfBoundsException {@inheritDoc}
*/
- public synchronized boolean addIfAbsent(E object) {
- if (contains(object)) {
- return false;
- }
- add(object);
- return true;
- }
+ public ListIterator<E> listIterator(int index) {
+ Object[] elements = getArray();
+ int len = elements.length;
+ if (index < 0 || index > len)
+ throw new IndexOutOfBoundsException(outOfBounds(index, len));
- @Override public synchronized void clear() {
- elements = EmptyArray.OBJECT;
- }
-
- public synchronized E remove(int index) {
- @SuppressWarnings("unchecked")
- E removed = (E) elements[index];
- removeRange(index, index + 1);
- return removed;
- }
-
- public synchronized boolean remove(Object o) {
- int index = indexOf(o);
- if (index == -1) {
- return false;
- }
- remove(index);
- return true;
- }
-
- public synchronized boolean removeAll(Collection<?> collection) {
- return removeOrRetain(collection, false, 0, elements.length) != 0;
- }
-
- public synchronized boolean retainAll(Collection<?> collection) {
- return removeOrRetain(collection, true, 0, elements.length) != 0;
+ return new COWIterator<E>(elements, index);
}
/**
- * Removes or retains the elements in {@code collection}. Returns the number
- * of elements removed.
- */
- private int removeOrRetain(Collection<?> collection, boolean retain, int from, int to) {
- for (int i = from; i < to; i++) {
- if (collection.contains(elements[i]) == retain) {
- continue;
- }
-
- /*
- * We've encountered an element that must be removed! Create a new
- * array and copy in the surviving elements one by one.
- */
- Object[] newElements = new Object[elements.length - 1];
- System.arraycopy(elements, 0, newElements, 0, i);
- int newSize = i;
- for (int j = i + 1; j < to; j++) {
- if (collection.contains(elements[j]) == retain) {
- newElements[newSize++] = elements[j];
- }
- }
-
- /*
- * Copy the elements after 'to'. This is only useful for sub lists,
- * where 'to' will be less than elements.length.
- */
- System.arraycopy(elements, to, newElements, newSize, elements.length - to);
- newSize += (elements.length - to);
-
- if (newSize < newElements.length) {
- newElements = Arrays.copyOfRange(newElements, 0, newSize); // trim to size
- }
- int removed = elements.length - newElements.length;
- elements = newElements;
- return removed;
- }
-
- // we made it all the way through the loop without making any changes
- return 0;
- }
-
- public synchronized E set(int index, E e) {
- Object[] newElements = elements.clone();
- @SuppressWarnings("unchecked")
- E result = (E) newElements[index];
- newElements[index] = e;
- elements = newElements;
- return result;
- }
-
- private void removeRange(int from, int to) {
- Object[] newElements = new Object[elements.length - (to - from)];
- System.arraycopy(elements, 0, newElements, 0, from);
- System.arraycopy(elements, to, newElements, from, elements.length - to);
- elements = newElements;
- }
-
- static int lastIndexOf(Object o, Object[] data, int from, int to) {
- if (o == null) {
- for (int i = to - 1; i >= from; i--) {
- if (data[i] == null) {
- return i;
- }
- }
- } else {
- for (int i = to - 1; i >= from; i--) {
- if (o.equals(data[i])) {
- return i;
- }
- }
- }
- return -1;
- }
-
- static int indexOf(Object o, Object[] data, int from, int to) {
- if (o == null) {
- for (int i = from; i < to; i++) {
- if (data[i] == null) {
- return i;
- }
- }
- } else {
- for (int i = from; i < to; i++) {
- if (o.equals(data[i])) {
- return i;
- }
- }
- }
- return -1;
- }
-
- final Object[] getArray() {
- // CopyOnWriteArraySet needs this.
- return elements;
- }
-
- /**
- * The sub list is thread safe and supports non-blocking reads. Doing so is
- * more difficult than in the full list, because each read needs to examine
- * four fields worth of state:
- * - the elements array of the full list
- * - two integers for the bounds of this sub list
- * - the expected elements array (to detect concurrent modification)
+ * Returns a {@link Spliterator} over the elements in this list.
*
- * This is accomplished by aggregating the sub list's three fields into a
- * single snapshot object representing the current slice. This permits reads
- * to be internally consistent without synchronization. This takes advantage
- * of Java's concurrency semantics for final fields.
+ * <p>The {@code Spliterator} reports {@link Spliterator#IMMUTABLE},
+ * {@link Spliterator#ORDERED}, {@link Spliterator#SIZED}, and
+ * {@link Spliterator#SUBSIZED}.
+ *
+ * <p>The spliterator provides a snapshot of the state of the list
+ * when the spliterator was constructed. No synchronization is needed while
+ * operating on the spliterator.
+ *
+ * @return a {@code Spliterator} over the elements in this list
+ * @since 1.8
*/
- class CowSubList extends AbstractList<E> {
-
- /*
- * An immutable snapshot of a sub list's state. By gathering all three
- * of the sub list's fields in an immutable object,
- */
- private volatile Slice slice;
-
- public CowSubList(Object[] expectedElements, int from, int to) {
- this.slice = new Slice(expectedElements, from, to);
- }
-
- @Override public int size() {
- Slice slice = this.slice;
- return slice.to - slice.from;
- }
-
- @Override public boolean isEmpty() {
- Slice slice = this.slice;
- return slice.from == slice.to;
- }
-
- @SuppressWarnings("unchecked")
- @Override public E get(int index) {
- Slice slice = this.slice;
- Object[] snapshot = elements;
- slice.checkElementIndex(index);
- slice.checkConcurrentModification(snapshot);
- return (E) snapshot[index + slice.from];
- }
-
- @Override public Iterator<E> iterator() {
- return listIterator(0);
- }
-
- @Override public ListIterator<E> listIterator() {
- return listIterator(0);
- }
-
- @Override public ListIterator<E> listIterator(int index) {
- Slice slice = this.slice;
- Object[] snapshot = elements;
- slice.checkPositionIndex(index);
- slice.checkConcurrentModification(snapshot);
- CowIterator<E> result = new CowIterator<E>(snapshot, slice.from, slice.to);
- result.index = slice.from + index;
- return result;
- }
-
- @Override public int indexOf(Object object) {
- Slice slice = this.slice;
- Object[] snapshot = elements;
- slice.checkConcurrentModification(snapshot);
- int result = CopyOnWriteArrayList.indexOf(object, snapshot, slice.from, slice.to);
- return (result != -1) ? (result - slice.from) : -1;
- }
-
- @Override public int lastIndexOf(Object object) {
- Slice slice = this.slice;
- Object[] snapshot = elements;
- slice.checkConcurrentModification(snapshot);
- int result = CopyOnWriteArrayList.lastIndexOf(object, snapshot, slice.from, slice.to);
- return (result != -1) ? (result - slice.from) : -1;
- }
-
- @Override public boolean contains(Object object) {
- return indexOf(object) != -1;
- }
-
- @Override public boolean containsAll(Collection<?> collection) {
- Slice slice = this.slice;
- Object[] snapshot = elements;
- slice.checkConcurrentModification(snapshot);
- return CopyOnWriteArrayList.containsAll(collection, snapshot, slice.from, slice.to);
- }
-
- @Override public List<E> subList(int from, int to) {
- Slice slice = this.slice;
- if (from < 0 || from > to || to > size()) {
- throw new IndexOutOfBoundsException("from=" + from + ", to=" + to +
- ", list size=" + size());
- }
- return new CowSubList(slice.expectedElements, slice.from + from, slice.from + to);
- }
-
- @Override public E remove(int index) {
- synchronized (CopyOnWriteArrayList.this) {
- slice.checkElementIndex(index);
- slice.checkConcurrentModification(elements);
- E removed = CopyOnWriteArrayList.this.remove(slice.from + index);
- slice = new Slice(elements, slice.from, slice.to - 1);
- return removed;
- }
- }
-
- @Override public void clear() {
- synchronized (CopyOnWriteArrayList.this) {
- slice.checkConcurrentModification(elements);
- CopyOnWriteArrayList.this.removeRange(slice.from, slice.to);
- slice = new Slice(elements, slice.from, slice.from);
- }
- }
-
- @Override public void add(int index, E object) {
- synchronized (CopyOnWriteArrayList.this) {
- slice.checkPositionIndex(index);
- slice.checkConcurrentModification(elements);
- CopyOnWriteArrayList.this.add(index + slice.from, object);
- slice = new Slice(elements, slice.from, slice.to + 1);
- }
- }
-
- @Override public boolean add(E object) {
- synchronized (CopyOnWriteArrayList.this) {
- add(slice.to - slice.from, object);
- return true;
- }
- }
-
- @Override public boolean addAll(int index, Collection<? extends E> collection) {
- synchronized (CopyOnWriteArrayList.this) {
- slice.checkPositionIndex(index);
- slice.checkConcurrentModification(elements);
- int oldSize = elements.length;
- boolean result = CopyOnWriteArrayList.this.addAll(index + slice.from, collection);
- slice = new Slice(elements, slice.from, slice.to + (elements.length - oldSize));
- return result;
- }
- }
-
- @Override public boolean addAll(Collection<? extends E> collection) {
- synchronized (CopyOnWriteArrayList.this) {
- return addAll(size(), collection);
- }
- }
-
- @Override public E set(int index, E object) {
- synchronized (CopyOnWriteArrayList.this) {
- slice.checkElementIndex(index);
- slice.checkConcurrentModification(elements);
- E result = CopyOnWriteArrayList.this.set(index + slice.from, object);
- slice = new Slice(elements, slice.from, slice.to);
- return result;
- }
- }
-
- @Override public boolean remove(Object object) {
- synchronized (CopyOnWriteArrayList.this) {
- int index = indexOf(object);
- if (index == -1) {
- return false;
- }
- remove(index);
- return true;
- }
- }
-
- @Override public boolean removeAll(Collection<?> collection) {
- synchronized (CopyOnWriteArrayList.this) {
- slice.checkConcurrentModification(elements);
- int removed = removeOrRetain(collection, false, slice.from, slice.to);
- slice = new Slice(elements, slice.from, slice.to - removed);
- return removed != 0;
- }
- }
-
- @Override public boolean retainAll(Collection<?> collection) {
- synchronized (CopyOnWriteArrayList.this) {
- slice.checkConcurrentModification(elements);
- int removed = removeOrRetain(collection, true, slice.from, slice.to);
- slice = new Slice(elements, slice.from, slice.to - removed);
- return removed != 0;
- }
- }
+ public Spliterator<E> spliterator() {
+ return Spliterators.spliterator
+ (getArray(), Spliterator.IMMUTABLE | Spliterator.ORDERED);
}
- static class Slice {
- private final Object[] expectedElements;
- private final int from;
- private final int to;
-
- Slice(Object[] expectedElements, int from, int to) {
- this.expectedElements = expectedElements;
- this.from = from;
- this.to = to;
- }
-
- /**
- * Throws if {@code index} doesn't identify an element in the array.
- */
- void checkElementIndex(int index) {
- if (index < 0 || index >= to - from) {
- throw new IndexOutOfBoundsException("index=" + index + ", size=" + (to - from));
- }
- }
-
- /**
- * Throws if {@code index} doesn't identify an insertion point in the
- * array. Unlike element index, it's okay to add or iterate at size().
- */
- void checkPositionIndex(int index) {
- if (index < 0 || index > to - from) {
- throw new IndexOutOfBoundsException("index=" + index + ", size=" + (to - from));
- }
- }
-
- void checkConcurrentModification(Object[] snapshot) {
- if (expectedElements != snapshot) {
- throw new ConcurrentModificationException();
- }
- }
- }
-
- /**
- * Iterates an immutable snapshot of the list.
- */
- static class CowIterator<E> implements ListIterator<E> {
+ static final class COWIterator<E> implements ListIterator<E> {
+ /** Snapshot of the array */
private final Object[] snapshot;
- private final int from;
- private final int to;
- private int index = 0;
+ /** Index of element to be returned by subsequent call to next. */
+ private int cursor;
- CowIterator(Object[] snapshot, int from, int to) {
- this.snapshot = snapshot;
- this.from = from;
- this.to = to;
- this.index = from;
- }
-
- public void add(E object) {
- throw new UnsupportedOperationException();
+ COWIterator(Object[] elements, int initialCursor) {
+ cursor = initialCursor;
+ snapshot = elements;
}
public boolean hasNext() {
- return index < to;
+ return cursor < snapshot.length;
}
public boolean hasPrevious() {
- return index > from;
+ return cursor > 0;
}
@SuppressWarnings("unchecked")
public E next() {
- if (index < to) {
- return (E) snapshot[index++];
- } else {
+ if (! hasNext())
throw new NoSuchElementException();
- }
- }
-
- public int nextIndex() {
- return index;
+ return (E) snapshot[cursor++];
}
@SuppressWarnings("unchecked")
public E previous() {
- if (index > from) {
- return (E) snapshot[--index];
- } else {
+ if (! hasPrevious())
throw new NoSuchElementException();
- }
+ return (E) snapshot[--cursor];
+ }
+
+ public int nextIndex() {
+ return cursor;
}
public int previousIndex() {
- return index - 1;
+ return cursor-1;
+ }
+
+ /**
+ * Not supported. Always throws UnsupportedOperationException.
+ * @throws UnsupportedOperationException always; {@code remove}
+ * is not supported by this iterator.
+ */
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Not supported. Always throws UnsupportedOperationException.
+ * @throws UnsupportedOperationException always; {@code set}
+ * is not supported by this iterator.
+ */
+ public void set(E e) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Not supported. Always throws UnsupportedOperationException.
+ * @throws UnsupportedOperationException always; {@code add}
+ * is not supported by this iterator.
+ */
+ public void add(E e) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void forEachRemaining(Consumer<? super E> action) {
+ Objects.requireNonNull(action);
+ final int size = snapshot.length;
+ for (int i = cursor; i < size; i++) {
+ action.accept((E) snapshot[i]);
+ }
+ cursor = size;
+ }
+ }
+
+ /**
+ * Returns a view of the portion of this list between
+ * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.
+ * The returned list is backed by this list, so changes in the
+ * returned list are reflected in this list.
+ *
+ * <p>The semantics of the list returned by this method become
+ * undefined if the backing list (i.e., this list) is modified in
+ * any way other than via the returned list.
+ *
+ * @param fromIndex low endpoint (inclusive) of the subList
+ * @param toIndex high endpoint (exclusive) of the subList
+ * @return a view of the specified range within this list
+ * @throws IndexOutOfBoundsException {@inheritDoc}
+ */
+ public List<E> subList(int fromIndex, int toIndex) {
+ synchronized (lock) {
+ Object[] elements = getArray();
+ int len = elements.length;
+ if (fromIndex < 0 || toIndex > len || fromIndex > toIndex)
+ throw new IndexOutOfBoundsException();
+ return new COWSubList<E>(this, fromIndex, toIndex);
+ }
+ }
+
+ /**
+ * Sublist for CopyOnWriteArrayList.
+ * This class extends AbstractList merely for convenience, to
+ * avoid having to define addAll, etc. This doesn't hurt, but
+ * is wasteful. This class does not need or use modCount
+ * mechanics in AbstractList, but does need to check for
+ * concurrent modification using similar mechanics. On each
+ * operation, the array that we expect the backing list to use
+ * is checked and updated. Since we do this for all of the
+ * base operations invoked by those defined in AbstractList,
+ * all is well. While inefficient, this is not worth
+ * improving. The kinds of list operations inherited from
+ * AbstractList are already so slow on COW sublists that
+ * adding a bit more space/time doesn't seem even noticeable.
+ */
+ private static class COWSubList<E>
+ extends AbstractList<E>
+ implements RandomAccess
+ {
+ private final CopyOnWriteArrayList<E> l;
+ private final int offset;
+ private int size;
+ private Object[] expectedArray;
+
+ // only call this holding l's lock
+ COWSubList(CopyOnWriteArrayList<E> list,
+ int fromIndex, int toIndex) {
+ // assert Thread.holdsLock(list.lock);
+ l = list;
+ expectedArray = l.getArray();
+ offset = fromIndex;
+ size = toIndex - fromIndex;
+ }
+
+ // only call this holding l's lock
+ private void checkForComodification() {
+ // assert Thread.holdsLock(l.lock);
+ if (l.getArray() != expectedArray)
+ throw new ConcurrentModificationException();
+ }
+
+ // only call this holding l's lock
+ private void rangeCheck(int index) {
+ // assert Thread.holdsLock(l.lock);
+ if (index < 0 || index >= size)
+ throw new IndexOutOfBoundsException(outOfBounds(index, size));
+ }
+
+ public E set(int index, E element) {
+ synchronized (l.lock) {
+ rangeCheck(index);
+ checkForComodification();
+ E x = l.set(index+offset, element);
+ expectedArray = l.getArray();
+ return x;
+ }
+ }
+
+ public E get(int index) {
+ synchronized (l.lock) {
+ rangeCheck(index);
+ checkForComodification();
+ return l.get(index+offset);
+ }
+ }
+
+ public int size() {
+ synchronized (l.lock) {
+ checkForComodification();
+ return size;
+ }
+ }
+
+ public void add(int index, E element) {
+ synchronized (l.lock) {
+ checkForComodification();
+ if (index < 0 || index > size)
+ throw new IndexOutOfBoundsException
+ (outOfBounds(index, size));
+ l.add(index+offset, element);
+ expectedArray = l.getArray();
+ size++;
+ }
+ }
+
+ public void clear() {
+ synchronized (l.lock) {
+ checkForComodification();
+ l.removeRange(offset, offset+size);
+ expectedArray = l.getArray();
+ size = 0;
+ }
+ }
+
+ public E remove(int index) {
+ synchronized (l.lock) {
+ rangeCheck(index);
+ checkForComodification();
+ E result = l.remove(index+offset);
+ expectedArray = l.getArray();
+ size--;
+ return result;
+ }
+ }
+
+ public boolean remove(Object o) {
+ int index = indexOf(o);
+ if (index == -1)
+ return false;
+ remove(index);
+ return true;
+ }
+
+ public Iterator<E> iterator() {
+ synchronized (l.lock) {
+ checkForComodification();
+ return new COWSubListIterator<E>(l, 0, offset, size);
+ }
+ }
+
+ public ListIterator<E> listIterator(int index) {
+ synchronized (l.lock) {
+ checkForComodification();
+ if (index < 0 || index > size)
+ throw new IndexOutOfBoundsException
+ (outOfBounds(index, size));
+ return new COWSubListIterator<E>(l, index, offset, size);
+ }
+ }
+
+ public List<E> subList(int fromIndex, int toIndex) {
+ synchronized (l.lock) {
+ checkForComodification();
+ if (fromIndex < 0 || toIndex > size || fromIndex > toIndex)
+ throw new IndexOutOfBoundsException();
+ return new COWSubList<E>(l, fromIndex + offset,
+ toIndex + offset);
+ }
+ }
+
+ public void forEach(Consumer<? super E> action) {
+ if (action == null) throw new NullPointerException();
+ int lo = offset;
+ int hi = offset + size;
+ Object[] a = expectedArray;
+ if (l.getArray() != a)
+ throw new ConcurrentModificationException();
+ if (lo < 0 || hi > a.length)
+ throw new IndexOutOfBoundsException();
+ for (int i = lo; i < hi; ++i) {
+ @SuppressWarnings("unchecked") E e = (E) a[i];
+ action.accept(e);
+ }
+ }
+
+ public void replaceAll(UnaryOperator<E> operator) {
+ if (operator == null) throw new NullPointerException();
+ synchronized (l.lock) {
+ int lo = offset;
+ int hi = offset + size;
+ Object[] elements = expectedArray;
+ if (l.getArray() != elements)
+ throw new ConcurrentModificationException();
+ int len = elements.length;
+ if (lo < 0 || hi > len)
+ throw new IndexOutOfBoundsException();
+ Object[] newElements = Arrays.copyOf(elements, len);
+ for (int i = lo; i < hi; ++i) {
+ @SuppressWarnings("unchecked") E e = (E) elements[i];
+ newElements[i] = operator.apply(e);
+ }
+ l.setArray(expectedArray = newElements);
+ }
+ }
+
+ public void sort(Comparator<? super E> c) {
+ synchronized (l.lock) {
+ int lo = offset;
+ int hi = offset + size;
+ Object[] elements = expectedArray;
+ if (l.getArray() != elements)
+ throw new ConcurrentModificationException();
+ int len = elements.length;
+ if (lo < 0 || hi > len)
+ throw new IndexOutOfBoundsException();
+ Object[] newElements = Arrays.copyOf(elements, len);
+ @SuppressWarnings("unchecked") E[] es = (E[])newElements;
+ Arrays.sort(es, lo, hi, c);
+ l.setArray(expectedArray = newElements);
+ }
+ }
+
+ public boolean removeAll(Collection<?> c) {
+ if (c == null) throw new NullPointerException();
+ boolean removed = false;
+ synchronized (l.lock) {
+ int n = size;
+ if (n > 0) {
+ int lo = offset;
+ int hi = offset + n;
+ Object[] elements = expectedArray;
+ if (l.getArray() != elements)
+ throw new ConcurrentModificationException();
+ int len = elements.length;
+ if (lo < 0 || hi > len)
+ throw new IndexOutOfBoundsException();
+ int newSize = 0;
+ Object[] temp = new Object[n];
+ for (int i = lo; i < hi; ++i) {
+ Object element = elements[i];
+ if (!c.contains(element))
+ temp[newSize++] = element;
+ }
+ if (newSize != n) {
+ Object[] newElements = new Object[len - n + newSize];
+ System.arraycopy(elements, 0, newElements, 0, lo);
+ System.arraycopy(temp, 0, newElements, lo, newSize);
+ System.arraycopy(elements, hi, newElements,
+ lo + newSize, len - hi);
+ size = newSize;
+ removed = true;
+ l.setArray(expectedArray = newElements);
+ }
+ }
+ }
+ return removed;
+ }
+
+ public boolean retainAll(Collection<?> c) {
+ if (c == null) throw new NullPointerException();
+ boolean removed = false;
+ synchronized (l.lock) {
+ int n = size;
+ if (n > 0) {
+ int lo = offset;
+ int hi = offset + n;
+ Object[] elements = expectedArray;
+ if (l.getArray() != elements)
+ throw new ConcurrentModificationException();
+ int len = elements.length;
+ if (lo < 0 || hi > len)
+ throw new IndexOutOfBoundsException();
+ int newSize = 0;
+ Object[] temp = new Object[n];
+ for (int i = lo; i < hi; ++i) {
+ Object element = elements[i];
+ if (c.contains(element))
+ temp[newSize++] = element;
+ }
+ if (newSize != n) {
+ Object[] newElements = new Object[len - n + newSize];
+ System.arraycopy(elements, 0, newElements, 0, lo);
+ System.arraycopy(temp, 0, newElements, lo, newSize);
+ System.arraycopy(elements, hi, newElements,
+ lo + newSize, len - hi);
+ size = newSize;
+ removed = true;
+ l.setArray(expectedArray = newElements);
+ }
+ }
+ }
+ return removed;
+ }
+
+ public boolean removeIf(Predicate<? super E> filter) {
+ if (filter == null) throw new NullPointerException();
+ boolean removed = false;
+ synchronized (l.lock) {
+ int n = size;
+ if (n > 0) {
+ int lo = offset;
+ int hi = offset + n;
+ Object[] elements = expectedArray;
+ if (l.getArray() != elements)
+ throw new ConcurrentModificationException();
+ int len = elements.length;
+ if (lo < 0 || hi > len)
+ throw new IndexOutOfBoundsException();
+ int newSize = 0;
+ Object[] temp = new Object[n];
+ for (int i = lo; i < hi; ++i) {
+ @SuppressWarnings("unchecked") E e = (E) elements[i];
+ if (!filter.test(e))
+ temp[newSize++] = e;
+ }
+ if (newSize != n) {
+ Object[] newElements = new Object[len - n + newSize];
+ System.arraycopy(elements, 0, newElements, 0, lo);
+ System.arraycopy(temp, 0, newElements, lo, newSize);
+ System.arraycopy(elements, hi, newElements,
+ lo + newSize, len - hi);
+ size = newSize;
+ removed = true;
+ l.setArray(expectedArray = newElements);
+ }
+ }
+ }
+ return removed;
+ }
+
+ public Spliterator<E> spliterator() {
+ int lo = offset;
+ int hi = offset + size;
+ Object[] a = expectedArray;
+ if (l.getArray() != a)
+ throw new ConcurrentModificationException();
+ if (lo < 0 || hi > a.length)
+ throw new IndexOutOfBoundsException();
+ return Spliterators.spliterator
+ (a, lo, hi, Spliterator.IMMUTABLE | Spliterator.ORDERED);
+ }
+
+ }
+
+ private static class COWSubListIterator<E> implements ListIterator<E> {
+ private final ListIterator<E> it;
+ private final int offset;
+ private final int size;
+
+ COWSubListIterator(List<E> l, int index, int offset, int size) {
+ this.offset = offset;
+ this.size = size;
+ it = l.listIterator(index+offset);
+ }
+
+ public boolean hasNext() {
+ return nextIndex() < size;
+ }
+
+ public E next() {
+ if (hasNext())
+ return it.next();
+ else
+ throw new NoSuchElementException();
+ }
+
+ public boolean hasPrevious() {
+ return previousIndex() >= 0;
+ }
+
+ public E previous() {
+ if (hasPrevious())
+ return it.previous();
+ else
+ throw new NoSuchElementException();
+ }
+
+ public int nextIndex() {
+ return it.nextIndex() - offset;
+ }
+
+ public int previousIndex() {
+ return it.previousIndex() - offset;
}
public void remove() {
throw new UnsupportedOperationException();
}
- public void set(E object) {
+ public void set(E e) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void add(E e) {
throw new UnsupportedOperationException();
}
@Override
+ @SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> action) {
- java.util.Objects.requireNonNull(action);
- Object[] elements = snapshot;
- for (int i = index; i < to; i++) {
- @SuppressWarnings("unchecked") E e = (E) elements[i];
- action.accept(e);
+ Objects.requireNonNull(action);
+ while (nextIndex() < size) {
+ action.accept(it.next());
}
- index = to;
}
}
- private void writeObject(ObjectOutputStream out) throws IOException {
- Object[] snapshot = elements;
- out.defaultWriteObject();
- out.writeInt(snapshot.length);
- for (Object o : snapshot) {
- out.writeObject(o);
- }
+ // Support for resetting lock while deserializing
+ private void resetLock() {
+ U.putObjectVolatile(this, LOCK, new Object());
}
-
- private synchronized void readObject(ObjectInputStream in)
- throws IOException, ClassNotFoundException {
- in.defaultReadObject();
- Object[] snapshot = new Object[in.readInt()];
- for (int i = 0; i < snapshot.length; i++) {
- snapshot[i] = in.readObject();
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long LOCK;
+ static {
+ try {
+ LOCK = U.objectFieldOffset
+ (CopyOnWriteArrayList.class.getDeclaredField("lock"));
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
}
- elements = snapshot;
}
}
diff --git a/luni/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java b/luni/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java
index 347ed14..0cf8558 100644
--- a/luni/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java
+++ b/luni/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java
@@ -6,10 +6,19 @@
package java.util.concurrent;
-import java.util.*;
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Objects;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
// BEGIN android-note
// removed link to collections framework docs
+// fixed framework docs link to "Collection#optional"
// END android-note
/**
@@ -35,12 +44,12 @@
* copy-on-write set to maintain a set of Handler objects that
* perform some action upon state updates.
*
- * <pre> {@code
+ * <pre> {@code
* class Handler { void handle(); ... }
*
* class X {
* private final CopyOnWriteArraySet<Handler> handlers
- * = new CopyOnWriteArraySet<Handler>();
+ * = new CopyOnWriteArraySet<>();
* public void addHandler(Handler h) { handlers.add(h); }
*
* private long internalState;
@@ -56,7 +65,7 @@
* @see CopyOnWriteArrayList
* @since 1.5
* @author Doug Lea
- * @param <E> the type of elements held in this collection
+ * @param <E> the type of elements held in this set
*/
public class CopyOnWriteArraySet<E> extends AbstractSet<E>
implements java.io.Serializable {
@@ -79,8 +88,15 @@
* @throws NullPointerException if the specified collection is null
*/
public CopyOnWriteArraySet(Collection<? extends E> c) {
- al = new CopyOnWriteArrayList<E>();
- al.addAllAbsent(c);
+ if (c.getClass() == CopyOnWriteArraySet.class) {
+ @SuppressWarnings("unchecked") CopyOnWriteArraySet<E> cc =
+ (CopyOnWriteArraySet<E>)c;
+ al = new CopyOnWriteArrayList<E>(cc.al);
+ }
+ else {
+ al = new CopyOnWriteArrayList<E>();
+ al.addAllAbsent(c);
+ }
}
/**
@@ -104,8 +120,7 @@
/**
* Returns {@code true} if this set contains the specified element.
* More formally, returns {@code true} if and only if this set
- * contains an element {@code e} such that
- * <tt>(o==null ? e==null : o.equals(e))</tt>.
+ * contains an element {@code e} such that {@code Objects.equals(o, e)}.
*
* @param o element whose presence in this set is to be tested
* @return {@code true} if this set contains the specified element
@@ -161,7 +176,7 @@
* The following code can be used to dump the set into a newly allocated
* array of {@code String}:
*
- * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
@@ -190,11 +205,10 @@
/**
* Removes the specified element from this set if it is present.
* More formally, removes an element {@code e} such that
- * <tt>(o==null ? e==null : o.equals(e))</tt>,
- * if this set contains such an element. Returns {@code true} if
- * this set contained the element (or equivalently, if this set
- * changed as a result of the call). (This set will not contain the
- * element once the call returns.)
+ * {@code Objects.equals(o, e)}, if this set contains such an element.
+ * Returns {@code true} if this set contained the element (or
+ * equivalently, if this set changed as a result of the call).
+ * (This set will not contain the element once the call returns.)
*
* @param o object to be removed from this set, if present
* @return {@code true} if this set contained the specified element
@@ -207,7 +221,7 @@
* Adds the specified element to this set if it is not already present.
* More formally, adds the specified element {@code e} to this set if
* the set contains no element {@code e2} such that
- * <tt>(e==null ? e2==null : e.equals(e2))</tt>.
+ * {@code Objects.equals(e, e2)}.
* If this set already contains the element, the call leaves the set
* unchanged and returns {@code false}.
*
@@ -231,7 +245,44 @@
* @see #contains(Object)
*/
public boolean containsAll(Collection<?> c) {
- return al.containsAll(c);
+ return (c instanceof Set)
+ ? compareSets(al.getArray(), (Set<?>) c) >= 0
+ : al.containsAll(c);
+ }
+
+ /**
+ * Tells whether the objects in snapshot (regarded as a set) are a
+ * superset of the given set.
+ *
+ * @return -1 if snapshot is not a superset, 0 if the two sets
+ * contain precisely the same elements, and 1 if snapshot is a
+ * proper superset of the given set
+ */
+ private static int compareSets(Object[] snapshot, Set<?> set) {
+ // Uses O(n^2) algorithm, that is only appropriate for small
+ // sets, which CopyOnWriteArraySets should be.
+ //
+ // Optimize up to O(n) if the two sets share a long common prefix,
+ // as might happen if one set was created as a copy of the other set.
+
+ final int len = snapshot.length;
+ // Mark matched elements to avoid re-checking
+ final boolean[] matched = new boolean[len];
+
+ // j is the largest int with matched[i] true for { i | 0 <= i < j }
+ int j = 0;
+ outer: for (Object x : set) {
+ for (int i = j; i < len; i++) {
+ if (!matched[i] && Objects.equals(x, snapshot[i])) {
+ matched[i] = true;
+ if (i == j)
+ do { j++; } while (j < len && matched[j]);
+ continue outer;
+ }
+ }
+ return -1;
+ }
+ return (j == len) ? 0 : 1;
}
/**
@@ -260,9 +311,11 @@
* @param c collection containing elements to be removed from this set
* @return {@code true} if this set changed as a result of the call
* @throws ClassCastException if the class of an element of this set
- * is incompatible with the specified collection (optional)
+ * is incompatible with the specified collection
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if this set contains a null element and the
- * specified collection does not permit null elements (optional),
+ * specified collection does not permit null elements
+ * (<a href="../Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see #remove(Object)
*/
@@ -281,9 +334,11 @@
* @param c collection containing elements to be retained in this set
* @return {@code true} if this set changed as a result of the call
* @throws ClassCastException if the class of an element of this set
- * is incompatible with the specified collection (optional)
+ * is incompatible with the specified collection
+ * (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if this set contains a null element and the
- * specified collection does not permit null elements (optional),
+ * specified collection does not permit null elements
+ * (<a href="../Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see #remove(Object)
*/
@@ -310,54 +365,49 @@
* Compares the specified object with this set for equality.
* Returns {@code true} if the specified object is the same object
* as this object, or if it is also a {@link Set} and the elements
- * returned by an {@linkplain List#iterator() iterator} over the
+ * returned by an {@linkplain Set#iterator() iterator} over the
* specified set are the same as the elements returned by an
* iterator over this set. More formally, the two iterators are
* considered to return the same elements if they return the same
* number of elements and for every element {@code e1} returned by
* the iterator over the specified set, there is an element
* {@code e2} returned by the iterator over this set such that
- * {@code (e1==null ? e2==null : e1.equals(e2))}.
+ * {@code Objects.equals(e1, e2)}.
*
* @param o object to be compared for equality with this set
* @return {@code true} if the specified object is equal to this set
*/
public boolean equals(Object o) {
- if (o == this)
- return true;
- if (!(o instanceof Set))
- return false;
- Set<?> set = (Set<?>)(o);
- Iterator<?> it = set.iterator();
+ return (o == this)
+ || ((o instanceof Set)
+ && compareSets(al.getArray(), (Set<?>) o) == 0);
+ }
- // Uses O(n^2) algorithm that is only appropriate
- // for small sets, which CopyOnWriteArraySets should be.
+ public boolean removeIf(Predicate<? super E> filter) {
+ return al.removeIf(filter);
+ }
- // Use a single snapshot of underlying array
- Object[] elements = al.getArray();
- int len = elements.length;
- // Mark matched elements to avoid re-checking
- boolean[] matched = new boolean[len];
- int k = 0;
- outer: while (it.hasNext()) {
- if (++k > len)
- return false;
- Object x = it.next();
- for (int i = 0; i < len; ++i) {
- if (!matched[i] && eq(x, elements[i])) {
- matched[i] = true;
- continue outer;
- }
- }
- return false;
- }
- return k == len;
+ public void forEach(Consumer<? super E> action) {
+ al.forEach(action);
}
/**
- * Tests for equality, coping with nulls.
+ * Returns a {@link Spliterator} over the elements in this set in the order
+ * in which these elements were added.
+ *
+ * <p>The {@code Spliterator} reports {@link Spliterator#IMMUTABLE},
+ * {@link Spliterator#DISTINCT}, {@link Spliterator#SIZED}, and
+ * {@link Spliterator#SUBSIZED}.
+ *
+ * <p>The spliterator provides a snapshot of the state of the set
+ * when the spliterator was constructed. No synchronization is needed while
+ * operating on the spliterator.
+ *
+ * @return a {@code Spliterator} over the elements in this set
+ * @since 1.8
*/
- private static boolean eq(Object o1, Object o2) {
- return (o1 == null) ? o2 == null : o1.equals(o2);
+ public Spliterator<E> spliterator() {
+ return Spliterators.spliterator
+ (al.getArray(), Spliterator.IMMUTABLE | Spliterator.DISTINCT);
}
}
diff --git a/luni/src/main/java/java/util/concurrent/CountDownLatch.java b/luni/src/main/java/java/util/concurrent/CountDownLatch.java
index 77093f7..680ea16 100644
--- a/luni/src/main/java/java/util/concurrent/CountDownLatch.java
+++ b/luni/src/main/java/java/util/concurrent/CountDownLatch.java
@@ -44,7 +44,7 @@
* until all workers have completed.
* </ul>
*
- * <pre> {@code
+ * <pre> {@code
* class Driver { // ...
* void main() throws InterruptedException {
* CountDownLatch startSignal = new CountDownLatch(1);
@@ -85,7 +85,7 @@
* will be able to pass through await. (When threads must repeatedly
* count down in this way, instead use a {@link CyclicBarrier}.)
*
- * <pre> {@code
+ * <pre> {@code
* class Driver2 { // ...
* void main() throws InterruptedException {
* CountDownLatch doneSignal = new CountDownLatch(N);
@@ -151,7 +151,7 @@
int c = getState();
if (c == 0)
return false;
- int nextc = c-1;
+ int nextc = c - 1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
diff --git a/luni/src/main/java/java/util/concurrent/CountedCompleter.java b/luni/src/main/java/java/util/concurrent/CountedCompleter.java
index b868037..9c8b1b2 100644
--- a/luni/src/main/java/java/util/concurrent/CountedCompleter.java
+++ b/luni/src/main/java/java/util/concurrent/CountedCompleter.java
@@ -13,7 +13,7 @@
* presence of subtask stalls and blockage than are other forms of
* ForkJoinTasks, but are less intuitive to program. Uses of
* CountedCompleter are similar to those of other completion based
- * components (such as {@link java.nio.channels.CompletionHandler})
+ * components
* except that multiple <em>pending</em> completions may be necessary
* to trigger the completion action {@link #onCompletion(CountedCompleter)},
* not just one.
@@ -139,7 +139,8 @@
* {@code tryComplete}) the pending count is set to one:
*
* <pre> {@code
- * class ForEach<E> ...
+ * class ForEach<E> ... {
+ * ...
* public void compute() { // version 2
* if (hi - lo >= 2) {
* int mid = (lo + hi) >>> 1;
@@ -153,18 +154,19 @@
* tryComplete();
* }
* }
- * }</pre>
+ * }}</pre>
*
- * As a further improvement, notice that the left task need not even exist.
+ * As a further optimization, notice that the left task need not even exist.
* Instead of creating a new one, we can iterate using the original task,
* and add a pending count for each fork. Additionally, because no task
* in this tree implements an {@link #onCompletion(CountedCompleter)} method,
* {@code tryComplete()} can be replaced with {@link #propagateCompletion}.
*
* <pre> {@code
- * class ForEach<E> ...
+ * class ForEach<E> ... {
+ * ...
* public void compute() { // version 3
- * int l = lo, h = hi;
+ * int l = lo, h = hi;
* while (h - l >= 2) {
* int mid = (l + h) >>> 1;
* addToPendingCount(1);
@@ -175,9 +177,9 @@
* op.apply(array[l]);
* propagateCompletion();
* }
- * }</pre>
+ * }}</pre>
*
- * Additional improvements of such classes might entail precomputing
+ * Additional optimizations of such classes might entail precomputing
* pending counts so that they can be established in constructors,
* specializing classes for leaf steps, subdividing by say, four,
* instead of two per iteration, and using an adaptive threshold
@@ -204,7 +206,7 @@
* }
* public E getRawResult() { return result.get(); }
* public void compute() { // similar to ForEach version 3
- * int l = lo, h = hi;
+ * int l = lo, h = hi;
* while (result.get() == null && h >= l) {
* if (h - l >= 2) {
* int mid = (l + h) >>> 1;
@@ -229,9 +231,9 @@
* }}</pre>
*
* In this example, as well as others in which tasks have no other
- * effects except to compareAndSet a common result, the trailing
- * unconditional invocation of {@code tryComplete} could be made
- * conditional ({@code if (result.get() == null) tryComplete();})
+ * effects except to {@code compareAndSet} a common result, the
+ * trailing unconditional invocation of {@code tryComplete} could be
+ * made conditional ({@code if (result.get() == null) tryComplete();})
* because no further bookkeeping is required to manage completions
* once the root task completes.
*
@@ -334,7 +336,7 @@
* this.next = next;
* }
* public void compute() {
- * int l = lo, h = hi;
+ * int l = lo, h = hi;
* while (h - l >= 2) {
* int mid = (l + h) >>> 1;
* addToPendingCount(1);
@@ -345,7 +347,7 @@
* result = mapper.apply(array[l]);
* // process completions by reducing along and advancing subtask links
* for (CountedCompleter<?> c = firstComplete(); c != null; c = c.nextComplete()) {
- * for (MapReducer t = (MapReducer)c, s = t.forks; s != null; s = t.forks = s.next)
+ * for (MapReducer t = (MapReducer)c, s = t.forks; s != null; s = t.forks = s.next)
* t.result = reducer.apply(t.result, s.result);
* }
* }
@@ -373,11 +375,9 @@
* // sample use:
* PacketSender p = new PacketSender();
* new HeaderBuilder(p, ...).fork();
- * new BodyBuilder(p, ...).fork();
- * }</pre>
+ * new BodyBuilder(p, ...).fork();}</pre>
*
* @since 1.8
- * @hide
* @author Doug Lea
*/
public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
@@ -495,8 +495,7 @@
* @param delta the value to add
*/
public final void addToPendingCount(int delta) {
- int c;
- do {} while (!U.compareAndSwapInt(this, PENDING, c = pending, c+delta));
+ U.getAndAddInt(this, PENDING, delta);
}
/**
@@ -596,7 +595,7 @@
* any one (versus all) of several subtask results are obtained.
* However, in the common (and recommended) case in which {@code
* setRawResult} is not overridden, this effect can be obtained
- * more simply using {@code quietlyCompleteRoot();}.
+ * more simply using {@link #quietlyCompleteRoot()}.
*
* @param rawResult the raw result
*/
@@ -611,9 +610,9 @@
/**
* If this task's pending count is zero, returns this task;
- * otherwise decrements its pending count and returns {@code
- * null}. This method is designed to be used with {@link
- * #nextComplete} in completion traversal loops.
+ * otherwise decrements its pending count and returns {@code null}.
+ * This method is designed to be used with {@link #nextComplete} in
+ * completion traversal loops.
*
* @return this task, if pending count was zero, else {@code null}
*/
@@ -667,6 +666,26 @@
}
/**
+ * If this task has not completed, attempts to process at most the
+ * given number of other unprocessed tasks for which this task is
+ * on the completion path, if any are known to exist.
+ *
+ * @param maxTasks the maximum number of tasks to process. If
+ * less than or equal to zero, then no tasks are
+ * processed.
+ */
+ public final void helpComplete(int maxTasks) {
+ Thread t; ForkJoinWorkerThread wt;
+ if (maxTasks > 0 && status >= 0) {
+ if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
+ (wt = (ForkJoinWorkerThread)t).pool.
+ helpComplete(wt.workQueue, this, maxTasks);
+ else
+ ForkJoinPool.common.externalHelpComplete(this, maxTasks);
+ }
+ }
+
+ /**
* Supports ForkJoinTask exception propagation.
*/
void internalPropagateException(Throwable ex) {
@@ -706,14 +725,13 @@
protected void setRawResult(T t) { }
// Unsafe mechanics
- private static final sun.misc.Unsafe U;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
private static final long PENDING;
static {
try {
- U = sun.misc.Unsafe.getUnsafe();
PENDING = U.objectFieldOffset
(CountedCompleter.class.getDeclaredField("pending"));
- } catch (Exception e) {
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
diff --git a/luni/src/main/java/java/util/concurrent/CyclicBarrier.java b/luni/src/main/java/java/util/concurrent/CyclicBarrier.java
index d698501..7219c93 100644
--- a/luni/src/main/java/java/util/concurrent/CyclicBarrier.java
+++ b/luni/src/main/java/java/util/concurrent/CyclicBarrier.java
@@ -23,10 +23,10 @@
* This <em>barrier action</em> is useful
* for updating shared-state before any of the parties continue.
*
- * <p><b>Sample usage:</b> Here is an example of
- * using a barrier in a parallel decomposition design:
+ * <p><b>Sample usage:</b> Here is an example of using a barrier in a
+ * parallel decomposition design:
*
- * <pre> {@code
+ * <pre> {@code
* class Solver {
* final int N;
* final float[][] data;
@@ -53,16 +53,20 @@
* public Solver(float[][] matrix) {
* data = matrix;
* N = matrix.length;
- * barrier = new CyclicBarrier(N,
- * new Runnable() {
- * public void run() {
- * mergeRows(...);
- * }
- * });
- * for (int i = 0; i < N; ++i)
- * new Thread(new Worker(i)).start();
+ * Runnable barrierAction =
+ * new Runnable() { public void run() { mergeRows(...); }};
+ * barrier = new CyclicBarrier(N, barrierAction);
*
- * waitUntilDone();
+ * List<Thread> threads = new ArrayList<>(N);
+ * for (int i = 0; i < N; i++) {
+ * Thread thread = new Thread(new Worker(i));
+ * threads.add(thread);
+ * thread.start();
+ * }
+ *
+ * // wait until done
+ * for (Thread thread : threads)
+ * thread.join();
* }
* }}</pre>
*
@@ -79,7 +83,7 @@
* {@link #await} returns the arrival index of that thread at the barrier.
* You can then choose which thread should execute the barrier action, for
* example:
- * <pre> {@code
+ * <pre> {@code
* if (barrier.await() == 0) {
* // log the completion of this iteration
* }}</pre>
@@ -117,7 +121,7 @@
* but no subsequent reset.
*/
private static class Generation {
- boolean broken = false;
+ boolean broken; // initially false
}
/** The lock for guarding barrier entry */
@@ -388,7 +392,8 @@
* to arrive and zero indicates the last to arrive
* @throws InterruptedException if the current thread was interrupted
* while waiting
- * @throws TimeoutException if the specified timeout elapses
+ * @throws TimeoutException if the specified timeout elapses.
+ * In this case the barrier will be broken.
* @throws BrokenBarrierException if <em>another</em> thread was
* interrupted or timed out while the current thread was
* waiting, or the barrier was reset, or the barrier was broken
diff --git a/luni/src/main/java/java/util/concurrent/DelayQueue.java b/luni/src/main/java/java/util/concurrent/DelayQueue.java
index e4a715e..d100a9c 100644
--- a/luni/src/main/java/java/util/concurrent/DelayQueue.java
+++ b/luni/src/main/java/java/util/concurrent/DelayQueue.java
@@ -7,9 +7,14 @@
package java.util.concurrent;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+import java.util.AbstractQueue;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.PriorityQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
-import java.util.*;
// BEGIN android-note
// removed link to collections framework docs
@@ -37,7 +42,7 @@
*
* @since 1.5
* @author Doug Lea
- * @param <E> the type of elements held in this collection
+ * @param <E> the type of elements held in this queue
*/
public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
implements BlockingQueue<E> {
@@ -157,10 +162,9 @@
lock.lock();
try {
E first = q.peek();
- if (first == null || first.getDelay(NANOSECONDS) > 0)
- return null;
- else
- return q.poll();
+ return (first == null || first.getDelay(NANOSECONDS) > 0)
+ ? null
+ : q.poll();
} finally {
lock.unlock();
}
@@ -183,7 +187,7 @@
available.await();
else {
long delay = first.getDelay(NANOSECONDS);
- if (delay <= 0)
+ if (delay <= 0L)
return q.poll();
first = null; // don't retain ref while waiting
if (leader != null)
@@ -225,15 +229,15 @@
for (;;) {
E first = q.peek();
if (first == null) {
- if (nanos <= 0)
+ if (nanos <= 0L)
return null;
else
nanos = available.awaitNanos(nanos);
} else {
long delay = first.getDelay(NANOSECONDS);
- if (delay <= 0)
+ if (delay <= 0L)
return q.poll();
- if (nanos <= 0)
+ if (nanos <= 0L)
return null;
first = null; // don't retain ref while waiting
if (nanos < delay || leader != null)
@@ -462,7 +466,7 @@
}
/**
- * Identity-based version for use in Itr.remove
+ * Identity-based version for use in Itr.remove.
*/
void removeEQ(Object o) {
final ReentrantLock lock = this.lock;
@@ -484,12 +488,8 @@
* unexpired) in this queue. The iterator does not return the
* elements in any particular order.
*
- * <p>The returned iterator is a "weakly consistent" iterator that
- * will never throw {@link java.util.ConcurrentModificationException
- * ConcurrentModificationException}, and guarantees to traverse
- * elements as they existed upon construction of the iterator, and
- * may (but is not guaranteed to) reflect any modifications
- * subsequent to construction.
+ * <p>The returned iterator is
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
*
* @return an iterator over the elements in this queue
*/
diff --git a/luni/src/main/java/java/util/concurrent/Exchanger.java b/luni/src/main/java/java/util/concurrent/Exchanger.java
index 60871b4..5f4c534 100644
--- a/luni/src/main/java/java/util/concurrent/Exchanger.java
+++ b/luni/src/main/java/java/util/concurrent/Exchanger.java
@@ -21,9 +21,9 @@
* to swap buffers between threads so that the thread filling the
* buffer gets a freshly emptied one when it needs it, handing off the
* filled one to the thread emptying the buffer.
- * <pre> {@code
+ * <pre> {@code
* class FillAndEmpty {
- * Exchanger<DataBuffer> exchanger = new Exchanger<DataBuffer>();
+ * Exchanger<DataBuffer> exchanger = new Exchanger<>();
* DataBuffer initialEmptyBuffer = ... a made-up type
* DataBuffer initialFullBuffer = ...
*
@@ -125,9 +125,10 @@
* writing, there is no way to determine cacheline size, we define
* a value that is enough for common platforms. Additionally,
* extra care elsewhere is taken to avoid other false/unintended
- * sharing and to enhance locality, including adding padding to
- * Nodes, embedding "bound" as an Exchanger field, and reworking
- * some park/unpark mechanics compared to LockSupport versions.
+ * sharing and to enhance locality, including adding padding (via
+ * @Contended) to Nodes, embedding "bound" as an Exchanger field,
+ * and reworking some park/unpark mechanics compared to
+ * LockSupport versions.
*
* The arena starts out with only one used slot. We expand the
* effective arena size by tracking collisions; i.e., failed CASes
@@ -274,8 +275,9 @@
/**
* Nodes hold partially exchanged data, plus other per-thread
- * bookkeeping.
+ * bookkeeping. Padded via @Contended to reduce memory contention.
*/
+ //@jdk.internal.vm.annotation.Contended // android-removed
static final class Node {
int index; // Arena index
int bound; // Last recorded value of Exchanger.bound
@@ -284,10 +286,6 @@
Object item; // This thread's current item
volatile Object match; // Item provided by releasing thread
volatile Thread parked; // Set to this thread when parked, else null
-
- // Padding to ameliorate unfortunate memory placements
- Object p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, pa, pb, pc, pd, pe, pf;
- Object q0, q1, q2, q3, q4, q5, q6, q7, q8, q9, qa, qb, qc, qd, qe, qf;
}
/** The corresponding thread local class */
@@ -296,7 +294,7 @@
}
/**
- * Per-thread state
+ * Per-thread state.
*/
private final Participant participant;
@@ -598,37 +596,33 @@
}
// Unsafe mechanics
- private static final sun.misc.Unsafe U;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
private static final long BOUND;
private static final long SLOT;
private static final long MATCH;
private static final long BLOCKER;
private static final int ABASE;
static {
- int s;
try {
- U = sun.misc.Unsafe.getUnsafe();
- Class<?> ek = Exchanger.class;
- Class<?> nk = Node.class;
- Class<?> ak = Node[].class;
- Class<?> tk = Thread.class;
BOUND = U.objectFieldOffset
- (ek.getDeclaredField("bound"));
+ (Exchanger.class.getDeclaredField("bound"));
SLOT = U.objectFieldOffset
- (ek.getDeclaredField("slot"));
- MATCH = U.objectFieldOffset
- (nk.getDeclaredField("match"));
- BLOCKER = U.objectFieldOffset
- (tk.getDeclaredField("parkBlocker"));
- s = U.arrayIndexScale(ak);
- // ABASE absorbs padding in front of element 0
- ABASE = U.arrayBaseOffset(ak) + (1 << ASHIFT);
+ (Exchanger.class.getDeclaredField("slot"));
- } catch (Exception e) {
+ MATCH = U.objectFieldOffset
+ (Node.class.getDeclaredField("match"));
+
+ BLOCKER = U.objectFieldOffset
+ (Thread.class.getDeclaredField("parkBlocker"));
+
+ int scale = U.arrayIndexScale(Node[].class);
+ if ((scale & (scale - 1)) != 0 || scale > (1 << ASHIFT))
+ throw new Error("Unsupported array scale");
+ // ABASE absorbs padding in front of element 0
+ ABASE = U.arrayBaseOffset(Node[].class) + (1 << ASHIFT);
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
- if ((s & (s-1)) != 0 || s > (1 << ASHIFT))
- throw new Error("Unsupported array scale");
}
}
diff --git a/luni/src/main/java/java/util/concurrent/Executor.java b/luni/src/main/java/java/util/concurrent/Executor.java
index 095ebfc..9dd3efb 100644
--- a/luni/src/main/java/java/util/concurrent/Executor.java
+++ b/luni/src/main/java/java/util/concurrent/Executor.java
@@ -15,30 +15,28 @@
* invoking {@code new Thread(new RunnableTask()).start()} for each
* of a set of tasks, you might use:
*
- * <pre>
- * Executor executor = <em>anExecutor</em>;
+ * <pre> {@code
+ * Executor executor = anExecutor();
* executor.execute(new RunnableTask1());
* executor.execute(new RunnableTask2());
- * ...
- * </pre>
+ * ...}</pre>
*
- * However, the {@code Executor} interface does not strictly
- * require that execution be asynchronous. In the simplest case, an
- * executor can run the submitted task immediately in the caller's
- * thread:
+ * However, the {@code Executor} interface does not strictly require
+ * that execution be asynchronous. In the simplest case, an executor
+ * can run the submitted task immediately in the caller's thread:
*
- * <pre> {@code
+ * <pre> {@code
* class DirectExecutor implements Executor {
* public void execute(Runnable r) {
* r.run();
* }
* }}</pre>
*
- * More typically, tasks are executed in some thread other
- * than the caller's thread. The executor below spawns a new thread
- * for each task.
+ * More typically, tasks are executed in some thread other than the
+ * caller's thread. The executor below spawns a new thread for each
+ * task.
*
- * <pre> {@code
+ * <pre> {@code
* class ThreadPerTaskExecutor implements Executor {
* public void execute(Runnable r) {
* new Thread(r).start();
@@ -50,7 +48,7 @@
* serializes the submission of tasks to a second executor,
* illustrating a composite executor.
*
- * <pre> {@code
+ * <pre> {@code
* class SerialExecutor implements Executor {
* final Queue<Runnable> tasks = new ArrayDeque<>();
* final Executor executor;
diff --git a/luni/src/main/java/java/util/concurrent/ExecutorCompletionService.java b/luni/src/main/java/java/util/concurrent/ExecutorCompletionService.java
index 9514246..cea0384 100644
--- a/luni/src/main/java/java/util/concurrent/ExecutorCompletionService.java
+++ b/luni/src/main/java/java/util/concurrent/ExecutorCompletionService.java
@@ -27,16 +27,16 @@
* void solve(Executor e,
* Collection<Callable<Result>> solvers)
* throws InterruptedException, ExecutionException {
- * CompletionService<Result> ecs
- * = new ExecutorCompletionService<Result>(e);
- * for (Callable<Result> s : solvers)
- * ecs.submit(s);
- * int n = solvers.size();
- * for (int i = 0; i < n; ++i) {
- * Result r = ecs.take().get();
- * if (r != null)
- * use(r);
- * }
+ * CompletionService<Result> ecs
+ * = new ExecutorCompletionService<Result>(e);
+ * for (Callable<Result> s : solvers)
+ * ecs.submit(s);
+ * int n = solvers.size();
+ * for (int i = 0; i < n; ++i) {
+ * Result r = ecs.take().get();
+ * if (r != null)
+ * use(r);
+ * }
* }}</pre>
*
* Suppose instead that you would like to use the first non-null result
@@ -47,32 +47,31 @@
* void solve(Executor e,
* Collection<Callable<Result>> solvers)
* throws InterruptedException {
- * CompletionService<Result> ecs
- * = new ExecutorCompletionService<Result>(e);
- * int n = solvers.size();
- * List<Future<Result>> futures
- * = new ArrayList<Future<Result>>(n);
- * Result result = null;
- * try {
- * for (Callable<Result> s : solvers)
- * futures.add(ecs.submit(s));
- * for (int i = 0; i < n; ++i) {
- * try {
- * Result r = ecs.take().get();
- * if (r != null) {
- * result = r;
- * break;
- * }
- * } catch (ExecutionException ignore) {}
+ * CompletionService<Result> ecs
+ * = new ExecutorCompletionService<Result>(e);
+ * int n = solvers.size();
+ * List<Future<Result>> futures = new ArrayList<>(n);
+ * Result result = null;
+ * try {
+ * for (Callable<Result> s : solvers)
+ * futures.add(ecs.submit(s));
+ * for (int i = 0; i < n; ++i) {
+ * try {
+ * Result r = ecs.take().get();
+ * if (r != null) {
+ * result = r;
+ * break;
* }
+ * } catch (ExecutionException ignore) {}
* }
- * finally {
- * for (Future<Result> f : futures)
- * f.cancel(true);
- * }
+ * }
+ * finally {
+ * for (Future<Result> f : futures)
+ * f.cancel(true);
+ * }
*
- * if (result != null)
- * use(result);
+ * if (result != null)
+ * use(result);
* }}</pre>
*/
public class ExecutorCompletionService<V> implements CompletionService<V> {
@@ -81,15 +80,18 @@
private final BlockingQueue<Future<V>> completionQueue;
/**
- * FutureTask extension to enqueue upon completion
+ * FutureTask extension to enqueue upon completion.
*/
- private class QueueingFuture extends FutureTask<Void> {
- QueueingFuture(RunnableFuture<V> task) {
+ private static class QueueingFuture<V> extends FutureTask<Void> {
+ QueueingFuture(RunnableFuture<V> task,
+ BlockingQueue<Future<V>> completionQueue) {
super(task, null);
this.task = task;
+ this.completionQueue = completionQueue;
}
- protected void done() { completionQueue.add(task); }
private final Future<V> task;
+ private final BlockingQueue<Future<V>> completionQueue;
+ protected void done() { completionQueue.add(task); }
}
private RunnableFuture<V> newTaskFor(Callable<V> task) {
@@ -149,14 +151,14 @@
public Future<V> submit(Callable<V> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task);
- executor.execute(new QueueingFuture(f));
+ executor.execute(new QueueingFuture<V>(f, completionQueue));
return f;
}
public Future<V> submit(Runnable task, V result) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task, result);
- executor.execute(new QueueingFuture(f));
+ executor.execute(new QueueingFuture<V>(f, completionQueue));
return f;
}
diff --git a/luni/src/main/java/java/util/concurrent/ExecutorService.java b/luni/src/main/java/java/util/concurrent/ExecutorService.java
index 58a2113..ce7b2c6 100644
--- a/luni/src/main/java/java/util/concurrent/ExecutorService.java
+++ b/luni/src/main/java/java/util/concurrent/ExecutorService.java
@@ -6,8 +6,8 @@
package java.util.concurrent;
-import java.util.List;
import java.util.Collection;
+import java.util.List;
// BEGIN android-note
// removed security manager docs
@@ -47,7 +47,7 @@
* pool service incoming requests. It uses the preconfigured {@link
* Executors#newFixedThreadPool} factory method:
*
- * <pre> {@code
+ * <pre> {@code
* class NetworkService implements Runnable {
* private final ServerSocket serverSocket;
* private final ExecutorService pool;
@@ -81,7 +81,7 @@
* first by calling {@code shutdown} to reject incoming tasks, and then
* calling {@code shutdownNow}, if necessary, to cancel any lingering tasks:
*
- * <pre> {@code
+ * <pre> {@code
* void shutdownAndAwaitTermination(ExecutorService pool) {
* pool.shutdown(); // Disable new tasks from being submitted
* try {
diff --git a/luni/src/main/java/java/util/concurrent/Executors.java b/luni/src/main/java/java/util/concurrent/Executors.java
index 2068fd7..3d49b82 100644
--- a/luni/src/main/java/java/util/concurrent/Executors.java
+++ b/luni/src/main/java/java/util/concurrent/Executors.java
@@ -6,17 +6,21 @@
package java.util.concurrent;
-import java.util.*;
-import java.util.concurrent.atomic.AtomicInteger;
import java.security.AccessControlContext;
+import java.security.AccessControlException;
import java.security.AccessController;
import java.security.PrivilegedAction;
-import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import sun.security.util.SecurityConstants;
// BEGIN android-note
// removed security manager docs
// END android-note
+
/**
* Factory and utility methods for {@link Executor}, {@link
* ExecutorService}, {@link ScheduledExecutorService}, {@link
@@ -24,18 +28,18 @@
* package. This class supports the following kinds of methods:
*
* <ul>
- * <li> Methods that create and return an {@link ExecutorService}
- * set up with commonly useful configuration settings.
- * <li> Methods that create and return a {@link ScheduledExecutorService}
- * set up with commonly useful configuration settings.
- * <li> Methods that create and return a "wrapped" ExecutorService, that
- * disables reconfiguration by making implementation-specific methods
- * inaccessible.
- * <li> Methods that create and return a {@link ThreadFactory}
- * that sets newly created threads to a known state.
- * <li> Methods that create and return a {@link Callable}
- * out of other closure-like forms, so they can be used
- * in execution methods requiring {@code Callable}.
+ * <li>Methods that create and return an {@link ExecutorService}
+ * set up with commonly useful configuration settings.
+ * <li>Methods that create and return a {@link ScheduledExecutorService}
+ * set up with commonly useful configuration settings.
+ * <li>Methods that create and return a "wrapped" ExecutorService, that
+ * disables reconfiguration by making implementation-specific methods
+ * inaccessible.
+ * <li>Methods that create and return a {@link ThreadFactory}
+ * that sets newly created threads to a known state.
+ * <li>Methods that create and return a {@link Callable}
+ * out of other closure-like forms, so they can be used
+ * in execution methods requiring {@code Callable}.
* </ul>
*
* @since 1.5
@@ -78,7 +82,6 @@
* @return the newly created thread pool
* @throws IllegalArgumentException if {@code parallelism <= 0}
* @since 1.8
- * @hide
*/
public static ExecutorService newWorkStealingPool(int parallelism) {
return new ForkJoinPool
@@ -88,12 +91,13 @@
}
/**
- * Creates a work-stealing thread pool using all
- * {@link Runtime#availableProcessors available processors}
+ * Creates a work-stealing thread pool using the number of
+ * {@linkplain Runtime#availableProcessors available processors}
* as its target parallelism level.
+ *
* @return the newly created thread pool
+ * @see #newWorkStealingPool(int)
* @since 1.8
- * @hide
*/
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
@@ -411,11 +415,11 @@
// Non-public classes supporting the public methods
/**
- * A callable that runs given task and returns given result
+ * A callable that runs given task and returns given result.
*/
- static final class RunnableAdapter<T> implements Callable<T> {
- final Runnable task;
- final T result;
+ private static final class RunnableAdapter<T> implements Callable<T> {
+ private final Runnable task;
+ private final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
@@ -427,11 +431,11 @@
}
/**
- * A callable that runs under established access control settings
+ * A callable that runs under established access control settings.
*/
- static final class PrivilegedCallable<T> implements Callable<T> {
- private final Callable<T> task;
- private final AccessControlContext acc;
+ private static final class PrivilegedCallable<T> implements Callable<T> {
+ final Callable<T> task;
+ final AccessControlContext acc;
PrivilegedCallable(Callable<T> task) {
this.task = task;
@@ -454,12 +458,13 @@
/**
* A callable that runs under established access control settings and
- * current ClassLoader
+ * current ClassLoader.
*/
- static final class PrivilegedCallableUsingCurrentClassLoader<T> implements Callable<T> {
- private final Callable<T> task;
- private final AccessControlContext acc;
- private final ClassLoader ccl;
+ private static final class PrivilegedCallableUsingCurrentClassLoader<T>
+ implements Callable<T> {
+ final Callable<T> task;
+ final AccessControlContext acc;
+ final ClassLoader ccl;
PrivilegedCallableUsingCurrentClassLoader(Callable<T> task) {
// BEGIN android-removed
@@ -469,7 +474,7 @@
// // never trigger a security check, but we check
// // whether our callers have this permission anyways.
// sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
- //
+
// // Whether setContextClassLoader turns out to be necessary
// // or not, we fail fast if permission is not available.
// sm.checkPermission(new RuntimePermission("setContextClassLoader"));
@@ -506,9 +511,9 @@
}
/**
- * The default thread factory
+ * The default thread factory.
*/
- static class DefaultThreadFactory implements ThreadFactory {
+ private static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
@@ -536,11 +541,11 @@
}
/**
- * Thread factory capturing access control context and class loader
+ * Thread factory capturing access control context and class loader.
*/
- static class PrivilegedThreadFactory extends DefaultThreadFactory {
- private final AccessControlContext acc;
- private final ClassLoader ccl;
+ private static class PrivilegedThreadFactory extends DefaultThreadFactory {
+ final AccessControlContext acc;
+ final ClassLoader ccl;
PrivilegedThreadFactory() {
super();
@@ -551,7 +556,7 @@
// // never trigger a security check, but we check
// // whether our callers have this permission anyways.
// sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
- //
+
// // Fail fast
// sm.checkPermission(new RuntimePermission("setContextClassLoader"));
// }
@@ -579,7 +584,8 @@
* A wrapper class that exposes only the ExecutorService methods
* of an ExecutorService implementation.
*/
- static class DelegatedExecutorService extends AbstractExecutorService {
+ private static class DelegatedExecutorService
+ extends AbstractExecutorService {
private final ExecutorService e;
DelegatedExecutorService(ExecutorService executor) { e = executor; }
public void execute(Runnable command) { e.execute(command); }
@@ -620,8 +626,8 @@
}
}
- static class FinalizableDelegatedExecutorService
- extends DelegatedExecutorService {
+ private static class FinalizableDelegatedExecutorService
+ extends DelegatedExecutorService {
FinalizableDelegatedExecutorService(ExecutorService executor) {
super(executor);
}
@@ -634,7 +640,7 @@
* A wrapper class that exposes only the ScheduledExecutorService
* methods of a ScheduledExecutorService implementation.
*/
- static class DelegatedScheduledExecutorService
+ private static class DelegatedScheduledExecutorService
extends DelegatedExecutorService
implements ScheduledExecutorService {
private final ScheduledExecutorService e;
diff --git a/luni/src/main/java/java/util/concurrent/ForkJoinPool.java b/luni/src/main/java/java/util/concurrent/ForkJoinPool.java
index 41dd161..184d07a 100644
--- a/luni/src/main/java/java/util/concurrent/ForkJoinPool.java
+++ b/luni/src/main/java/java/util/concurrent/ForkJoinPool.java
@@ -7,11 +7,16 @@
package java.util.concurrent;
import java.lang.Thread.UncaughtExceptionHandler;
+import java.security.AccessControlContext;
+import java.security.Permissions;
+import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.locks.LockSupport;
/**
* An {@link ExecutorService} for running {@link ForkJoinTask}s.
@@ -31,7 +36,7 @@
* ForkJoinPool}s may also be appropriate for use with event-style
* tasks that are never joined.
*
- * <p>A static {@code commonPool()} is available and appropriate for
+ * <p>A static {@link #commonPool()} is available and appropriate for
* most applications. The common pool is used by any ForkJoinTask that
* is not explicitly submitted to a specified pool. Using the common
* pool normally reduces resource usage (its threads are slowly
@@ -40,9 +45,9 @@
*
* <p>For applications that require separate or custom pools, a {@code
* ForkJoinPool} may be constructed with a given target parallelism
- * level; by default, equal to the number of available processors. The
- * pool attempts to maintain enough active (or available) threads by
- * dynamically adding, suspending, or resuming internal worker
+ * level; by default, equal to the number of available processors.
+ * The pool attempts to maintain enough active (or available) threads
+ * by dynamically adding, suspending, or resuming internal worker
* threads, even if some tasks are stalled waiting to join others.
* However, no such adjustments are guaranteed in the face of blocked
* I/O or other unmanaged synchronization. The nested {@link
@@ -102,12 +107,19 @@
* - the class name of a {@link ForkJoinWorkerThreadFactory}
* <li>{@code java.util.concurrent.ForkJoinPool.common.exceptionHandler}
* - the class name of a {@link UncaughtExceptionHandler}
+ * <li>{@code java.util.concurrent.ForkJoinPool.common.maximumSpares}
+ * - the maximum number of allowed extra threads to maintain target
+ * parallelism (default 256).
* </ul>
+ * If a {@link SecurityManager} is present and no factory is
+ * specified, then the default pool uses a factory supplying
+ * threads that have no {@link Permissions} enabled.
* The system class loader is used to load these classes.
* Upon any error in establishing these settings, default parameters
* are used. It is possible to disable or limit the use of threads in
* the common pool by setting the parallelism property to zero, and/or
- * using a factory that may return {@code null}.
+ * using a factory that may return {@code null}. However doing so may
+ * cause unjoined tasks to never be executed.
*
* <p><b>Implementation notes</b>: This implementation restricts the
* maximum number of running threads to 32767. Attempts to create
@@ -121,6 +133,7 @@
* @since 1.7
* @author Doug Lea
*/
+//@jdk.internal.vm.annotation.Contended // android-removed
public class ForkJoinPool extends AbstractExecutorService {
/*
@@ -133,7 +146,14 @@
* that may be stolen by other workers. Preference rules give
* first priority to processing tasks from their own queues (LIFO
* or FIFO, depending on mode), then to randomized FIFO steals of
- * tasks in other queues.
+ * tasks in other queues. This framework began as vehicle for
+ * supporting tree-structured parallelism using work-stealing.
+ * Over time, its scalability advantages led to extensions and
+ * changes to better support more diverse usage contexts. Because
+ * most internal methods and nested classes are interrelated,
+ * their main rationale and descriptions are presented here;
+ * individual methods and nested classes contain only brief
+ * comments about details.
*
* WorkQueues
* ==========
@@ -153,200 +173,305 @@
* (http://research.sun.com/scalable/pubs/index.html) and
* "Idempotent work stealing" by Michael, Saraswat, and Vechev,
* PPoPP 2009 (http://portal.acm.org/citation.cfm?id=1504186).
- * See also "Correct and Efficient Work-Stealing for Weak Memory
- * Models" by Le, Pop, Cohen, and Nardelli, PPoPP 2013
+ * The main differences ultimately stem from GC requirements that
+ * we null out taken slots as soon as we can, to maintain as small
+ * a footprint as possible even in programs generating huge
+ * numbers of tasks. To accomplish this, we shift the CAS
+ * arbitrating pop vs poll (steal) from being on the indices
+ * ("base" and "top") to the slots themselves.
+ *
+ * Adding tasks then takes the form of a classic array push(task)
+ * in a circular buffer:
+ * q.array[q.top++ % length] = task;
+ *
+ * (The actual code needs to null-check and size-check the array,
+ * uses masking, not mod, for indexing a power-of-two-sized array,
+ * properly fences accesses, and possibly signals waiting workers
+ * to start scanning -- see below.) Both a successful pop and
+ * poll mainly entail a CAS of a slot from non-null to null.
+ *
+ * The pop operation (always performed by owner) is:
+ * if ((the task at top slot is not null) and
+ * (CAS slot to null))
+ * decrement top and return task;
+ *
+ * And the poll operation (usually by a stealer) is
+ * if ((the task at base slot is not null) and
+ * (CAS slot to null))
+ * increment base and return task;
+ *
+ * There are several variants of each of these; for example most
+ * versions of poll pre-screen the CAS by rechecking that the base
+ * has not changed since reading the slot, and most methods only
+ * attempt the CAS if base appears not to be equal to top.
+ *
+ * Memory ordering. See "Correct and Efficient Work-Stealing for
+ * Weak Memory Models" by Le, Pop, Cohen, and Nardelli, PPoPP 2013
* (http://www.di.ens.fr/~zappa/readings/ppopp13.pdf) for an
- * analysis of memory ordering (atomic, volatile etc) issues. The
- * main differences ultimately stem from GC requirements that we
- * null out taken slots as soon as we can, to maintain as small a
- * footprint as possible even in programs generating huge numbers
- * of tasks. To accomplish this, we shift the CAS arbitrating pop
- * vs poll (steal) from being on the indices ("base" and "top") to
- * the slots themselves. So, both a successful pop and poll
- * mainly entail a CAS of a slot from non-null to null. Because
- * we rely on CASes of references, we do not need tag bits on base
- * or top. They are simple ints as used in any circular
- * array-based queue (see for example ArrayDeque). Updates to the
- * indices must still be ordered in a way that guarantees that top
- * == base means the queue is empty, but otherwise may err on the
- * side of possibly making the queue appear nonempty when a push,
- * pop, or poll have not fully committed. Note that this means
- * that the poll operation, considered individually, is not
- * wait-free. One thief cannot successfully continue until another
- * in-progress one (or, if previously empty, a push) completes.
+ * analysis of memory ordering requirements in work-stealing
+ * algorithms similar to (but different than) the one used here.
+ * Extracting tasks in array slots via (fully fenced) CAS provides
+ * primary synchronization. The base and top indices imprecisely
+ * guide where to extract from. We do not always require strict
+ * orderings of array and index updates, so sometimes let them be
+ * subject to compiler and processor reorderings. However, the
+ * volatile "base" index also serves as a basis for memory
+ * ordering: Slot accesses are preceded by a read of base,
+ * ensuring happens-before ordering with respect to stealers (so
+ * the slots themselves can be read via plain array reads.) The
+ * only other memory orderings relied on are maintained in the
+ * course of signalling and activation (see below). A check that
+ * base == top indicates (momentary) emptiness, but otherwise may
+ * err on the side of possibly making the queue appear nonempty
+ * when a push, pop, or poll have not fully committed, or making
+ * it appear empty when an update of top has not yet been visibly
+ * written. (Method isEmpty() checks the case of a partially
+ * completed removal of the last element.) Because of this, the
+ * poll operation, considered individually, is not wait-free. One
+ * thief cannot successfully continue until another in-progress
+ * one (or, if previously empty, a push) visibly completes.
* However, in the aggregate, we ensure at least probabilistic
- * non-blockingness. If an attempted steal fails, a thief always
- * chooses a different random victim target to try next. So, in
- * order for one thief to progress, it suffices for any
+ * non-blockingness. If an attempted steal fails, a scanning
+ * thief chooses a different random victim target to try next. So,
+ * in order for one thief to progress, it suffices for any
* in-progress poll or new push on any empty queue to
* complete. (This is why we normally use method pollAt and its
* variants that try once at the apparent base index, else
- * consider alternative actions, rather than method poll.)
+ * consider alternative actions, rather than method poll, which
+ * retries.)
*
- * This approach also enables support of a user mode in which local
- * task processing is in FIFO, not LIFO order, simply by using
- * poll rather than pop. This can be useful in message-passing
- * frameworks in which tasks are never joined. However neither
- * mode considers affinities, loads, cache localities, etc, so
- * rarely provide the best possible performance on a given
- * machine, but portably provide good throughput by averaging over
- * these factors. (Further, even if we did try to use such
- * information, we do not usually have a basis for exploiting it.
- * For example, some sets of tasks profit from cache affinities,
- * but others are harmed by cache pollution effects.)
+ * This approach also enables support of a user mode in which
+ * local task processing is in FIFO, not LIFO order, simply by
+ * using poll rather than pop. This can be useful in
+ * message-passing frameworks in which tasks are never joined.
*
* WorkQueues are also used in a similar way for tasks submitted
* to the pool. We cannot mix these tasks in the same queues used
- * for work-stealing (this would contaminate lifo/fifo
- * processing). Instead, we randomly associate submission queues
+ * by workers. Instead, we randomly associate submission queues
* with submitting threads, using a form of hashing. The
- * Submitter probe value serves as a hash code for
+ * ThreadLocalRandom probe value serves as a hash code for
* choosing existing queues, and may be randomly repositioned upon
* contention with other submitters. In essence, submitters act
* like workers except that they are restricted to executing local
- * tasks that they submitted. However, because most
- * shared/external queue operations are more expensive than
- * internal, and because, at steady state, external submitters
- * will compete for CPU with workers, ForkJoinTask.join and
- * related methods disable them from repeatedly helping to process
- * tasks if all workers are active. Insertion of tasks in shared
- * mode requires a lock (mainly to protect in the case of
- * resizing) but we use only a simple spinlock (using bits in
+ * tasks that they submitted (or in the case of CountedCompleters,
+ * others with the same root task). Insertion of tasks in shared
+ * mode requires a lock but we use only a simple spinlock (using
* field qlock), because submitters encountering a busy queue move
* on to try or create other queues -- they block only when
- * creating and registering new queues.
+ * creating and registering new queues. Because it is used only as
+ * a spinlock, unlocking requires only a "releasing" store (using
+ * putOrderedInt). The qlock is also used during termination
+ * detection, in which case it is forced to a negative
+ * non-lockable value.
*
* Management
* ==========
*
* The main throughput advantages of work-stealing stem from
* decentralized control -- workers mostly take tasks from
- * themselves or each other. We cannot negate this in the
- * implementation of other management responsibilities. The main
- * tactic for avoiding bottlenecks is packing nearly all
- * essentially atomic control state into two volatile variables
- * that are by far most often read (not written) as status and
- * consistency checks.
+ * themselves or each other, at rates that can exceed a billion
+ * per second. The pool itself creates, activates (enables
+ * scanning for and running tasks), deactivates, blocks, and
+ * terminates threads, all with minimal central information.
+ * There are only a few properties that we can globally track or
+ * maintain, so we pack them into a small number of variables,
+ * often maintaining atomicity without blocking or locking.
+ * Nearly all essentially atomic control state is held in two
+ * volatile variables that are by far most often read (not
+ * written) as status and consistency checks. (Also, field
+ * "config" holds unchanging configuration state.)
*
- * Field "ctl" contains 64 bits holding all the information needed
- * to atomically decide to add, inactivate, enqueue (on an event
+ * Field "ctl" contains 64 bits holding information needed to
+ * atomically decide to add, inactivate, enqueue (on an event
* queue), dequeue, and/or re-activate workers. To enable this
* packing, we restrict maximum parallelism to (1<<15)-1 (which is
* far in excess of normal operating range) to allow ids, counts,
* and their negations (used for thresholding) to fit into 16bit
- * fields.
+ * subfields.
*
- * Field "plock" is a form of sequence lock with a saturating
- * shutdown bit (similarly for per-queue "qlocks"), mainly
- * protecting updates to the workQueues array, as well as to
- * enable shutdown. When used as a lock, it is normally only very
- * briefly held, so is nearly always available after at most a
- * brief spin, but we use a monitor-based backup strategy to
- * block when needed.
+ * Field "runState" holds lifetime status, atomically and
+ * monotonically setting STARTED, SHUTDOWN, STOP, and finally
+ * TERMINATED bits.
*
- * Recording WorkQueues. WorkQueues are recorded in the
- * "workQueues" array that is created upon first use and expanded
- * if necessary. Updates to the array while recording new workers
- * and unrecording terminated ones are protected from each other
- * by a lock but the array is otherwise concurrently readable, and
- * accessed directly. To simplify index-based operations, the
- * array size is always a power of two, and all readers must
- * tolerate null slots. Worker queues are at odd indices. Shared
- * (submission) queues are at even indices, up to a maximum of 64
- * slots, to limit growth even if array needs to expand to add
- * more workers. Grouping them together in this way simplifies and
+ * Field "auxState" is a ReentrantLock subclass that also
+ * opportunistically holds some other bookkeeping fields accessed
+ * only when locked. It is mainly used to lock (infrequent)
+ * updates to workQueues. The auxState instance is itself lazily
+ * constructed (see tryInitialize), requiring a double-check-style
+ * bootstrapping use of field runState, and locking a private
+ * static.
+ *
+ * Field "workQueues" holds references to WorkQueues. It is
+ * updated (only during worker creation and termination) under the
+ * lock, but is otherwise concurrently readable, and accessed
+ * directly. We also ensure that reads of the array reference
+ * itself never become too stale (for example, re-reading before
+ * each scan). To simplify index-based operations, the array size
+ * is always a power of two, and all readers must tolerate null
+ * slots. Worker queues are at odd indices. Shared (submission)
+ * queues are at even indices, up to a maximum of 64 slots, to
+ * limit growth even if array needs to expand to add more
+ * workers. Grouping them together in this way simplifies and
* speeds up task scanning.
*
* All worker thread creation is on-demand, triggered by task
* submissions, replacement of terminated workers, and/or
* compensation for blocked workers. However, all other support
* code is set up to work with other policies. To ensure that we
- * do not hold on to worker references that would prevent GC, ALL
+ * do not hold on to worker references that would prevent GC, all
* accesses to workQueues are via indices into the workQueues
* array (which is one source of some of the messy code
* constructions here). In essence, the workQueues array serves as
- * a weak reference mechanism. Thus for example the wait queue
- * field of ctl stores indices, not references. Access to the
- * workQueues in associated methods (for example signalWork) must
- * both index-check and null-check the IDs. All such accesses
- * ignore bad IDs by returning out early from what they are doing,
- * since this can only be associated with termination, in which
- * case it is OK to give up. All uses of the workQueues array
- * also check that it is non-null (even if previously
- * non-null). This allows nulling during termination, which is
- * currently not necessary, but remains an option for
- * resource-revocation-based shutdown schemes. It also helps
- * reduce JIT issuance of uncommon-trap code, which tends to
- * unnecessarily complicate control flow in some methods.
+ * a weak reference mechanism. Thus for example the stack top
+ * subfield of ctl stores indices, not references.
*
- * Event Queuing. Unlike HPC work-stealing frameworks, we cannot
- * let workers spin indefinitely scanning for tasks when none can
- * be found immediately, and we cannot start/resume workers unless
- * there appear to be tasks available. On the other hand, we must
- * quickly prod them into action when new tasks are submitted or
- * generated. In many usages, ramp-up time to activate workers is
- * the main limiting factor in overall performance (this is
- * compounded at program start-up by JIT compilation and
- * allocation). So we try to streamline this as much as possible.
- * We park/unpark workers after placing in an event wait queue
- * when they cannot find work. This "queue" is actually a simple
- * Treiber stack, headed by the "id" field of ctl, plus a 15bit
- * counter value (that reflects the number of times a worker has
- * been inactivated) to avoid ABA effects (we need only as many
- * version numbers as worker threads). Successors are held in
- * field WorkQueue.nextWait. Queuing deals with several intrinsic
- * races, mainly that a task-producing thread can miss seeing (and
- * signalling) another thread that gave up looking for work but
- * has not yet entered the wait queue. We solve this by requiring
- * a full sweep of all workers (via repeated calls to method
- * scan()) both before and after a newly waiting worker is added
- * to the wait queue. Because enqueued workers may actually be
- * rescanning rather than waiting, we set and clear the "parker"
- * field of WorkQueues to reduce unnecessary calls to unpark.
- * (This requires a secondary recheck to avoid missed signals.)
- * Note the unusual conventions about Thread.interrupts
- * surrounding parking and other blocking: Because interrupts are
- * used solely to alert threads to check termination, which is
- * checked anyway upon blocking, we clear status (using
- * Thread.interrupted) before any call to park, so that park does
- * not immediately return due to status being set via some other
- * unrelated call to interrupt in user code.
+ * Queuing Idle Workers. Unlike HPC work-stealing frameworks, we
+ * cannot let workers spin indefinitely scanning for tasks when
+ * none can be found immediately, and we cannot start/resume
+ * workers unless there appear to be tasks available. On the
+ * other hand, we must quickly prod them into action when new
+ * tasks are submitted or generated. In many usages, ramp-up time
+ * to activate workers is the main limiting factor in overall
+ * performance, which is compounded at program start-up by JIT
+ * compilation and allocation. So we streamline this as much as
+ * possible.
*
- * Signalling. We create or wake up workers only when there
- * appears to be at least one task they might be able to find and
- * execute. When a submission is added or another worker adds a
- * task to a queue that has fewer than two tasks, they signal
- * waiting workers (or trigger creation of new ones if fewer than
- * the given parallelism level -- signalWork). These primary
- * signals are buttressed by others whenever other threads remove
- * a task from a queue and notice that there are other tasks there
- * as well. So in general, pools will be over-signalled. On most
- * platforms, signalling (unpark) overhead time is noticeably
- * long, and the time between signalling a thread and it actually
- * making progress can be very noticeably long, so it is worth
- * offloading these delays from critical paths as much as
- * possible. Additionally, workers spin-down gradually, by staying
- * alive so long as they see the ctl state changing. Similar
- * stability-sensing techniques are also used before blocking in
- * awaitJoin and helpComplete.
+ * The "ctl" field atomically maintains active and total worker
+ * counts as well as a queue to place waiting threads so they can
+ * be located for signalling. Active counts also play the role of
+ * quiescence indicators, so are decremented when workers believe
+ * that there are no more tasks to execute. The "queue" is
+ * actually a form of Treiber stack. A stack is ideal for
+ * activating threads in most-recently used order. This improves
+ * performance and locality, outweighing the disadvantages of
+ * being prone to contention and inability to release a worker
+ * unless it is topmost on stack. We block/unblock workers after
+ * pushing on the idle worker stack (represented by the lower
+ * 32bit subfield of ctl) when they cannot find work. The top
+ * stack state holds the value of the "scanState" field of the
+ * worker: its index and status, plus a version counter that, in
+ * addition to the count subfields (also serving as version
+ * stamps) provide protection against Treiber stack ABA effects.
+ *
+ * Creating workers. To create a worker, we pre-increment total
+ * count (serving as a reservation), and attempt to construct a
+ * ForkJoinWorkerThread via its factory. Upon construction, the
+ * new thread invokes registerWorker, where it constructs a
+ * WorkQueue and is assigned an index in the workQueues array
+ * (expanding the array if necessary). The thread is then started.
+ * Upon any exception across these steps, or null return from
+ * factory, deregisterWorker adjusts counts and records
+ * accordingly. If a null return, the pool continues running with
+ * fewer than the target number workers. If exceptional, the
+ * exception is propagated, generally to some external caller.
+ * Worker index assignment avoids the bias in scanning that would
+ * occur if entries were sequentially packed starting at the front
+ * of the workQueues array. We treat the array as a simple
+ * power-of-two hash table, expanding as needed. The seedIndex
+ * increment ensures no collisions until a resize is needed or a
+ * worker is deregistered and replaced, and thereafter keeps
+ * probability of collision low. We cannot use
+ * ThreadLocalRandom.getProbe() for similar purposes here because
+ * the thread has not started yet, but do so for creating
+ * submission queues for existing external threads (see
+ * externalPush).
+ *
+ * WorkQueue field scanState is used by both workers and the pool
+ * to manage and track whether a worker is UNSIGNALLED (possibly
+ * blocked waiting for a signal). When a worker is inactivated,
+ * its scanState field is set, and is prevented from executing
+ * tasks, even though it must scan once for them to avoid queuing
+ * races. Note that scanState updates lag queue CAS releases so
+ * usage requires care. When queued, the lower 16 bits of
+ * scanState must hold its pool index. So we place the index there
+ * upon initialization (see registerWorker) and otherwise keep it
+ * there or restore it when necessary.
+ *
+ * The ctl field also serves as the basis for memory
+ * synchronization surrounding activation. This uses a more
+ * efficient version of a Dekker-like rule that task producers and
+ * consumers sync with each other by both writing/CASing ctl (even
+ * if to its current value). This would be extremely costly. So
+ * we relax it in several ways: (1) Producers only signal when
+ * their queue is empty. Other workers propagate this signal (in
+ * method scan) when they find tasks. (2) Workers only enqueue
+ * after scanning (see below) and not finding any tasks. (3)
+ * Rather than CASing ctl to its current value in the common case
+ * where no action is required, we reduce write contention by
+ * equivalently prefacing signalWork when called by an external
+ * task producer using a memory access with full-volatile
+ * semantics or a "fullFence". (4) For internal task producers we
+ * rely on the fact that even if no other workers awaken, the
+ * producer itself will eventually see the task and execute it.
+ *
+ * Almost always, too many signals are issued. A task producer
+ * cannot in general tell if some existing worker is in the midst
+ * of finishing one task (or already scanning) and ready to take
+ * another without being signalled. So the producer might instead
+ * activate a different worker that does not find any work, and
+ * then inactivates. This scarcely matters in steady-state
+ * computations involving all workers, but can create contention
+ * and bookkeeping bottlenecks during ramp-up, ramp-down, and small
+ * computations involving only a few workers.
+ *
+ * Scanning. Method scan() performs top-level scanning for tasks.
+ * Each scan traverses (and tries to poll from) each queue in
+ * pseudorandom permutation order by randomly selecting an origin
+ * index and a step value. (The pseudorandom generator need not
+ * have high-quality statistical properties in the long term, but
+ * just within computations; We use 64bit and 32bit Marsaglia
+ * XorShifts, which are cheap and suffice here.) Scanning also
+ * employs contention reduction: When scanning workers fail a CAS
+ * polling for work, they soon restart with a different
+ * pseudorandom scan order (thus likely retrying at different
+ * intervals). This improves throughput when many threads are
+ * trying to take tasks from few queues. Scans do not otherwise
+ * explicitly take into account core affinities, loads, cache
+ * localities, etc, However, they do exploit temporal locality
+ * (which usually approximates these) by preferring to re-poll (up
+ * to POLL_LIMIT times) from the same queue after a successful
+ * poll before trying others. Restricted forms of scanning occur
+ * in methods helpComplete and findNonEmptyStealQueue, and take
+ * similar but simpler forms.
+ *
+ * Deactivation and waiting. Queuing encounters several intrinsic
+ * races; most notably that an inactivating scanning worker can
+ * miss seeing a task produced during a scan. So when a worker
+ * cannot find a task to steal, it inactivates and enqueues, and
+ * then rescans to ensure that it didn't miss one, reactivating
+ * upon seeing one with probability approximately proportional to
+ * probability of a miss. (In most cases, the worker will be
+ * signalled before self-signalling, avoiding cascades of multiple
+ * signals for the same task).
+ *
+ * Workers block (in method awaitWork) using park/unpark;
+ * advertising the need for signallers to unpark by setting their
+ * "parker" fields.
*
* Trimming workers. To release resources after periods of lack of
* use, a worker starting to wait when the pool is quiescent will
- * time out and terminate if the pool has remained quiescent for a
- * given period -- a short period if there are more threads than
- * parallelism, longer as the number of threads decreases. This
- * will slowly propagate, eventually terminating all workers after
- * periods of non-use.
+ * time out and terminate (see awaitWork) if the pool has remained
+ * quiescent for period given by IDLE_TIMEOUT_MS, increasing the
+ * period as the number of threads decreases, eventually removing
+ * all workers.
*
- * Shutdown and Termination. A call to shutdownNow atomically sets
- * a plock bit and then (non-atomically) sets each worker's
- * qlock status, cancels all unprocessed tasks, and wakes up
- * all waiting workers. Detecting whether termination should
- * commence after a non-abrupt shutdown() call requires more work
- * and bookkeeping. We need consensus about quiescence (i.e., that
- * there is no more work). The active count provides a primary
- * indication but non-abrupt shutdown still requires a rechecking
- * scan for any workers that are inactive but not queued.
+ * Shutdown and Termination. A call to shutdownNow invokes
+ * tryTerminate to atomically set a runState bit. The calling
+ * thread, as well as every other worker thereafter terminating,
+ * helps terminate others by setting their (qlock) status,
+ * cancelling their unprocessed tasks, and waking them up, doing
+ * so repeatedly until stable. Calls to non-abrupt shutdown()
+ * preface this by checking whether termination should commence.
+ * This relies primarily on the active count bits of "ctl"
+ * maintaining consensus -- tryTerminate is called from awaitWork
+ * whenever quiescent. However, external submitters do not take
+ * part in this consensus. So, tryTerminate sweeps through queues
+ * (until stable) to ensure lack of in-flight submissions and
+ * workers about to process them before triggering the "STOP"
+ * phase of termination. (Note: there is an intrinsic conflict if
+ * helpQuiescePool is called when shutdown is enabled. Both wait
+ * for quiescence, but tryTerminate is biased to not trigger until
+ * helpQuiescePool completes.)
*
* Joining Tasks
* =============
@@ -357,9 +482,9 @@
* just let them block (as in Thread.join). We also cannot just
* reassign the joiner's run-time stack with another and replace
* it later, which would be a form of "continuation", that even if
- * possible is not necessarily a good idea since we sometimes need
- * both an unblocked task and its continuation to progress.
- * Instead we combine two tactics:
+ * possible is not necessarily a good idea since we may need both
+ * an unblocked task and its continuation to progress. Instead we
+ * combine two tactics:
*
* Helping: Arranging for the joiner to execute some task that it
* would be running if the steal had not occurred.
@@ -379,16 +504,16 @@
* The ManagedBlocker extension API can't use helping so relies
* only on compensation in method awaitBlocker.
*
- * The algorithm in tryHelpStealer entails a form of "linear"
- * helping: Each worker records (in field currentSteal) the most
- * recent task it stole from some other worker. Plus, it records
- * (in field currentJoin) the task it is currently actively
- * joining. Method tryHelpStealer uses these markers to try to
- * find a worker to help (i.e., steal back a task from and execute
- * it) that could hasten completion of the actively joined task.
- * In essence, the joiner executes a task that would be on its own
- * local deque had the to-be-joined task not been stolen. This may
- * be seen as a conservative variant of the approach in Wagner &
+ * The algorithm in helpStealer entails a form of "linear
+ * helping". Each worker records (in field currentSteal) the most
+ * recent task it stole from some other worker (or a submission).
+ * It also records (in field currentJoin) the task it is currently
+ * actively joining. Method helpStealer uses these markers to try
+ * to find a worker to help (i.e., steal back a task from and
+ * execute it) that could hasten completion of the actively joined
+ * task. Thus, the joiner executes a task that would be on its
+ * own local deque had the to-be-joined task not been stolen. This
+ * is a conservative variant of the approach described in Wagner &
* Calder "Leapfrogging: a portable technique for implementing
* efficient futures" SIGPLAN Notices, 1993
* (http://portal.acm.org/citation.cfm?id=155354). It differs in
@@ -406,31 +531,45 @@
* which means that we miss links in the chain during long-lived
* tasks, GC stalls etc (which is OK since blocking in such cases
* is usually a good idea). (4) We bound the number of attempts
- * to find work (see MAX_HELP) and fall back to suspending the
+ * to find work using checksums and fall back to suspending the
* worker and if necessary replacing it with another.
*
- * It is impossible to keep exactly the target parallelism number
- * of threads running at any given time. Determining the
- * existence of conservatively safe helping targets, the
- * availability of already-created spares, and the apparent need
- * to create new spares are all racy, so we rely on multiple
- * retries of each. Compensation in the apparent absence of
- * helping opportunities is challenging to control on JVMs, where
- * GC and other activities can stall progress of tasks that in
- * turn stall out many other dependent tasks, without us being
- * able to determine whether they will ever require compensation.
- * Even though work-stealing otherwise encounters little
- * degradation in the presence of more threads than cores,
- * aggressively adding new threads in such cases entails risk of
- * unwanted positive feedback control loops in which more threads
- * cause more dependent stalls (as well as delayed progress of
- * unblocked threads to the point that we know they are available)
- * leading to more situations requiring more threads, and so
- * on. This aspect of control can be seen as an (analytically
- * intractable) game with an opponent that may choose the worst
- * (for us) active thread to stall at any time. We take several
- * precautions to bound losses (and thus bound gains), mainly in
- * methods tryCompensate and awaitJoin.
+ * Helping actions for CountedCompleters do not require tracking
+ * currentJoins: Method helpComplete takes and executes any task
+ * with the same root as the task being waited on (preferring
+ * local pops to non-local polls). However, this still entails
+ * some traversal of completer chains, so is less efficient than
+ * using CountedCompleters without explicit joins.
+ *
+ * Compensation does not aim to keep exactly the target
+ * parallelism number of unblocked threads running at any given
+ * time. Some previous versions of this class employed immediate
+ * compensations for any blocked join. However, in practice, the
+ * vast majority of blockages are transient byproducts of GC and
+ * other JVM or OS activities that are made worse by replacement.
+ * Currently, compensation is attempted only after validating that
+ * all purportedly active threads are processing tasks by checking
+ * field WorkQueue.scanState, which eliminates most false
+ * positives. Also, compensation is bypassed (tolerating fewer
+ * threads) in the most common case in which it is rarely
+ * beneficial: when a worker with an empty queue (thus no
+ * continuation tasks) blocks on a join and there still remain
+ * enough threads to ensure liveness.
+ *
+ * Spare threads are removed as soon as they notice that the
+ * target parallelism level has been exceeded, in method
+ * tryDropSpare. (Method scan arranges returns for rechecks upon
+ * each probe via the "bound" parameter.)
+ *
+ * The compensation mechanism may be bounded. Bounds for the
+ * commonPool (see COMMON_MAX_SPARES) better enable JVMs to cope
+ * with programming errors and abuse before running out of
+ * resources to do so. In other cases, users may supply factories
+ * that limit thread construction. The effects of bounding in this
+ * pool (like all others) is imprecise. Total worker counts are
+ * decremented when threads deregister, not when they exit and
+ * resources are reclaimed by the JVM and OS. So the number of
+ * simultaneously live threads may transiently exceed bounds.
*
* Common Pool
* ===========
@@ -440,24 +579,52 @@
* never be used, we minimize initial construction overhead and
* footprint to the setup of about a dozen fields, with no nested
* allocation. Most bootstrapping occurs within method
- * fullExternalPush during the first submission to the pool.
+ * externalSubmit during the first submission to the pool.
*
* When external threads submit to the common pool, they can
- * perform subtask processing (see externalHelpJoin and related
- * methods). This caller-helps policy makes it sensible to set
- * common pool parallelism level to one (or more) less than the
- * total number of available cores, or even zero for pure
- * caller-runs. We do not need to record whether external
- * submissions are to the common pool -- if not, externalHelpJoin
- * returns quickly (at the most helping to signal some common pool
- * workers). These submitters would otherwise be blocked waiting
- * for completion, so the extra effort (with liberally sprinkled
- * task status checks) in inapplicable cases amounts to an odd
- * form of limited spin-wait before blocking in ForkJoinTask.join.
+ * perform subtask processing (see externalHelpComplete and
+ * related methods) upon joins. This caller-helps policy makes it
+ * sensible to set common pool parallelism level to one (or more)
+ * less than the total number of available cores, or even zero for
+ * pure caller-runs. We do not need to record whether external
+ * submissions are to the common pool -- if not, external help
+ * methods return quickly. These submitters would otherwise be
+ * blocked waiting for completion, so the extra effort (with
+ * liberally sprinkled task status checks) in inapplicable cases
+ * amounts to an odd form of limited spin-wait before blocking in
+ * ForkJoinTask.join.
+ *
+ * As a more appropriate default in managed environments, unless
+ * overridden by system properties, we use workers of subclass
+ * InnocuousForkJoinWorkerThread when there is a SecurityManager
+ * present. These workers have no permissions set, do not belong
+ * to any user-defined ThreadGroup, and erase all ThreadLocals
+ * after executing any top-level task (see WorkQueue.runTask).
+ * The associated mechanics (mainly in ForkJoinWorkerThread) may
+ * be JVM-dependent and must access particular Thread class fields
+ * to achieve this effect.
*
* Style notes
* ===========
*
+ * Memory ordering relies mainly on Unsafe intrinsics that carry
+ * the further responsibility of explicitly performing null- and
+ * bounds- checks otherwise carried out implicitly by JVMs. This
+ * can be awkward and ugly, but also reflects the need to control
+ * outcomes across the unusual cases that arise in very racy code
+ * with very few invariants. So these explicit checks would exist
+ * in some form anyway. All fields are read into locals before
+ * use, and null-checked if they are references. This is usually
+ * done in a "C"-like style of listing declarations at the heads
+ * of methods or blocks, and using inline assignments on first
+ * encounter. Array bounds-checks are usually performed by
+ * masking with array.length-1, which relies on the invariant that
+ * these arrays are created with positive lengths, which is itself
+ * paranoically checked. Nearly all explicit checks lead to
+ * bypass/return, not exception throws, because they may
+ * legitimately arise due to cancellation/revocation during
+ * shutdown.
+ *
* There is a lot of representation-level coupling among classes
* ForkJoinPool, ForkJoinWorkerThread, and ForkJoinTask. The
* fields of WorkQueue maintain data structures managed by
@@ -465,22 +632,13 @@
* trying to reduce this, since any associated future changes in
* representations will need to be accompanied by algorithmic
* changes anyway. Several methods intrinsically sprawl because
- * they must accumulate sets of consistent reads of volatiles held
- * in local variables. Methods signalWork() and scan() are the
- * main bottlenecks, so are especially heavily
- * micro-optimized/mangled. There are lots of inline assignments
- * (of form "while ((local = field) != 0)") which are usually the
- * simplest way to ensure the required read orderings (which are
- * sometimes critical). This leads to a "C"-like style of listing
- * declarations of these locals at the heads of methods or blocks.
- * There are several occurrences of the unusual "do {} while
- * (!cas...)" which is the simplest way to force an update of a
- * CAS'ed variable. There are also other coding oddities (including
- * several unnecessary-looking hoisted null checks) that help
- * some methods perform reasonably even when interpreted (not
- * compiled).
+ * they must accumulate sets of consistent reads of fields held in
+ * local variables. There are also other coding oddities
+ * (including several unnecessary-looking hoisted null checks)
+ * that help some methods perform reasonably even when interpreted
+ * (not compiled).
*
- * The order of declarations in this file is:
+ * The order of declarations in this file is (with a few exceptions):
* (1) Static utility functions
* (2) Nested (static) classes
* (3) Static fields
@@ -490,7 +648,6 @@
* (7) Exported methods
* (8) Static block initializing statics in minimally dependent order
*/
- // android-note: Removed references to CountedCompleters.
// Static utilities
@@ -517,7 +674,8 @@
* Returns a new worker thread operating in the given pool.
*
* @param pool the pool this thread works in
- * @return the new worker thread
+ * @return the new worker thread, or {@code null} if the request
+ * to create a thread is rejected
* @throws NullPointerException if the pool is null
*/
public ForkJoinWorkerThread newThread(ForkJoinPool pool);
@@ -527,7 +685,7 @@
* Default ForkJoinWorkerThreadFactory implementation; creates a
* new ForkJoinWorkerThread.
*/
- static final class DefaultForkJoinWorkerThreadFactory
+ private static final class DefaultForkJoinWorkerThreadFactory
implements ForkJoinWorkerThreadFactory {
public final ForkJoinWorkerThread newThread(ForkJoinPool pool) {
return new ForkJoinWorkerThread(pool);
@@ -540,7 +698,7 @@
* in WorkQueue.tryRemoveAndExec. We don't need the proxy to
* actually do anything beyond having a unique identity.
*/
- static final class EmptyTask extends ForkJoinTask<Void> {
+ private static final class EmptyTask extends ForkJoinTask<Void> {
private static final long serialVersionUID = -7721805057305804111L;
EmptyTask() { status = ForkJoinTask.NORMAL; } // force done
public final Void getRawResult() { return null; }
@@ -549,54 +707,54 @@
}
/**
+ * Additional fields and lock created upon initialization.
+ */
+ private static final class AuxState extends ReentrantLock {
+ private static final long serialVersionUID = -6001602636862214147L;
+ volatile long stealCount; // cumulative steal count
+ long indexSeed; // index bits for registerWorker
+ AuxState() {}
+ }
+
+ // Constants shared across ForkJoinPool and WorkQueue
+
+ // Bounds
+ static final int SMASK = 0xffff; // short bits == max index
+ static final int MAX_CAP = 0x7fff; // max #workers - 1
+ static final int EVENMASK = 0xfffe; // even short bits
+ static final int SQMASK = 0x007e; // max 64 (even) slots
+
+ // Masks and units for WorkQueue.scanState and ctl sp subfield
+ static final int UNSIGNALLED = 1 << 31; // must be negative
+ static final int SS_SEQ = 1 << 16; // version count
+
+ // Mode bits for ForkJoinPool.config and WorkQueue.config
+ static final int MODE_MASK = 0xffff << 16; // top half of int
+ static final int SPARE_WORKER = 1 << 17; // set if tc > 0 on creation
+ static final int UNREGISTERED = 1 << 18; // to skip some of deregister
+ static final int FIFO_QUEUE = 1 << 31; // must be negative
+ static final int LIFO_QUEUE = 0; // for clarity
+ static final int IS_OWNED = 1; // low bit 0 if shared
+
+ /**
+ * The maximum number of task executions from the same queue
+ * before checking other queues, bounding unfairness and impact of
+ * infinite user task recursion. Must be a power of two minus 1.
+ */
+ static final int POLL_LIMIT = (1 << 10) - 1;
+
+ /**
* Queues supporting work-stealing as well as external task
- * submission. See above for main rationale and algorithms.
- * Implementation relies heavily on "Unsafe" intrinsics
- * and selective use of "volatile":
- *
- * Field "base" is the index (mod array.length) of the least valid
- * queue slot, which is always the next position to steal (poll)
- * from if nonempty. Reads and writes require volatile orderings
- * but not CAS, because updates are only performed after slot
- * CASes.
- *
- * Field "top" is the index (mod array.length) of the next queue
- * slot to push to or pop from. It is written only by owner thread
- * for push, or under lock for external/shared push, and accessed
- * by other threads only after reading (volatile) base. Both top
- * and base are allowed to wrap around on overflow, but (top -
- * base) (or more commonly -(base - top) to force volatile read of
- * base before top) still estimates size. The lock ("qlock") is
- * forced to -1 on termination, causing all further lock attempts
- * to fail. (Note: we don't need CAS for termination state because
- * upon pool shutdown, all shared-queues will stop being used
- * anyway.) Nearly all lock bodies are set up so that exceptions
- * within lock bodies are "impossible" (modulo JVM errors that
- * would cause failure anyway.)
- *
- * The array slots are read and written using the emulation of
- * volatiles/atomics provided by Unsafe. Insertions must in
- * general use putOrderedObject as a form of releasing store to
- * ensure that all writes to the task object are ordered before
- * its publication in the queue. All removals entail a CAS to
- * null. The array is always a power of two. To ensure safety of
- * Unsafe array operations, all accesses perform explicit null
- * checks and implicit bounds checks via power-of-two masking.
- *
- * In addition to basic queuing support, this class contains
- * fields described elsewhere to control execution. It turns out
- * to work better memory-layout-wise to include them in this class
- * rather than a separate class.
- *
+ * submission. See above for descriptions and algorithms.
* Performance on most platforms is very sensitive to placement of
* instances of both WorkQueues and their arrays -- we absolutely
* do not want multiple WorkQueue instances or multiple queue
- * arrays sharing cache lines. (It would be best for queue objects
- * and their arrays to share, but there is nothing available to
- * help arrange that). The @Contended annotation alerts JVMs to
- * try to keep instances apart.
+ * arrays sharing cache lines. The @Contended annotation alerts
+ * JVMs to try to keep instances apart.
*/
+ //@jdk.internal.vm.annotation.Contended // android-removed
static final class WorkQueue {
+
/**
* Capacity of work-stealing queue array upon initialization.
* Must be a power of two; at least 4, but should be larger to
@@ -617,43 +775,44 @@
*/
static final int MAXIMUM_QUEUE_CAPACITY = 1 << 26; // 64M
- // Heuristic padding to ameliorate unfortunate memory placements
- volatile long pad00, pad01, pad02, pad03, pad04, pad05, pad06;
+ // Instance fields
- volatile int eventCount; // encoded inactivation count; < 0 if inactive
- int nextWait; // encoded record of next event waiter
+ volatile int scanState; // versioned, negative if inactive
+ int stackPred; // pool stack (ctl) predecessor
int nsteals; // number of steals
- int hint; // steal index hint
- short poolIndex; // index of this queue in pool
- final short mode; // 0: lifo, > 0: fifo, < 0: shared
- volatile int qlock; // 1: locked, -1: terminate; else 0
+ int hint; // randomization and stealer index hint
+ int config; // pool index and mode
+ volatile int qlock; // 1: locked, < 0: terminate; else 0
volatile int base; // index of next slot for poll
int top; // index of next slot for push
ForkJoinTask<?>[] array; // the elements (initially unallocated)
final ForkJoinPool pool; // the containing pool (may be null)
final ForkJoinWorkerThread owner; // owning thread or null if shared
volatile Thread parker; // == owner during call to park; else null
- volatile ForkJoinTask<?> currentJoin; // task being joined in awaitJoin
- ForkJoinTask<?> currentSteal; // current non-local task being executed
+ volatile ForkJoinTask<?> currentJoin; // task being joined in awaitJoin
- volatile Object pad10, pad11, pad12, pad13, pad14, pad15, pad16, pad17;
- volatile Object pad18, pad19, pad1a, pad1b, pad1c, pad1d;
+ // @jdk.internal.vm.annotation.Contended("group2") // segregate // android-removed
+ volatile ForkJoinTask<?> currentSteal; // nonnull when running some task
- WorkQueue(ForkJoinPool pool, ForkJoinWorkerThread owner, int mode,
- int seed) {
+ WorkQueue(ForkJoinPool pool, ForkJoinWorkerThread owner) {
this.pool = pool;
this.owner = owner;
- this.mode = (short)mode;
- this.hint = seed; // store initial seed for runWorker
// Place indices in the center of array (that is not yet allocated)
base = top = INITIAL_QUEUE_CAPACITY >>> 1;
}
/**
+ * Returns an exportable index (used by ForkJoinWorkerThread).
+ */
+ final int getPoolIndex() {
+ return (config & 0xffff) >>> 1; // ignore odd/even tag bit
+ }
+
+ /**
* Returns the approximate number of tasks in the queue.
*/
final int queueSize() {
- int n = base - top; // non-owner callers must read base first
+ int n = base - top; // read base first
return (n >= 0) ? 0 : -n; // ignore transient negative
}
@@ -663,32 +822,31 @@
* near-empty queue has at least one unclaimed task.
*/
final boolean isEmpty() {
- ForkJoinTask<?>[] a; int m, s;
- int n = base - (s = top);
- return (n >= 0 ||
- (n == -1 &&
- ((a = array) == null ||
- (m = a.length - 1) < 0 ||
- U.getObject
- (a, (long)((m & (s - 1)) << ASHIFT) + ABASE) == null)));
+ ForkJoinTask<?>[] a; int n, al, s;
+ return ((n = base - (s = top)) >= 0 || // possibly one task
+ (n == -1 && ((a = array) == null ||
+ (al = a.length) == 0 ||
+ a[(al - 1) & (s - 1)] == null)));
}
/**
- * Pushes a task. Call only by owner in unshared queues. (The
- * shared-queue version is embedded in method externalPush.)
+ * Pushes a task. Call only by owner in unshared queues.
*
* @param task the task. Caller must ensure non-null.
* @throws RejectedExecutionException if array cannot be resized
*/
final void push(ForkJoinTask<?> task) {
- ForkJoinTask<?>[] a; ForkJoinPool p;
- int s = top, n;
- if ((a = array) != null) { // ignore if queue removed
- int m = a.length - 1;
- U.putOrderedObject(a, ((m & s) << ASHIFT) + ABASE, task);
- if ((n = (top = s + 1) - base) <= 2)
- (p = pool).signalWork(p.workQueues, this);
- else if (n >= m)
+ U.storeFence(); // ensure safe publication
+ int s = top, al, d; ForkJoinTask<?>[] a;
+ if ((a = array) != null && (al = a.length) > 0) {
+ a[(al - 1) & s] = task; // relaxed writes OK
+ top = s + 1;
+ ForkJoinPool p = pool;
+ if ((d = base - s) == 0 && p != null) {
+ U.fullFence();
+ p.signalWork();
+ }
+ else if (al + d == 1)
growArray();
}
}
@@ -701,22 +859,23 @@
final ForkJoinTask<?>[] growArray() {
ForkJoinTask<?>[] oldA = array;
int size = oldA != null ? oldA.length << 1 : INITIAL_QUEUE_CAPACITY;
- if (size > MAXIMUM_QUEUE_CAPACITY)
+ if (size < INITIAL_QUEUE_CAPACITY || size > MAXIMUM_QUEUE_CAPACITY)
throw new RejectedExecutionException("Queue capacity exceeded");
int oldMask, t, b;
ForkJoinTask<?>[] a = array = new ForkJoinTask<?>[size];
- if (oldA != null && (oldMask = oldA.length - 1) >= 0 &&
+ if (oldA != null && (oldMask = oldA.length - 1) > 0 &&
(t = top) - (b = base) > 0) {
int mask = size - 1;
- do {
- ForkJoinTask<?> x;
- int oldj = ((b & oldMask) << ASHIFT) + ABASE;
- int j = ((b & mask) << ASHIFT) + ABASE;
- x = (ForkJoinTask<?>)U.getObjectVolatile(oldA, oldj);
+ do { // emulate poll from old array, push to new array
+ int index = b & oldMask;
+ long offset = ((long)index << ASHIFT) + ABASE;
+ ForkJoinTask<?> x = (ForkJoinTask<?>)
+ U.getObjectVolatile(oldA, offset);
if (x != null &&
- U.compareAndSwapObject(oldA, oldj, x, null))
- U.putObjectVolatile(a, j, x);
+ U.compareAndSwapObject(oldA, offset, x, null))
+ a[b & mask] = x;
} while (++b != t);
+ U.storeFence();
}
return a;
}
@@ -726,16 +885,16 @@
* by owner in unshared queues.
*/
final ForkJoinTask<?> pop() {
- ForkJoinTask<?>[] a; ForkJoinTask<?> t; int m;
- if ((a = array) != null && (m = a.length - 1) >= 0) {
- for (int s; (s = top - 1) - base >= 0;) {
- long j = ((m & s) << ASHIFT) + ABASE;
- if ((t = (ForkJoinTask<?>)U.getObject(a, j)) == null)
- break;
- if (U.compareAndSwapObject(a, j, t, null)) {
- top = s;
- return t;
- }
+ int b = base, s = top, al, i; ForkJoinTask<?>[] a;
+ if ((a = array) != null && b != s && (al = a.length) > 0) {
+ int index = (al - 1) & --s;
+ long offset = ((long)index << ASHIFT) + ABASE;
+ ForkJoinTask<?> t = (ForkJoinTask<?>)
+ U.getObject(a, offset);
+ if (t != null &&
+ U.compareAndSwapObject(a, offset, t, null)) {
+ top = s;
+ return t;
}
}
return null;
@@ -744,15 +903,18 @@
/**
* Takes a task in FIFO order if b is base of queue and a task
* can be claimed without contention. Specialized versions
- * appear in ForkJoinPool methods scan and tryHelpStealer.
+ * appear in ForkJoinPool methods scan and helpStealer.
*/
final ForkJoinTask<?> pollAt(int b) {
- ForkJoinTask<?> t; ForkJoinTask<?>[] a;
- if ((a = array) != null) {
- int j = (((a.length - 1) & b) << ASHIFT) + ABASE;
- if ((t = (ForkJoinTask<?>)U.getObjectVolatile(a, j)) != null &&
- base == b && U.compareAndSwapObject(a, j, t, null)) {
- U.putOrderedInt(this, QBASE, b + 1);
+ ForkJoinTask<?>[] a; int al;
+ if ((a = array) != null && (al = a.length) > 0) {
+ int index = (al - 1) & b;
+ long offset = ((long)index << ASHIFT) + ABASE;
+ ForkJoinTask<?> t = (ForkJoinTask<?>)
+ U.getObjectVolatile(a, offset);
+ if (t != null && b++ == base &&
+ U.compareAndSwapObject(a, offset, t, null)) {
+ base = b;
return t;
}
}
@@ -763,21 +925,27 @@
* Takes next task, if one exists, in FIFO order.
*/
final ForkJoinTask<?> poll() {
- ForkJoinTask<?>[] a; int b; ForkJoinTask<?> t;
- while ((b = base) - top < 0 && (a = array) != null) {
- int j = (((a.length - 1) & b) << ASHIFT) + ABASE;
- t = (ForkJoinTask<?>)U.getObjectVolatile(a, j);
- if (t != null) {
- if (U.compareAndSwapObject(a, j, t, null)) {
- U.putOrderedInt(this, QBASE, b + 1);
- return t;
+ for (;;) {
+ int b = base, s = top, d, al; ForkJoinTask<?>[] a;
+ if ((a = array) != null && (d = b - s) < 0 &&
+ (al = a.length) > 0) {
+ int index = (al - 1) & b;
+ long offset = ((long)index << ASHIFT) + ABASE;
+ ForkJoinTask<?> t = (ForkJoinTask<?>)
+ U.getObjectVolatile(a, offset);
+ if (b++ == base) {
+ if (t != null) {
+ if (U.compareAndSwapObject(a, offset, t, null)) {
+ base = b;
+ return t;
+ }
+ }
+ else if (d == -1)
+ break; // now empty
}
}
- else if (base == b) {
- if (b + 1 == top)
- break;
- Thread.yield(); // wait for lagging update (very rare)
- }
+ else
+ break;
}
return null;
}
@@ -786,218 +954,350 @@
* Takes next task, if one exists, in order specified by mode.
*/
final ForkJoinTask<?> nextLocalTask() {
- return mode == 0 ? pop() : poll();
+ return (config < 0) ? poll() : pop();
}
/**
* Returns next task, if one exists, in order specified by mode.
*/
final ForkJoinTask<?> peek() {
- ForkJoinTask<?>[] a = array; int m;
- if (a == null || (m = a.length - 1) < 0)
- return null;
- int i = mode == 0 ? top - 1 : base;
- int j = ((i & m) << ASHIFT) + ABASE;
- return (ForkJoinTask<?>)U.getObjectVolatile(a, j);
+ int al; ForkJoinTask<?>[] a;
+ return ((a = array) != null && (al = a.length) > 0) ?
+ a[(al - 1) & (config < 0 ? base : top - 1)] : null;
}
/**
* Pops the given task only if it is at the current top.
- * (A shared version is available only via FJP.tryExternalUnpush)
*/
- final boolean tryUnpush(ForkJoinTask<?> t) {
- ForkJoinTask<?>[] a; int s;
- if ((a = array) != null && (s = top) != base &&
- U.compareAndSwapObject
- (a, (((a.length - 1) & --s) << ASHIFT) + ABASE, t, null)) {
- top = s;
- return true;
+ final boolean tryUnpush(ForkJoinTask<?> task) {
+ int b = base, s = top, al; ForkJoinTask<?>[] a;
+ if ((a = array) != null && b != s && (al = a.length) > 0) {
+ int index = (al - 1) & --s;
+ long offset = ((long)index << ASHIFT) + ABASE;
+ if (U.compareAndSwapObject(a, offset, task, null)) {
+ top = s;
+ return true;
+ }
}
return false;
}
/**
+ * Shared version of push. Fails if already locked.
+ *
+ * @return status: > 0 locked, 0 possibly was empty, < 0 was nonempty
+ */
+ final int sharedPush(ForkJoinTask<?> task) {
+ int stat;
+ if (U.compareAndSwapInt(this, QLOCK, 0, 1)) {
+ int b = base, s = top, al, d; ForkJoinTask<?>[] a;
+ if ((a = array) != null && (al = a.length) > 0 &&
+ al - 1 + (d = b - s) > 0) {
+ a[(al - 1) & s] = task;
+ top = s + 1; // relaxed writes OK here
+ qlock = 0;
+ stat = (d < 0 && b == base) ? d : 0;
+ }
+ else {
+ growAndSharedPush(task);
+ stat = 0;
+ }
+ }
+ else
+ stat = 1;
+ return stat;
+ }
+
+ /**
+ * Helper for sharedPush; called only when locked and resize
+ * needed.
+ */
+ private void growAndSharedPush(ForkJoinTask<?> task) {
+ try {
+ growArray();
+ int s = top, al; ForkJoinTask<?>[] a;
+ if ((a = array) != null && (al = a.length) > 0) {
+ a[(al - 1) & s] = task;
+ top = s + 1;
+ }
+ } finally {
+ qlock = 0;
+ }
+ }
+
+ /**
+ * Shared version of tryUnpush.
+ */
+ final boolean trySharedUnpush(ForkJoinTask<?> task) {
+ boolean popped = false;
+ int s = top - 1, al; ForkJoinTask<?>[] a;
+ if ((a = array) != null && (al = a.length) > 0) {
+ int index = (al - 1) & s;
+ long offset = ((long)index << ASHIFT) + ABASE;
+ ForkJoinTask<?> t = (ForkJoinTask<?>) U.getObject(a, offset);
+ if (t == task &&
+ U.compareAndSwapInt(this, QLOCK, 0, 1)) {
+ if (top == s + 1 && array == a &&
+ U.compareAndSwapObject(a, offset, task, null)) {
+ popped = true;
+ top = s;
+ }
+ U.putOrderedInt(this, QLOCK, 0);
+ }
+ }
+ return popped;
+ }
+
+ /**
* Removes and cancels all known tasks, ignoring any exceptions.
*/
final void cancelAll() {
- ForkJoinTask.cancelIgnoringExceptions(currentJoin);
- ForkJoinTask.cancelIgnoringExceptions(currentSteal);
- for (ForkJoinTask<?> t; (t = poll()) != null; )
+ ForkJoinTask<?> t;
+ if ((t = currentJoin) != null) {
+ currentJoin = null;
+ ForkJoinTask.cancelIgnoringExceptions(t);
+ }
+ if ((t = currentSteal) != null) {
+ currentSteal = null;
+ ForkJoinTask.cancelIgnoringExceptions(t);
+ }
+ while ((t = poll()) != null)
ForkJoinTask.cancelIgnoringExceptions(t);
}
// Specialized execution methods
/**
- * Polls and runs tasks until empty.
+ * Pops and executes up to POLL_LIMIT tasks or until empty.
*/
- final void pollAndExecAll() {
- for (ForkJoinTask<?> t; (t = poll()) != null;)
- t.doExec();
+ final void localPopAndExec() {
+ for (int nexec = 0;;) {
+ int b = base, s = top, al; ForkJoinTask<?>[] a;
+ if ((a = array) != null && b != s && (al = a.length) > 0) {
+ int index = (al - 1) & --s;
+ long offset = ((long)index << ASHIFT) + ABASE;
+ ForkJoinTask<?> t = (ForkJoinTask<?>)
+ U.getAndSetObject(a, offset, null);
+ if (t != null) {
+ top = s;
+ (currentSteal = t).doExec();
+ if (++nexec > POLL_LIMIT)
+ break;
+ }
+ else
+ break;
+ }
+ else
+ break;
+ }
}
/**
- * Executes a top-level task and any local tasks remaining
- * after execution.
+ * Polls and executes up to POLL_LIMIT tasks or until empty.
+ */
+ final void localPollAndExec() {
+ for (int nexec = 0;;) {
+ int b = base, s = top, al; ForkJoinTask<?>[] a;
+ if ((a = array) != null && b != s && (al = a.length) > 0) {
+ int index = (al - 1) & b++;
+ long offset = ((long)index << ASHIFT) + ABASE;
+ ForkJoinTask<?> t = (ForkJoinTask<?>)
+ U.getAndSetObject(a, offset, null);
+ if (t != null) {
+ base = b;
+ t.doExec();
+ if (++nexec > POLL_LIMIT)
+ break;
+ }
+ }
+ else
+ break;
+ }
+ }
+
+ /**
+ * Executes the given task and (some) remaining local tasks.
*/
final void runTask(ForkJoinTask<?> task) {
- if ((currentSteal = task) != null) {
+ if (task != null) {
task.doExec();
- ForkJoinTask<?>[] a = array;
- int md = mode;
- ++nsteals;
+ if (config < 0)
+ localPollAndExec();
+ else
+ localPopAndExec();
+ int ns = ++nsteals;
+ ForkJoinWorkerThread thread = owner;
currentSteal = null;
- if (md != 0)
- pollAndExecAll();
- else if (a != null) {
- int s, m = a.length - 1;
- while ((s = top - 1) - base >= 0) {
- long i = ((m & s) << ASHIFT) + ABASE;
- ForkJoinTask<?> t = (ForkJoinTask<?>)U.getObject(a, i);
- if (t == null)
- break;
- if (U.compareAndSwapObject(a, i, t, null)) {
- top = s;
- t.doExec();
- }
- }
+ if (ns < 0) // collect on overflow
+ transferStealCount(pool);
+ if (thread != null)
+ thread.afterTopLevelExec();
+ }
+ }
+
+ /**
+ * Adds steal count to pool steal count if it exists, and resets.
+ */
+ final void transferStealCount(ForkJoinPool p) {
+ AuxState aux;
+ if (p != null && (aux = p.auxState) != null) {
+ long s = nsteals;
+ nsteals = 0; // if negative, correct for overflow
+ if (s < 0) s = Integer.MAX_VALUE;
+ aux.lock();
+ try {
+ aux.stealCount += s;
+ } finally {
+ aux.unlock();
}
}
}
/**
* If present, removes from queue and executes the given task,
- * or any other cancelled task. Returns (true) on any CAS
- * or consistency check failure so caller can retry.
+ * or any other cancelled task. Used only by awaitJoin.
*
- * @return false if no progress can be made, else true
+ * @return true if queue empty and task not known to be done
*/
final boolean tryRemoveAndExec(ForkJoinTask<?> task) {
- boolean stat;
- ForkJoinTask<?>[] a; int m, s, b, n;
- if (task != null && (a = array) != null && (m = a.length - 1) >= 0 &&
- (n = (s = top) - (b = base)) > 0) {
- boolean removed = false, empty = true;
- stat = true;
- for (ForkJoinTask<?> t;;) { // traverse from s to b
- long j = ((--s & m) << ASHIFT) + ABASE;
- t = (ForkJoinTask<?>)U.getObject(a, j);
- if (t == null) // inconsistent length
- break;
- else if (t == task) {
- if (s + 1 == top) { // pop
- if (!U.compareAndSwapObject(a, j, task, null))
- break;
- top = s;
- removed = true;
+ if (task != null && task.status >= 0) {
+ int b, s, d, al; ForkJoinTask<?>[] a;
+ while ((d = (b = base) - (s = top)) < 0 &&
+ (a = array) != null && (al = a.length) > 0) {
+ for (;;) { // traverse from s to b
+ int index = --s & (al - 1);
+ long offset = (index << ASHIFT) + ABASE;
+ ForkJoinTask<?> t = (ForkJoinTask<?>)
+ U.getObjectVolatile(a, offset);
+ if (t == null)
+ break; // restart
+ else if (t == task) {
+ boolean removed = false;
+ if (s + 1 == top) { // pop
+ if (U.compareAndSwapObject(a, offset, t, null)) {
+ top = s;
+ removed = true;
+ }
+ }
+ else if (base == b) // replace with proxy
+ removed = U.compareAndSwapObject(a, offset, t,
+ new EmptyTask());
+ if (removed) {
+ ForkJoinTask<?> ps = currentSteal;
+ (currentSteal = task).doExec();
+ currentSteal = ps;
+ }
+ break;
}
- else if (base == b) // replace with proxy
- removed = U.compareAndSwapObject(a, j, task,
- new EmptyTask());
- break;
+ else if (t.status < 0 && s + 1 == top) {
+ if (U.compareAndSwapObject(a, offset, t, null)) {
+ top = s;
+ }
+ break; // was cancelled
+ }
+ else if (++d == 0) {
+ if (base != b) // rescan
+ break;
+ return false;
+ }
}
- else if (t.status >= 0)
- empty = false;
- else if (s + 1 == top) { // pop and throw away
- if (U.compareAndSwapObject(a, j, t, null))
- top = s;
- break;
- }
- if (--n == 0) {
- if (!empty && base == b)
- stat = false;
- break;
+ if (task.status < 0)
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Pops task if in the same CC computation as the given task,
+ * in either shared or owned mode. Used only by helpComplete.
+ */
+ final CountedCompleter<?> popCC(CountedCompleter<?> task, int mode) {
+ int b = base, s = top, al; ForkJoinTask<?>[] a;
+ if ((a = array) != null && b != s && (al = a.length) > 0) {
+ int index = (al - 1) & (s - 1);
+ long offset = ((long)index << ASHIFT) + ABASE;
+ ForkJoinTask<?> o = (ForkJoinTask<?>)
+ U.getObjectVolatile(a, offset);
+ if (o instanceof CountedCompleter) {
+ CountedCompleter<?> t = (CountedCompleter<?>)o;
+ for (CountedCompleter<?> r = t;;) {
+ if (r == task) {
+ if ((mode & IS_OWNED) == 0) {
+ boolean popped = false;
+ if (U.compareAndSwapInt(this, QLOCK, 0, 1)) {
+ if (top == s && array == a &&
+ U.compareAndSwapObject(a, offset,
+ t, null)) {
+ popped = true;
+ top = s - 1;
+ }
+ U.putOrderedInt(this, QLOCK, 0);
+ if (popped)
+ return t;
+ }
+ }
+ else if (U.compareAndSwapObject(a, offset,
+ t, null)) {
+ top = s - 1;
+ return t;
+ }
+ break;
+ }
+ else if ((r = r.completer) == null) // try parent
+ break;
}
}
- if (removed)
- task.doExec();
+ }
+ return null;
+ }
+
+ /**
+ * Steals and runs a task in the same CC computation as the
+ * given task if one exists and can be taken without
+ * contention. Otherwise returns a checksum/control value for
+ * use by method helpComplete.
+ *
+ * @return 1 if successful, 2 if retryable (lost to another
+ * stealer), -1 if non-empty but no matching task found, else
+ * the base index, forced negative.
+ */
+ final int pollAndExecCC(CountedCompleter<?> task) {
+ ForkJoinTask<?>[] a;
+ int b = base, s = top, al, h;
+ if ((a = array) != null && b != s && (al = a.length) > 0) {
+ int index = (al - 1) & b;
+ long offset = ((long)index << ASHIFT) + ABASE;
+ ForkJoinTask<?> o = (ForkJoinTask<?>)
+ U.getObjectVolatile(a, offset);
+ if (o == null)
+ h = 2; // retryable
+ else if (!(o instanceof CountedCompleter))
+ h = -1; // unmatchable
+ else {
+ CountedCompleter<?> t = (CountedCompleter<?>)o;
+ for (CountedCompleter<?> r = t;;) {
+ if (r == task) {
+ if (b++ == base &&
+ U.compareAndSwapObject(a, offset, t, null)) {
+ base = b;
+ t.doExec();
+ h = 1; // success
+ }
+ else
+ h = 2; // lost CAS
+ break;
+ }
+ else if ((r = r.completer) == null) {
+ h = -1; // unmatched
+ break;
+ }
+ }
+ }
}
else
- stat = false;
- return stat;
- }
-
- /**
- * Tries to poll for and execute the given task or any other
- * task in its CountedCompleter computation.
- */
- final boolean pollAndExecCC(CountedCompleter<?> root) {
- ForkJoinTask<?>[] a; int b; Object o; CountedCompleter<?> t, r;
- if ((b = base) - top < 0 && (a = array) != null) {
- long j = (((a.length - 1) & b) << ASHIFT) + ABASE;
- if ((o = U.getObjectVolatile(a, j)) == null)
- return true; // retry
- if (o instanceof CountedCompleter) {
- for (t = (CountedCompleter<?>)o, r = t;;) {
- if (r == root) {
- if (base == b &&
- U.compareAndSwapObject(a, j, t, null)) {
- U.putOrderedInt(this, QBASE, b + 1);
- t.doExec();
- }
- return true;
- }
- else if ((r = r.completer) == null)
- break; // not part of root computation
- }
- }
- }
- return false;
- }
-
- /**
- * Tries to pop and execute the given task or any other task
- * in its CountedCompleter computation.
- */
- final boolean externalPopAndExecCC(CountedCompleter<?> root) {
- ForkJoinTask<?>[] a; int s; Object o; CountedCompleter<?> t, r;
- if (base - (s = top) < 0 && (a = array) != null) {
- long j = (((a.length - 1) & (s - 1)) << ASHIFT) + ABASE;
- if ((o = U.getObject(a, j)) instanceof CountedCompleter) {
- for (t = (CountedCompleter<?>)o, r = t;;) {
- if (r == root) {
- if (U.compareAndSwapInt(this, QLOCK, 0, 1)) {
- if (top == s && array == a &&
- U.compareAndSwapObject(a, j, t, null)) {
- top = s - 1;
- qlock = 0;
- t.doExec();
- }
- else
- qlock = 0;
- }
- return true;
- }
- else if ((r = r.completer) == null)
- break;
- }
- }
- }
- return false;
- }
-
- /**
- * Internal version
- */
- final boolean internalPopAndExecCC(CountedCompleter<?> root) {
- ForkJoinTask<?>[] a; int s; Object o; CountedCompleter<?> t, r;
- if (base - (s = top) < 0 && (a = array) != null) {
- long j = (((a.length - 1) & (s - 1)) << ASHIFT) + ABASE;
- if ((o = U.getObject(a, j)) instanceof CountedCompleter) {
- for (t = (CountedCompleter<?>)o, r = t;;) {
- if (r == root) {
- if (U.compareAndSwapObject(a, j, t, null)) {
- top = s - 1;
- t.doExec();
- }
- return true;
- }
- else if ((r = r.completer) == null)
- break;
- }
- }
- }
- return false;
+ h = b | Integer.MIN_VALUE; // to sense movement on re-poll
+ return h;
}
/**
@@ -1005,34 +1305,28 @@
*/
final boolean isApparentlyUnblocked() {
Thread wt; Thread.State s;
- return (eventCount >= 0 &&
+ return (scanState >= 0 &&
(wt = owner) != null &&
(s = wt.getState()) != Thread.State.BLOCKED &&
s != Thread.State.WAITING &&
s != Thread.State.TIMED_WAITING);
}
- // Unsafe mechanics
- private static final sun.misc.Unsafe U;
- private static final long QBASE;
+ // Unsafe mechanics. Note that some are (and must be) the same as in FJP
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
private static final long QLOCK;
private static final int ABASE;
private static final int ASHIFT;
static {
try {
- U = sun.misc.Unsafe.getUnsafe();
- Class<?> k = WorkQueue.class;
- Class<?> ak = ForkJoinTask[].class;
- QBASE = U.objectFieldOffset
- (k.getDeclaredField("base"));
QLOCK = U.objectFieldOffset
- (k.getDeclaredField("qlock"));
- ABASE = U.arrayBaseOffset(ak);
- int scale = U.arrayIndexScale(ak);
+ (WorkQueue.class.getDeclaredField("qlock"));
+ ABASE = U.arrayBaseOffset(ForkJoinTask[].class);
+ int scale = U.arrayIndexScale(ForkJoinTask[].class);
if ((scale & (scale - 1)) != 0)
- throw new Error("data type scale not a power of two");
+ throw new Error("array index scale not a power of two");
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
- } catch (Exception e) {
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
@@ -1041,15 +1335,6 @@
// static fields (initialized in static initializer below)
/**
- * Per-thread submission bookkeeping. Shared across all pools
- * to reduce ThreadLocal pollution and because random motion
- * to avoid contention in one pool is likely to hold for others.
- * Lazily initialized on first submission (but null-checked
- * in other contexts to avoid unnecessary initialization).
- */
- static final ThreadLocal<Submitter> submitters;
-
- /**
* Creates a new ForkJoinWorkerThread. This factory is used unless
* overridden in ForkJoinPool constructors.
*/
@@ -1058,9 +1343,9 @@
/**
* Permission required for callers of methods that may start or
- * kill threads.
+ * kill threads. Also used as a static lock in tryInitialize.
*/
- private static final RuntimePermission modifyThreadPermission;
+ static final RuntimePermission modifyThreadPermission;
/**
* Common (static) pool. Non-null for public use unless a static
@@ -1076,7 +1361,12 @@
* common.parallelism field to be zero, but in that case still report
* parallelism as 1 to reflect resulting caller-runs mechanics.
*/
- static final int commonParallelism;
+ static final int COMMON_PARALLELISM;
+
+ /**
+ * Limit on spare thread construction in tryCompensate.
+ */
+ private static final int COMMON_MAX_SPARES;
/**
* Sequence number for creating workerNamePrefix.
@@ -1091,270 +1381,215 @@
return ++poolNumberSequence;
}
- // static constants
+ // static configuration constants
/**
- * Initial timeout value (in nanoseconds) for the thread
+ * Initial timeout value (in milliseconds) for the thread
* triggering quiescence to park waiting for new work. On timeout,
- * the thread will instead try to shrink the number of
- * workers. The value should be large enough to avoid overly
- * aggressive shrinkage during most transient stalls (long GCs
- * etc).
+ * the thread will instead try to shrink the number of workers.
+ * The value should be large enough to avoid overly aggressive
+ * shrinkage during most transient stalls (long GCs etc).
*/
- private static final long IDLE_TIMEOUT = 2000L * 1000L * 1000L; // 2sec
+ private static final long IDLE_TIMEOUT_MS = 2000L; // 2sec
/**
- * Timeout value when there are more threads than parallelism level
+ * Tolerance for idle timeouts, to cope with timer undershoots.
*/
- private static final long FAST_IDLE_TIMEOUT = 200L * 1000L * 1000L;
+ private static final long TIMEOUT_SLOP_MS = 20L; // 20ms
/**
- * Tolerance for idle timeouts, to cope with timer undershoots
+ * The default value for COMMON_MAX_SPARES. Overridable using the
+ * "java.util.concurrent.ForkJoinPool.common.maximumSpares" system
+ * property. The default value is far in excess of normal
+ * requirements, but also far short of MAX_CAP and typical OS
+ * thread limits, so allows JVMs to catch misuse/abuse before
+ * running out of resources needed to do so.
*/
- private static final long TIMEOUT_SLOP = 2000000L;
-
- /**
- * The maximum stolen->joining link depth allowed in method
- * tryHelpStealer. Must be a power of two. Depths for legitimate
- * chains are unbounded, but we use a fixed constant to avoid
- * (otherwise unchecked) cycles and to bound staleness of
- * traversal parameters at the expense of sometimes blocking when
- * we could be helping.
- */
- private static final int MAX_HELP = 64;
+ private static final int DEFAULT_COMMON_MAX_SPARES = 256;
/**
* Increment for seed generators. See class ThreadLocal for
* explanation.
*/
- private static final int SEED_INCREMENT = 0x61c88647;
+ private static final int SEED_INCREMENT = 0x9e3779b9;
/*
- * Bits and masks for control variables
+ * Bits and masks for field ctl, packed with 4 16 bit subfields:
+ * AC: Number of active running workers minus target parallelism
+ * TC: Number of total workers minus target parallelism
+ * SS: version count and status of top waiting thread
+ * ID: poolIndex of top of Treiber stack of waiters
*
- * Field ctl is a long packed with:
- * AC: Number of active running workers minus target parallelism (16 bits)
- * TC: Number of total workers minus target parallelism (16 bits)
- * ST: true if pool is terminating (1 bit)
- * EC: the wait count of top waiting thread (15 bits)
- * ID: poolIndex of top of Treiber stack of waiters (16 bits)
+ * When convenient, we can extract the lower 32 stack top bits
+ * (including version bits) as sp=(int)ctl. The offsets of counts
+ * by the target parallelism and the positionings of fields makes
+ * it possible to perform the most common checks via sign tests of
+ * fields: When ac is negative, there are not enough active
+ * workers, when tc is negative, there are not enough total
+ * workers. When sp is non-zero, there are waiting workers. To
+ * deal with possibly negative fields, we use casts in and out of
+ * "short" and/or signed shifts to maintain signedness.
*
- * When convenient, we can extract the upper 32 bits of counts and
- * the lower 32 bits of queue state, u = (int)(ctl >>> 32) and e =
- * (int)ctl. The ec field is never accessed alone, but always
- * together with id and st. The offsets of counts by the target
- * parallelism and the positionings of fields makes it possible to
- * perform the most common checks via sign tests of fields: When
- * ac is negative, there are not enough active workers, when tc is
- * negative, there are not enough total workers, and when e is
- * negative, the pool is terminating. To deal with these possibly
- * negative fields, we use casts in and out of "short" and/or
- * signed shifts to maintain signedness.
- *
- * When a thread is queued (inactivated), its eventCount field is
- * set negative, which is the only way to tell if a worker is
- * prevented from executing tasks, even though it must continue to
- * scan for them to avoid queuing races. Note however that
- * eventCount updates lag releases so usage requires care.
- *
- * Field plock is an int packed with:
- * SHUTDOWN: true if shutdown is enabled (1 bit)
- * SEQ: a sequence lock, with PL_LOCK bit set if locked (30 bits)
- * SIGNAL: set when threads may be waiting on the lock (1 bit)
- *
- * The sequence number enables simple consistency checks:
- * Staleness of read-only operations on the workQueues array can
- * be checked by comparing plock before vs after the reads.
+ * Because it occupies uppermost bits, we can add one active count
+ * using getAndAddLong of AC_UNIT, rather than CAS, when returning
+ * from a blocked join. Other updates entail multiple subfields
+ * and masking, requiring CAS.
*/
- // bit positions/shifts for fields
+ // Lower and upper word masks
+ private static final long SP_MASK = 0xffffffffL;
+ private static final long UC_MASK = ~SP_MASK;
+
+ // Active counts
private static final int AC_SHIFT = 48;
+ private static final long AC_UNIT = 0x0001L << AC_SHIFT;
+ private static final long AC_MASK = 0xffffL << AC_SHIFT;
+
+ // Total counts
private static final int TC_SHIFT = 32;
- private static final int ST_SHIFT = 31;
- private static final int EC_SHIFT = 16;
+ private static final long TC_UNIT = 0x0001L << TC_SHIFT;
+ private static final long TC_MASK = 0xffffL << TC_SHIFT;
+ private static final long ADD_WORKER = 0x0001L << (TC_SHIFT + 15); // sign
- // bounds
- private static final int SMASK = 0xffff; // short bits
- private static final int MAX_CAP = 0x7fff; // max #workers - 1
- private static final int EVENMASK = 0xfffe; // even short bits
- private static final int SQMASK = 0x007e; // max 64 (even) slots
- private static final int SHORT_SIGN = 1 << 15;
- private static final int INT_SIGN = 1 << 31;
-
- // masks
- private static final long STOP_BIT = 0x0001L << ST_SHIFT;
- private static final long AC_MASK = ((long)SMASK) << AC_SHIFT;
- private static final long TC_MASK = ((long)SMASK) << TC_SHIFT;
-
- // units for incrementing and decrementing
- private static final long TC_UNIT = 1L << TC_SHIFT;
- private static final long AC_UNIT = 1L << AC_SHIFT;
-
- // masks and units for dealing with u = (int)(ctl >>> 32)
- private static final int UAC_SHIFT = AC_SHIFT - 32;
- private static final int UTC_SHIFT = TC_SHIFT - 32;
- private static final int UAC_MASK = SMASK << UAC_SHIFT;
- private static final int UTC_MASK = SMASK << UTC_SHIFT;
- private static final int UAC_UNIT = 1 << UAC_SHIFT;
- private static final int UTC_UNIT = 1 << UTC_SHIFT;
-
- // masks and units for dealing with e = (int)ctl
- private static final int E_MASK = 0x7fffffff; // no STOP_BIT
- private static final int E_SEQ = 1 << EC_SHIFT;
-
- // plock bits
- private static final int SHUTDOWN = 1 << 31;
- private static final int PL_LOCK = 2;
- private static final int PL_SIGNAL = 1;
- private static final int PL_SPINS = 1 << 8;
-
- // access mode for WorkQueue
- static final int LIFO_QUEUE = 0;
- static final int FIFO_QUEUE = 1;
- static final int SHARED_QUEUE = -1;
-
- // Heuristic padding to ameliorate unfortunate memory placements
- volatile long pad00, pad01, pad02, pad03, pad04, pad05, pad06;
+ // runState bits: SHUTDOWN must be negative, others arbitrary powers of two
+ private static final int STARTED = 1;
+ private static final int STOP = 1 << 1;
+ private static final int TERMINATED = 1 << 2;
+ private static final int SHUTDOWN = 1 << 31;
// Instance fields
- volatile long stealCount; // collects worker counts
- volatile long ctl; // main pool control
- volatile int plock; // shutdown status and seqLock
- volatile int indexSeed; // worker/submitter index seed
- final short parallelism; // parallelism level
- final short mode; // LIFO/FIFO
- WorkQueue[] workQueues; // main registry
+ volatile long ctl; // main pool control
+ volatile int runState;
+ final int config; // parallelism, mode
+ AuxState auxState; // lock, steal counts
+ volatile WorkQueue[] workQueues; // main registry
+ final String workerNamePrefix; // to create worker name string
final ForkJoinWorkerThreadFactory factory;
- final UncaughtExceptionHandler ueh; // per-worker UEH
- final String workerNamePrefix; // to create worker name string
-
- volatile Object pad10, pad11, pad12, pad13, pad14, pad15, pad16, pad17;
- volatile Object pad18, pad19, pad1a, pad1b;
+ final UncaughtExceptionHandler ueh; // per-worker UEH
/**
- * Acquires the plock lock to protect worker array and related
- * updates. This method is called only if an initial CAS on plock
- * fails. This acts as a spinlock for normal cases, but falls back
- * to builtin monitor to block when (rarely) needed. This would be
- * a terrible idea for a highly contended lock, but works fine as
- * a more conservative alternative to a pure spinlock.
+ * Instantiates fields upon first submission, or upon shutdown if
+ * no submissions. If checkTermination true, also responds to
+ * termination by external calls submitting tasks.
*/
- private int acquirePlock() {
- int spins = PL_SPINS, ps, nps;
- for (;;) {
- if (((ps = plock) & PL_LOCK) == 0 &&
- U.compareAndSwapInt(this, PLOCK, ps, nps = ps + PL_LOCK))
- return nps;
- else if (spins >= 0) {
- if (ThreadLocalRandom.current().nextInt() >= 0)
- --spins;
- }
- else if (U.compareAndSwapInt(this, PLOCK, ps, ps | PL_SIGNAL)) {
- synchronized (this) {
- if ((plock & PL_SIGNAL) != 0) {
- try {
- wait();
- } catch (InterruptedException ie) {
- try {
- Thread.currentThread().interrupt();
- } catch (SecurityException ignore) {
- }
- }
- }
- else
- notifyAll();
+ private void tryInitialize(boolean checkTermination) {
+ if (runState == 0) { // bootstrap by locking static field
+ int p = config & SMASK;
+ int n = (p > 1) ? p - 1 : 1; // ensure at least 2 slots
+ n |= n >>> 1; // create workQueues array with size a power of two
+ n |= n >>> 2;
+ n |= n >>> 4;
+ n |= n >>> 8;
+ n |= n >>> 16;
+ n = ((n + 1) << 1) & SMASK;
+ AuxState aux = new AuxState();
+ WorkQueue[] ws = new WorkQueue[n];
+ synchronized (modifyThreadPermission) { // double-check
+ if (runState == 0) {
+ workQueues = ws;
+ auxState = aux;
+ runState = STARTED;
}
}
}
+ if (checkTermination && runState < 0) {
+ tryTerminate(false, false); // help terminate
+ throw new RejectedExecutionException();
+ }
+ }
+
+ // Creating, registering and deregistering workers
+
+ /**
+ * Tries to construct and start one worker. Assumes that total
+ * count has already been incremented as a reservation. Invokes
+ * deregisterWorker on any failure.
+ *
+ * @param isSpare true if this is a spare thread
+ * @return true if successful
+ */
+ private boolean createWorker(boolean isSpare) {
+ ForkJoinWorkerThreadFactory fac = factory;
+ Throwable ex = null;
+ ForkJoinWorkerThread wt = null;
+ WorkQueue q;
+ try {
+ if (fac != null && (wt = fac.newThread(this)) != null) {
+ if (isSpare && (q = wt.workQueue) != null)
+ q.config |= SPARE_WORKER;
+ wt.start();
+ return true;
+ }
+ } catch (Throwable rex) {
+ ex = rex;
+ }
+ deregisterWorker(wt, ex);
+ return false;
}
/**
- * Unlocks and signals any thread waiting for plock. Called only
- * when CAS of seq value for unlock fails.
+ * Tries to add one worker, incrementing ctl counts before doing
+ * so, relying on createWorker to back out on failure.
+ *
+ * @param c incoming ctl value, with total count negative and no
+ * idle workers. On CAS failure, c is refreshed and retried if
+ * this holds (otherwise, a new worker is not needed).
*/
- private void releasePlock(int ps) {
- plock = ps;
- synchronized (this) { notifyAll(); }
- }
-
- /**
- * Tries to create and start one worker if fewer than target
- * parallelism level exist. Adjusts counts etc on failure.
- */
- private void tryAddWorker() {
- long c; int u, e;
- while ((u = (int)((c = ctl) >>> 32)) < 0 &&
- (u & SHORT_SIGN) != 0 && (e = (int)c) >= 0) {
- long nc = ((long)(((u + UTC_UNIT) & UTC_MASK) |
- ((u + UAC_UNIT) & UAC_MASK)) << 32) | (long)e;
- if (U.compareAndSwapLong(this, CTL, c, nc)) {
- ForkJoinWorkerThreadFactory fac;
- Throwable ex = null;
- ForkJoinWorkerThread wt = null;
- try {
- if ((fac = factory) != null &&
- (wt = fac.newThread(this)) != null) {
- wt.start();
- break;
- }
- } catch (Throwable rex) {
- ex = rex;
- }
- deregisterWorker(wt, ex);
+ private void tryAddWorker(long c) {
+ do {
+ long nc = ((AC_MASK & (c + AC_UNIT)) |
+ (TC_MASK & (c + TC_UNIT)));
+ if (ctl == c && U.compareAndSwapLong(this, CTL, c, nc)) {
+ createWorker(false);
break;
}
- }
+ } while (((c = ctl) & ADD_WORKER) != 0L && (int)c == 0);
}
- // Registering and deregistering workers
-
/**
- * Callback from ForkJoinWorkerThread to establish and record its
- * WorkQueue. To avoid scanning bias due to packing entries in
- * front of the workQueues array, we treat the array as a simple
- * power-of-two hash table using per-thread seed as hash,
- * expanding as needed.
+ * Callback from ForkJoinWorkerThread constructor to establish and
+ * record its WorkQueue.
*
* @param wt the worker thread
* @return the worker's queue
*/
final WorkQueue registerWorker(ForkJoinWorkerThread wt) {
- UncaughtExceptionHandler handler; WorkQueue[] ws; int s, ps;
- wt.setDaemon(true);
+ UncaughtExceptionHandler handler;
+ AuxState aux;
+ wt.setDaemon(true); // configure thread
if ((handler = ueh) != null)
wt.setUncaughtExceptionHandler(handler);
- do {} while (!U.compareAndSwapInt(this, INDEXSEED, s = indexSeed,
- s += SEED_INCREMENT) ||
- s == 0); // skip 0
- WorkQueue w = new WorkQueue(this, wt, mode, s);
- if (((ps = plock) & PL_LOCK) != 0 ||
- !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
- ps = acquirePlock();
- int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN);
- try {
- if ((ws = workQueues) != null) { // skip if shutting down
- int n = ws.length, m = n - 1;
- int r = (s << 1) | 1; // use odd-numbered indices
- if (ws[r &= m] != null) { // collision
- int probes = 0; // step by approx half size
- int step = (n <= 4) ? 2 : ((n >>> 1) & EVENMASK) + 2;
- while (ws[r = (r + step) & m] != null) {
- if (++probes >= n) {
- workQueues = ws = Arrays.copyOf(ws, n <<= 1);
- m = n - 1;
- probes = 0;
+ WorkQueue w = new WorkQueue(this, wt);
+ int i = 0; // assign a pool index
+ int mode = config & MODE_MASK;
+ if ((aux = auxState) != null) {
+ aux.lock();
+ try {
+ int s = (int)(aux.indexSeed += SEED_INCREMENT), n, m;
+ WorkQueue[] ws = workQueues;
+ if (ws != null && (n = ws.length) > 0) {
+ i = (m = n - 1) & ((s << 1) | 1); // odd-numbered indices
+ if (ws[i] != null) { // collision
+ int probes = 0; // step by approx half n
+ int step = (n <= 4) ? 2 : ((n >>> 1) & EVENMASK) + 2;
+ while (ws[i = (i + step) & m] != null) {
+ if (++probes >= n) {
+ workQueues = ws = Arrays.copyOf(ws, n <<= 1);
+ m = n - 1;
+ probes = 0;
+ }
}
}
+ w.hint = s; // use as random seed
+ w.config = i | mode;
+ w.scanState = i | (s & 0x7fff0000); // random seq bits
+ ws[i] = w;
}
- w.poolIndex = (short)r;
- w.eventCount = r; // volatile write orders
- ws[r] = w;
+ } finally {
+ aux.unlock();
}
- } finally {
- if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
- releasePlock(nps);
}
- wt.setName(workerNamePrefix.concat(Integer.toString(w.poolIndex >>> 1)));
+ wt.setName(workerNamePrefix.concat(Integer.toString(i >>> 1)));
return w;
}
@@ -1370,383 +1605,231 @@
final void deregisterWorker(ForkJoinWorkerThread wt, Throwable ex) {
WorkQueue w = null;
if (wt != null && (w = wt.workQueue) != null) {
- int ps; long sc;
- w.qlock = -1; // ensure set
- do {} while (!U.compareAndSwapLong(this, STEALCOUNT,
- sc = stealCount,
- sc + w.nsteals));
- if (((ps = plock) & PL_LOCK) != 0 ||
- !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
- ps = acquirePlock();
- int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN);
- try {
- int idx = w.poolIndex;
- WorkQueue[] ws = workQueues;
- if (ws != null && idx >= 0 && idx < ws.length && ws[idx] == w)
- ws[idx] = null;
- } finally {
- if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
- releasePlock(nps);
+ AuxState aux; WorkQueue[] ws; // remove index from array
+ int idx = w.config & SMASK;
+ int ns = w.nsteals;
+ if ((aux = auxState) != null) {
+ aux.lock();
+ try {
+ if ((ws = workQueues) != null && ws.length > idx &&
+ ws[idx] == w)
+ ws[idx] = null;
+ aux.stealCount += ns;
+ } finally {
+ aux.unlock();
+ }
}
}
-
- long c; // adjust ctl counts
- do {} while (!U.compareAndSwapLong
- (this, CTL, c = ctl, (((c - AC_UNIT) & AC_MASK) |
- ((c - TC_UNIT) & TC_MASK) |
- (c & ~(AC_MASK|TC_MASK)))));
-
- if (!tryTerminate(false, false) && w != null && w.array != null) {
- w.cancelAll(); // cancel remaining tasks
- WorkQueue[] ws; WorkQueue v; Thread p; int u, i, e;
- while ((u = (int)((c = ctl) >>> 32)) < 0 && (e = (int)c) >= 0) {
- if (e > 0) { // activate or create replacement
- if ((ws = workQueues) == null ||
- (i = e & SMASK) >= ws.length ||
- (v = ws[i]) == null)
- break;
- long nc = (((long)(v.nextWait & E_MASK)) |
- ((long)(u + UAC_UNIT) << 32));
- if (v.eventCount != (e | INT_SIGN))
- break;
- if (U.compareAndSwapLong(this, CTL, c, nc)) {
- v.eventCount = (e + E_SEQ) & E_MASK;
- if ((p = v.parker) != null)
- U.unpark(p);
- break;
- }
- }
- else {
- if ((short)u < 0)
- tryAddWorker();
+ if (w == null || (w.config & UNREGISTERED) == 0) { // else pre-adjusted
+ long c; // decrement counts
+ do {} while (!U.compareAndSwapLong
+ (this, CTL, c = ctl, ((AC_MASK & (c - AC_UNIT)) |
+ (TC_MASK & (c - TC_UNIT)) |
+ (SP_MASK & c))));
+ }
+ if (w != null) {
+ w.currentSteal = null;
+ w.qlock = -1; // ensure set
+ w.cancelAll(); // cancel remaining tasks
+ }
+ while (tryTerminate(false, false) >= 0) { // possibly replace
+ WorkQueue[] ws; int wl, sp; long c;
+ if (w == null || w.array == null ||
+ (ws = workQueues) == null || (wl = ws.length) <= 0)
+ break;
+ else if ((sp = (int)(c = ctl)) != 0) { // wake up replacement
+ if (tryRelease(c, ws[(wl - 1) & sp], AC_UNIT))
break;
- }
}
+ else if (ex != null && (c & ADD_WORKER) != 0L) {
+ tryAddWorker(c); // create replacement
+ break;
+ }
+ else // don't need replacement
+ break;
}
- if (ex == null) // help clean refs on way out
+ if (ex == null) // help clean on way out
ForkJoinTask.helpExpungeStaleExceptions();
- else // rethrow
+ else // rethrow
ForkJoinTask.rethrow(ex);
}
- // Submissions
-
- /**
- * Per-thread records for threads that submit to pools. Currently
- * holds only pseudo-random seed / index that is used to choose
- * submission queues in method externalPush. In the future, this may
- * also incorporate a means to implement different task rejection
- * and resubmission policies.
- *
- * Seeds for submitters and workers/workQueues work in basically
- * the same way but are initialized and updated using slightly
- * different mechanics. Both are initialized using the same
- * approach as in class ThreadLocal, where successive values are
- * unlikely to collide with previous values. Seeds are then
- * randomly modified upon collisions using xorshifts, which
- * requires a non-zero seed.
- */
- static final class Submitter {
- int seed;
- Submitter(int s) { seed = s; }
- }
-
- /**
- * Unless shutting down, adds the given task to a submission queue
- * at submitter's current queue index (modulo submission
- * range). Only the most common path is directly handled in this
- * method. All others are relayed to fullExternalPush.
- *
- * @param task the task. Caller must ensure non-null.
- */
- final void externalPush(ForkJoinTask<?> task) {
- Submitter z = submitters.get();
- WorkQueue q; int r, m, s, n, am; ForkJoinTask<?>[] a;
- int ps = plock;
- WorkQueue[] ws = workQueues;
- if (z != null && ps > 0 && ws != null && (m = (ws.length - 1)) >= 0 &&
- (q = ws[m & (r = z.seed) & SQMASK]) != null && r != 0 &&
- U.compareAndSwapInt(q, QLOCK, 0, 1)) { // lock
- if ((a = q.array) != null &&
- (am = a.length - 1) > (n = (s = q.top) - q.base)) {
- int j = ((am & s) << ASHIFT) + ABASE;
- U.putOrderedObject(a, j, task);
- q.top = s + 1; // push on to deque
- q.qlock = 0;
- if (n <= 1)
- signalWork(ws, q);
- return;
- }
- q.qlock = 0;
- }
- fullExternalPush(task);
- }
-
- /**
- * Full version of externalPush. This method is called, among
- * other times, upon the first submission of the first task to the
- * pool, so must perform secondary initialization. It also
- * detects first submission by an external thread by looking up
- * its ThreadLocal, and creates a new shared queue if the one at
- * index if empty or contended. The plock lock body must be
- * exception-free (so no try/finally) so we optimistically
- * allocate new queues outside the lock and throw them away if
- * (very rarely) not needed.
- *
- * Secondary initialization occurs when plock is zero, to create
- * workQueue array and set plock to a valid value. This lock body
- * must also be exception-free. Because the plock seq value can
- * eventually wrap around zero, this method harmlessly fails to
- * reinitialize if workQueues exists, while still advancing plock.
- */
- private void fullExternalPush(ForkJoinTask<?> task) {
- int r = 0; // random index seed
- for (Submitter z = submitters.get();;) {
- WorkQueue[] ws; WorkQueue q; int ps, m, k;
- if (z == null) {
- if (U.compareAndSwapInt(this, INDEXSEED, r = indexSeed,
- r += SEED_INCREMENT) && r != 0)
- submitters.set(z = new Submitter(r));
- }
- else if (r == 0) { // move to a different index
- r = z.seed;
- r ^= r << 13; // same xorshift as WorkQueues
- r ^= r >>> 17;
- z.seed = r ^= (r << 5);
- }
- if ((ps = plock) < 0)
- throw new RejectedExecutionException();
- else if (ps == 0 || (ws = workQueues) == null ||
- (m = ws.length - 1) < 0) { // initialize workQueues
- int p = parallelism; // find power of two table size
- int n = (p > 1) ? p - 1 : 1; // ensure at least 2 slots
- n |= n >>> 1; n |= n >>> 2; n |= n >>> 4;
- n |= n >>> 8; n |= n >>> 16; n = (n + 1) << 1;
- WorkQueue[] nws = ((ws = workQueues) == null || ws.length == 0 ?
- new WorkQueue[n] : null);
- if (((ps = plock) & PL_LOCK) != 0 ||
- !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
- ps = acquirePlock();
- if (((ws = workQueues) == null || ws.length == 0) && nws != null)
- workQueues = nws;
- int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN);
- if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
- releasePlock(nps);
- }
- else if ((q = ws[k = r & m & SQMASK]) != null) {
- if (q.qlock == 0 && U.compareAndSwapInt(q, QLOCK, 0, 1)) {
- ForkJoinTask<?>[] a = q.array;
- int s = q.top;
- boolean submitted = false;
- try { // locked version of push
- if ((a != null && a.length > s + 1 - q.base) ||
- (a = q.growArray()) != null) { // must presize
- int j = (((a.length - 1) & s) << ASHIFT) + ABASE;
- U.putOrderedObject(a, j, task);
- q.top = s + 1;
- submitted = true;
- }
- } finally {
- q.qlock = 0; // unlock
- }
- if (submitted) {
- signalWork(ws, q);
- return;
- }
- }
- r = 0; // move on failure
- }
- else if (((ps = plock) & PL_LOCK) == 0) { // create new queue
- q = new WorkQueue(this, null, SHARED_QUEUE, r);
- q.poolIndex = (short)k;
- if (((ps = plock) & PL_LOCK) != 0 ||
- !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
- ps = acquirePlock();
- if ((ws = workQueues) != null && k < ws.length && ws[k] == null)
- ws[k] = q;
- int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN);
- if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
- releasePlock(nps);
- }
- else
- r = 0;
- }
- }
-
- // Maintaining ctl counts
-
- /**
- * Increments active count; mainly called upon return from blocking.
- */
- final void incrementActiveCount() {
- long c;
- do {} while (!U.compareAndSwapLong
- (this, CTL, c = ctl, ((c & ~AC_MASK) |
- ((c & AC_MASK) + AC_UNIT))));
- }
+ // Signalling
/**
* Tries to create or activate a worker if too few are active.
- *
- * @param ws the worker array to use to find signallees
- * @param q if non-null, the queue holding tasks to be processed
*/
- final void signalWork(WorkQueue[] ws, WorkQueue q) {
+ final void signalWork() {
for (;;) {
- long c; int e, u, i; WorkQueue w; Thread p;
- if ((u = (int)((c = ctl) >>> 32)) >= 0)
+ long c; int sp, i; WorkQueue v; WorkQueue[] ws;
+ if ((c = ctl) >= 0L) // enough workers
break;
- if ((e = (int)c) <= 0) {
- if ((short)u < 0)
- tryAddWorker();
+ else if ((sp = (int)c) == 0) { // no idle workers
+ if ((c & ADD_WORKER) != 0L) // too few workers
+ tryAddWorker(c);
break;
}
- if (ws == null || ws.length <= (i = e & SMASK) ||
- (w = ws[i]) == null)
- break;
- long nc = (((long)(w.nextWait & E_MASK)) |
- ((long)(u + UAC_UNIT)) << 32);
- int ne = (e + E_SEQ) & E_MASK;
- if (w.eventCount == (e | INT_SIGN) &&
+ else if ((ws = workQueues) == null)
+ break; // unstarted/terminated
+ else if (ws.length <= (i = sp & SMASK))
+ break; // terminated
+ else if ((v = ws[i]) == null)
+ break; // terminating
+ else {
+ int ns = sp & ~UNSIGNALLED;
+ int vs = v.scanState;
+ long nc = (v.stackPred & SP_MASK) | (UC_MASK & (c + AC_UNIT));
+ if (sp == vs && U.compareAndSwapLong(this, CTL, c, nc)) {
+ v.scanState = ns;
+ LockSupport.unpark(v.parker);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Signals and releases worker v if it is top of idle worker
+ * stack. This performs a one-shot version of signalWork only if
+ * there is (apparently) at least one idle worker.
+ *
+ * @param c incoming ctl value
+ * @param v if non-null, a worker
+ * @param inc the increment to active count (zero when compensating)
+ * @return true if successful
+ */
+ private boolean tryRelease(long c, WorkQueue v, long inc) {
+ int sp = (int)c, ns = sp & ~UNSIGNALLED;
+ if (v != null) {
+ int vs = v.scanState;
+ long nc = (v.stackPred & SP_MASK) | (UC_MASK & (c + inc));
+ if (sp == vs && U.compareAndSwapLong(this, CTL, c, nc)) {
+ v.scanState = ns;
+ LockSupport.unpark(v.parker);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * With approx probability of a missed signal, tries (once) to
+ * reactivate worker w (or some other worker), failing if stale or
+ * known to be already active.
+ *
+ * @param w the worker
+ * @param ws the workQueue array to use
+ * @param r random seed
+ */
+ private void tryReactivate(WorkQueue w, WorkQueue[] ws, int r) {
+ long c; int sp, wl; WorkQueue v;
+ if ((sp = (int)(c = ctl)) != 0 && w != null &&
+ ws != null && (wl = ws.length) > 0 &&
+ ((sp ^ r) & SS_SEQ) == 0 &&
+ (v = ws[(wl - 1) & sp]) != null) {
+ long nc = (v.stackPred & SP_MASK) | (UC_MASK & (c + AC_UNIT));
+ int ns = sp & ~UNSIGNALLED;
+ if (w.scanState < 0 &&
+ v.scanState == sp &&
U.compareAndSwapLong(this, CTL, c, nc)) {
- w.eventCount = ne;
- if ((p = w.parker) != null)
- U.unpark(p);
- break;
- }
- if (q != null && q.base >= q.top)
- break;
- }
- }
-
- // Scanning for tasks
-
- /**
- * Top-level runloop for workers, called by ForkJoinWorkerThread.run.
- */
- final void runWorker(WorkQueue w) {
- w.growArray(); // allocate queue
- for (int r = w.hint; scan(w, r) == 0; ) {
- r ^= r << 13; r ^= r >>> 17; r ^= r << 5; // xorshift
- }
- }
-
- /**
- * Scans for and, if found, runs one task, else possibly
- * inactivates the worker. This method operates on single reads of
- * volatile state and is designed to be re-invoked continuously,
- * in part because it returns upon detecting inconsistencies,
- * contention, or state changes that indicate possible success on
- * re-invocation.
- *
- * The scan searches for tasks across queues starting at a random
- * index, checking each at least twice. The scan terminates upon
- * either finding a non-empty queue, or completing the sweep. If
- * the worker is not inactivated, it takes and runs a task from
- * this queue. Otherwise, if not activated, it tries to activate
- * itself or some other worker by signalling. On failure to find a
- * task, returns (for retry) if pool state may have changed during
- * an empty scan, or tries to inactivate if active, else possibly
- * blocks or terminates via method awaitWork.
- *
- * @param w the worker (via its WorkQueue)
- * @param r a random seed
- * @return worker qlock status if would have waited, else 0
- */
- private final int scan(WorkQueue w, int r) {
- WorkQueue[] ws; int m;
- long c = ctl; // for consistency check
- if ((ws = workQueues) != null && (m = ws.length - 1) >= 0 && w != null) {
- for (int j = m + m + 1, ec = w.eventCount;;) {
- WorkQueue q; int b, e; ForkJoinTask<?>[] a; ForkJoinTask<?> t;
- if ((q = ws[(r - j) & m]) != null &&
- (b = q.base) - q.top < 0 && (a = q.array) != null) {
- long i = (((a.length - 1) & b) << ASHIFT) + ABASE;
- if ((t = ((ForkJoinTask<?>)
- U.getObjectVolatile(a, i))) != null) {
- if (ec < 0)
- helpRelease(c, ws, w, q, b);
- else if (q.base == b &&
- U.compareAndSwapObject(a, i, t, null)) {
- U.putOrderedInt(q, QBASE, b + 1);
- if ((b + 1) - q.top < 0)
- signalWork(ws, q);
- w.runTask(t);
- }
- }
- break;
- }
- else if (--j < 0) {
- if ((ec | (e = (int)c)) < 0) // inactive or terminating
- return awaitWork(w, c, ec);
- else if (ctl == c) { // try to inactivate and enqueue
- long nc = (long)ec | ((c - AC_UNIT) & (AC_MASK|TC_MASK));
- w.nextWait = e;
- w.eventCount = ec | INT_SIGN;
- if (!U.compareAndSwapLong(this, CTL, c, nc))
- w.eventCount = ec; // back out
- }
- break;
- }
+ v.scanState = ns;
+ LockSupport.unpark(v.parker);
}
}
- return 0;
}
/**
- * A continuation of scan(), possibly blocking or terminating
- * worker w. Returns without blocking if pool state has apparently
- * changed since last invocation. Also, if inactivating w has
- * caused the pool to become quiescent, checks for pool
- * termination, and, so long as this is not the only worker, waits
- * for event for up to a given duration. On timeout, if ctl has
- * not changed, terminates the worker, which will in turn wake up
+ * If worker w exists and is active, enqueues and sets status to inactive.
+ *
+ * @param w the worker
+ * @param ss current (non-negative) scanState
+ */
+ private void inactivate(WorkQueue w, int ss) {
+ int ns = (ss + SS_SEQ) | UNSIGNALLED;
+ long lc = ns & SP_MASK, nc, c;
+ if (w != null) {
+ w.scanState = ns;
+ do {
+ nc = lc | (UC_MASK & ((c = ctl) - AC_UNIT));
+ w.stackPred = (int)c;
+ } while (!U.compareAndSwapLong(this, CTL, c, nc));
+ }
+ }
+
+ /**
+ * Possibly blocks worker w waiting for signal, or returns
+ * negative status if the worker should terminate. May return
+ * without status change if multiple stale unparks and/or
+ * interrupts occur.
+ *
+ * @param w the calling worker
+ * @return negative if w should terminate
+ */
+ private int awaitWork(WorkQueue w) {
+ int stat = 0;
+ if (w != null && w.scanState < 0) {
+ long c = ctl;
+ if ((int)(c >> AC_SHIFT) + (config & SMASK) <= 0)
+ stat = timedAwaitWork(w, c); // possibly quiescent
+ else if ((runState & STOP) != 0)
+ stat = w.qlock = -1; // pool terminating
+ else if (w.scanState < 0) {
+ w.parker = Thread.currentThread();
+ if (w.scanState < 0) // recheck after write
+ LockSupport.park(this);
+ w.parker = null;
+ if ((runState & STOP) != 0)
+ stat = w.qlock = -1; // recheck
+ else if (w.scanState < 0)
+ Thread.interrupted(); // clear status
+ }
+ }
+ return stat;
+ }
+
+ /**
+ * Possibly triggers shutdown and tries (once) to block worker
+ * when pool is (or may be) quiescent. Waits up to a duration
+ * determined by number of workers. On timeout, if ctl has not
+ * changed, terminates the worker, which will in turn wake up
* another worker to possibly repeat this process.
*
* @param w the calling worker
- * @param c the ctl value on entry to scan
- * @param ec the worker's eventCount on entry to scan
+ * @return negative if w should terminate
*/
- private final int awaitWork(WorkQueue w, long c, int ec) {
- int stat, ns; long parkTime, deadline;
- if ((stat = w.qlock) >= 0 && w.eventCount == ec && ctl == c &&
- !Thread.interrupted()) {
- int e = (int)c;
- int u = (int)(c >>> 32);
- int d = (u >> UAC_SHIFT) + parallelism; // active count
-
- if (e < 0 || (d <= 0 && tryTerminate(false, false)))
- stat = w.qlock = -1; // pool is terminating
- else if ((ns = w.nsteals) != 0) { // collect steals and retry
- long sc;
- w.nsteals = 0;
- do {} while (!U.compareAndSwapLong(this, STEALCOUNT,
- sc = stealCount, sc + ns));
- }
- else {
- long pc = ((d > 0 || ec != (e | INT_SIGN)) ? 0L :
- ((long)(w.nextWait & E_MASK)) | // ctl to restore
- ((long)(u + UAC_UNIT)) << 32);
- if (pc != 0L) { // timed wait if last waiter
- int dc = -(short)(c >>> TC_SHIFT);
- parkTime = (dc < 0 ? FAST_IDLE_TIMEOUT:
- (dc + 1) * IDLE_TIMEOUT);
- deadline = System.nanoTime() + parkTime - TIMEOUT_SLOP;
- }
- else
- parkTime = deadline = 0L;
- if (w.eventCount == ec && ctl == c) {
- Thread wt = Thread.currentThread();
- U.putObject(wt, PARKBLOCKER, this);
- w.parker = wt; // emulate LockSupport.park
- if (w.eventCount == ec && ctl == c)
- U.park(false, parkTime); // must recheck before park
- w.parker = null;
- U.putObject(wt, PARKBLOCKER, null);
- if (parkTime != 0L && ctl == c &&
- deadline - System.nanoTime() <= 0L &&
- U.compareAndSwapLong(this, CTL, c, pc))
- stat = w.qlock = -1; // shrink pool
+ private int timedAwaitWork(WorkQueue w, long c) {
+ int stat = 0;
+ int scale = 1 - (short)(c >>> TC_SHIFT);
+ long deadline = (((scale <= 0) ? 1 : scale) * IDLE_TIMEOUT_MS +
+ System.currentTimeMillis());
+ if ((runState >= 0 || (stat = tryTerminate(false, false)) > 0) &&
+ w != null && w.scanState < 0) {
+ int ss; AuxState aux;
+ w.parker = Thread.currentThread();
+ if (w.scanState < 0)
+ LockSupport.parkUntil(this, deadline);
+ w.parker = null;
+ if ((runState & STOP) != 0)
+ stat = w.qlock = -1; // pool terminating
+ else if ((ss = w.scanState) < 0 && !Thread.interrupted() &&
+ (int)c == ss && (aux = auxState) != null && ctl == c &&
+ deadline - System.currentTimeMillis() <= TIMEOUT_SLOP_MS) {
+ aux.lock();
+ try { // pre-deregister
+ WorkQueue[] ws;
+ int cfg = w.config, idx = cfg & SMASK;
+ long nc = ((UC_MASK & (c - TC_UNIT)) |
+ (SP_MASK & w.stackPred));
+ if ((runState & STOP) == 0 &&
+ (ws = workQueues) != null &&
+ idx < ws.length && idx >= 0 && ws[idx] == w &&
+ U.compareAndSwapLong(this, CTL, c, nc)) {
+ ws[idx] = null;
+ w.config = cfg | UNREGISTERED;
+ stat = w.qlock = -1;
+ }
+ } finally {
+ aux.unlock();
}
}
}
@@ -1754,210 +1837,55 @@
}
/**
- * Possibly releases (signals) a worker. Called only from scan()
- * when a worker with apparently inactive status finds a non-empty
- * queue. This requires revalidating all of the associated state
- * from caller.
- */
- private final void helpRelease(long c, WorkQueue[] ws, WorkQueue w,
- WorkQueue q, int b) {
- WorkQueue v; int e, i; Thread p;
- if (w != null && w.eventCount < 0 && (e = (int)c) > 0 &&
- ws != null && ws.length > (i = e & SMASK) &&
- (v = ws[i]) != null && ctl == c) {
- long nc = (((long)(v.nextWait & E_MASK)) |
- ((long)((int)(c >>> 32) + UAC_UNIT)) << 32);
- int ne = (e + E_SEQ) & E_MASK;
- if (q != null && q.base == b && w.eventCount < 0 &&
- v.eventCount == (e | INT_SIGN) &&
- U.compareAndSwapLong(this, CTL, c, nc)) {
- v.eventCount = ne;
- if ((p = v.parker) != null)
- U.unpark(p);
- }
- }
- }
-
- /**
- * Tries to locate and execute tasks for a stealer of the given
- * task, or in turn one of its stealers, Traces currentSteal ->
- * currentJoin links looking for a thread working on a descendant
- * of the given task and with a non-empty queue to steal back and
- * execute tasks from. The first call to this method upon a
- * waiting join will often entail scanning/search, (which is OK
- * because the joiner has nothing better to do), but this method
- * leaves hints in workers to speed up subsequent calls. The
- * implementation is very branchy to cope with potential
- * inconsistencies or loops encountering chains that are stale,
- * unknown, or so long that they are likely cyclic.
+ * If the given worker is a spare with no queued tasks, and there
+ * are enough existing workers, drops it from ctl counts and sets
+ * its state to terminated.
*
- * @param joiner the joining worker
- * @param task the task to join
- * @return 0 if no progress can be made, negative if task
- * known complete, else positive
+ * @param w the calling worker -- must be a spare
+ * @return true if dropped (in which case it must not process more tasks)
*/
- private int tryHelpStealer(WorkQueue joiner, ForkJoinTask<?> task) {
- int stat = 0, steps = 0; // bound to avoid cycles
- if (task != null && joiner != null &&
- joiner.base - joiner.top >= 0) { // hoist checks
- restart: for (;;) {
- ForkJoinTask<?> subtask = task; // current target
- for (WorkQueue j = joiner, v;;) { // v is stealer of subtask
- WorkQueue[] ws; int m, s, h;
- if ((s = task.status) < 0) {
- stat = s;
- break restart;
- }
- if ((ws = workQueues) == null || (m = ws.length - 1) <= 0)
- break restart; // shutting down
- if ((v = ws[h = (j.hint | 1) & m]) == null ||
- v.currentSteal != subtask) {
- for (int origin = h;;) { // find stealer
- if (((h = (h + 2) & m) & 15) == 1 &&
- (subtask.status < 0 || j.currentJoin != subtask))
- continue restart; // occasional staleness check
- if ((v = ws[h]) != null &&
- v.currentSteal == subtask) {
- j.hint = h; // save hint
- break;
- }
- if (h == origin)
- break restart; // cannot find stealer
- }
- }
- for (;;) { // help stealer or descend to its stealer
- ForkJoinTask[] a; int b;
- if (subtask.status < 0) // surround probes with
- continue restart; // consistency checks
- if ((b = v.base) - v.top < 0 && (a = v.array) != null) {
- int i = (((a.length - 1) & b) << ASHIFT) + ABASE;
- ForkJoinTask<?> t =
- (ForkJoinTask<?>)U.getObjectVolatile(a, i);
- if (subtask.status < 0 || j.currentJoin != subtask ||
- v.currentSteal != subtask)
- continue restart; // stale
- stat = 1; // apparent progress
- if (v.base == b) {
- if (t == null)
- break restart;
- if (U.compareAndSwapObject(a, i, t, null)) {
- U.putOrderedInt(v, QBASE, b + 1);
- ForkJoinTask<?> ps = joiner.currentSteal;
- int jt = joiner.top;
- do {
- joiner.currentSteal = t;
- t.doExec(); // clear local tasks too
- } while (task.status >= 0 &&
- joiner.top != jt &&
- (t = joiner.pop()) != null);
- joiner.currentSteal = ps;
- break restart;
- }
- }
- }
- else { // empty -- try to descend
- ForkJoinTask<?> next = v.currentJoin;
- if (subtask.status < 0 || j.currentJoin != subtask ||
- v.currentSteal != subtask)
- continue restart; // stale
- else if (next == null || ++steps == MAX_HELP)
- break restart; // dead-end or maybe cyclic
- else {
- subtask = next;
- j = v;
- break;
- }
- }
- }
+ private boolean tryDropSpare(WorkQueue w) {
+ if (w != null && w.isEmpty()) { // no local tasks
+ long c; int sp, wl; WorkQueue[] ws; WorkQueue v;
+ while ((short)((c = ctl) >> TC_SHIFT) > 0 &&
+ ((sp = (int)c) != 0 || (int)(c >> AC_SHIFT) > 0) &&
+ (ws = workQueues) != null && (wl = ws.length) > 0) {
+ boolean dropped, canDrop;
+ if (sp == 0) { // no queued workers
+ long nc = ((AC_MASK & (c - AC_UNIT)) |
+ (TC_MASK & (c - TC_UNIT)) | (SP_MASK & c));
+ dropped = U.compareAndSwapLong(this, CTL, c, nc);
}
- }
- }
- return stat;
- }
-
- /**
- * Analog of tryHelpStealer for CountedCompleters. Tries to steal
- * and run tasks within the target's computation.
- *
- * @param task the task to join
- */
- private int helpComplete(WorkQueue joiner, CountedCompleter<?> task) {
- WorkQueue[] ws; int m;
- int s = 0;
- if ((ws = workQueues) != null && (m = ws.length - 1) >= 0 &&
- joiner != null && task != null) {
- int j = joiner.poolIndex;
- int scans = m + m + 1;
- long c = 0L; // for stability check
- for (int k = scans; ; j += 2) {
- WorkQueue q;
- if ((s = task.status) < 0)
- break;
- else if (joiner.internalPopAndExecCC(task))
- k = scans;
- else if ((s = task.status) < 0)
- break;
- else if ((q = ws[j & m]) != null && q.pollAndExecCC(task))
- k = scans;
- else if (--k < 0) {
- if (c == (c = ctl))
- break;
- k = scans;
- }
- }
- }
- return s;
- }
-
- /**
- * Tries to decrement active count (sometimes implicitly) and
- * possibly release or create a compensating worker in preparation
- * for blocking. Fails on contention or termination. Otherwise,
- * adds a new thread if no idle workers are available and pool
- * may become starved.
- *
- * @param c the assumed ctl value
- */
- final boolean tryCompensate(long c) {
- WorkQueue[] ws = workQueues;
- int pc = parallelism, e = (int)c, m, tc;
- if (ws != null && (m = ws.length - 1) >= 0 && e >= 0 && ctl == c) {
- WorkQueue w = ws[e & m];
- if (e != 0 && w != null) {
- Thread p;
- long nc = ((long)(w.nextWait & E_MASK) |
- (c & (AC_MASK|TC_MASK)));
- int ne = (e + E_SEQ) & E_MASK;
- if (w.eventCount == (e | INT_SIGN) &&
- U.compareAndSwapLong(this, CTL, c, nc)) {
- w.eventCount = ne;
- if ((p = w.parker) != null)
- U.unpark(p);
- return true; // replace with idle worker
- }
- }
- else if ((tc = (short)(c >>> TC_SHIFT)) >= 0 &&
- (int)(c >> AC_SHIFT) + pc > 1) {
- long nc = ((c - AC_UNIT) & AC_MASK) | (c & ~AC_MASK);
- if (U.compareAndSwapLong(this, CTL, c, nc))
- return true; // no compensation
- }
- else if (tc + pc < MAX_CAP) {
- long nc = ((c + TC_UNIT) & TC_MASK) | (c & ~TC_MASK);
- if (U.compareAndSwapLong(this, CTL, c, nc)) {
- ForkJoinWorkerThreadFactory fac;
- Throwable ex = null;
- ForkJoinWorkerThread wt = null;
- try {
- if ((fac = factory) != null &&
- (wt = fac.newThread(this)) != null) {
- wt.start();
- return true;
- }
- } catch (Throwable rex) {
- ex = rex;
+ else if (
+ (v = ws[(wl - 1) & sp]) == null || v.scanState != sp)
+ dropped = false; // stale; retry
+ else {
+ long nc = v.stackPred & SP_MASK;
+ if (w == v || w.scanState >= 0) {
+ canDrop = true; // w unqueued or topmost
+ nc |= ((AC_MASK & c) | // ensure replacement
+ (TC_MASK & (c - TC_UNIT)));
}
- deregisterWorker(wt, ex); // clean up and return false
+ else { // w may be queued
+ canDrop = false; // help uncover
+ nc |= ((AC_MASK & (c + AC_UNIT)) |
+ (TC_MASK & c));
+ }
+ if (U.compareAndSwapLong(this, CTL, c, nc)) {
+ v.scanState = sp & ~UNSIGNALLED;
+ LockSupport.unpark(v.parker);
+ dropped = canDrop;
+ }
+ else
+ dropped = false;
+ }
+ if (dropped) { // pre-deregister
+ int cfg = w.config, idx = cfg & SMASK;
+ if (idx >= 0 && idx < ws.length && ws[idx] == w)
+ ws[idx] = null;
+ w.config = cfg | UNREGISTERED;
+ w.qlock = -1;
+ return true;
}
}
}
@@ -1965,97 +1893,366 @@
}
/**
- * Helps and/or blocks until the given task is done.
+ * Top-level runloop for workers, called by ForkJoinWorkerThread.run.
+ */
+ final void runWorker(WorkQueue w) {
+ w.growArray(); // allocate queue
+ int bound = (w.config & SPARE_WORKER) != 0 ? 0 : POLL_LIMIT;
+ long seed = w.hint * 0xdaba0b6eb09322e3L; // initial random seed
+ if ((runState & STOP) == 0) {
+ for (long r = (seed == 0L) ? 1L : seed;;) { // ensure nonzero
+ if (bound == 0 && tryDropSpare(w))
+ break;
+ // high bits of prev seed for step; current low bits for idx
+ int step = (int)(r >>> 48) | 1;
+ r ^= r >>> 12; r ^= r << 25; r ^= r >>> 27; // xorshift
+ if (scan(w, bound, step, (int)r) < 0 && awaitWork(w) < 0)
+ break;
+ }
+ }
+ }
+
+ // Scanning for tasks
+
+ /**
+ * Repeatedly scans for and tries to steal and execute (via
+ * workQueue.runTask) a queued task. Each scan traverses queues in
+ * pseudorandom permutation. Upon finding a non-empty queue, makes
+ * at most the given bound attempts to re-poll (fewer if
+ * contended) on the same queue before returning (impossible
+ * scanState value) 0 to restart scan. Else returns after at least
+ * 1 and at most 32 full scans.
*
- * @param joiner the joining worker
- * @param task the task
+ * @param w the worker (via its WorkQueue)
+ * @param bound repoll bound as bitmask (0 if spare)
+ * @param step (circular) index increment per iteration (must be odd)
+ * @param r a random seed for origin index
+ * @return negative if should await signal
+ */
+ private int scan(WorkQueue w, int bound, int step, int r) {
+ int stat = 0, wl; WorkQueue[] ws;
+ if ((ws = workQueues) != null && w != null && (wl = ws.length) > 0) {
+ for (int m = wl - 1,
+ origin = m & r, idx = origin,
+ npolls = 0,
+ ss = w.scanState;;) { // negative if inactive
+ WorkQueue q; ForkJoinTask<?>[] a; int b, al;
+ if ((q = ws[idx]) != null && (b = q.base) - q.top < 0 &&
+ (a = q.array) != null && (al = a.length) > 0) {
+ int index = (al - 1) & b;
+ long offset = ((long)index << ASHIFT) + ABASE;
+ ForkJoinTask<?> t = (ForkJoinTask<?>)
+ U.getObjectVolatile(a, offset);
+ if (t == null)
+ break; // empty or busy
+ else if (b++ != q.base)
+ break; // busy
+ else if (ss < 0) {
+ tryReactivate(w, ws, r);
+ break; // retry upon rescan
+ }
+ else if (!U.compareAndSwapObject(a, offset, t, null))
+ break; // contended
+ else {
+ q.base = b;
+ w.currentSteal = t;
+ if (b != q.top) // propagate signal
+ signalWork();
+ w.runTask(t);
+ if (++npolls > bound)
+ break;
+ }
+ }
+ else if (npolls != 0) // rescan
+ break;
+ else if ((idx = (idx + step) & m) == origin) {
+ if (ss < 0) { // await signal
+ stat = ss;
+ break;
+ }
+ else if (r >= 0) {
+ inactivate(w, ss);
+ break;
+ }
+ else
+ r <<= 1; // at most 31 rescans
+ }
+ }
+ }
+ return stat;
+ }
+
+ // Joining tasks
+
+ /**
+ * Tries to steal and run tasks within the target's computation.
+ * Uses a variant of the top-level algorithm, restricted to tasks
+ * with the given task as ancestor: It prefers taking and running
+ * eligible tasks popped from the worker's own queue (via
+ * popCC). Otherwise it scans others, randomly moving on
+ * contention or execution, deciding to give up based on a
+ * checksum (via return codes from pollAndExecCC). The maxTasks
+ * argument supports external usages; internal calls use zero,
+ * allowing unbounded steps (external calls trap non-positive
+ * values).
+ *
+ * @param w caller
+ * @param maxTasks if non-zero, the maximum number of other tasks to run
* @return task status on exit
*/
- final int awaitJoin(WorkQueue joiner, ForkJoinTask<?> task) {
- int s = 0;
- if (task != null && (s = task.status) >= 0 && joiner != null) {
- ForkJoinTask<?> prevJoin = joiner.currentJoin;
- joiner.currentJoin = task;
- do {} while (joiner.tryRemoveAndExec(task) && // process local tasks
- (s = task.status) >= 0);
- if (s >= 0 && (task instanceof CountedCompleter))
- s = helpComplete(joiner, (CountedCompleter<?>)task);
- long cc = 0; // for stability checks
- while (s >= 0 && (s = task.status) >= 0) {
- if ((s = tryHelpStealer(joiner, task)) == 0 &&
- (s = task.status) >= 0) {
- if (!tryCompensate(cc))
- cc = ctl;
- else {
- if (task.trySetSignal() && (s = task.status) >= 0) {
- synchronized (task) {
- if (task.status >= 0) {
- try { // see ForkJoinTask
- task.wait(); // for explanation
- } catch (InterruptedException ie) {
- }
- }
- else
- task.notifyAll();
- }
- }
- long c; // reactivate
- do {} while (!U.compareAndSwapLong
- (this, CTL, c = ctl,
- ((c & ~AC_MASK) |
- ((c & AC_MASK) + AC_UNIT))));
+ final int helpComplete(WorkQueue w, CountedCompleter<?> task,
+ int maxTasks) {
+ WorkQueue[] ws; int s = 0, wl;
+ if ((ws = workQueues) != null && (wl = ws.length) > 1 &&
+ task != null && w != null) {
+ for (int m = wl - 1,
+ mode = w.config,
+ r = ~mode, // scanning seed
+ origin = r & m, k = origin, // first queue to scan
+ step = 3, // first scan step
+ h = 1, // 1:ran, >1:contended, <0:hash
+ oldSum = 0, checkSum = 0;;) {
+ CountedCompleter<?> p; WorkQueue q; int i;
+ if ((s = task.status) < 0)
+ break;
+ if (h == 1 && (p = w.popCC(task, mode)) != null) {
+ p.doExec(); // run local task
+ if (maxTasks != 0 && --maxTasks == 0)
+ break;
+ origin = k; // reset
+ oldSum = checkSum = 0;
+ }
+ else { // poll other worker queues
+ if ((i = k | 1) < 0 || i > m || (q = ws[i]) == null)
+ h = 0;
+ else if ((h = q.pollAndExecCC(task)) < 0)
+ checkSum += h;
+ if (h > 0) {
+ if (h == 1 && maxTasks != 0 && --maxTasks == 0)
+ break;
+ step = (r >>> 16) | 3;
+ r ^= r << 13; r ^= r >>> 17; r ^= r << 5; // xorshift
+ k = origin = r & m; // move and restart
+ oldSum = checkSum = 0;
+ }
+ else if ((k = (k + step) & m) == origin) {
+ if (oldSum == (oldSum = checkSum))
+ break;
+ checkSum = 0;
}
}
}
- joiner.currentJoin = prevJoin;
}
return s;
}
/**
- * Stripped-down variant of awaitJoin used by timed joins. Tries
- * to help join only while there is continuous progress. (Caller
- * will then enter a timed wait.)
+ * Tries to locate and execute tasks for a stealer of the given
+ * task, or in turn one of its stealers. Traces currentSteal ->
+ * currentJoin links looking for a thread working on a descendant
+ * of the given task and with a non-empty queue to steal back and
+ * execute tasks from. The first call to this method upon a
+ * waiting join will often entail scanning/search, (which is OK
+ * because the joiner has nothing better to do), but this method
+ * leaves hints in workers to speed up subsequent calls.
*
- * @param joiner the joining worker
- * @param task the task
+ * @param w caller
+ * @param task the task to join
*/
- final void helpJoinOnce(WorkQueue joiner, ForkJoinTask<?> task) {
- int s;
- if (joiner != null && task != null && (s = task.status) >= 0) {
- ForkJoinTask<?> prevJoin = joiner.currentJoin;
- joiner.currentJoin = task;
- do {} while (joiner.tryRemoveAndExec(task) && // process local tasks
- (s = task.status) >= 0);
- if (s >= 0) {
- if (task instanceof CountedCompleter)
- helpComplete(joiner, (CountedCompleter<?>)task);
- do {} while (task.status >= 0 &&
- tryHelpStealer(joiner, task) > 0);
+ private void helpStealer(WorkQueue w, ForkJoinTask<?> task) {
+ if (task != null && w != null) {
+ ForkJoinTask<?> ps = w.currentSteal;
+ WorkQueue[] ws; int wl, oldSum = 0;
+ outer: while (w.tryRemoveAndExec(task) && task.status >= 0 &&
+ (ws = workQueues) != null && (wl = ws.length) > 0) {
+ ForkJoinTask<?> subtask;
+ int m = wl - 1, checkSum = 0; // for stability check
+ WorkQueue j = w, v; // v is subtask stealer
+ descent: for (subtask = task; subtask.status >= 0; ) {
+ for (int h = j.hint | 1, k = 0, i;;) {
+ if ((v = ws[i = (h + (k << 1)) & m]) != null) {
+ if (v.currentSteal == subtask) {
+ j.hint = i;
+ break;
+ }
+ checkSum += v.base;
+ }
+ if (++k > m) // can't find stealer
+ break outer;
+ }
+
+ for (;;) { // help v or descend
+ ForkJoinTask<?>[] a; int b, al;
+ if (subtask.status < 0) // too late to help
+ break descent;
+ checkSum += (b = v.base);
+ ForkJoinTask<?> next = v.currentJoin;
+ ForkJoinTask<?> t = null;
+ if ((a = v.array) != null && (al = a.length) > 0) {
+ int index = (al - 1) & b;
+ long offset = ((long)index << ASHIFT) + ABASE;
+ t = (ForkJoinTask<?>)
+ U.getObjectVolatile(a, offset);
+ if (t != null && b++ == v.base) {
+ if (j.currentJoin != subtask ||
+ v.currentSteal != subtask ||
+ subtask.status < 0)
+ break descent; // stale
+ if (U.compareAndSwapObject(a, offset, t, null)) {
+ v.base = b;
+ w.currentSteal = t;
+ for (int top = w.top;;) {
+ t.doExec(); // help
+ w.currentSteal = ps;
+ if (task.status < 0)
+ break outer;
+ if (w.top == top)
+ break; // run local tasks
+ if ((t = w.pop()) == null)
+ break descent;
+ w.currentSteal = t;
+ }
+ }
+ }
+ }
+ if (t == null && b == v.base && b - v.top >= 0) {
+ if ((subtask = next) == null) { // try to descend
+ if (next == v.currentJoin &&
+ oldSum == (oldSum = checkSum))
+ break outer;
+ break descent;
+ }
+ j = v;
+ break;
+ }
+ }
+ }
}
- joiner.currentJoin = prevJoin;
}
}
/**
+ * Tries to decrement active count (sometimes implicitly) and
+ * possibly release or create a compensating worker in preparation
+ * for blocking. Returns false (retryable by caller), on
+ * contention, detected staleness, instability, or termination.
+ *
+ * @param w caller
+ */
+ private boolean tryCompensate(WorkQueue w) {
+ boolean canBlock; int wl;
+ long c = ctl;
+ WorkQueue[] ws = workQueues;
+ int pc = config & SMASK;
+ int ac = pc + (int)(c >> AC_SHIFT);
+ int tc = pc + (short)(c >> TC_SHIFT);
+ if (w == null || w.qlock < 0 || pc == 0 || // terminating or disabled
+ ws == null || (wl = ws.length) <= 0)
+ canBlock = false;
+ else {
+ int m = wl - 1, sp;
+ boolean busy = true; // validate ac
+ for (int i = 0; i <= m; ++i) {
+ int k; WorkQueue v;
+ if ((k = (i << 1) | 1) <= m && k >= 0 && (v = ws[k]) != null &&
+ v.scanState >= 0 && v.currentSteal == null) {
+ busy = false;
+ break;
+ }
+ }
+ if (!busy || ctl != c)
+ canBlock = false; // unstable or stale
+ else if ((sp = (int)c) != 0) // release idle worker
+ canBlock = tryRelease(c, ws[m & sp], 0L);
+ else if (tc >= pc && ac > 1 && w.isEmpty()) {
+ long nc = ((AC_MASK & (c - AC_UNIT)) |
+ (~AC_MASK & c)); // uncompensated
+ canBlock = U.compareAndSwapLong(this, CTL, c, nc);
+ }
+ else if (tc >= MAX_CAP ||
+ (this == common && tc >= pc + COMMON_MAX_SPARES))
+ throw new RejectedExecutionException(
+ "Thread limit exceeded replacing blocked worker");
+ else { // similar to tryAddWorker
+ boolean isSpare = (tc >= pc);
+ long nc = (AC_MASK & c) | (TC_MASK & (c + TC_UNIT));
+ canBlock = (U.compareAndSwapLong(this, CTL, c, nc) &&
+ createWorker(isSpare)); // throws on exception
+ }
+ }
+ return canBlock;
+ }
+
+ /**
+ * Helps and/or blocks until the given task is done or timeout.
+ *
+ * @param w caller
+ * @param task the task
+ * @param deadline for timed waits, if nonzero
+ * @return task status on exit
+ */
+ final int awaitJoin(WorkQueue w, ForkJoinTask<?> task, long deadline) {
+ int s = 0;
+ if (w != null) {
+ ForkJoinTask<?> prevJoin = w.currentJoin;
+ if (task != null && (s = task.status) >= 0) {
+ w.currentJoin = task;
+ CountedCompleter<?> cc = (task instanceof CountedCompleter) ?
+ (CountedCompleter<?>)task : null;
+ for (;;) {
+ if (cc != null)
+ helpComplete(w, cc, 0);
+ else
+ helpStealer(w, task);
+ if ((s = task.status) < 0)
+ break;
+ long ms, ns;
+ if (deadline == 0L)
+ ms = 0L;
+ else if ((ns = deadline - System.nanoTime()) <= 0L)
+ break;
+ else if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) <= 0L)
+ ms = 1L;
+ if (tryCompensate(w)) {
+ task.internalWait(ms);
+ U.getAndAddLong(this, CTL, AC_UNIT);
+ }
+ if ((s = task.status) < 0)
+ break;
+ }
+ w.currentJoin = prevJoin;
+ }
+ }
+ return s;
+ }
+
+ // Specialized scanning
+
+ /**
* Returns a (probably) non-empty steal queue, if one is found
* during a scan, else null. This method must be retried by
* caller if, by the time it tries to use the queue, it is empty.
*/
private WorkQueue findNonEmptyStealQueue() {
- int r = ThreadLocalRandom.current().nextInt();
- for (;;) {
- int ps = plock, m; WorkQueue[] ws; WorkQueue q;
- if ((ws = workQueues) != null && (m = ws.length - 1) >= 0) {
- for (int j = (m + 1) << 2; j >= 0; --j) {
- if ((q = ws[(((r - j) << 1) | 1) & m]) != null &&
- q.base - q.top < 0)
+ WorkQueue[] ws; int wl; // one-shot version of scan loop
+ int r = ThreadLocalRandom.nextSecondarySeed();
+ if ((ws = workQueues) != null && (wl = ws.length) > 0) {
+ int m = wl - 1, origin = r & m;
+ for (int k = origin, oldSum = 0, checkSum = 0;;) {
+ WorkQueue q; int b;
+ if ((q = ws[k]) != null) {
+ if ((b = q.base) - q.top < 0)
return q;
+ checkSum += b;
+ }
+ if ((k = (k + 1) & m) == origin) {
+ if (oldSum == (oldSum = checkSum))
+ break;
+ checkSum = 0;
}
}
- if (plock == ps)
- return null;
}
+ return null;
}
/**
@@ -2065,35 +2262,33 @@
* find tasks either.
*/
final void helpQuiescePool(WorkQueue w) {
- ForkJoinTask<?> ps = w.currentSteal;
+ ForkJoinTask<?> ps = w.currentSteal; // save context
+ int wc = w.config;
for (boolean active = true;;) {
- long c; WorkQueue q; ForkJoinTask<?> t; int b;
- while ((t = w.nextLocalTask()) != null)
- t.doExec();
- if ((q = findNonEmptyStealQueue()) != null) {
+ long c; WorkQueue q; ForkJoinTask<?> t;
+ if (wc >= 0 && (t = w.pop()) != null) { // run locals if LIFO
+ (w.currentSteal = t).doExec();
+ w.currentSteal = ps;
+ }
+ else if ((q = findNonEmptyStealQueue()) != null) {
if (!active) { // re-establish active count
active = true;
- do {} while (!U.compareAndSwapLong
- (this, CTL, c = ctl,
- ((c & ~AC_MASK) |
- ((c & AC_MASK) + AC_UNIT))));
+ U.getAndAddLong(this, CTL, AC_UNIT);
}
- if ((b = q.base) - q.top < 0 && (t = q.pollAt(b)) != null) {
+ if ((t = q.pollAt(q.base)) != null) {
(w.currentSteal = t).doExec();
w.currentSteal = ps;
+ if (++w.nsteals < 0)
+ w.transferStealCount(this);
}
}
else if (active) { // decrement active count without queuing
- long nc = ((c = ctl) & ~AC_MASK) | ((c & AC_MASK) - AC_UNIT);
- if ((int)(nc >> AC_SHIFT) + parallelism == 0)
- break; // bypass decrement-then-increment
+ long nc = (AC_MASK & ((c = ctl) - AC_UNIT)) | (~AC_MASK & c);
if (U.compareAndSwapLong(this, CTL, c, nc))
active = false;
}
- else if ((int)((c = ctl) >> AC_SHIFT) + parallelism <= 0 &&
- U.compareAndSwapLong
- (this, CTL, c, ((c & ~AC_MASK) |
- ((c & AC_MASK) + AC_UNIT))))
+ else if ((int)((c = ctl) >> AC_SHIFT) + (config & SMASK) <= 0 &&
+ U.compareAndSwapLong(this, CTL, c, c + AC_UNIT))
break;
}
}
@@ -2105,12 +2300,12 @@
*/
final ForkJoinTask<?> nextTaskFor(WorkQueue w) {
for (ForkJoinTask<?> t;;) {
- WorkQueue q; int b;
+ WorkQueue q;
if ((t = w.nextLocalTask()) != null)
return t;
if ((q = findNonEmptyStealQueue()) == null)
return null;
- if ((b = q.base) - q.top < 0 && (t = q.pollAt(b)) != null)
+ if ((t = q.pollAt(q.base)) != null)
return t;
}
}
@@ -2118,7 +2313,7 @@
/**
* Returns a cheap heuristic guide for task partitioning when
* programmers, frameworks, tools, or languages have little or no
- * idea about task granularity. In essence by offering this
+ * idea about task granularity. In essence, by offering this
* method, we ask users only about tradeoffs in overhead vs
* expected throughput and its variance, rather than how finely to
* partition tasks.
@@ -2156,15 +2351,11 @@
* many of these by further considering the number of "idle"
* threads, that are known to have zero queued tasks, so
* compensate by a factor of (#idle/#active) threads.
- *
- * Note: The approximation of #busy workers as #active workers is
- * not very good under current signalling scheme, and should be
- * improved.
*/
static int getSurplusQueuedTaskCount() {
Thread t; ForkJoinWorkerThread wt; ForkJoinPool pool; WorkQueue q;
- if (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)) {
- int p = (pool = (wt = (ForkJoinWorkerThread)t).pool).parallelism;
+ if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) {
+ int p = (pool = (wt = (ForkJoinWorkerThread)t).pool).config & SMASK;
int n = (q = wt.workQueue).top - q.base;
int a = (int)(pool.ctl >> AC_SHIFT) + p;
return n - (a > (p >>>= 1) ? 0 :
@@ -2179,170 +2370,203 @@
// Termination
/**
- * Possibly initiates and/or completes termination. The caller
- * triggering termination runs three passes through workQueues:
- * (0) Setting termination status, followed by wakeups of queued
- * workers; (1) cancelling all tasks; (2) interrupting lagging
- * threads (likely in external tasks, but possibly also blocked in
- * joins). Each pass repeats previous steps because of potential
- * lagging thread creation.
+ * Possibly initiates and/or completes termination.
*
* @param now if true, unconditionally terminate, else only
* if no work and no active workers
- * @param enable if true, enable shutdown when next possible
- * @return true if now terminating or terminated
+ * @param enable if true, terminate when next possible
+ * @return -1: terminating/terminated, 0: retry if internal caller, else 1
*/
- private boolean tryTerminate(boolean now, boolean enable) {
- int ps;
- if (this == common) // cannot shut down
- return false;
- if ((ps = plock) >= 0) { // enable by setting plock
- if (!enable)
- return false;
- if ((ps & PL_LOCK) != 0 ||
- !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK))
- ps = acquirePlock();
- int nps = ((ps + PL_LOCK) & ~SHUTDOWN) | SHUTDOWN;
- if (!U.compareAndSwapInt(this, PLOCK, ps, nps))
- releasePlock(nps);
+ private int tryTerminate(boolean now, boolean enable) {
+ int rs; // 3 phases: try to set SHUTDOWN, then STOP, then TERMINATED
+
+ while ((rs = runState) >= 0) {
+ if (!enable || this == common) // cannot shutdown
+ return 1;
+ else if (rs == 0)
+ tryInitialize(false); // ensure initialized
+ else
+ U.compareAndSwapInt(this, RUNSTATE, rs, rs | SHUTDOWN);
}
- for (long c;;) {
- if (((c = ctl) & STOP_BIT) != 0) { // already terminating
- if ((short)(c >>> TC_SHIFT) + parallelism <= 0) {
- synchronized (this) {
- notifyAll(); // signal when 0 workers
- }
- }
- return true;
- }
- if (!now) { // check if idle & no tasks
- WorkQueue[] ws; WorkQueue w;
- if ((int)(c >> AC_SHIFT) + parallelism > 0)
- return false;
- if ((ws = workQueues) != null) {
- for (int i = 0; i < ws.length; ++i) {
- if ((w = ws[i]) != null &&
- (!w.isEmpty() ||
- ((i & 1) != 0 && w.eventCount >= 0))) {
- signalWork(ws, w);
- return false;
+
+ if ((rs & STOP) == 0) { // try to initiate termination
+ if (!now) { // check quiescence
+ for (long oldSum = 0L;;) { // repeat until stable
+ WorkQueue[] ws; WorkQueue w; int b;
+ long checkSum = ctl;
+ if ((int)(checkSum >> AC_SHIFT) + (config & SMASK) > 0)
+ return 0; // still active workers
+ if ((ws = workQueues) != null) {
+ for (int i = 0; i < ws.length; ++i) {
+ if ((w = ws[i]) != null) {
+ checkSum += (b = w.base);
+ if (w.currentSteal != null || b != w.top)
+ return 0; // retry if internal caller
+ }
}
}
+ if (oldSum == (oldSum = checkSum))
+ break;
}
}
- if (U.compareAndSwapLong(this, CTL, c, c | STOP_BIT)) {
- for (int pass = 0; pass < 3; ++pass) {
- WorkQueue[] ws; WorkQueue w; Thread wt;
- if ((ws = workQueues) != null) {
- int n = ws.length;
- for (int i = 0; i < n; ++i) {
- if ((w = ws[i]) != null) {
- w.qlock = -1;
- if (pass > 0) {
- w.cancelAll();
- if (pass > 1 && (wt = w.owner) != null) {
- if (!wt.isInterrupted()) {
- try {
- wt.interrupt();
- } catch (Throwable ignore) {
- }
- }
- U.unpark(wt);
- }
+ do {} while (!U.compareAndSwapInt(this, RUNSTATE,
+ rs = runState, rs | STOP));
+ }
+
+ for (long oldSum = 0L;;) { // repeat until stable
+ WorkQueue[] ws; WorkQueue w; ForkJoinWorkerThread wt;
+ long checkSum = ctl;
+ if ((ws = workQueues) != null) { // help terminate others
+ for (int i = 0; i < ws.length; ++i) {
+ if ((w = ws[i]) != null) {
+ w.cancelAll(); // clear queues
+ checkSum += w.base;
+ if (w.qlock >= 0) {
+ w.qlock = -1; // racy set OK
+ if ((wt = w.owner) != null) {
+ try { // unblock join or park
+ wt.interrupt();
+ } catch (Throwable ignore) {
}
}
}
- // Wake up workers parked on event queue
- int i, e; long cc; Thread p;
- while ((e = (int)(cc = ctl) & E_MASK) != 0 &&
- (i = e & SMASK) < n && i >= 0 &&
- (w = ws[i]) != null) {
- long nc = ((long)(w.nextWait & E_MASK) |
- ((cc + AC_UNIT) & AC_MASK) |
- (cc & (TC_MASK|STOP_BIT)));
- if (w.eventCount == (e | INT_SIGN) &&
- U.compareAndSwapLong(this, CTL, cc, nc)) {
- w.eventCount = (e + E_SEQ) & E_MASK;
- w.qlock = -1;
- if ((p = w.parker) != null)
- U.unpark(p);
- }
- }
}
}
}
+ if (oldSum == (oldSum = checkSum))
+ break;
+ }
+
+ if ((short)(ctl >>> TC_SHIFT) + (config & SMASK) <= 0) {
+ runState = (STARTED | SHUTDOWN | STOP | TERMINATED); // final write
+ synchronized (this) {
+ notifyAll(); // for awaitTermination
+ }
+ }
+
+ return -1;
+ }
+
+ // External operations
+
+ /**
+ * Constructs and tries to install a new external queue,
+ * failing if the workQueues array already has a queue at
+ * the given index.
+ *
+ * @param index the index of the new queue
+ */
+ private void tryCreateExternalQueue(int index) {
+ AuxState aux;
+ if ((aux = auxState) != null && index >= 0) {
+ WorkQueue q = new WorkQueue(this, null);
+ q.config = index;
+ q.scanState = ~UNSIGNALLED;
+ q.qlock = 1; // lock queue
+ boolean installed = false;
+ aux.lock();
+ try { // lock pool to install
+ WorkQueue[] ws;
+ if ((ws = workQueues) != null && index < ws.length &&
+ ws[index] == null) {
+ ws[index] = q; // else throw away
+ installed = true;
+ }
+ } finally {
+ aux.unlock();
+ }
+ if (installed) {
+ try {
+ q.growArray();
+ } finally {
+ q.qlock = 0;
+ }
+ }
}
}
- // external operations on common pool
+ /**
+ * Adds the given task to a submission queue at submitter's
+ * current queue. Also performs secondary initialization upon the
+ * first submission of the first task to the pool, and detects
+ * first submission by an external thread and creates a new shared
+ * queue if the one at index if empty or contended.
+ *
+ * @param task the task. Caller must ensure non-null.
+ */
+ final void externalPush(ForkJoinTask<?> task) {
+ int r; // initialize caller's probe
+ if ((r = ThreadLocalRandom.getProbe()) == 0) {
+ ThreadLocalRandom.localInit();
+ r = ThreadLocalRandom.getProbe();
+ }
+ for (;;) {
+ WorkQueue q; int wl, k, stat;
+ int rs = runState;
+ WorkQueue[] ws = workQueues;
+ if (rs <= 0 || ws == null || (wl = ws.length) <= 0)
+ tryInitialize(true);
+ else if ((q = ws[k = (wl - 1) & r & SQMASK]) == null)
+ tryCreateExternalQueue(k);
+ else if ((stat = q.sharedPush(task)) < 0)
+ break;
+ else if (stat == 0) {
+ signalWork();
+ break;
+ }
+ else // move if busy
+ r = ThreadLocalRandom.advanceProbe(r);
+ }
+ }
/**
- * Returns common pool queue for a thread that has submitted at
- * least one task.
+ * Pushes a possibly-external submission.
+ */
+ private <T> ForkJoinTask<T> externalSubmit(ForkJoinTask<T> task) {
+ Thread t; ForkJoinWorkerThread w; WorkQueue q;
+ if (task == null)
+ throw new NullPointerException();
+ if (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) &&
+ (w = (ForkJoinWorkerThread)t).pool == this &&
+ (q = w.workQueue) != null)
+ q.push(task);
+ else
+ externalPush(task);
+ return task;
+ }
+
+ /**
+ * Returns common pool queue for an external thread.
*/
static WorkQueue commonSubmitterQueue() {
- Submitter z; ForkJoinPool p; WorkQueue[] ws; int m, r;
- return ((z = submitters.get()) != null &&
- (p = common) != null &&
- (ws = p.workQueues) != null &&
- (m = ws.length - 1) >= 0) ?
- ws[m & z.seed & SQMASK] : null;
+ ForkJoinPool p = common;
+ int r = ThreadLocalRandom.getProbe();
+ WorkQueue[] ws; int wl;
+ return (p != null && (ws = p.workQueues) != null &&
+ (wl = ws.length) > 0) ?
+ ws[(wl - 1) & r & SQMASK] : null;
}
/**
- * Tries to pop the given task from submitter's queue in common pool.
+ * Performs tryUnpush for an external submitter.
*/
final boolean tryExternalUnpush(ForkJoinTask<?> task) {
- WorkQueue joiner; ForkJoinTask<?>[] a; int m, s;
- Submitter z = submitters.get();
- WorkQueue[] ws = workQueues;
- boolean popped = false;
- if (z != null && ws != null && (m = ws.length - 1) >= 0 &&
- (joiner = ws[z.seed & m & SQMASK]) != null &&
- joiner.base != (s = joiner.top) &&
- (a = joiner.array) != null) {
- long j = (((a.length - 1) & (s - 1)) << ASHIFT) + ABASE;
- if (U.getObject(a, j) == task &&
- U.compareAndSwapInt(joiner, QLOCK, 0, 1)) {
- if (joiner.top == s && joiner.array == a &&
- U.compareAndSwapObject(a, j, task, null)) {
- joiner.top = s - 1;
- popped = true;
- }
- joiner.qlock = 0;
- }
- }
- return popped;
+ int r = ThreadLocalRandom.getProbe();
+ WorkQueue[] ws; WorkQueue w; int wl;
+ return ((ws = workQueues) != null &&
+ (wl = ws.length) > 0 &&
+ (w = ws[(wl - 1) & r & SQMASK]) != null &&
+ w.trySharedUnpush(task));
}
- final int externalHelpComplete(CountedCompleter<?> task) {
- WorkQueue joiner; int m, j;
- Submitter z = submitters.get();
- WorkQueue[] ws = workQueues;
- int s = 0;
- if (z != null && ws != null && (m = ws.length - 1) >= 0 &&
- (joiner = ws[(j = z.seed) & m & SQMASK]) != null && task != null) {
- int scans = m + m + 1;
- long c = 0L; // for stability check
- j |= 1; // poll odd queues
- for (int k = scans; ; j += 2) {
- WorkQueue q;
- if ((s = task.status) < 0)
- break;
- else if (joiner.externalPopAndExecCC(task))
- k = scans;
- else if ((s = task.status) < 0)
- break;
- else if ((q = ws[j & m]) != null && q.pollAndExecCC(task))
- k = scans;
- else if (--k < 0) {
- if (c == (c = ctl))
- break;
- k = scans;
- }
- }
- }
- return s;
+ /**
+ * Performs helpComplete for an external submitter.
+ */
+ final int externalHelpComplete(CountedCompleter<?> task, int maxTasks) {
+ WorkQueue[] ws; int wl;
+ int r = ThreadLocalRandom.getProbe();
+ return ((ws = workQueues) != null && (wl = ws.length) > 0) ?
+ helpComplete(ws[(wl - 1) & r & SQMASK], task, maxTasks) : 0;
}
// Exported methods
@@ -2354,6 +2578,11 @@
* java.lang.Runtime#availableProcessors}, using the {@linkplain
* #defaultForkJoinWorkerThreadFactory default thread factory},
* no UncaughtExceptionHandler, and non-async LIFO processing mode.
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not permitted to modify threads
+ * because it does not hold {@link
+ * java.lang.RuntimePermission}{@code ("modifyThread")}
*/
public ForkJoinPool() {
this(Math.min(MAX_CAP, Runtime.getRuntime().availableProcessors()),
@@ -2369,6 +2598,10 @@
* @param parallelism the parallelism level
* @throws IllegalArgumentException if parallelism less than or
* equal to zero, or greater than implementation limit
+ * @throws SecurityException if a security manager exists and
+ * the caller is not permitted to modify threads
+ * because it does not hold {@link
+ * java.lang.RuntimePermission}{@code ("modifyThread")}
*/
public ForkJoinPool(int parallelism) {
this(parallelism, defaultForkJoinWorkerThreadFactory, null, false);
@@ -2393,6 +2626,10 @@
* @throws IllegalArgumentException if parallelism less than or
* equal to zero, or greater than implementation limit
* @throws NullPointerException if the factory is null
+ * @throws SecurityException if a security manager exists and
+ * the caller is not permitted to modify threads
+ * because it does not hold {@link
+ * java.lang.RuntimePermission}{@code ("modifyThread")}
*/
public ForkJoinPool(int parallelism,
ForkJoinWorkerThreadFactory factory,
@@ -2401,7 +2638,7 @@
this(checkParallelism(parallelism),
checkFactory(factory),
handler,
- (asyncMode ? FIFO_QUEUE : LIFO_QUEUE),
+ asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
"ForkJoinPool-" + nextPoolId() + "-worker-");
checkPermission();
}
@@ -2432,8 +2669,7 @@
this.workerNamePrefix = workerNamePrefix;
this.factory = factory;
this.ueh = handler;
- this.mode = (short)mode;
- this.parallelism = (short)parallelism;
+ this.config = (parallelism & SMASK) | mode;
long np = (long)(-parallelism); // offset ctl counts
this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
}
@@ -2450,7 +2686,6 @@
*
* @return the common pool instance
* @since 1.8
- * @hide
*/
public static ForkJoinPool commonPool() {
// assert common != null : "static init error";
@@ -2479,7 +2714,7 @@
public <T> T invoke(ForkJoinTask<T> task) {
if (task == null)
throw new NullPointerException();
- externalPush(task);
+ externalSubmit(task);
return task.join();
}
@@ -2492,9 +2727,7 @@
* scheduled for execution
*/
public void execute(ForkJoinTask<?> task) {
- if (task == null)
- throw new NullPointerException();
- externalPush(task);
+ externalSubmit(task);
}
// AbstractExecutorService methods
@@ -2512,7 +2745,7 @@
job = (ForkJoinTask<?>) task;
else
job = new ForkJoinTask.RunnableExecuteAction(task);
- externalPush(job);
+ externalSubmit(job);
}
/**
@@ -2526,10 +2759,7 @@
* scheduled for execution
*/
public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task) {
- if (task == null)
- throw new NullPointerException();
- externalPush(task);
- return task;
+ return externalSubmit(task);
}
/**
@@ -2538,9 +2768,7 @@
* scheduled for execution
*/
public <T> ForkJoinTask<T> submit(Callable<T> task) {
- ForkJoinTask<T> job = new ForkJoinTask.AdaptedCallable<T>(task);
- externalPush(job);
- return job;
+ return externalSubmit(new ForkJoinTask.AdaptedCallable<T>(task));
}
/**
@@ -2549,9 +2777,7 @@
* scheduled for execution
*/
public <T> ForkJoinTask<T> submit(Runnable task, T result) {
- ForkJoinTask<T> job = new ForkJoinTask.AdaptedRunnable<T>(task, result);
- externalPush(job);
- return job;
+ return externalSubmit(new ForkJoinTask.AdaptedRunnable<T>(task, result));
}
/**
@@ -2567,8 +2793,7 @@
job = (ForkJoinTask<?>) task;
else
job = new ForkJoinTask.AdaptedRunnableAction(task);
- externalPush(job);
- return job;
+ return externalSubmit(job);
}
/**
@@ -2579,23 +2804,21 @@
// In previous versions of this class, this method constructed
// a task to run ForkJoinTask.invokeAll, but now external
// invocation of multiple tasks is at least as efficient.
- ArrayList<Future<T>> futures = new ArrayList<Future<T>>(tasks.size());
+ ArrayList<Future<T>> futures = new ArrayList<>(tasks.size());
- boolean done = false;
try {
for (Callable<T> t : tasks) {
ForkJoinTask<T> f = new ForkJoinTask.AdaptedCallable<T>(t);
futures.add(f);
- externalPush(f);
+ externalSubmit(f);
}
for (int i = 0, size = futures.size(); i < size; i++)
((ForkJoinTask<?>)futures.get(i)).quietlyJoin();
- done = true;
return futures;
- } finally {
- if (!done)
- for (int i = 0, size = futures.size(); i < size; i++)
- futures.get(i).cancel(false);
+ } catch (Throwable t) {
+ for (int i = 0, size = futures.size(); i < size; i++)
+ futures.get(i).cancel(false);
+ throw t;
}
}
@@ -2625,7 +2848,7 @@
*/
public int getParallelism() {
int par;
- return ((par = parallelism) > 0) ? par : 1;
+ return ((par = config & SMASK) > 0) ? par : 1;
}
/**
@@ -2633,10 +2856,9 @@
*
* @return the targeted parallelism level of the common pool
* @since 1.8
- * @hide
*/
public static int getCommonPoolParallelism() {
- return commonParallelism;
+ return COMMON_PARALLELISM;
}
/**
@@ -2648,7 +2870,7 @@
* @return the number of worker threads
*/
public int getPoolSize() {
- return parallelism + (short)(ctl >>> TC_SHIFT);
+ return (config & SMASK) + (short)(ctl >>> TC_SHIFT);
}
/**
@@ -2658,7 +2880,7 @@
* @return {@code true} if this pool uses async mode
*/
public boolean getAsyncMode() {
- return mode == FIFO_QUEUE;
+ return (config & FIFO_QUEUE) != 0;
}
/**
@@ -2689,7 +2911,7 @@
* @return the number of active threads
*/
public int getActiveThreadCount() {
- int r = parallelism + (int)(ctl >> AC_SHIFT);
+ int r = (config & SMASK) + (int)(ctl >> AC_SHIFT);
return (r <= 0) ? 0 : r; // suppress momentarily negative values
}
@@ -2705,7 +2927,7 @@
* @return {@code true} if all threads are currently idle
*/
public boolean isQuiescent() {
- return parallelism + (int)(ctl >> AC_SHIFT) <= 0;
+ return (config & SMASK) + (int)(ctl >> AC_SHIFT) <= 0;
}
/**
@@ -2720,7 +2942,8 @@
* @return the number of steals
*/
public long getStealCount() {
- long count = stealCount;
+ AuxState sc = auxState;
+ long count = (sc == null) ? 0L : sc.stealCount;
WorkQueue[] ws; WorkQueue w;
if ((ws = workQueues) != null) {
for (int i = 1; i < ws.length; i += 2) {
@@ -2797,10 +3020,11 @@
* @return the next submission, or {@code null} if none
*/
protected ForkJoinTask<?> pollSubmission() {
- WorkQueue[] ws; WorkQueue w; ForkJoinTask<?> t;
- if ((ws = workQueues) != null) {
- for (int i = 0; i < ws.length; i += 2) {
- if ((w = ws[i]) != null && (t = w.poll()) != null)
+ WorkQueue[] ws; int wl; WorkQueue w; ForkJoinTask<?> t;
+ int r = ThreadLocalRandom.nextSecondarySeed();
+ if ((ws = workQueues) != null && (wl = ws.length) > 0) {
+ for (int m = wl - 1, i = 0; i < wl; ++i) {
+ if ((w = ws[(i << 1) & m]) != null && (t = w.poll()) != null)
return t;
}
}
@@ -2850,7 +3074,8 @@
public String toString() {
// Use a single pass through workQueues to collect counts
long qt = 0L, qs = 0L; int rc = 0;
- long st = stealCount;
+ AuxState sc = auxState;
+ long st = (sc == null) ? 0L : sc.stealCount;
long c = ctl;
WorkQueue[] ws; WorkQueue w;
if ((ws = workQueues) != null) {
@@ -2868,16 +3093,16 @@
}
}
}
- int pc = parallelism;
+ int pc = (config & SMASK);
int tc = pc + (short)(c >>> TC_SHIFT);
int ac = pc + (int)(c >> AC_SHIFT);
if (ac < 0) // ignore transient negative
ac = 0;
- String level;
- if ((c & STOP_BIT) != 0)
- level = (tc == 0) ? "Terminated" : "Terminating";
- else
- level = plock < 0 ? "Shutting down" : "Running";
+ int rs = runState;
+ String level = ((rs & TERMINATED) != 0 ? "Terminated" :
+ (rs & STOP) != 0 ? "Terminating" :
+ (rs & SHUTDOWN) != 0 ? "Shutting down" :
+ "Running");
return super.toString() +
"[" + level +
", parallelism = " + pc +
@@ -2894,10 +3119,15 @@
* Possibly initiates an orderly shutdown in which previously
* submitted tasks are executed, but no new tasks will be
* accepted. Invocation has no effect on execution state if this
- * is the {@code commonPool()}, and no additional effect if
+ * is the {@link #commonPool()}, and no additional effect if
* already shut down. Tasks that are in the process of being
* submitted concurrently during the course of this method may or
* may not be rejected.
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not permitted to modify threads
+ * because it does not hold {@link
+ * java.lang.RuntimePermission}{@code ("modifyThread")}
*/
public void shutdown() {
checkPermission();
@@ -2907,7 +3137,7 @@
/**
* Possibly attempts to cancel and/or stop all tasks, and reject
* all subsequently submitted tasks. Invocation has no effect on
- * execution state if this is the {@code commonPool()}, and no
+ * execution state if this is the {@link #commonPool()}, and no
* additional effect if already shut down. Otherwise, tasks that
* are in the process of being submitted or executed concurrently
* during the course of this method may or may not be
@@ -2917,6 +3147,10 @@
* (unlike the case for some other Executors).
*
* @return an empty list
+ * @throws SecurityException if a security manager exists and
+ * the caller is not permitted to modify threads
+ * because it does not hold {@link
+ * java.lang.RuntimePermission}{@code ("modifyThread")}
*/
public List<Runnable> shutdownNow() {
checkPermission();
@@ -2930,9 +3164,7 @@
* @return {@code true} if all tasks have completed following shut down
*/
public boolean isTerminated() {
- long c = ctl;
- return ((c & STOP_BIT) != 0L &&
- (short)(c >>> TC_SHIFT) + parallelism <= 0);
+ return (runState & TERMINATED) != 0;
}
/**
@@ -2949,9 +3181,8 @@
* @return {@code true} if terminating but not yet terminated
*/
public boolean isTerminating() {
- long c = ctl;
- return ((c & STOP_BIT) != 0L &&
- (short)(c >>> TC_SHIFT) + parallelism > 0);
+ int rs = runState;
+ return (rs & STOP) != 0 && (rs & TERMINATED) == 0;
}
/**
@@ -2960,14 +3191,14 @@
* @return {@code true} if this pool has been shut down
*/
public boolean isShutdown() {
- return plock < 0;
+ return (runState & SHUTDOWN) != 0;
}
/**
* Blocks until all tasks have completed execution after a
* shutdown request, or the timeout occurs, or the current thread
- * is interrupted, whichever happens first. Because the {@code
- * commonPool()} never terminates until program shutdown, when
+ * is interrupted, whichever happens first. Because the {@link
+ * #commonPool()} never terminates until program shutdown, when
* applied to the common pool, this method is equivalent to {@link
* #awaitQuiescence(long, TimeUnit)} but always returns {@code false}.
*
@@ -3026,19 +3257,20 @@
}
long startTime = System.nanoTime();
WorkQueue[] ws;
- int r = 0, m;
+ int r = 0, wl;
boolean found = true;
while (!isQuiescent() && (ws = workQueues) != null &&
- (m = ws.length - 1) >= 0) {
+ (wl = ws.length) > 0) {
if (!found) {
if ((System.nanoTime() - startTime) > nanos)
return false;
Thread.yield(); // cannot block
}
found = false;
- for (int j = (m + 1) << 2; j >= 0; --j) {
- ForkJoinTask<?> t; WorkQueue q; int b;
- if ((q = ws[r++ & m]) != null && (b = q.base) - q.top < 0) {
+ for (int m = wl - 1, j = (m + 1) << 2; j >= 0; --j) {
+ ForkJoinTask<?> t; WorkQueue q; int b, k;
+ if ((k = r++ & m) <= m && k >= 0 && (q = ws[k]) != null &&
+ (b = q.base) - q.top < 0) {
found = true;
if ((t = q.pollAt(b)) != null)
t.doExec();
@@ -3051,7 +3283,7 @@
/**
* Waits and/or attempts to assist performing tasks indefinitely
- * until the {@code commonPool()} {@link #isQuiescent}.
+ * until the {@link #commonPool()} {@link #isQuiescent}.
*/
static void quiesceCommonPool() {
common.awaitQuiescence(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
@@ -3062,8 +3294,8 @@
* in {@link ForkJoinPool}s.
*
* <p>A {@code ManagedBlocker} provides two methods. Method
- * {@code isReleasable} must return {@code true} if blocking is
- * not necessary. Method {@code block} blocks the current thread
+ * {@link #isReleasable} must return {@code true} if blocking is
+ * not necessary. Method {@link #block} blocks the current thread
* if necessary (perhaps internally invoking {@code isReleasable}
* before actually blocking). These actions are performed by any
* thread invoking {@link ForkJoinPool#managedBlock(ManagedBlocker)}.
@@ -3077,7 +3309,7 @@
*
* <p>For example, here is a ManagedBlocker based on a
* ReentrantLock:
- * <pre> {@code
+ * <pre> {@code
* class ManagedLocker implements ManagedBlocker {
* final ReentrantLock lock;
* boolean hasLock = false;
@@ -3094,7 +3326,7 @@
*
* <p>Here is a class that possibly blocks waiting for an
* item on a given queue:
- * <pre> {@code
+ * <pre> {@code
* class QueueTaker<E> implements ManagedBlocker {
* final BlockingQueue<E> queue;
* volatile E item = null;
@@ -3132,37 +3364,46 @@
}
/**
- * Blocks in accord with the given blocker. If the current thread
- * is a {@link ForkJoinWorkerThread}, this method possibly
- * arranges for a spare thread to be activated if necessary to
- * ensure sufficient parallelism while the current thread is blocked.
+ * Runs the given possibly blocking task. When {@linkplain
+ * ForkJoinTask#inForkJoinPool() running in a ForkJoinPool}, this
+ * method possibly arranges for a spare thread to be activated if
+ * necessary to ensure sufficient parallelism while the current
+ * thread is blocked in {@link ManagedBlocker#block blocker.block()}.
*
- * <p>If the caller is not a {@link ForkJoinTask}, this method is
+ * <p>This method repeatedly calls {@code blocker.isReleasable()} and
+ * {@code blocker.block()} until either method returns {@code true}.
+ * Every call to {@code blocker.block()} is preceded by a call to
+ * {@code blocker.isReleasable()} that returned {@code false}.
+ *
+ * <p>If not running in a ForkJoinPool, this method is
* behaviorally equivalent to
- * <pre> {@code
+ * <pre> {@code
* while (!blocker.isReleasable())
* if (blocker.block())
- * return;
- * }</pre>
+ * break;}</pre>
*
- * If the caller is a {@code ForkJoinTask}, then the pool may
- * first be expanded to ensure parallelism, and later adjusted.
+ * If running in a ForkJoinPool, the pool may first be expanded to
+ * ensure sufficient parallelism available during the call to
+ * {@code blocker.block()}.
*
- * @param blocker the blocker
- * @throws InterruptedException if blocker.block did so
+ * @param blocker the blocker task
+ * @throws InterruptedException if {@code blocker.block()} did so
*/
public static void managedBlock(ManagedBlocker blocker)
throws InterruptedException {
+ ForkJoinPool p;
+ ForkJoinWorkerThread wt;
Thread t = Thread.currentThread();
- if (t instanceof ForkJoinWorkerThread) {
- ForkJoinPool p = ((ForkJoinWorkerThread)t).pool;
+ if ((t instanceof ForkJoinWorkerThread) &&
+ (p = (wt = (ForkJoinWorkerThread)t).pool) != null) {
+ WorkQueue w = wt.workQueue;
while (!blocker.isReleasable()) {
- if (p.tryCompensate(p.ctl)) {
+ if (p.tryCompensate(w)) {
try {
do {} while (!blocker.isReleasable() &&
!blocker.block());
} finally {
- p.incrementActiveCount();
+ U.getAndAddLong(p, CTL, AC_UNIT);
}
break;
}
@@ -3187,49 +3428,40 @@
}
// Unsafe mechanics
- private static final sun.misc.Unsafe U;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
private static final long CTL;
- private static final long PARKBLOCKER;
+ private static final long RUNSTATE;
private static final int ABASE;
private static final int ASHIFT;
- private static final long STEALCOUNT;
- private static final long PLOCK;
- private static final long INDEXSEED;
- private static final long QBASE;
- private static final long QLOCK;
static {
- // initialize field offsets for CAS etc
try {
- U = sun.misc.Unsafe.getUnsafe();
- Class<?> k = ForkJoinPool.class;
CTL = U.objectFieldOffset
- (k.getDeclaredField("ctl"));
- STEALCOUNT = U.objectFieldOffset
- (k.getDeclaredField("stealCount"));
- PLOCK = U.objectFieldOffset
- (k.getDeclaredField("plock"));
- INDEXSEED = U.objectFieldOffset
- (k.getDeclaredField("indexSeed"));
- Class<?> tk = Thread.class;
- PARKBLOCKER = U.objectFieldOffset
- (tk.getDeclaredField("parkBlocker"));
- Class<?> wk = WorkQueue.class;
- QBASE = U.objectFieldOffset
- (wk.getDeclaredField("base"));
- QLOCK = U.objectFieldOffset
- (wk.getDeclaredField("qlock"));
- Class<?> ak = ForkJoinTask[].class;
- ABASE = U.arrayBaseOffset(ak);
- int scale = U.arrayIndexScale(ak);
+ (ForkJoinPool.class.getDeclaredField("ctl"));
+ RUNSTATE = U.objectFieldOffset
+ (ForkJoinPool.class.getDeclaredField("runState"));
+ ABASE = U.arrayBaseOffset(ForkJoinTask[].class);
+ int scale = U.arrayIndexScale(ForkJoinTask[].class);
if ((scale & (scale - 1)) != 0)
- throw new Error("data type scale not a power of two");
+ throw new Error("array index scale not a power of two");
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
- } catch (Exception e) {
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
- submitters = new ThreadLocal<Submitter>();
+ // Reduce the risk of rare disastrous classloading in first call to
+ // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773
+ Class<?> ensureLoaded = LockSupport.class;
+
+ int commonMaxSpares = DEFAULT_COMMON_MAX_SPARES;
+ try {
+ String p = System.getProperty
+ ("java.util.concurrent.ForkJoinPool.common.maximumSpares");
+ if (p != null)
+ commonMaxSpares = Integer.parseInt(p);
+ } catch (Exception ignore) {}
+ COMMON_MAX_SPARES = commonMaxSpares;
+
defaultForkJoinWorkerThreadFactory =
new DefaultForkJoinWorkerThreadFactory();
modifyThreadPermission = new RuntimePermission("modifyThread");
@@ -3237,18 +3469,18 @@
common = java.security.AccessController.doPrivileged
(new java.security.PrivilegedAction<ForkJoinPool>() {
public ForkJoinPool run() { return makeCommonPool(); }});
- int par = common.parallelism; // report 1 even if threads disabled
- commonParallelism = par > 0 ? par : 1;
+
+ // report 1 even if threads disabled
+ COMMON_PARALLELISM = Math.max(common.config & SMASK, 1);
}
/**
* Creates and returns the common pool, respecting user settings
* specified via system properties.
*/
- private static ForkJoinPool makeCommonPool() {
+ static ForkJoinPool makeCommonPool() {
int parallelism = -1;
- ForkJoinWorkerThreadFactory factory
- = defaultForkJoinWorkerThreadFactory;
+ ForkJoinWorkerThreadFactory factory = null;
UncaughtExceptionHandler handler = null;
try { // ignore exceptions in accessing/parsing properties
String pp = System.getProperty
@@ -3267,14 +3499,52 @@
getSystemClassLoader().loadClass(hp).newInstance());
} catch (Exception ignore) {
}
-
+ if (factory == null) {
+ if (System.getSecurityManager() == null)
+ factory = defaultForkJoinWorkerThreadFactory;
+ else // use security-managed default
+ factory = new InnocuousForkJoinWorkerThreadFactory();
+ }
if (parallelism < 0 && // default 1 less than #cores
- (parallelism = Runtime.getRuntime().availableProcessors() - 1) < 0)
- parallelism = 0;
+ (parallelism = Runtime.getRuntime().availableProcessors() - 1) <= 0)
+ parallelism = 1;
if (parallelism > MAX_CAP)
parallelism = MAX_CAP;
return new ForkJoinPool(parallelism, factory, handler, LIFO_QUEUE,
"ForkJoinPool.commonPool-worker-");
}
+ /**
+ * Factory for innocuous worker threads.
+ */
+ private static final class InnocuousForkJoinWorkerThreadFactory
+ implements ForkJoinWorkerThreadFactory {
+
+ /**
+ * An ACC to restrict permissions for the factory itself.
+ * The constructed workers have no permissions set.
+ */
+ private static final AccessControlContext innocuousAcc;
+ static {
+ Permissions innocuousPerms = new Permissions();
+ innocuousPerms.add(modifyThreadPermission);
+ innocuousPerms.add(new RuntimePermission(
+ "enableContextClassLoaderOverride"));
+ innocuousPerms.add(new RuntimePermission(
+ "modifyThreadGroup"));
+ innocuousAcc = new AccessControlContext(new ProtectionDomain[] {
+ new ProtectionDomain(null, innocuousPerms)
+ });
+ }
+
+ public final ForkJoinWorkerThread newThread(ForkJoinPool pool) {
+ return java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<ForkJoinWorkerThread>() {
+ public ForkJoinWorkerThread run() {
+ return new ForkJoinWorkerThread.
+ InnocuousForkJoinWorkerThread(pool);
+ }}, innocuousAcc);
+ }
+ }
+
}
diff --git a/luni/src/main/java/java/util/concurrent/ForkJoinTask.java b/luni/src/main/java/java/util/concurrent/ForkJoinTask.java
index 3a1a381..a8d97db 100644
--- a/luni/src/main/java/java/util/concurrent/ForkJoinTask.java
+++ b/luni/src/main/java/java/util/concurrent/ForkJoinTask.java
@@ -7,13 +7,17 @@
package java.util.concurrent;
import java.io.Serializable;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.List;
import java.util.RandomAccess;
-import java.lang.ref.WeakReference;
-import java.lang.ref.ReferenceQueue;
import java.util.concurrent.locks.ReentrantLock;
-import java.lang.reflect.Constructor;
+
+// BEGIN android-note
+// removed java 9 code
+// END android-note
/**
* Abstract base class for tasks that run within a {@link ForkJoinPool}.
@@ -24,8 +28,8 @@
*
* <p>A "main" {@code ForkJoinTask} begins execution when it is
* explicitly submitted to a {@link ForkJoinPool}, or, if not already
- * engaged in a ForkJoin computation, commenced in the {@code
- * ForkJoinPool.commonPool()} via {@link #fork}, {@link #invoke}, or
+ * engaged in a ForkJoin computation, commenced in the {@link
+ * ForkJoinPool#commonPool()} via {@link #fork}, {@link #invoke}, or
* related methods. Once started, it will usually in turn start other
* subtasks. As indicated by the name of this class, many programs
* using {@code ForkJoinTask} employ only methods {@link #fork} and
@@ -66,9 +70,10 @@
* but doing do requires three further considerations: (1) Completion
* of few if any <em>other</em> tasks should be dependent on a task
* that blocks on external synchronization or I/O. Event-style async
- * tasks that are never joined often fall into this category.
- * (2) To minimize resource impact, tasks should be small; ideally
- * performing only the (possibly) blocking action. (3) Unless the {@link
+ * tasks that are never joined (for example, those subclassing {@link
+ * CountedCompleter}) often fall into this category. (2) To minimize
+ * resource impact, tasks should be small; ideally performing only the
+ * (possibly) blocking action. (3) Unless the {@link
* ForkJoinPool.ManagedBlocker} API is used, or the number of possibly
* blocked tasks is known to be less than the pool's {@link
* ForkJoinPool#getParallelism} level, the pool cannot guarantee that
@@ -111,11 +116,13 @@
* <p>The ForkJoinTask class is not usually directly subclassed.
* Instead, you subclass one of the abstract classes that support a
* particular style of fork/join processing, typically {@link
- * RecursiveAction} for most computations that do not return results
- * and {@link RecursiveTask} for those that do. Normally, a concrete
- * ForkJoinTask subclass declares fields comprising its parameters,
- * established in a constructor, and then defines a {@code compute}
- * method that somehow uses the control methods supplied by this base class.
+ * RecursiveAction} for most computations that do not return results,
+ * {@link RecursiveTask} for those that do, and {@link
+ * CountedCompleter} for those in which completed actions trigger
+ * other actions. Normally, a concrete ForkJoinTask subclass declares
+ * fields comprising its parameters, established in a constructor, and
+ * then defines a {@code compute} method that somehow uses the control
+ * methods supplied by this base class.
*
* <p>Method {@link #join} and its variants are appropriate for use
* only when completion dependencies are acyclic; that is, the
@@ -127,9 +134,9 @@
* may be of use in constructing custom subclasses for problems that
* are not statically structured as DAGs. To support such usages, a
* ForkJoinTask may be atomically <em>tagged</em> with a {@code short}
- * value using {@code setForkJoinTaskTag} or {@code
- * compareAndSetForkJoinTaskTag} and checked using {@code
- * getForkJoinTaskTag}. The ForkJoinTask implementation does not use
+ * value using {@link #setForkJoinTaskTag} or {@link
+ * #compareAndSetForkJoinTaskTag} and checked using {@link
+ * #getForkJoinTaskTag}. The ForkJoinTask implementation does not use
* these {@code protected} methods or tags for any purpose, but they
* may be of use in the construction of specialized subclasses. For
* example, parallel graph traversals can use the supplied methods to
@@ -169,8 +176,6 @@
* @since 1.7
* @author Doug Lea
*/
-// android-note: Removed references to hidden apis commonPool, CountedCompleter
-// etc.
public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
/*
@@ -259,15 +264,22 @@
}
/**
- * Tries to set SIGNAL status unless already completed. Used by
- * ForkJoinPool. Other variants are directly incorporated into
- * externalAwaitDone etc.
+ * If not done, sets SIGNAL status and performs Object.wait(timeout).
+ * This task may or may not be done on exit. Ignores interrupts.
*
- * @return true if successful
+ * @param timeout using Object.wait conventions.
*/
- final boolean trySetSignal() {
- int s = status;
- return s >= 0 && U.compareAndSwapInt(this, STATUS, s, s | SIGNAL);
+ final void internalWait(long timeout) {
+ int s;
+ if ((s = status) >= 0 && // force completer to issue notify
+ U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
+ synchronized (this) {
+ if (status >= 0)
+ try { wait(timeout); } catch (InterruptedException ie) { }
+ else
+ notifyAll();
+ }
+ }
}
/**
@@ -275,35 +287,29 @@
* @return status upon completion
*/
private int externalAwaitDone() {
- int s;
- ForkJoinPool cp = ForkJoinPool.common;
- if ((s = status) >= 0) {
- if (cp != null) {
- if (this instanceof CountedCompleter)
- s = cp.externalHelpComplete((CountedCompleter<?>)this);
- else if (cp.tryExternalUnpush(this))
- s = doExec();
- }
- if (s >= 0 && (s = status) >= 0) {
- boolean interrupted = false;
- do {
- if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
- synchronized (this) {
- if (status >= 0) {
- try {
- wait();
- } catch (InterruptedException ie) {
- interrupted = true;
- }
+ int s = ((this instanceof CountedCompleter) ? // try helping
+ ForkJoinPool.common.externalHelpComplete(
+ (CountedCompleter<?>)this, 0) :
+ ForkJoinPool.common.tryExternalUnpush(this) ? doExec() : 0);
+ if (s >= 0 && (s = status) >= 0) {
+ boolean interrupted = false;
+ do {
+ if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
+ synchronized (this) {
+ if (status >= 0) {
+ try {
+ wait(0L);
+ } catch (InterruptedException ie) {
+ interrupted = true;
}
- else
- notifyAll();
}
+ else
+ notifyAll();
}
- } while ((s = status) >= 0);
- if (interrupted)
- Thread.currentThread().interrupt();
- }
+ }
+ } while ((s = status) >= 0);
+ if (interrupted)
+ Thread.currentThread().interrupt();
}
return s;
}
@@ -313,22 +319,22 @@
*/
private int externalInterruptibleAwaitDone() throws InterruptedException {
int s;
- ForkJoinPool cp = ForkJoinPool.common;
if (Thread.interrupted())
throw new InterruptedException();
- if ((s = status) >= 0 && cp != null) {
- if (this instanceof CountedCompleter)
- cp.externalHelpComplete((CountedCompleter<?>)this);
- else if (cp.tryExternalUnpush(this))
- doExec();
- }
- while ((s = status) >= 0) {
- if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
- synchronized (this) {
- if (status >= 0)
- wait();
- else
- notifyAll();
+ if ((s = status) >= 0 &&
+ (s = ((this instanceof CountedCompleter) ?
+ ForkJoinPool.common.externalHelpComplete(
+ (CountedCompleter<?>)this, 0) :
+ ForkJoinPool.common.tryExternalUnpush(this) ? doExec() :
+ 0)) >= 0) {
+ while ((s = status) >= 0) {
+ if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
+ synchronized (this) {
+ if (status >= 0)
+ wait(0L);
+ else
+ notifyAll();
+ }
}
}
}
@@ -348,7 +354,7 @@
((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
(w = (wt = (ForkJoinWorkerThread)t).workQueue).
tryUnpush(this) && (s = doExec()) < 0 ? s :
- wt.pool.awaitJoin(w, this) :
+ wt.pool.awaitJoin(w, this, 0L) :
externalAwaitDone();
}
@@ -361,7 +367,8 @@
int s; Thread t; ForkJoinWorkerThread wt;
return (s = doExec()) < 0 ? s :
((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
- (wt = (ForkJoinWorkerThread)t).pool.awaitJoin(wt.workQueue, this) :
+ (wt = (ForkJoinWorkerThread)t).pool.
+ awaitJoin(wt.workQueue, this, 0L) :
externalAwaitDone();
}
@@ -402,7 +409,8 @@
ExceptionNode next;
final long thrower; // use id not ref to avoid weak cycles
final int hashCode; // store task hashCode before weak ref disappears
- ExceptionNode(ForkJoinTask<?> task, Throwable ex, ExceptionNode next) {
+ ExceptionNode(ForkJoinTask<?> task, Throwable ex, ExceptionNode next,
+ ReferenceQueue<Object> exceptionTableRefQueue) {
super(task, exceptionTableRefQueue);
this.ex = ex;
this.next = next;
@@ -428,7 +436,8 @@
int i = h & (t.length - 1);
for (ExceptionNode e = t[i]; ; e = e.next) {
if (e == null) {
- t[i] = new ExceptionNode(this, ex, t[i]);
+ t[i] = new ExceptionNode(this, ex, t[i],
+ exceptionTableRefQueue);
break;
}
if (e.get() == this) // already present
@@ -507,22 +516,20 @@
}
/**
- * Returns a rethrowable exception for the given task, if
- * available. To provide accurate stack traces, if the exception
- * was not thrown by the current thread, we try to create a new
- * exception of the same type as the one thrown, but with the
- * recorded exception as its cause. If there is no such
- * constructor, we instead try to use a no-arg constructor,
- * followed by initCause, to the same effect. If none of these
- * apply, or any fail due to other exceptions, we return the
- * recorded exception, which is still correct, although it may
- * contain a misleading stack trace.
+ * Returns a rethrowable exception for this task, if available.
+ * To provide accurate stack traces, if the exception was not
+ * thrown by the current thread, we try to create a new exception
+ * of the same type as the one thrown, but with the recorded
+ * exception as its cause. If there is no such constructor, we
+ * instead try to use a no-arg constructor, followed by initCause,
+ * to the same effect. If none of these apply, or any fail due to
+ * other exceptions, we return the recorded exception, which is
+ * still correct, although it may contain a misleading stack
+ * trace.
*
* @return the exception, or null if none
*/
private Throwable getThrowableException() {
- if ((status & DONE_MASK) != EXCEPTIONAL)
- return null;
int h = System.identityHashCode(this);
ExceptionNode e;
final ReentrantLock lock = exceptionTableLock;
@@ -539,21 +546,19 @@
Throwable ex;
if (e == null || (ex = e.ex) == null)
return null;
- if (false && e.thrower != Thread.currentThread().getId()) {
- Class<? extends Throwable> ec = ex.getClass();
+ if (e.thrower != Thread.currentThread().getId()) {
try {
Constructor<?> noArgCtor = null;
- Constructor<?>[] cs = ec.getConstructors();// public ctors only
- for (int i = 0; i < cs.length; ++i) {
- Constructor<?> c = cs[i];
+ // public ctors only
+ for (Constructor<?> c : ex.getClass().getConstructors()) {
Class<?>[] ps = c.getParameterTypes();
if (ps.length == 0)
noArgCtor = c;
else if (ps.length == 1 && ps[0] == Throwable.class)
- return (Throwable)(c.newInstance(ex));
+ return (Throwable)c.newInstance(ex);
}
if (noArgCtor != null) {
- Throwable wx = (Throwable)(noArgCtor.newInstance());
+ Throwable wx = (Throwable)noArgCtor.newInstance();
wx.initCause(ex);
return wx;
}
@@ -564,7 +569,7 @@
}
/**
- * Poll stale refs and remove them. Call only while holding lock.
+ * Polls stale refs and removes them. Call only while holding lock.
*/
private static void expungeStaleExceptions() {
for (Object x; (x = exceptionTableRefQueue.poll()) != null;) {
@@ -591,7 +596,7 @@
}
/**
- * If lock is available, poll stale refs and remove them.
+ * If lock is available, polls stale refs and removes them.
* Called from ForkJoinPool when pools become quiescent.
*/
static final void helpExpungeStaleExceptions() {
@@ -606,21 +611,23 @@
}
/**
- * A version of "sneaky throw" to relay exceptions
+ * A version of "sneaky throw" to relay exceptions.
*/
static void rethrow(Throwable ex) {
- if (ex != null)
- ForkJoinTask.<RuntimeException>uncheckedThrow(ex);
+ ForkJoinTask.<RuntimeException>uncheckedThrow(ex);
}
/**
* The sneaky part of sneaky throw, relying on generics
* limitations to evade compiler complaints about rethrowing
- * unchecked exceptions
+ * unchecked exceptions.
*/
@SuppressWarnings("unchecked") static <T extends Throwable>
- void uncheckedThrow(Throwable t) throws T {
- throw (T)t; // rely on vacuous cast
+ void uncheckedThrow(Throwable t) throws T {
+ if (t != null)
+ throw (T)t; // rely on vacuous cast
+ else
+ throw new Error("Unknown Exception");
}
/**
@@ -637,8 +644,8 @@
/**
* Arranges to asynchronously execute this task in the pool the
- * current task is running in, if applicable, or using the {@code
- * ForkJoinPool.commonPool()} if not {@link #inForkJoinPool}. While
+ * current task is running in, if applicable, or using the {@link
+ * ForkJoinPool#commonPool()} if not {@link #inForkJoinPool}. While
* it is not necessarily enforced, it is a usage error to fork a
* task more than once unless it has completed and been
* reinitialized. Subsequent modifications to the state of this
@@ -936,7 +943,6 @@
* invocations of {@code join} and related operations.
*
* @since 1.8
- * @hide
*/
public final void quietlyComplete() {
setCompletion(NORMAL);
@@ -956,11 +962,10 @@
public final V get() throws InterruptedException, ExecutionException {
int s = (Thread.currentThread() instanceof ForkJoinWorkerThread) ?
doJoin() : externalInterruptibleAwaitDone();
- Throwable ex;
if ((s &= DONE_MASK) == CANCELLED)
throw new CancellationException();
- if (s == EXCEPTIONAL && (ex = getThrowableException()) != null)
- throw new ExecutionException(ex);
+ if (s == EXCEPTIONAL)
+ throw new ExecutionException(getThrowableException());
return getRawResult();
}
@@ -980,75 +985,46 @@
*/
public final V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
+ int s;
+ long nanos = unit.toNanos(timeout);
if (Thread.interrupted())
throw new InterruptedException();
- // Messy in part because we measure in nanosecs, but wait in millisecs
- int s; long ms;
- long ns = unit.toNanos(timeout);
- ForkJoinPool cp;
- if ((s = status) >= 0 && ns > 0L) {
- long deadline = System.nanoTime() + ns;
- ForkJoinPool p = null;
- ForkJoinPool.WorkQueue w = null;
+ if ((s = status) >= 0 && nanos > 0L) {
+ long d = System.nanoTime() + nanos;
+ long deadline = (d == 0L) ? 1L : d; // avoid 0
Thread t = Thread.currentThread();
if (t instanceof ForkJoinWorkerThread) {
ForkJoinWorkerThread wt = (ForkJoinWorkerThread)t;
- p = wt.pool;
- w = wt.workQueue;
- p.helpJoinOnce(w, this); // no retries on failure
+ s = wt.pool.awaitJoin(wt.workQueue, this, deadline);
}
- else if ((cp = ForkJoinPool.common) != null) {
- if (this instanceof CountedCompleter)
- cp.externalHelpComplete((CountedCompleter<?>)this);
- else if (cp.tryExternalUnpush(this))
- doExec();
- }
- boolean canBlock = false;
- boolean interrupted = false;
- try {
- while ((s = status) >= 0) {
- if (w != null && w.qlock < 0)
- cancelIgnoringExceptions(this);
- else if (!canBlock) {
- if (p == null || p.tryCompensate(p.ctl))
- canBlock = true;
- }
- else {
- if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) > 0L &&
- U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
- synchronized (this) {
- if (status >= 0) {
- try {
- wait(ms);
- } catch (InterruptedException ie) {
- if (p == null)
- interrupted = true;
- }
- }
- else
- notifyAll();
- }
+ else if ((s = ((this instanceof CountedCompleter) ?
+ ForkJoinPool.common.externalHelpComplete(
+ (CountedCompleter<?>)this, 0) :
+ ForkJoinPool.common.tryExternalUnpush(this) ?
+ doExec() : 0)) >= 0) {
+ long ns, ms; // measure in nanosecs, but wait in millisecs
+ while ((s = status) >= 0 &&
+ (ns = deadline - System.nanoTime()) > 0L) {
+ if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) > 0L &&
+ U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
+ synchronized (this) {
+ if (status >= 0)
+ wait(ms); // OK to throw InterruptedException
+ else
+ notifyAll();
}
- if ((s = status) < 0 || interrupted ||
- (ns = deadline - System.nanoTime()) <= 0L)
- break;
}
}
- } finally {
- if (p != null && canBlock)
- p.incrementActiveCount();
}
- if (interrupted)
- throw new InterruptedException();
}
+ if (s >= 0)
+ s = status;
if ((s &= DONE_MASK) != NORMAL) {
- Throwable ex;
if (s == CANCELLED)
throw new CancellationException();
if (s != EXCEPTIONAL)
throw new TimeoutException();
- if ((ex = getThrowableException()) != null)
- throw new ExecutionException(ex);
+ throw new ExecutionException(getThrowableException());
}
return getRawResult();
}
@@ -1074,10 +1050,10 @@
/**
* Possibly executes tasks until the pool hosting the current task
- * {@link ForkJoinPool#isQuiescent is quiescent}. This method may
- * be of use in designs in which many tasks are forked, but none
- * are explicitly joined, instead executing them until all are
- * processed.
+ * {@linkplain ForkJoinPool#isQuiescent is quiescent}. This
+ * method may be of use in designs in which many tasks are forked,
+ * but none are explicitly joined, instead executing them until
+ * all are processed.
*/
public static void helpQuiesce() {
Thread t;
@@ -1113,10 +1089,12 @@
}
/**
- * Returns the pool hosting the current task execution, or null
- * if this task is executing outside of any ForkJoinPool.
+ * Returns the pool hosting the current thread, or {@code null}
+ * if the current thread is executing outside of any ForkJoinPool.
*
- * @see #inForkJoinPool
+ * <p>This method returns {@code null} if and only if {@link
+ * #inForkJoinPool} returns {@code false}.
+ *
* @return the pool, or {@code null} if none
*/
public static ForkJoinPool getPool() {
@@ -1283,6 +1261,25 @@
null;
}
+ /**
+ * If the current thread is operating in a ForkJoinPool,
+ * unschedules and returns, without executing, a task externally
+ * submitted to the pool, if one is available. Availability may be
+ * transient, so a {@code null} result does not necessarily imply
+ * quiescence of the pool. This method is designed primarily to
+ * support extensions, and is unlikely to be useful otherwise.
+ *
+ * @return a task, or {@code null} if none are available
+ * @since 9
+ * @hide
+ */
+ // android-changed - hidden
+ protected static ForkJoinTask<?> pollSubmission() {
+ Thread t;
+ return ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
+ ((ForkJoinWorkerThread)t).pool.pollSubmission() : null;
+ }
+
// tag operations
/**
@@ -1290,24 +1287,22 @@
*
* @return the tag for this task
* @since 1.8
- * @hide
*/
public final short getForkJoinTaskTag() {
return (short)status;
}
/**
- * Atomically sets the tag value for this task.
+ * Atomically sets the tag value for this task and returns the old value.
*
- * @param tag the tag value
+ * @param newValue the new tag value
* @return the previous value of the tag
* @since 1.8
- * @hide
*/
- public final short setForkJoinTaskTag(short tag) {
+ public final short setForkJoinTaskTag(short newValue) {
for (int s;;) {
if (U.compareAndSwapInt(this, STATUS, s = status,
- (s & ~SMASK) | (tag & SMASK)))
+ (s & ~SMASK) | (newValue & SMASK)))
return (short)s;
}
}
@@ -1320,25 +1315,24 @@
* before processing, otherwise exiting because the node has
* already been visited.
*
- * @param e the expected tag value
- * @param tag the new tag value
+ * @param expect the expected tag value
+ * @param update the new tag value
* @return {@code true} if successful; i.e., the current value was
- * equal to e and is now tag.
+ * equal to {@code expect} and was changed to {@code update}.
* @since 1.8
- * @hide
*/
- public final boolean compareAndSetForkJoinTaskTag(short e, short tag) {
+ public final boolean compareAndSetForkJoinTaskTag(short expect, short update) {
for (int s;;) {
- if ((short)(s = status) != e)
+ if ((short)(s = status) != expect)
return false;
if (U.compareAndSwapInt(this, STATUS, s,
- (s & ~SMASK) | (tag & SMASK)))
+ (s & ~SMASK) | (update & SMASK)))
return true;
}
}
/**
- * Adaptor for Runnables. This implements RunnableFuture
+ * Adapter for Runnables. This implements RunnableFuture
* to be compliant with AbstractExecutorService constraints
* when used in ForkJoinPool.
*/
@@ -1359,7 +1353,7 @@
}
/**
- * Adaptor for Runnables without results
+ * Adapter for Runnables without results.
*/
static final class AdaptedRunnableAction extends ForkJoinTask<Void>
implements RunnableFuture<Void> {
@@ -1376,7 +1370,7 @@
}
/**
- * Adaptor for Runnables in which failure forces worker exception
+ * Adapter for Runnables in which failure forces worker exception.
*/
static final class RunnableExecuteAction extends ForkJoinTask<Void> {
final Runnable runnable;
@@ -1394,7 +1388,7 @@
}
/**
- * Adaptor for Callables
+ * Adapter for Callables.
*/
static final class AdaptedCallable<T> extends ForkJoinTask<T>
implements RunnableFuture<T> {
@@ -1467,6 +1461,8 @@
/**
* Saves this task to a stream (that is, serializes it).
*
+ * @param s the stream
+ * @throws java.io.IOException if an I/O error occurs
* @serialData the current run status and the exception thrown
* during execution, or {@code null} if none
*/
@@ -1478,6 +1474,10 @@
/**
* Reconstitutes this task from a stream (that is, deserializes it).
+ * @param s the stream
+ * @throws ClassNotFoundException if the class of a serialized object
+ * could not be found
+ * @throws java.io.IOException if an I/O error occurs
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
@@ -1488,7 +1488,7 @@
}
// Unsafe mechanics
- private static final sun.misc.Unsafe U;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
private static final long STATUS;
static {
@@ -1496,12 +1496,11 @@
exceptionTableRefQueue = new ReferenceQueue<Object>();
exceptionTable = new ExceptionNode[EXCEPTION_MAP_CAPACITY];
try {
- U = sun.misc.Unsafe.getUnsafe();
- Class<?> k = ForkJoinTask.class;
STATUS = U.objectFieldOffset
- (k.getDeclaredField("status"));
- } catch (Exception e) {
+ (ForkJoinTask.class.getDeclaredField("status"));
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
+
}
diff --git a/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java b/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java
index ae28700..664d56e 100644
--- a/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java
+++ b/luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java
@@ -6,6 +6,9 @@
package java.util.concurrent;
+import java.security.AccessControlContext;
+import java.security.ProtectionDomain;
+
/**
* A thread managed by a {@link ForkJoinPool}, which executes
* {@link ForkJoinTask}s.
@@ -32,6 +35,10 @@
* completes. This leads to a visibility race, that is tolerated
* by requiring that the workQueue field is only accessed by the
* owning thread.
+ *
+ * Support for (non-public) subclass InnocuousForkJoinWorkerThread
+ * requires that we break quite a lot of encapsulation (via Unsafe)
+ * both here and in the subclass to access and set Thread fields.
*/
final ForkJoinPool pool; // the pool this thread works in
@@ -51,6 +58,18 @@
}
/**
+ * Version for InnocuousForkJoinWorkerThread.
+ */
+ ForkJoinWorkerThread(ForkJoinPool pool, ThreadGroup threadGroup,
+ AccessControlContext acc) {
+ super(threadGroup, null, "aForkJoinWorkerThread");
+ U.putOrderedObject(this, INHERITEDACCESSCONTROLCONTEXT, acc);
+ eraseThreadLocals(); // clear before registering
+ this.pool = pool;
+ this.workQueue = pool.registerWorker(this);
+ }
+
+ /**
* Returns the pool hosting this thread.
*
* @return the pool
@@ -70,7 +89,7 @@
* @return the index number
*/
public int getPoolIndex() {
- return workQueue.poolIndex >>> 1; // ignore odd/even tag bit
+ return workQueue.getPoolIndex();
}
/**
@@ -102,21 +121,124 @@
* {@link ForkJoinTask}s.
*/
public void run() {
- Throwable exception = null;
- try {
- onStart();
- pool.runWorker(workQueue);
- } catch (Throwable ex) {
- exception = ex;
- } finally {
+ if (workQueue.array == null) { // only run once
+ Throwable exception = null;
try {
- onTermination(exception);
+ onStart();
+ pool.runWorker(workQueue);
} catch (Throwable ex) {
- if (exception == null)
- exception = ex;
+ exception = ex;
} finally {
- pool.deregisterWorker(this, exception);
+ try {
+ onTermination(exception);
+ } catch (Throwable ex) {
+ if (exception == null)
+ exception = ex;
+ } finally {
+ pool.deregisterWorker(this, exception);
+ }
}
}
}
+
+ /**
+ * Erases ThreadLocals by nulling out Thread maps.
+ */
+ final void eraseThreadLocals() {
+ U.putObject(this, THREADLOCALS, null);
+ U.putObject(this, INHERITABLETHREADLOCALS, null);
+ }
+
+ /**
+ * Non-public hook method for InnocuousForkJoinWorkerThread.
+ */
+ void afterTopLevelExec() {
+ }
+
+ // Set up to allow setting thread fields in constructor
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long THREADLOCALS;
+ private static final long INHERITABLETHREADLOCALS;
+ private static final long INHERITEDACCESSCONTROLCONTEXT;
+ static {
+ try {
+ THREADLOCALS = U.objectFieldOffset
+ (Thread.class.getDeclaredField("threadLocals"));
+ INHERITABLETHREADLOCALS = U.objectFieldOffset
+ (Thread.class.getDeclaredField("inheritableThreadLocals"));
+ INHERITEDACCESSCONTROLCONTEXT = U.objectFieldOffset
+ (Thread.class.getDeclaredField("inheritedAccessControlContext"));
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
+ }
+
+ /**
+ * A worker thread that has no permissions, is not a member of any
+ * user-defined ThreadGroup, and erases all ThreadLocals after
+ * running each top-level task.
+ */
+ static final class InnocuousForkJoinWorkerThread extends ForkJoinWorkerThread {
+ /** The ThreadGroup for all InnocuousForkJoinWorkerThreads */
+ private static final ThreadGroup innocuousThreadGroup =
+ createThreadGroup();
+
+ /** An AccessControlContext supporting no privileges */
+ private static final AccessControlContext INNOCUOUS_ACC =
+ new AccessControlContext(
+ new ProtectionDomain[] {
+ new ProtectionDomain(null, null)
+ });
+
+ InnocuousForkJoinWorkerThread(ForkJoinPool pool) {
+ super(pool, innocuousThreadGroup, INNOCUOUS_ACC);
+ }
+
+ @Override // to erase ThreadLocals
+ void afterTopLevelExec() {
+ eraseThreadLocals();
+ }
+
+ @Override // to always report system loader
+ public ClassLoader getContextClassLoader() {
+ return ClassLoader.getSystemClassLoader();
+ }
+
+ @Override // to silently fail
+ public void setUncaughtExceptionHandler(UncaughtExceptionHandler x) { }
+
+ @Override // paranoically
+ public void setContextClassLoader(ClassLoader cl) {
+ throw new SecurityException("setContextClassLoader");
+ }
+
+ /**
+ * Returns a new group with the system ThreadGroup (the
+ * topmost, parent-less group) as parent. Uses Unsafe to
+ * traverse Thread.group and ThreadGroup.parent fields.
+ */
+ private static ThreadGroup createThreadGroup() {
+ try {
+ sun.misc.Unsafe u = sun.misc.Unsafe.getUnsafe();
+ long tg = u.objectFieldOffset
+ (Thread.class.getDeclaredField("group"));
+ long gp = u.objectFieldOffset
+ (ThreadGroup.class.getDeclaredField("parent"));
+ ThreadGroup group = (ThreadGroup)
+ u.getObject(Thread.currentThread(), tg);
+ while (group != null) {
+ ThreadGroup parent = (ThreadGroup)u.getObject(group, gp);
+ if (parent == null)
+ return new ThreadGroup(group,
+ "InnocuousForkJoinWorkerThreadGroup");
+ group = parent;
+ }
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
+ // fall through if null as cannot-happen safeguard
+ throw new Error("Cannot create ThreadGroup");
+ }
+ }
+
}
diff --git a/luni/src/main/java/java/util/concurrent/Future.java b/luni/src/main/java/java/util/concurrent/Future.java
index 32e8145..28d2de5 100644
--- a/luni/src/main/java/java/util/concurrent/Future.java
+++ b/luni/src/main/java/java/util/concurrent/Future.java
@@ -23,8 +23,9 @@
*
* <p>
* <b>Sample Usage</b> (Note that the following classes are all
- * made-up.) <p>
- * <pre> {@code
+ * made-up.)
+ *
+ * <pre> {@code
* interface ArchiveSearcher { String search(String target); }
* class App {
* ExecutorService executor = ...
@@ -46,9 +47,9 @@
* The {@link FutureTask} class is an implementation of {@code Future} that
* implements {@code Runnable}, and so may be executed by an {@code Executor}.
* For example, the above construction with {@code submit} could be replaced by:
- * <pre> {@code
+ * <pre> {@code
* FutureTask<String> future =
- * new FutureTask<String>(new Callable<String>() {
+ * new FutureTask<>(new Callable<String>() {
* public String call() {
* return searcher.search(target);
* }});
diff --git a/luni/src/main/java/java/util/concurrent/FutureTask.java b/luni/src/main/java/java/util/concurrent/FutureTask.java
index 5e24fc8..ed42817 100644
--- a/luni/src/main/java/java/util/concurrent/FutureTask.java
+++ b/luni/src/main/java/java/util/concurrent/FutureTask.java
@@ -367,7 +367,7 @@
throws InterruptedException {
// The code below is very delicate, to achieve these goals:
// - call nanoTime exactly once for each call to park
- // - if nanos <= 0, return promptly without allocation or nanoTime
+ // - if nanos <= 0L, return promptly without allocation or nanoTime
// - if nanos == Long.MIN_VALUE, don't underflow
// - if nanos == Long.MAX_VALUE, and nanoTime is non-monotonic
// and we suffer a spurious wakeup, we will do no worse than
@@ -467,7 +467,7 @@
(FutureTask.class.getDeclaredField("runner"));
WAITERS = U.objectFieldOffset
(FutureTask.class.getDeclaredField("waiters"));
- } catch (Exception e) {
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
diff --git a/luni/src/main/java/java/util/concurrent/Helpers.java b/luni/src/main/java/java/util/concurrent/Helpers.java
new file mode 100644
index 0000000..9051e2f
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/Helpers.java
@@ -0,0 +1,89 @@
+/*
+ * 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/
+ */
+
+package java.util.concurrent;
+
+import java.util.Collection;
+
+/** Shared implementation code for java.util.concurrent. */
+class Helpers {
+ private Helpers() {} // non-instantiable
+
+ /**
+ * An implementation of Collection.toString() suitable for classes
+ * with locks. Instead of holding a lock for the entire duration of
+ * toString(), or acquiring a lock for each call to Iterator.next(),
+ * we hold the lock only during the call to toArray() (less
+ * disruptive to other threads accessing the collection) and follows
+ * the maxim "Never call foreign code while holding a lock".
+ */
+ static String collectionToString(Collection<?> c) {
+ final Object[] a = c.toArray();
+ final int size = a.length;
+ if (size == 0)
+ return "[]";
+ int charLength = 0;
+
+ // Replace every array element with its string representation
+ for (int i = 0; i < size; i++) {
+ Object e = a[i];
+ // Extreme compatibility with AbstractCollection.toString()
+ String s = (e == c) ? "(this Collection)" : objectToString(e);
+ a[i] = s;
+ charLength += s.length();
+ }
+
+ return toString(a, size, charLength);
+ }
+
+ /**
+ * Like Arrays.toString(), but caller guarantees that size > 0,
+ * each element with index 0 <= i < size is a non-null String,
+ * and charLength is the sum of the lengths of the input Strings.
+ */
+ static String toString(Object[] a, int size, int charLength) {
+ // assert a != null;
+ // assert size > 0;
+
+ // Copy each string into a perfectly sized char[]
+ // Length of [ , , , ] == 2 * size
+ final char[] chars = new char[charLength + 2 * size];
+ chars[0] = '[';
+ int j = 1;
+ for (int i = 0; i < size; i++) {
+ if (i > 0) {
+ chars[j++] = ',';
+ chars[j++] = ' ';
+ }
+ String s = (String) a[i];
+ int len = s.length();
+ s.getChars(0, len, chars, j);
+ j += len;
+ }
+ chars[j] = ']';
+ // assert j == chars.length - 1;
+ return new String(chars);
+ }
+
+ /** Optimized form of: key + "=" + val */
+ static String mapEntryToString(Object key, Object val) {
+ final String k, v;
+ final int klen, vlen;
+ final char[] chars =
+ new char[(klen = (k = objectToString(key)).length()) +
+ (vlen = (v = objectToString(val)).length()) + 1];
+ k.getChars(0, klen, chars, 0);
+ chars[klen] = '=';
+ v.getChars(0, vlen, chars, klen + 1);
+ return new String(chars);
+ }
+
+ private static String objectToString(Object x) {
+ // Extreme compatibility with StringBuilder.append(null)
+ String s;
+ return (x == null || (s = x.toString()) == null) ? "null" : s;
+ }
+}
diff --git a/luni/src/main/java/java/util/concurrent/LinkedBlockingDeque.java b/luni/src/main/java/java/util/concurrent/LinkedBlockingDeque.java
index 64b0bf1..b1d196d 100644
--- a/luni/src/main/java/java/util/concurrent/LinkedBlockingDeque.java
+++ b/luni/src/main/java/java/util/concurrent/LinkedBlockingDeque.java
@@ -10,8 +10,11 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
+import java.util.Spliterator;
+import java.util.Spliterators;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.Consumer;
// BEGIN android-note
// removed link to collections framework docs
@@ -40,7 +43,7 @@
*
* @since 1.6
* @author Doug Lea
- * @param <E> the type of elements held in this collection
+ * @param <E> the type of elements held in this deque
*/
public class LinkedBlockingDeque<E>
extends AbstractQueue<E>
@@ -286,8 +289,8 @@
// BlockingDeque methods
/**
- * @throws IllegalStateException {@inheritDoc}
- * @throws NullPointerException {@inheritDoc}
+ * @throws IllegalStateException if this deque is full
+ * @throws NullPointerException {@inheritDoc}
*/
public void addFirst(E e) {
if (!offerFirst(e))
@@ -295,7 +298,7 @@
}
/**
- * @throws IllegalStateException {@inheritDoc}
+ * @throws IllegalStateException if this deque is full
* @throws NullPointerException {@inheritDoc}
*/
public void addLast(E e) {
@@ -380,7 +383,7 @@
lock.lockInterruptibly();
try {
while (!linkFirst(node)) {
- if (nanos <= 0)
+ if (nanos <= 0L)
return false;
nanos = notFull.awaitNanos(nanos);
}
@@ -403,7 +406,7 @@
lock.lockInterruptibly();
try {
while (!linkLast(node)) {
- if (nanos <= 0)
+ if (nanos <= 0L)
return false;
nanos = notFull.awaitNanos(nanos);
}
@@ -485,7 +488,7 @@
try {
E x;
while ( (x = unlinkFirst()) == null) {
- if (nanos <= 0)
+ if (nanos <= 0L)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
@@ -503,7 +506,7 @@
try {
E x;
while ( (x = unlinkLast()) == null) {
- if (nanos <= 0)
+ if (nanos <= 0L)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
@@ -594,8 +597,7 @@
*
* <p>This method is equivalent to {@link #addLast}.
*
- * @throws IllegalStateException if the element cannot be added at this
- * time due to capacity restrictions
+ * @throws IllegalStateException if this deque is full
* @throws NullPointerException if the specified element is null
*/
public boolean add(E e) {
@@ -732,8 +734,8 @@
// Stack methods
/**
- * @throws IllegalStateException {@inheritDoc}
- * @throws NullPointerException {@inheritDoc}
+ * @throws IllegalStateException if this deque is full
+ * @throws NullPointerException {@inheritDoc}
*/
public void push(E e) {
addFirst(e);
@@ -823,7 +825,7 @@
// * @throws ClassCastException {@inheritDoc}
// * @throws NullPointerException {@inheritDoc}
// * @throws IllegalArgumentException {@inheritDoc}
-// * @throws IllegalStateException {@inheritDoc}
+// * @throws IllegalStateException if this deque is full
// * @see #add(Object)
// */
// public boolean addAll(Collection<? extends E> c) {
@@ -893,7 +895,7 @@
* The following code can be used to dump the deque into a newly
* allocated array of {@code String}:
*
- * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
@@ -928,26 +930,7 @@
}
public String toString() {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- Node<E> p = first;
- if (p == null)
- return "[]";
-
- StringBuilder sb = new StringBuilder();
- sb.append('[');
- for (;;) {
- E e = p.item;
- sb.append(e == this ? "(this Collection)" : e);
- p = p.next;
- if (p == null)
- return sb.append(']').toString();
- sb.append(',').append(' ');
- }
- } finally {
- lock.unlock();
- }
+ return Helpers.collectionToString(this);
}
/**
@@ -977,12 +960,8 @@
* Returns an iterator over the elements in this deque in proper sequence.
* The elements will be returned in order from first (head) to last (tail).
*
- * <p>The returned iterator is a "weakly consistent" iterator that
- * will never throw {@link java.util.ConcurrentModificationException
- * ConcurrentModificationException}, and guarantees to traverse
- * elements as they existed upon construction of the iterator, and
- * may (but is not guaranteed to) reflect any modifications
- * subsequent to construction.
+ * <p>The returned iterator is
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
*
* @return an iterator over the elements in this deque in proper sequence
*/
@@ -995,12 +974,8 @@
* sequential order. The elements will be returned in order from
* last (tail) to first (head).
*
- * <p>The returned iterator is a "weakly consistent" iterator that
- * will never throw {@link java.util.ConcurrentModificationException
- * ConcurrentModificationException}, and guarantees to traverse
- * elements as they existed upon construction of the iterator, and
- * may (but is not guaranteed to) reflect any modifications
- * subsequent to construction.
+ * <p>The returned iterator is
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
*
* @return an iterator over the elements in this deque in reverse order
*/
@@ -1009,11 +984,11 @@
}
/**
- * Base class for Iterators for LinkedBlockingDeque
+ * Base class for LinkedBlockingDeque iterators.
*/
private abstract class AbstractItr implements Iterator<E> {
/**
- * The next node to return in next()
+ * The next node to return in next().
*/
Node<E> next;
@@ -1122,9 +1097,149 @@
Node<E> nextNode(Node<E> n) { return n.prev; }
}
+ /** A customized variant of Spliterators.IteratorSpliterator */
+ static final class LBDSpliterator<E> implements Spliterator<E> {
+ static final int MAX_BATCH = 1 << 25; // max batch array size;
+ final LinkedBlockingDeque<E> queue;
+ Node<E> current; // current node; null until initialized
+ int batch; // batch size for splits
+ boolean exhausted; // true when no more nodes
+ long est; // size estimate
+ LBDSpliterator(LinkedBlockingDeque<E> queue) {
+ this.queue = queue;
+ this.est = queue.size();
+ }
+
+ public long estimateSize() { return est; }
+
+ public Spliterator<E> trySplit() {
+ Node<E> h;
+ final LinkedBlockingDeque<E> q = this.queue;
+ int b = batch;
+ int n = (b <= 0) ? 1 : (b >= MAX_BATCH) ? MAX_BATCH : b + 1;
+ if (!exhausted &&
+ ((h = current) != null || (h = q.first) != null) &&
+ h.next != null) {
+ Object[] a = new Object[n];
+ final ReentrantLock lock = q.lock;
+ int i = 0;
+ Node<E> p = current;
+ lock.lock();
+ try {
+ if (p != null || (p = q.first) != null) {
+ do {
+ if ((a[i] = p.item) != null)
+ ++i;
+ } while ((p = p.next) != null && i < n);
+ }
+ } finally {
+ lock.unlock();
+ }
+ if ((current = p) == null) {
+ est = 0L;
+ exhausted = true;
+ }
+ else if ((est -= i) < 0L)
+ est = 0L;
+ if (i > 0) {
+ batch = i;
+ return Spliterators.spliterator
+ (a, 0, i, (Spliterator.ORDERED |
+ Spliterator.NONNULL |
+ Spliterator.CONCURRENT));
+ }
+ }
+ return null;
+ }
+
+ public void forEachRemaining(Consumer<? super E> action) {
+ if (action == null) throw new NullPointerException();
+ final LinkedBlockingDeque<E> q = this.queue;
+ final ReentrantLock lock = q.lock;
+ if (!exhausted) {
+ exhausted = true;
+ Node<E> p = current;
+ do {
+ E e = null;
+ lock.lock();
+ try {
+ if (p == null)
+ p = q.first;
+ while (p != null) {
+ e = p.item;
+ p = p.next;
+ if (e != null)
+ break;
+ }
+ } finally {
+ lock.unlock();
+ }
+ if (e != null)
+ action.accept(e);
+ } while (p != null);
+ }
+ }
+
+ public boolean tryAdvance(Consumer<? super E> action) {
+ if (action == null) throw new NullPointerException();
+ final LinkedBlockingDeque<E> q = this.queue;
+ final ReentrantLock lock = q.lock;
+ if (!exhausted) {
+ E e = null;
+ lock.lock();
+ try {
+ if (current == null)
+ current = q.first;
+ while (current != null) {
+ e = current.item;
+ current = current.next;
+ if (e != null)
+ break;
+ }
+ } finally {
+ lock.unlock();
+ }
+ if (current == null)
+ exhausted = true;
+ if (e != null) {
+ action.accept(e);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public int characteristics() {
+ return Spliterator.ORDERED | Spliterator.NONNULL |
+ Spliterator.CONCURRENT;
+ }
+ }
+
+ /**
+ * Returns a {@link Spliterator} over the elements in this deque.
+ *
+ * <p>The returned spliterator is
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
+ *
+ * <p>The {@code Spliterator} reports {@link Spliterator#CONCURRENT},
+ * {@link Spliterator#ORDERED}, and {@link Spliterator#NONNULL}.
+ *
+ * @implNote
+ * The {@code Spliterator} implements {@code trySplit} to permit limited
+ * parallelism.
+ *
+ * @return a {@code Spliterator} over the elements in this deque
+ * @since 1.8
+ */
+ public Spliterator<E> spliterator() {
+ return new LBDSpliterator<E>(this);
+ }
+
/**
* Saves this deque to a stream (that is, serializes it).
*
+ * @param s the stream
+ * @throws java.io.IOException if an I/O error occurs
* @serialData The capacity (int), followed by elements (each an
* {@code Object}) in the proper order, followed by a null
*/
@@ -1147,6 +1262,10 @@
/**
* Reconstitutes this deque from a stream (that is, deserializes it).
+ * @param s the stream
+ * @throws ClassNotFoundException if the class of a serialized object
+ * could not be found
+ * @throws java.io.IOException if an I/O error occurs
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
diff --git a/luni/src/main/java/java/util/concurrent/LinkedBlockingQueue.java b/luni/src/main/java/java/util/concurrent/LinkedBlockingQueue.java
index 0719828..86ed04a 100644
--- a/luni/src/main/java/java/util/concurrent/LinkedBlockingQueue.java
+++ b/luni/src/main/java/java/util/concurrent/LinkedBlockingQueue.java
@@ -6,13 +6,16 @@
package java.util.concurrent;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.ReentrantLock;
import java.util.AbstractQueue;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.Consumer;
// BEGIN android-note
// removed link to collections framework docs
@@ -43,7 +46,7 @@
*
* @since 1.5
* @author Doug Lea
- * @param <E> the type of elements held in this collection
+ * @param <E> the type of elements held in this queue
*/
public class LinkedBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
@@ -85,7 +88,7 @@
*/
/**
- * Linked list node class
+ * Linked list node class.
*/
static class Node<E> {
E item;
@@ -348,7 +351,7 @@
putLock.lockInterruptibly();
try {
while (count.get() == capacity) {
- if (nanos <= 0)
+ if (nanos <= 0L)
return false;
nanos = notFull.awaitNanos(nanos);
}
@@ -430,7 +433,7 @@
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
- if (nanos <= 0)
+ if (nanos <= 0L)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
@@ -475,11 +478,7 @@
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
- Node<E> first = head.next;
- if (first == null)
- return null;
- else
- return first.item;
+ return (count.get() > 0) ? head.next.item : null;
} finally {
takeLock.unlock();
}
@@ -598,7 +597,7 @@
* The following code can be used to dump the queue into a newly
* allocated array of {@code String}:
*
- * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
@@ -633,25 +632,7 @@
}
public String toString() {
- fullyLock();
- try {
- Node<E> p = head.next;
- if (p == null)
- return "[]";
-
- StringBuilder sb = new StringBuilder();
- sb.append('[');
- for (;;) {
- E e = p.item;
- sb.append(e == this ? "(this Collection)" : e);
- p = p.next;
- if (p == null)
- return sb.append(']').toString();
- sb.append(',').append(' ');
- }
- } finally {
- fullyUnlock();
- }
+ return Helpers.collectionToString(this);
}
/**
@@ -734,12 +715,8 @@
* Returns an iterator over the elements in this queue in proper sequence.
* The elements will be returned in order from first (head) to last (tail).
*
- * <p>The returned iterator is a "weakly consistent" iterator that
- * will never throw {@link java.util.ConcurrentModificationException
- * ConcurrentModificationException}, and guarantees to traverse
- * elements as they existed upon construction of the iterator, and
- * may (but is not guaranteed to) reflect any modifications
- * subsequent to construction.
+ * <p>The returned iterator is
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
*
* @return an iterator over the elements in this queue in proper sequence
*/
@@ -773,34 +750,26 @@
return current != null;
}
- /**
- * Returns the next live successor of p, or null if no such.
- *
- * Unlike other traversal methods, iterators need to handle both:
- * - dequeued nodes (p.next == p)
- * - (possibly multiple) interior removed nodes (p.item == null)
- */
- private Node<E> nextNode(Node<E> p) {
- for (;;) {
- Node<E> s = p.next;
- if (s == p)
- return head.next;
- if (s == null || s.item != null)
- return s;
- p = s;
- }
- }
-
public E next() {
fullyLock();
try {
if (current == null)
throw new NoSuchElementException();
- E x = currentElement;
lastRet = current;
- current = nextNode(current);
- currentElement = (current == null) ? null : current.item;
- return x;
+ E item = null;
+ // Unlike other traversal methods, iterators must handle both:
+ // - dequeued nodes (p.next == p)
+ // - (possibly multiple) interior removed nodes (p.item == null)
+ for (Node<E> p = current, q;; p = q) {
+ if ((q = p.next) == p)
+ q = head.next;
+ if (q == null || (item = q.item) != null) {
+ current = q;
+ E x = currentElement;
+ currentElement = item;
+ return x;
+ }
+ }
} finally {
fullyUnlock();
}
@@ -827,9 +796,146 @@
}
}
+ /** A customized variant of Spliterators.IteratorSpliterator */
+ static final class LBQSpliterator<E> implements Spliterator<E> {
+ static final int MAX_BATCH = 1 << 25; // max batch array size;
+ final LinkedBlockingQueue<E> queue;
+ Node<E> current; // current node; null until initialized
+ int batch; // batch size for splits
+ boolean exhausted; // true when no more nodes
+ long est; // size estimate
+ LBQSpliterator(LinkedBlockingQueue<E> queue) {
+ this.queue = queue;
+ this.est = queue.size();
+ }
+
+ public long estimateSize() { return est; }
+
+ public Spliterator<E> trySplit() {
+ Node<E> h;
+ final LinkedBlockingQueue<E> q = this.queue;
+ int b = batch;
+ int n = (b <= 0) ? 1 : (b >= MAX_BATCH) ? MAX_BATCH : b + 1;
+ if (!exhausted &&
+ ((h = current) != null || (h = q.head.next) != null) &&
+ h.next != null) {
+ Object[] a = new Object[n];
+ int i = 0;
+ Node<E> p = current;
+ q.fullyLock();
+ try {
+ if (p != null || (p = q.head.next) != null) {
+ do {
+ if ((a[i] = p.item) != null)
+ ++i;
+ } while ((p = p.next) != null && i < n);
+ }
+ } finally {
+ q.fullyUnlock();
+ }
+ if ((current = p) == null) {
+ est = 0L;
+ exhausted = true;
+ }
+ else if ((est -= i) < 0L)
+ est = 0L;
+ if (i > 0) {
+ batch = i;
+ return Spliterators.spliterator
+ (a, 0, i, (Spliterator.ORDERED |
+ Spliterator.NONNULL |
+ Spliterator.CONCURRENT));
+ }
+ }
+ return null;
+ }
+
+ public void forEachRemaining(Consumer<? super E> action) {
+ if (action == null) throw new NullPointerException();
+ final LinkedBlockingQueue<E> q = this.queue;
+ if (!exhausted) {
+ exhausted = true;
+ Node<E> p = current;
+ do {
+ E e = null;
+ q.fullyLock();
+ try {
+ if (p == null)
+ p = q.head.next;
+ while (p != null) {
+ e = p.item;
+ p = p.next;
+ if (e != null)
+ break;
+ }
+ } finally {
+ q.fullyUnlock();
+ }
+ if (e != null)
+ action.accept(e);
+ } while (p != null);
+ }
+ }
+
+ public boolean tryAdvance(Consumer<? super E> action) {
+ if (action == null) throw new NullPointerException();
+ final LinkedBlockingQueue<E> q = this.queue;
+ if (!exhausted) {
+ E e = null;
+ q.fullyLock();
+ try {
+ if (current == null)
+ current = q.head.next;
+ while (current != null) {
+ e = current.item;
+ current = current.next;
+ if (e != null)
+ break;
+ }
+ } finally {
+ q.fullyUnlock();
+ }
+ if (current == null)
+ exhausted = true;
+ if (e != null) {
+ action.accept(e);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public int characteristics() {
+ return Spliterator.ORDERED | Spliterator.NONNULL |
+ Spliterator.CONCURRENT;
+ }
+ }
+
+ /**
+ * Returns a {@link Spliterator} over the elements in this queue.
+ *
+ * <p>The returned spliterator is
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
+ *
+ * <p>The {@code Spliterator} reports {@link Spliterator#CONCURRENT},
+ * {@link Spliterator#ORDERED}, and {@link Spliterator#NONNULL}.
+ *
+ * @implNote
+ * The {@code Spliterator} implements {@code trySplit} to permit limited
+ * parallelism.
+ *
+ * @return a {@code Spliterator} over the elements in this queue
+ * @since 1.8
+ */
+ public Spliterator<E> spliterator() {
+ return new LBQSpliterator<E>(this);
+ }
+
/**
* Saves this queue to a stream (that is, serializes it).
*
+ * @param s the stream
+ * @throws java.io.IOException if an I/O error occurs
* @serialData The capacity is emitted (int), followed by all of
* its elements (each an {@code Object}) in the proper order,
* followed by a null
@@ -855,6 +961,10 @@
/**
* Reconstitutes this queue from a stream (that is, deserializes it).
+ * @param s the stream
+ * @throws ClassNotFoundException if the class of a serialized object
+ * could not be found
+ * @throws java.io.IOException if an I/O error occurs
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
diff --git a/luni/src/main/java/java/util/concurrent/LinkedTransferQueue.java b/luni/src/main/java/java/util/concurrent/LinkedTransferQueue.java
index db48420..185ebfd 100644
--- a/luni/src/main/java/java/util/concurrent/LinkedTransferQueue.java
+++ b/luni/src/main/java/java/util/concurrent/LinkedTransferQueue.java
@@ -7,11 +7,15 @@
package java.util.concurrent;
import java.util.AbstractQueue;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Queue;
+import java.util.Spliterator;
+import java.util.Spliterators;
import java.util.concurrent.locks.LockSupport;
+import java.util.function.Consumer;
// BEGIN android-note
// removed link to collections framework docs
@@ -50,7 +54,7 @@
*
* @since 1.7
* @author Doug Lea
- * @param <E> the type of elements held in this collection
+ * @param <E> the type of elements held in this queue
*/
public class LinkedTransferQueue<E> extends AbstractQueue<E>
implements TransferQueue<E>, java.io.Serializable {
@@ -182,7 +186,7 @@
* of costly-to-reclaim garbage caused by the sequential "next"
* links of nodes starting at old forgotten head nodes: As first
* described in detail by Boehm
- * (http://portal.acm.org/citation.cfm?doid=503272.503282) if a GC
+ * (http://portal.acm.org/citation.cfm?doid=503272.503282), if a GC
* delays noticing that any arbitrarily old node has become
* garbage, all newer dead nodes will also be unreclaimed.
* (Similar issues arise in non-GC environments.) To cope with
@@ -423,12 +427,12 @@
// CAS methods for fields
final boolean casNext(Node cmp, Node val) {
- return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
+ return U.compareAndSwapObject(this, NEXT, cmp, val);
}
final boolean casItem(Object cmp, Object val) {
// assert cmp == null || cmp.getClass() != Node.class;
- return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
+ return U.compareAndSwapObject(this, ITEM, cmp, val);
}
/**
@@ -436,7 +440,7 @@
* only be seen after publication via casNext.
*/
Node(Object item, boolean isData) {
- UNSAFE.putObject(this, itemOffset, item); // relaxed write
+ U.putObject(this, ITEM, item); // relaxed write
this.isData = isData;
}
@@ -445,7 +449,7 @@
* only after CASing head field, so uses relaxed write.
*/
final void forgetNext() {
- UNSAFE.putObject(this, nextOffset, this);
+ U.putObject(this, NEXT, this);
}
/**
@@ -458,8 +462,8 @@
* else we don't care).
*/
final void forgetContents() {
- UNSAFE.putObject(this, itemOffset, this);
- UNSAFE.putObject(this, waiterOffset, null);
+ U.putObject(this, ITEM, this);
+ U.putObject(this, WAITER, null);
}
/**
@@ -505,21 +509,19 @@
private static final long serialVersionUID = -3375979862319811754L;
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE;
- private static final long itemOffset;
- private static final long nextOffset;
- private static final long waiterOffset;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long ITEM;
+ private static final long NEXT;
+ private static final long WAITER;
static {
try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- Class<?> k = Node.class;
- itemOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("item"));
- nextOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("next"));
- waiterOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("waiter"));
- } catch (Exception e) {
+ ITEM = U.objectFieldOffset
+ (Node.class.getDeclaredField("item"));
+ NEXT = U.objectFieldOffset
+ (Node.class.getDeclaredField("next"));
+ WAITER = U.objectFieldOffset
+ (Node.class.getDeclaredField("waiter"));
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
@@ -536,15 +538,15 @@
// CAS methods for fields
private boolean casTail(Node cmp, Node val) {
- return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val);
+ return U.compareAndSwapObject(this, TAIL, cmp, val);
}
private boolean casHead(Node cmp, Node val) {
- return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val);
+ return U.compareAndSwapObject(this, HEAD, cmp, val);
}
private boolean casSweepVotes(int cmp, int val) {
- return UNSAFE.compareAndSwapInt(this, sweepVotesOffset, cmp, val);
+ return U.compareAndSwapInt(this, SWEEPVOTES, cmp, val);
}
/*
@@ -555,12 +557,6 @@
private static final int SYNC = 2; // for transfer, take
private static final int TIMED = 3; // for timed poll, tryTransfer
- @SuppressWarnings("unchecked")
- static <E> E cast(Object item) {
- // assert item == null || item.getClass() != Node.class;
- return (E) item;
- }
-
/**
* Implements all queuing methods. See above for explanation.
*
@@ -597,7 +593,8 @@
break; // unless slack < 2
}
LockSupport.unpark(p.waiter);
- return LinkedTransferQueue.<E>cast(item);
+ @SuppressWarnings("unchecked") E itemE = (E) item;
+ return itemE;
}
}
Node n = p.next;
@@ -675,15 +672,15 @@
if (item != e) { // matched
// assert item != s;
s.forgetContents(); // avoid garbage
- return LinkedTransferQueue.<E>cast(item);
+ @SuppressWarnings("unchecked") E itemE = (E) item;
+ return itemE;
}
- if ((w.isInterrupted() || (timed && nanos <= 0)) &&
- s.casItem(e, s)) { // cancel
- unsplice(pred, s);
- return e;
+ else if (w.isInterrupted() || (timed && nanos <= 0L)) {
+ unsplice(pred, s); // try to unlink and cancel
+ if (s.casItem(e, s)) // return normally if lost CAS
+ return e;
}
-
- if (spins < 0) { // establish spins at/near front
+ else if (spins < 0) { // establish spins at/near front
if ((spins = spinsFor(pred, s.isData)) > 0)
randomYields = ThreadLocalRandom.current();
}
@@ -735,32 +732,25 @@
}
/**
- * Returns the first unmatched node of the given mode, or null if
- * none. Used by methods isEmpty, hasWaitingConsumer.
+ * Returns the first unmatched data node, or null if none.
+ * Callers must recheck if the returned node's item field is null
+ * or self-linked before using.
*/
- private Node firstOfMode(boolean isData) {
- for (Node p = head; p != null; p = succ(p)) {
- if (!p.isMatched())
- return (p.isData == isData) ? p : null;
- }
- return null;
- }
-
- /**
- * Returns the item in the first unmatched node with isData; or
- * null if none. Used by peek.
- */
- private E firstDataItem() {
- for (Node p = head; p != null; p = succ(p)) {
- Object item = p.item;
- if (p.isData) {
- if (item != null && item != p)
- return LinkedTransferQueue.<E>cast(item);
+ final Node firstDataNode() {
+ restartFromHead: for (;;) {
+ for (Node p = head; p != null;) {
+ Object item = p.item;
+ if (p.isData) {
+ if (item != null && item != p)
+ return p;
+ }
+ else if (item == null)
+ break;
+ if (p == (p = p.next))
+ continue restartFromHead;
}
- else if (item == null)
- return null;
+ return null;
}
- return null;
}
/**
@@ -768,23 +758,140 @@
* Used by methods size and getWaitingConsumerCount.
*/
private int countOfMode(boolean data) {
- int count = 0;
- for (Node p = head; p != null; ) {
- if (!p.isMatched()) {
- if (p.isData != data)
- return 0;
- if (++count == Integer.MAX_VALUE) // saturated
- break;
+ restartFromHead: for (;;) {
+ int count = 0;
+ for (Node p = head; p != null;) {
+ if (!p.isMatched()) {
+ if (p.isData != data)
+ return 0;
+ if (++count == Integer.MAX_VALUE)
+ break; // @see Collection.size()
+ }
+ if (p == (p = p.next))
+ continue restartFromHead;
}
- Node n = p.next;
- if (n != p)
- p = n;
- else {
- count = 0;
- p = head;
- }
+ return count;
}
- return count;
+ }
+
+ public String toString() {
+ String[] a = null;
+ restartFromHead: for (;;) {
+ int charLength = 0;
+ int size = 0;
+ for (Node p = head; p != null;) {
+ Object item = p.item;
+ if (p.isData) {
+ if (item != null && item != p) {
+ if (a == null)
+ a = new String[4];
+ else if (size == a.length)
+ a = Arrays.copyOf(a, 2 * size);
+ String s = item.toString();
+ a[size++] = s;
+ charLength += s.length();
+ }
+ } else if (item == null)
+ break;
+ if (p == (p = p.next))
+ continue restartFromHead;
+ }
+
+ if (size == 0)
+ return "[]";
+
+ return Helpers.toString(a, size, charLength);
+ }
+ }
+
+ private Object[] toArrayInternal(Object[] a) {
+ Object[] x = a;
+ restartFromHead: for (;;) {
+ int size = 0;
+ for (Node p = head; p != null;) {
+ Object item = p.item;
+ if (p.isData) {
+ if (item != null && item != p) {
+ if (x == null)
+ x = new Object[4];
+ else if (size == x.length)
+ x = Arrays.copyOf(x, 2 * (size + 4));
+ x[size++] = item;
+ }
+ } else if (item == null)
+ break;
+ if (p == (p = p.next))
+ continue restartFromHead;
+ }
+ if (x == null)
+ return new Object[0];
+ else if (a != null && size <= a.length) {
+ if (a != x)
+ System.arraycopy(x, 0, a, 0, size);
+ if (size < a.length)
+ a[size] = null;
+ return a;
+ }
+ return (size == x.length) ? x : Arrays.copyOf(x, size);
+ }
+ }
+
+ /**
+ * Returns an array containing all of the elements in this queue, in
+ * proper sequence.
+ *
+ * <p>The returned array will be "safe" in that no references to it are
+ * maintained by this queue. (In other words, this method must allocate
+ * a new array). The caller is thus free to modify the returned array.
+ *
+ * <p>This method acts as bridge between array-based and collection-based
+ * APIs.
+ *
+ * @return an array containing all of the elements in this queue
+ */
+ public Object[] toArray() {
+ return toArrayInternal(null);
+ }
+
+ /**
+ * Returns an array containing all of the elements in this queue, in
+ * proper sequence; the runtime type of the returned array is that of
+ * the specified array. If the queue fits in the specified array, it
+ * is returned therein. Otherwise, a new array is allocated with the
+ * runtime type of the specified array and the size of this queue.
+ *
+ * <p>If this queue fits in the specified array with room to spare
+ * (i.e., the array has more elements than this queue), the element in
+ * the array immediately following the end of the queue is set to
+ * {@code null}.
+ *
+ * <p>Like the {@link #toArray()} method, this method acts as bridge between
+ * array-based and collection-based APIs. Further, this method allows
+ * precise control over the runtime type of the output array, and may,
+ * under certain circumstances, be used to save allocation costs.
+ *
+ * <p>Suppose {@code x} is a queue known to contain only strings.
+ * The following code can be used to dump the queue into a newly
+ * allocated array of {@code String}:
+ *
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
+ *
+ * Note that {@code toArray(new Object[0])} is identical in function to
+ * {@code toArray()}.
+ *
+ * @param a the array into which the elements of the queue are to
+ * be stored, if it is big enough; otherwise, a new array of the
+ * same runtime type is allocated for this purpose
+ * @return an array containing all of the elements in this queue
+ * @throws ArrayStoreException if the runtime type of the specified array
+ * is not a supertype of the runtime type of every element in
+ * this queue
+ * @throws NullPointerException if the specified array is null
+ */
+ @SuppressWarnings("unchecked")
+ public <T> T[] toArray(T[] a) {
+ if (a == null) throw new NullPointerException();
+ return (T[]) toArrayInternal(a);
}
final class Itr implements Iterator<E> {
@@ -833,7 +940,8 @@
Object item = s.item;
if (s.isData) {
if (item != null && item != s) {
- nextItem = LinkedTransferQueue.<E>cast(item);
+ @SuppressWarnings("unchecked") E itemE = (E) item;
+ nextItem = itemE;
nextNode = s;
return;
}
@@ -880,6 +988,111 @@
}
}
+ /** A customized variant of Spliterators.IteratorSpliterator */
+ final class LTQSpliterator<E> implements Spliterator<E> {
+ static final int MAX_BATCH = 1 << 25; // max batch array size;
+ Node current; // current node; null until initialized
+ int batch; // batch size for splits
+ boolean exhausted; // true when no more nodes
+ LTQSpliterator() {}
+
+ public Spliterator<E> trySplit() {
+ Node p;
+ int b = batch;
+ int n = (b <= 0) ? 1 : (b >= MAX_BATCH) ? MAX_BATCH : b + 1;
+ if (!exhausted &&
+ ((p = current) != null || (p = firstDataNode()) != null) &&
+ p.next != null) {
+ Object[] a = new Object[n];
+ int i = 0;
+ do {
+ Object e = p.item;
+ if (e != p && (a[i] = e) != null)
+ ++i;
+ if (p == (p = p.next))
+ p = firstDataNode();
+ } while (p != null && i < n && p.isData);
+ if ((current = p) == null)
+ exhausted = true;
+ if (i > 0) {
+ batch = i;
+ return Spliterators.spliterator
+ (a, 0, i, (Spliterator.ORDERED |
+ Spliterator.NONNULL |
+ Spliterator.CONCURRENT));
+ }
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void forEachRemaining(Consumer<? super E> action) {
+ Node p;
+ if (action == null) throw new NullPointerException();
+ if (!exhausted &&
+ ((p = current) != null || (p = firstDataNode()) != null)) {
+ exhausted = true;
+ do {
+ Object e = p.item;
+ if (e != null && e != p)
+ action.accept((E)e);
+ if (p == (p = p.next))
+ p = firstDataNode();
+ } while (p != null && p.isData);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public boolean tryAdvance(Consumer<? super E> action) {
+ Node p;
+ if (action == null) throw new NullPointerException();
+ if (!exhausted &&
+ ((p = current) != null || (p = firstDataNode()) != null)) {
+ Object e;
+ do {
+ if ((e = p.item) == p)
+ e = null;
+ if (p == (p = p.next))
+ p = firstDataNode();
+ } while (e == null && p != null && p.isData);
+ if ((current = p) == null)
+ exhausted = true;
+ if (e != null) {
+ action.accept((E)e);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public long estimateSize() { return Long.MAX_VALUE; }
+
+ public int characteristics() {
+ return Spliterator.ORDERED | Spliterator.NONNULL |
+ Spliterator.CONCURRENT;
+ }
+ }
+
+ /**
+ * Returns a {@link Spliterator} over the elements in this queue.
+ *
+ * <p>The returned spliterator is
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
+ *
+ * <p>The {@code Spliterator} reports {@link Spliterator#CONCURRENT},
+ * {@link Spliterator#ORDERED}, and {@link Spliterator#NONNULL}.
+ *
+ * @implNote
+ * The {@code Spliterator} implements {@code trySplit} to permit limited
+ * parallelism.
+ *
+ * @return a {@code Spliterator} over the elements in this queue
+ * @since 1.8
+ */
+ public Spliterator<E> spliterator() {
+ return new LTQSpliterator<E>();
+ }
+
/* -------------- Removal methods -------------- */
/**
@@ -891,7 +1104,7 @@
* @param s the node to be unspliced
*/
final void unsplice(Node pred, Node s) {
- s.forgetContents(); // forget unneeded fields
+ s.waiter = null; // disable signals
/*
* See above for rationale. Briefly: if pred still points to
* s, try to unlink s. If s cannot be unlinked, because it is
@@ -1159,12 +1372,8 @@
* Returns an iterator over the elements in this queue in proper sequence.
* The elements will be returned in order from first (head) to last (tail).
*
- * <p>The returned iterator is a "weakly consistent" iterator that
- * will never throw {@link java.util.ConcurrentModificationException
- * ConcurrentModificationException}, and guarantees to traverse
- * elements as they existed upon construction of the iterator, and
- * may (but is not guaranteed to) reflect any modifications
- * subsequent to construction.
+ * <p>The returned iterator is
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
*
* @return an iterator over the elements in this queue in proper sequence
*/
@@ -1173,7 +1382,22 @@
}
public E peek() {
- return firstDataItem();
+ restartFromHead: for (;;) {
+ for (Node p = head; p != null;) {
+ Object item = p.item;
+ if (p.isData) {
+ if (item != null && item != p) {
+ @SuppressWarnings("unchecked") E e = (E) item;
+ return e;
+ }
+ }
+ else if (item == null)
+ break;
+ if (p == (p = p.next))
+ continue restartFromHead;
+ }
+ return null;
+ }
}
/**
@@ -1182,15 +1406,24 @@
* @return {@code true} if this queue contains no elements
*/
public boolean isEmpty() {
- for (Node p = head; p != null; p = succ(p)) {
- if (!p.isMatched())
- return !p.isData;
- }
- return true;
+ return firstDataNode() == null;
}
public boolean hasWaitingConsumer() {
- return firstOfMode(false) != null;
+ restartFromHead: for (;;) {
+ for (Node p = head; p != null;) {
+ Object item = p.item;
+ if (p.isData) {
+ if (item != null && item != p)
+ break;
+ }
+ else if (item == null)
+ return true;
+ if (p == (p = p.next))
+ continue restartFromHead;
+ }
+ return false;
+ }
}
/**
@@ -1237,15 +1470,16 @@
* @return {@code true} if this queue contains the specified element
*/
public boolean contains(Object o) {
- if (o == null) return false;
- for (Node p = head; p != null; p = succ(p)) {
- Object item = p.item;
- if (p.isData) {
- if (item != null && item != p && o.equals(item))
- return true;
+ if (o != null) {
+ for (Node p = head; p != null; p = succ(p)) {
+ Object item = p.item;
+ if (p.isData) {
+ if (item != null && item != p && o.equals(item))
+ return true;
+ }
+ else if (item == null)
+ break;
}
- else if (item == null)
- break;
}
return false;
}
@@ -1265,6 +1499,8 @@
/**
* Saves this queue to a stream (that is, serializes it).
*
+ * @param s the stream
+ * @throws java.io.IOException if an I/O error occurs
* @serialData All of the elements (each an {@code E}) in
* the proper order, followed by a null
*/
@@ -1279,6 +1515,10 @@
/**
* Reconstitutes this queue from a stream (that is, deserializes it).
+ * @param s the stream
+ * @throws ClassNotFoundException if the class of a serialized object
+ * could not be found
+ * @throws java.io.IOException if an I/O error occurs
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
@@ -1295,21 +1535,19 @@
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE;
- private static final long headOffset;
- private static final long tailOffset;
- private static final long sweepVotesOffset;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long HEAD;
+ private static final long TAIL;
+ private static final long SWEEPVOTES;
static {
try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- Class<?> k = LinkedTransferQueue.class;
- headOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("head"));
- tailOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("tail"));
- sweepVotesOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("sweepVotes"));
- } catch (Exception e) {
+ HEAD = U.objectFieldOffset
+ (LinkedTransferQueue.class.getDeclaredField("head"));
+ TAIL = U.objectFieldOffset
+ (LinkedTransferQueue.class.getDeclaredField("tail"));
+ SWEEPVOTES = U.objectFieldOffset
+ (LinkedTransferQueue.class.getDeclaredField("sweepVotes"));
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
diff --git a/luni/src/main/java/java/util/concurrent/Phaser.java b/luni/src/main/java/java/util/concurrent/Phaser.java
index c5faf16..9b2a7a1 100644
--- a/luni/src/main/java/java/util/concurrent/Phaser.java
+++ b/luni/src/main/java/java/util/concurrent/Phaser.java
@@ -42,7 +42,7 @@
*
* <ul>
*
- * <li> <b>Arrival.</b> Methods {@link #arrive} and
+ * <li><b>Arrival.</b> Methods {@link #arrive} and
* {@link #arriveAndDeregister} record arrival. These methods
* do not block, but return an associated <em>arrival phase
* number</em>; that is, the phase number of the phaser to which
@@ -55,7 +55,7 @@
* flexible than, providing a barrier action to a {@code
* CyclicBarrier}.
*
- * <li> <b>Waiting.</b> Method {@link #awaitAdvance} requires an
+ * <li><b>Waiting.</b> Method {@link #awaitAdvance} requires an
* argument indicating an arrival phase number, and returns when
* the phaser advances to (or is already at) a different phase.
* Unlike similar constructions using {@code CyclicBarrier},
@@ -66,9 +66,10 @@
* state of the phaser. If necessary, you can perform any
* associated recovery within handlers of those exceptions,
* often after invoking {@code forceTermination}. Phasers may
- * also be used by tasks executing in a {@link ForkJoinPool},
- * which will ensure sufficient parallelism to execute tasks
- * when others are blocked waiting for a phase to advance.
+ * also be used by tasks executing in a {@link ForkJoinPool}.
+ * Progress is ensured if the pool's parallelismLevel can
+ * accommodate the maximum number of simultaneously blocked
+ * parties.
*
* </ul>
*
@@ -124,7 +125,7 @@
* The typical idiom is for the method setting this up to first
* register, then start the actions, then deregister, as in:
*
- * <pre> {@code
+ * <pre> {@code
* void runTasks(List<Runnable> tasks) {
* final Phaser phaser = new Phaser(1); // "1" to register self
* // create and start threads
@@ -145,7 +146,7 @@
* <p>One way to cause a set of threads to repeatedly perform actions
* for a given number of iterations is to override {@code onAdvance}:
*
- * <pre> {@code
+ * <pre> {@code
* void startTasks(List<Runnable> tasks, final int iterations) {
* final Phaser phaser = new Phaser() {
* protected boolean onAdvance(int phase, int registeredParties) {
@@ -169,7 +170,7 @@
*
* If the main task must later await termination, it
* may re-register and then execute a similar loop:
- * <pre> {@code
+ * <pre> {@code
* // ...
* phaser.register();
* while (!phaser.isTerminated())
@@ -179,7 +180,7 @@
* in contexts where you are sure that the phase will never wrap around
* {@code Integer.MAX_VALUE}. For example:
*
- * <pre> {@code
+ * <pre> {@code
* void awaitPhase(Phaser phaser, int phase) {
* int p = phaser.register(); // assumes caller not already registered
* while (p < phase) {
@@ -199,7 +200,7 @@
* new Phaser())}, these tasks could then be started, for example by
* submitting to a pool:
*
- * <pre> {@code
+ * <pre> {@code
* void build(Task[] tasks, int lo, int hi, Phaser ph) {
* if (hi - lo > TASKS_PER_PHASER) {
* for (int i = lo; i < hi; i += TASKS_PER_PHASER) {
@@ -300,7 +301,7 @@
}
/**
- * The parent of this phaser, or null if none
+ * The parent of this phaser, or null if none.
*/
private final Phaser parent;
@@ -358,7 +359,7 @@
int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
if (unarrived <= 0)
throw new IllegalStateException(badArrive(s));
- if (UNSAFE.compareAndSwapLong(this, stateOffset, s, s-=adjust)) {
+ if (U.compareAndSwapLong(this, STATE, s, s-=adjust)) {
if (unarrived == 1) {
long n = s & PARTIES_MASK; // base of next state
int nextUnarrived = (int)n >>> PARTIES_SHIFT;
@@ -371,13 +372,12 @@
n |= nextUnarrived;
int nextPhase = (phase + 1) & MAX_PHASE;
n |= (long)nextPhase << PHASE_SHIFT;
- UNSAFE.compareAndSwapLong(this, stateOffset, s, n);
+ U.compareAndSwapLong(this, STATE, s, n);
releaseWaiters(phase);
}
else if (nextUnarrived == 0) { // propagate deregistration
phase = parent.doArrive(ONE_DEREGISTER);
- UNSAFE.compareAndSwapLong(this, stateOffset,
- s, s | EMPTY);
+ U.compareAndSwapLong(this, STATE, s, s | EMPTY);
}
else
phase = parent.doArrive(ONE_ARRIVAL);
@@ -388,7 +388,7 @@
}
/**
- * Implementation of register, bulkRegister
+ * Implementation of register, bulkRegister.
*
* @param registrations number to add to both parties and
* unarrived fields. Must be greater than zero.
@@ -412,14 +412,13 @@
if (parent == null || reconcileState() == s) {
if (unarrived == 0) // wait out advance
root.internalAwaitAdvance(phase, null);
- else if (UNSAFE.compareAndSwapLong(this, stateOffset,
- s, s + adjust))
+ else if (U.compareAndSwapLong(this, STATE, s, s + adjust))
break;
}
}
else if (parent == null) { // 1st root registration
long next = ((long)phase << PHASE_SHIFT) | adjust;
- if (UNSAFE.compareAndSwapLong(this, stateOffset, s, next))
+ if (U.compareAndSwapLong(this, STATE, s, next))
break;
}
else {
@@ -431,8 +430,8 @@
// finish registration whenever parent registration
// succeeded, even when racing with termination,
// since these are part of the same "transaction".
- while (!UNSAFE.compareAndSwapLong
- (this, stateOffset, s,
+ while (!U.compareAndSwapLong
+ (this, STATE, s,
((long)phase << PHASE_SHIFT) | adjust)) {
s = state;
phase = (int)(root.state >>> PHASE_SHIFT);
@@ -463,8 +462,8 @@
// CAS to root phase with current parties, tripping unarrived
while ((phase = (int)(root.state >>> PHASE_SHIFT)) !=
(int)(s >>> PHASE_SHIFT) &&
- !UNSAFE.compareAndSwapLong
- (this, stateOffset, s,
+ !U.compareAndSwapLong
+ (this, STATE, s,
s = (((long)phase << PHASE_SHIFT) |
((phase < 0) ? (s & COUNTS_MASK) :
(((p = (int)s >>> PARTIES_SHIFT) == 0) ? EMPTY :
@@ -653,8 +652,7 @@
int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
if (unarrived <= 0)
throw new IllegalStateException(badArrive(s));
- if (UNSAFE.compareAndSwapLong(this, stateOffset, s,
- s -= ONE_ARRIVAL)) {
+ if (U.compareAndSwapLong(this, STATE, s, s -= ONE_ARRIVAL)) {
if (unarrived > 1)
return root.internalAwaitAdvance(phase, null);
if (root != this)
@@ -669,7 +667,7 @@
n |= nextUnarrived;
int nextPhase = (phase + 1) & MAX_PHASE;
n |= (long)nextPhase << PHASE_SHIFT;
- if (!UNSAFE.compareAndSwapLong(this, stateOffset, s, n))
+ if (!U.compareAndSwapLong(this, STATE, s, n))
return (int)(state >>> PHASE_SHIFT); // terminated
releaseWaiters(phase);
return nextPhase;
@@ -785,8 +783,7 @@
final Phaser root = this.root;
long s;
while ((s = root.state) >= 0) {
- if (UNSAFE.compareAndSwapLong(root, stateOffset,
- s, s | TERMINATION_BIT)) {
+ if (U.compareAndSwapLong(root, STATE, s, s | TERMINATION_BIT)) {
// signal all threads
releaseWaiters(0); // Waiters on evenQ
releaseWaiters(1); // Waiters on oddQ
@@ -925,7 +922,7 @@
}
/**
- * Implementation of toString and string-based error messages
+ * Implementation of toString and string-based error messages.
*/
private String stateToString(long s) {
return super.toString() +
@@ -1034,7 +1031,7 @@
else {
try {
ForkJoinPool.managedBlock(node);
- } catch (InterruptedException ie) {
+ } catch (InterruptedException cantHappen) {
node.wasInterrupted = true;
}
}
@@ -1053,7 +1050,7 @@
}
/**
- * Wait nodes for Treiber stack representing wait queue
+ * Wait nodes for Treiber stack representing wait queue.
*/
static final class QNode implements ForkJoinPool.ManagedBlocker {
final Phaser phaser;
@@ -1090,40 +1087,34 @@
thread = null;
return true;
}
- if (timed) {
- if (nanos > 0L) {
- nanos = deadline - System.nanoTime();
- }
- if (nanos <= 0L) {
- thread = null;
- return true;
- }
+ if (timed &&
+ (nanos <= 0L || (nanos = deadline - System.nanoTime()) <= 0L)) {
+ thread = null;
+ return true;
}
return false;
}
public boolean block() {
- if (isReleasable())
- return true;
- else if (!timed)
- LockSupport.park(this);
- else if (nanos > 0L)
- LockSupport.parkNanos(this, nanos);
- return isReleasable();
+ while (!isReleasable()) {
+ if (timed)
+ LockSupport.parkNanos(this, nanos);
+ else
+ LockSupport.park(this);
+ }
+ return true;
}
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE;
- private static final long stateOffset;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long STATE;
static {
try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- Class<?> k = Phaser.class;
- stateOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("state"));
- } catch (Exception e) {
+ STATE = U.objectFieldOffset
+ (Phaser.class.getDeclaredField("state"));
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
diff --git a/luni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java b/luni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java
index 40b3510..2044406 100644
--- a/luni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java
+++ b/luni/src/main/java/java/util/concurrent/PriorityBlockingQueue.java
@@ -6,9 +6,19 @@
package java.util.concurrent;
+import java.util.AbstractQueue;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.PriorityQueue;
+import java.util.Queue;
+import java.util.SortedSet;
+import java.util.Spliterator;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
-import java.util.*;
+import java.util.function.Consumer;
// BEGIN android-note
// removed link to collections framework docs
@@ -43,7 +53,7 @@
* tie-breaking to comparable elements. To use it, you would insert a
* {@code new FIFOEntry(anEntry)} instead of a plain entry object.
*
- * <pre> {@code
+ * <pre> {@code
* class FIFOEntry<E extends Comparable<? super E>>
* implements Comparable<FIFOEntry<E>> {
* static final AtomicLong seq = new AtomicLong(0);
@@ -64,7 +74,7 @@
*
* @since 1.5
* @author Doug Lea
- * @param <E> the type of elements held in this collection
+ * @param <E> the type of elements held in this queue
*/
@SuppressWarnings("unchecked")
public class PriorityBlockingQueue<E> extends AbstractQueue<E>
@@ -122,12 +132,12 @@
private transient Comparator<? super E> comparator;
/**
- * Lock used for all public operations
+ * Lock used for all public operations.
*/
private final ReentrantLock lock;
/**
- * Condition for blocking when empty
+ * Condition for blocking when empty.
*/
private final Condition notEmpty;
@@ -250,8 +260,7 @@
lock.unlock(); // must release and then re-acquire main lock
Object[] newArray = null;
if (allocationSpinLock == 0 &&
- UNSAFE.compareAndSwapInt(this, allocationSpinLockOffset,
- 0, 1)) {
+ U.compareAndSwapInt(this, ALLOCATIONSPINLOCK, 0, 1)) {
try {
int newCap = oldCap + ((oldCap < 64) ?
(oldCap + 2) : // grow faster if small
@@ -633,7 +642,7 @@
}
/**
- * Identity-based version for use in Itr.remove
+ * Identity-based version for use in Itr.remove.
*/
void removeEQ(Object o) {
final ReentrantLock lock = this.lock;
@@ -669,48 +678,8 @@
}
}
- /**
- * Returns an array containing all of the elements in this queue.
- * The returned array elements are in no particular order.
- *
- * <p>The returned array will be "safe" in that no references to it are
- * maintained by this queue. (In other words, this method must allocate
- * a new array). The caller is thus free to modify the returned array.
- *
- * <p>This method acts as bridge between array-based and collection-based
- * APIs.
- *
- * @return an array containing all of the elements in this queue
- */
- public Object[] toArray() {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- return Arrays.copyOf(queue, size);
- } finally {
- lock.unlock();
- }
- }
-
public String toString() {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- int n = size;
- if (n == 0)
- return "[]";
- StringBuilder sb = new StringBuilder();
- sb.append('[');
- for (int i = 0; i < n; ++i) {
- Object e = queue[i];
- sb.append(e == this ? "(this Collection)" : e);
- if (i != n - 1)
- sb.append(',').append(' ');
- }
- return sb.append(']').toString();
- } finally {
- lock.unlock();
- }
+ return Helpers.collectionToString(this);
}
/**
@@ -769,6 +738,29 @@
}
/**
+ * Returns an array containing all of the elements in this queue.
+ * The returned array elements are in no particular order.
+ *
+ * <p>The returned array will be "safe" in that no references to it are
+ * maintained by this queue. (In other words, this method must allocate
+ * a new array). The caller is thus free to modify the returned array.
+ *
+ * <p>This method acts as bridge between array-based and collection-based
+ * APIs.
+ *
+ * @return an array containing all of the elements in this queue
+ */
+ public Object[] toArray() {
+ final ReentrantLock lock = this.lock;
+ lock.lock();
+ try {
+ return Arrays.copyOf(queue, size);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
* Returns an array containing all of the elements in this queue; the
* runtime type of the returned array is that of the specified array.
* The returned array elements are in no particular order.
@@ -790,7 +782,7 @@
* The following code can be used to dump the queue into a newly
* allocated array of {@code String}:
*
- * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
@@ -825,12 +817,8 @@
* Returns an iterator over the elements in this queue. The
* iterator does not return the elements in any particular order.
*
- * <p>The returned iterator is a "weakly consistent" iterator that
- * will never throw {@link java.util.ConcurrentModificationException
- * ConcurrentModificationException}, and guarantees to traverse
- * elements as they existed upon construction of the iterator, and
- * may (but is not guaranteed to) reflect any modifications
- * subsequent to construction.
+ * <p>The returned iterator is
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
*
* @return an iterator over the elements in this queue
*/
@@ -876,6 +864,9 @@
* For compatibility with previous version of this class, elements
* are first copied to a java.util.PriorityQueue, which is then
* serialized.
+ *
+ * @param s the stream
+ * @throws java.io.IOException if an I/O error occurs
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
@@ -893,6 +884,10 @@
/**
* Reconstitutes this queue from a stream (that is, deserializes it).
+ * @param s the stream
+ * @throws ClassNotFoundException if the class of a serialized object
+ * could not be found
+ * @throws java.io.IOException if an I/O error occurs
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
@@ -906,16 +901,93 @@
}
}
+ // Similar to Collections.ArraySnapshotSpliterator but avoids
+ // commitment to toArray until needed
+ static final class PBQSpliterator<E> implements Spliterator<E> {
+ final PriorityBlockingQueue<E> queue;
+ Object[] array;
+ int index;
+ int fence;
+
+ PBQSpliterator(PriorityBlockingQueue<E> queue, Object[] array,
+ int index, int fence) {
+ this.queue = queue;
+ this.array = array;
+ this.index = index;
+ this.fence = fence;
+ }
+
+ final int getFence() {
+ int hi;
+ if ((hi = fence) < 0)
+ hi = fence = (array = queue.toArray()).length;
+ return hi;
+ }
+
+ public PBQSpliterator<E> trySplit() {
+ int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
+ return (lo >= mid) ? null :
+ new PBQSpliterator<E>(queue, array, lo, index = mid);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void forEachRemaining(Consumer<? super E> action) {
+ Object[] a; int i, hi; // hoist accesses and checks from loop
+ if (action == null)
+ throw new NullPointerException();
+ if ((a = array) == null)
+ fence = (a = queue.toArray()).length;
+ if ((hi = fence) <= a.length &&
+ (i = index) >= 0 && i < (index = hi)) {
+ do { action.accept((E)a[i]); } while (++i < hi);
+ }
+ }
+
+ public boolean tryAdvance(Consumer<? super E> action) {
+ if (action == null)
+ throw new NullPointerException();
+ if (getFence() > index && index >= 0) {
+ @SuppressWarnings("unchecked") E e = (E) array[index++];
+ action.accept(e);
+ return true;
+ }
+ return false;
+ }
+
+ public long estimateSize() { return (long)(getFence() - index); }
+
+ public int characteristics() {
+ return Spliterator.NONNULL | Spliterator.SIZED | Spliterator.SUBSIZED;
+ }
+ }
+
+ /**
+ * Returns a {@link Spliterator} over the elements in this queue.
+ *
+ * <p>The returned spliterator is
+ * <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
+ *
+ * <p>The {@code Spliterator} reports {@link Spliterator#SIZED} and
+ * {@link Spliterator#NONNULL}.
+ *
+ * @implNote
+ * The {@code Spliterator} additionally reports {@link Spliterator#SUBSIZED}.
+ *
+ * @return a {@code Spliterator} over the elements in this queue
+ * @since 1.8
+ */
+ public Spliterator<E> spliterator() {
+ return new PBQSpliterator<E>(this, null, 0, -1);
+ }
+
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE;
- private static final long allocationSpinLockOffset;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long ALLOCATIONSPINLOCK;
static {
try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- Class<?> k = PriorityBlockingQueue.class;
- allocationSpinLockOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("allocationSpinLock"));
- } catch (Exception e) {
+ ALLOCATIONSPINLOCK = U.objectFieldOffset
+ (PriorityBlockingQueue.class.getDeclaredField("allocationSpinLock"));
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
diff --git a/luni/src/main/java/java/util/concurrent/RecursiveAction.java b/luni/src/main/java/java/util/concurrent/RecursiveAction.java
index e3a6340..8631a89 100644
--- a/luni/src/main/java/java/util/concurrent/RecursiveAction.java
+++ b/luni/src/main/java/java/util/concurrent/RecursiveAction.java
@@ -16,7 +16,7 @@
* <p><b>Sample Usages.</b> Here is a simple but complete ForkJoin
* sort that sorts a given {@code long[]} array:
*
- * <pre> {@code
+ * <pre> {@code
* static class SortTask extends RecursiveAction {
* final long[] array; final int lo, hi;
* SortTask(long[] array, int lo, int hi) {
@@ -50,7 +50,7 @@
* SortTask(anArray)} and invoking it in a ForkJoinPool. As a more
* concrete simple example, the following task increments each element
* of an array:
- * <pre> {@code
+ * <pre> {@code
* class IncrementTask extends RecursiveAction {
* final long[] array; final int lo, hi;
* IncrementTask(long[] array, int lo, int hi) {
@@ -81,7 +81,7 @@
* performing leaf actions on unstolen tasks rather than further
* subdividing.
*
- * <pre> {@code
+ * <pre> {@code
* double sumOfSquares(ForkJoinPool pool, double[] array) {
* int n = array.length;
* Applyer a = new Applyer(array, 0, n, null);
diff --git a/luni/src/main/java/java/util/concurrent/RecursiveTask.java b/luni/src/main/java/java/util/concurrent/RecursiveTask.java
index d201bd6..5cba1da 100644
--- a/luni/src/main/java/java/util/concurrent/RecursiveTask.java
+++ b/luni/src/main/java/java/util/concurrent/RecursiveTask.java
@@ -11,11 +11,11 @@
*
* <p>For a classic example, here is a task computing Fibonacci numbers:
*
- * <pre> {@code
+ * <pre> {@code
* class Fibonacci extends RecursiveTask<Integer> {
* final int n;
* Fibonacci(int n) { this.n = n; }
- * Integer compute() {
+ * protected Integer compute() {
* if (n <= 1)
* return n;
* Fibonacci f1 = new Fibonacci(n - 1);
diff --git a/luni/src/main/java/java/util/concurrent/RunnableScheduledFuture.java b/luni/src/main/java/java/util/concurrent/RunnableScheduledFuture.java
index 7125233..604f180 100644
--- a/luni/src/main/java/java/util/concurrent/RunnableScheduledFuture.java
+++ b/luni/src/main/java/java/util/concurrent/RunnableScheduledFuture.java
@@ -19,11 +19,11 @@
public interface RunnableScheduledFuture<V> extends RunnableFuture<V>, ScheduledFuture<V> {
/**
- * Returns true if this is a periodic task. A periodic task may
+ * Returns {@code true} if this task is periodic. A periodic task may
* re-run according to some schedule. A non-periodic task can be
* run only once.
*
- * @return true if this task is periodic
+ * @return {@code true} if this task is periodic
*/
boolean isPeriodic();
}
diff --git a/luni/src/main/java/java/util/concurrent/ScheduledExecutorService.java b/luni/src/main/java/java/util/concurrent/ScheduledExecutorService.java
index c07a5b9..eae47b3 100644
--- a/luni/src/main/java/java/util/concurrent/ScheduledExecutorService.java
+++ b/luni/src/main/java/java/util/concurrent/ScheduledExecutorService.java
@@ -41,7 +41,7 @@
* Here is a class with a method that sets up a ScheduledExecutorService
* to beep every ten seconds for an hour:
*
- * <pre> {@code
+ * <pre> {@code
* import static java.util.concurrent.TimeUnit.*;
* class BeeperControl {
* private final ScheduledExecutorService scheduler =
@@ -100,23 +100,37 @@
/**
* Creates and executes a periodic action that becomes enabled first
* after the given initial delay, and subsequently with the given
- * period; that is executions will commence after
- * {@code initialDelay} then {@code initialDelay+period}, then
+ * period; that is, executions will commence after
+ * {@code initialDelay}, then {@code initialDelay + period}, then
* {@code initialDelay + 2 * period}, and so on.
- * If any execution of the task
- * encounters an exception, subsequent executions are suppressed.
- * Otherwise, the task will only terminate via cancellation or
- * termination of the executor. If any execution of this task
- * takes longer than its period, then subsequent executions
- * may start late, but will not concurrently execute.
+ *
+ * <p>The sequence of task executions continues indefinitely until
+ * one of the following exceptional completions occur:
+ * <ul>
+ * <li>The task is {@linkplain Future#cancel explicitly cancelled}
+ * via the returned future.
+ * <li>The executor terminates, also resulting in task cancellation.
+ * <li>An execution of the task throws an exception. In this case
+ * calling {@link Future#get() get} on the returned future will
+ * throw {@link ExecutionException}.
+ * </ul>
+ * Subsequent executions are suppressed. Subsequent calls to
+ * {@link Future#isDone isDone()} on the returned future will
+ * return {@code true}.
+ *
+ * <p>If any execution of this task takes longer than its period, then
+ * subsequent executions may start late, but will not concurrently
+ * execute.
*
* @param command the task to execute
* @param initialDelay the time to delay first execution
* @param period the period between successive executions
* @param unit the time unit of the initialDelay and period parameters
* @return a ScheduledFuture representing pending completion of
- * the task, and whose {@code get()} method will throw an
- * exception upon cancellation
+ * the series of repeated tasks. The future's {@link
+ * Future#get() get()} method will never return normally,
+ * and will throw an exception upon task cancellation or
+ * abnormal termination of a task execution.
* @throws RejectedExecutionException if the task cannot be
* scheduled for execution
* @throws NullPointerException if command is null
@@ -131,10 +145,21 @@
* Creates and executes a periodic action that becomes enabled first
* after the given initial delay, and subsequently with the
* given delay between the termination of one execution and the
- * commencement of the next. If any execution of the task
- * encounters an exception, subsequent executions are suppressed.
- * Otherwise, the task will only terminate via cancellation or
- * termination of the executor.
+ * commencement of the next.
+ *
+ * <p>The sequence of task executions continues indefinitely until
+ * one of the following exceptional completions occur:
+ * <ul>
+ * <li>The task is {@linkplain Future#cancel explicitly cancelled}
+ * via the returned future.
+ * <li>The executor terminates, also resulting in task cancellation.
+ * <li>An execution of the task throws an exception. In this case
+ * calling {@link Future#get() get} on the returned future will
+ * throw {@link ExecutionException}.
+ * </ul>
+ * Subsequent executions are suppressed. Subsequent calls to
+ * {@link Future#isDone isDone()} on the returned future will
+ * return {@code true}.
*
* @param command the task to execute
* @param initialDelay the time to delay first execution
@@ -142,8 +167,10 @@
* execution and the commencement of the next
* @param unit the time unit of the initialDelay and delay parameters
* @return a ScheduledFuture representing pending completion of
- * the task, and whose {@code get()} method will throw an
- * exception upon cancellation
+ * the series of repeated tasks. The future's {@link
+ * Future#get() get()} method will never return normally,
+ * and will throw an exception upon task cancellation or
+ * abnormal termination of a task execution.
* @throws RejectedExecutionException if the task cannot be
* scheduled for execution
* @throws NullPointerException if command is null
diff --git a/luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java b/luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java
index 821342d..f9a2000 100644
--- a/luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java
+++ b/luni/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java
@@ -6,12 +6,18 @@
package java.util.concurrent;
-import static java.util.concurrent.TimeUnit.NANOSECONDS;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+import java.util.AbstractQueue;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
-import java.util.*;
// BEGIN android-note
// omit class-level docs on setRemoveOnCancelPolicy()
@@ -32,17 +38,17 @@
* submission.
*
* <p>When a submitted task is cancelled before it is run, execution
- * is suppressed. By default, such a cancelled task is not
- * automatically removed from the work queue until its delay
- * elapses. While this enables further inspection and monitoring, it
- * may also cause unbounded retention of cancelled tasks.
+ * is suppressed. By default, such a cancelled task is not
+ * automatically removed from the work queue until its delay elapses.
+ * While this enables further inspection and monitoring, it may also
+ * cause unbounded retention of cancelled tasks.
*
* <p>Successive executions of a periodic task scheduled via
- * {@link #scheduleAtFixedRate} or
- * {@link #scheduleWithFixedDelay} do not overlap. While different
- * executions may be performed by different threads, the effects of
- * prior executions <a
- * href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
+ * {@link #scheduleAtFixedRate scheduleAtFixedRate} or
+ * {@link #scheduleWithFixedDelay scheduleWithFixedDelay}
+ * do not overlap. While different executions may be performed by
+ * different threads, the effects of prior executions
+ * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a>
* those of subsequent ones.
*
* <p>While this class inherits from {@link ThreadPoolExecutor}, a few
@@ -72,7 +78,7 @@
* {@link FutureTask}. However, this may be modified or replaced using
* subclasses of the form:
*
- * <pre> {@code
+ * <pre> {@code
* public class CustomScheduledExecutor extends ScheduledThreadPoolExecutor {
*
* static class CustomTask<V> implements RunnableScheduledFuture<V> { ... }
@@ -99,11 +105,10 @@
/*
* This class specializes ThreadPoolExecutor implementation by
*
- * 1. Using a custom task type, ScheduledFutureTask for
- * tasks, even those that don't require scheduling (i.e.,
- * those submitted using ExecutorService execute, not
- * ScheduledExecutorService methods) which are treated as
- * delayed tasks with a delay of zero.
+ * 1. Using a custom task type ScheduledFutureTask, even for tasks
+ * that don't require scheduling because they are submitted
+ * using ExecutorService rather than ScheduledExecutorService
+ * methods, which are treated as tasks with a delay of zero.
*
* 2. Using a custom queue (DelayedWorkQueue), a variant of
* unbounded DelayQueue. The lack of capacity constraint and
@@ -136,7 +141,7 @@
/**
* True if ScheduledFutureTask.cancel should remove from queue.
*/
- private volatile boolean removeOnCancel = false;
+ volatile boolean removeOnCancel;
/**
* Sequence number to break scheduling ties, and in turn to
@@ -144,24 +149,17 @@
*/
private static final AtomicLong sequencer = new AtomicLong();
- /**
- * Returns current nanosecond time.
- */
- final long now() {
- return System.nanoTime();
- }
-
private class ScheduledFutureTask<V>
extends FutureTask<V> implements RunnableScheduledFuture<V> {
/** Sequence number to break ties FIFO */
private final long sequenceNumber;
- /** The time the task is enabled to execute in nanoTime units */
- private long time;
+ /** The nanoTime-based time when the task is enabled to execute. */
+ private volatile long time;
/**
- * Period in nanoseconds for repeating tasks.
+ * Period for repeating tasks, in nanoseconds.
* A positive value indicates fixed-rate execution.
* A negative value indicates fixed-delay execution.
* A value of 0 indicates a non-repeating (one-shot) task.
@@ -179,11 +177,12 @@
/**
* Creates a one-shot action with given nanoTime-based trigger time.
*/
- ScheduledFutureTask(Runnable r, V result, long triggerTime) {
+ ScheduledFutureTask(Runnable r, V result, long triggerTime,
+ long sequenceNumber) {
super(r, result);
this.time = triggerTime;
this.period = 0;
- this.sequenceNumber = sequencer.getAndIncrement();
+ this.sequenceNumber = sequenceNumber;
}
/**
@@ -191,25 +190,26 @@
* trigger time and period.
*/
ScheduledFutureTask(Runnable r, V result, long triggerTime,
- long period) {
+ long period, long sequenceNumber) {
super(r, result);
this.time = triggerTime;
this.period = period;
- this.sequenceNumber = sequencer.getAndIncrement();
+ this.sequenceNumber = sequenceNumber;
}
/**
* Creates a one-shot action with given nanoTime-based trigger time.
*/
- ScheduledFutureTask(Callable<V> callable, long triggerTime) {
+ ScheduledFutureTask(Callable<V> callable, long triggerTime,
+ long sequenceNumber) {
super(callable);
this.time = triggerTime;
this.period = 0;
- this.sequenceNumber = sequencer.getAndIncrement();
+ this.sequenceNumber = sequenceNumber;
}
public long getDelay(TimeUnit unit) {
- return unit.convert(time - now(), NANOSECONDS);
+ return unit.convert(time - System.nanoTime(), NANOSECONDS);
}
public int compareTo(Delayed other) {
@@ -252,6 +252,9 @@
}
public boolean cancel(boolean mayInterruptIfRunning) {
+ // The racy read of heapIndex below is benign:
+ // if heapIndex < 0, then OOTA guarantees that we have surely
+ // been removed; else we recheck under lock in remove()
boolean cancelled = super.cancel(mayInterruptIfRunning);
if (cancelled && removeOnCancel && heapIndex >= 0)
remove(this);
@@ -266,8 +269,8 @@
if (!canRunInCurrentRunState(periodic))
cancel(false);
else if (!periodic)
- ScheduledFutureTask.super.run();
- else if (ScheduledFutureTask.super.runAndReset()) {
+ super.run();
+ else if (super.runAndReset()) {
setNextRunTime();
reExecutePeriodic(outerTask);
}
@@ -493,7 +496,7 @@
* Returns the nanoTime-based trigger time of a delayed action.
*/
long triggerTime(long delay) {
- return now() +
+ return System.nanoTime() +
((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
}
@@ -525,7 +528,8 @@
throw new NullPointerException();
RunnableScheduledFuture<Void> t = decorateTask(command,
new ScheduledFutureTask<Void>(command, null,
- triggerTime(delay, unit)));
+ triggerTime(delay, unit),
+ sequencer.getAndIncrement()));
delayedExecute(t);
return t;
}
@@ -541,7 +545,8 @@
throw new NullPointerException();
RunnableScheduledFuture<V> t = decorateTask(callable,
new ScheduledFutureTask<V>(callable,
- triggerTime(delay, unit)));
+ triggerTime(delay, unit),
+ sequencer.getAndIncrement()));
delayedExecute(t);
return t;
}
@@ -557,13 +562,14 @@
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
- if (period <= 0)
+ if (period <= 0L)
throw new IllegalArgumentException();
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
- unit.toNanos(period));
+ unit.toNanos(period),
+ sequencer.getAndIncrement());
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
delayedExecute(t);
@@ -581,13 +587,14 @@
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
- if (delay <= 0)
+ if (delay <= 0L)
throw new IllegalArgumentException();
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
- unit.toNanos(-delay));
+ -unit.toNanos(delay),
+ sequencer.getAndIncrement());
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
delayedExecute(t);
@@ -759,7 +766,8 @@
/**
* Attempts to stop all actively executing tasks, halts the
* processing of waiting tasks, and returns a list of the tasks
- * that were awaiting execution.
+ * that were awaiting execution. These tasks are drained (removed)
+ * from the task queue upon return from this method.
*
* <p>This method does not wait for actively executing tasks to
* terminate. Use {@link #awaitTermination awaitTermination} to
@@ -767,7 +775,7 @@
*
* <p>There are no guarantees beyond best-effort attempts to stop
* processing actively executing tasks. This implementation
- * cancels tasks via {@link Thread#interrupt}, so any task that
+ * interrupts tasks via {@link Thread#interrupt}; any task that
* fails to respond to interrupts may never terminate.
*
* @return list of tasks that never commenced execution.
@@ -775,8 +783,8 @@
* For tasks submitted via one of the {@code schedule}
* methods, the element will be identical to the returned
* {@code ScheduledFuture}. For tasks submitted using
- * {@link #execute}, the element will be a zero-delay {@code
- * ScheduledFuture}.
+ * {@link #execute execute}, the element will be a
+ * zero-delay {@code ScheduledFuture}.
*/
// android-note: Removed "throws SecurityException" doc.
public List<Runnable> shutdownNow() {
@@ -784,12 +792,16 @@
}
/**
- * Returns the task queue used by this executor.
- * Each element of this list is a {@link ScheduledFuture}.
+ * Returns the task queue used by this executor. Access to the
+ * task queue is intended primarily for debugging and monitoring.
+ * This queue may be in active use. Retrieving the task queue
+ * does not prevent queued tasks from executing.
+ *
+ * <p>Each element of this queue is a {@link ScheduledFuture}.
* For tasks submitted via one of the {@code schedule} methods, the
* element will be identical to the returned {@code ScheduledFuture}.
- * For tasks submitted using {@link #execute}, the element will be a
- * zero-delay {@code ScheduledFuture}.
+ * For tasks submitted using {@link #execute execute}, the element
+ * will be a zero-delay {@code ScheduledFuture}.
*
* <p>Iteration over this queue is <em>not</em> guaranteed to traverse
* tasks in the order in which they will execute.
@@ -1061,10 +1073,9 @@
lock.lock();
try {
RunnableScheduledFuture<?> first = queue[0];
- if (first == null || first.getDelay(NANOSECONDS) > 0)
- return null;
- else
- return finishPoll(first);
+ return (first == null || first.getDelay(NANOSECONDS) > 0)
+ ? null
+ : finishPoll(first);
} finally {
lock.unlock();
}
@@ -1080,7 +1091,7 @@
available.await();
else {
long delay = first.getDelay(NANOSECONDS);
- if (delay <= 0)
+ if (delay <= 0L)
return finishPoll(first);
first = null; // don't retain ref while waiting
if (leader != null)
@@ -1113,15 +1124,15 @@
for (;;) {
RunnableScheduledFuture<?> first = queue[0];
if (first == null) {
- if (nanos <= 0)
+ if (nanos <= 0L)
return null;
else
nanos = available.awaitNanos(nanos);
} else {
long delay = first.getDelay(NANOSECONDS);
- if (delay <= 0)
+ if (delay <= 0L)
return finishPoll(first);
- if (nanos <= 0)
+ if (nanos <= 0L)
return null;
first = null; // don't retain ref while waiting
if (nanos < delay || leader != null)
@@ -1253,8 +1264,8 @@
*/
private class Itr implements Iterator<Runnable> {
final RunnableScheduledFuture<?>[] array;
- int cursor = 0; // index of next element to return
- int lastRet = -1; // index of last element, or -1 if no such
+ int cursor; // index of next element to return; initially 0
+ int lastRet = -1; // index of last element returned; -1 if no such
Itr(RunnableScheduledFuture<?>[] array) {
this.array = array;
diff --git a/luni/src/main/java/java/util/concurrent/Semaphore.java b/luni/src/main/java/java/util/concurrent/Semaphore.java
index b4b7edd..856b02b 100644
--- a/luni/src/main/java/java/util/concurrent/Semaphore.java
+++ b/luni/src/main/java/java/util/concurrent/Semaphore.java
@@ -20,7 +20,7 @@
* <p>Semaphores are often used to restrict the number of threads than can
* access some (physical or logical) resource. For example, here is
* a class that uses a semaphore to control access to a pool of items:
- * <pre> {@code
+ * <pre> {@code
* class Pool {
* private static final int MAX_AVAILABLE = 100;
* private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
@@ -113,8 +113,13 @@
*
* <p>This class also provides convenience methods to {@link
* #acquire(int) acquire} and {@link #release(int) release} multiple
- * permits at a time. Beware of the increased risk of indefinite
- * postponement when these methods are used without fairness set true.
+ * permits at a time. These methods are generally more efficient and
+ * effective than loops. However, they do not establish any preference
+ * order. For example, if thread A invokes {@code s.acquire(3}) and
+ * thread B invokes {@code s.acquire(2)}, and two permits become
+ * available, then there is no guarantee that thread B will obtain
+ * them unless its acquire came first and Semaphore {@code s} is in
+ * fair mode.
*
* <p>Memory consistency effects: Actions in a thread prior to calling
* a "release" method such as {@code release()}
@@ -405,14 +410,16 @@
*
* <p>Acquires the given number of permits, if they are available,
* and returns immediately, reducing the number of available permits
- * by the given amount.
+ * by the given amount. This method has the same effect as the
+ * loop {@code for (int i = 0; i < permits; ++i) acquire();} except
+ * that it atomically acquires the permits all at once:
*
* <p>If insufficient permits are available then the current thread becomes
* disabled for thread scheduling purposes and lies dormant until
* one of two things happens:
* <ul>
* <li>Some other thread invokes one of the {@link #release() release}
- * methods for this semaphore, the current thread is next to be assigned
+ * methods for this semaphore and the current thread is next to be assigned
* permits and the number of available permits satisfies this request; or
* <li>Some other thread {@linkplain Thread#interrupt interrupts}
* the current thread.
@@ -445,12 +452,14 @@
*
* <p>Acquires the given number of permits, if they are available,
* and returns immediately, reducing the number of available permits
- * by the given amount.
+ * by the given amount. This method has the same effect as the
+ * loop {@code for (int i = 0; i < permits; ++i) acquireUninterruptibly();}
+ * except that it atomically acquires the permits all at once:
*
* <p>If insufficient permits are available then the current thread becomes
* disabled for thread scheduling purposes and lies dormant until
* some other thread invokes one of the {@link #release() release}
- * methods for this semaphore, the current thread is next to be assigned
+ * methods for this semaphore and the current thread is next to be assigned
* permits and the number of available permits satisfies this request.
*
* <p>If the current thread is {@linkplain Thread#interrupt interrupted}
@@ -512,7 +521,7 @@
* purposes and lies dormant until one of three things happens:
* <ul>
* <li>Some other thread invokes one of the {@link #release() release}
- * methods for this semaphore, the current thread is next to be assigned
+ * methods for this semaphore and the current thread is next to be assigned
* permits and the number of available permits satisfies this request; or
* <li>Some other thread {@linkplain Thread#interrupt interrupts}
* the current thread; or
@@ -559,7 +568,7 @@
*
* <p>Releases the given number of permits, increasing the number of
* available permits by that amount.
- * If any threads are trying to acquire permits, then one
+ * If any threads are trying to acquire permits, then one thread
* is selected and given the permits that were just released.
* If the number of available permits satisfies that thread's request
* then that thread is (re)enabled for thread scheduling purposes;
@@ -643,7 +652,7 @@
* Returns an estimate of the number of threads waiting to acquire.
* The value is only an estimate because the number of threads may
* change dynamically while this method traverses internal data
- * structures. This method is designed for use in monitoring of the
+ * structures. This method is designed for use in monitoring
* system state, not for synchronization control.
*
* @return the estimated number of threads waiting for this lock
diff --git a/luni/src/main/java/java/util/concurrent/SynchronousQueue.java b/luni/src/main/java/java/util/concurrent/SynchronousQueue.java
index 69452af..a46f672 100644
--- a/luni/src/main/java/java/util/concurrent/SynchronousQueue.java
+++ b/luni/src/main/java/java/util/concurrent/SynchronousQueue.java
@@ -7,9 +7,14 @@
package java.util.concurrent;
+import java.util.AbstractQueue;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Spliterator;
+import java.util.Spliterators;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
-import java.util.*;
// BEGIN android-note
// removed link to collections framework docs
@@ -49,7 +54,7 @@
*
* @since 1.5
* @author Doug Lea and Bill Scherer and Michael Scott
- * @param <E> the type of elements held in this collection
+ * @param <E> the type of elements held in this queue
*/
public class SynchronousQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
@@ -152,9 +157,6 @@
abstract E transfer(E e, boolean timed, long nanos);
}
- /** The number of CPUs, for spin control */
- static final int NCPUS = Runtime.getRuntime().availableProcessors();
-
/**
* The number of times to spin before blocking in timed waits.
* The value is empirically derived -- it works well across a
@@ -162,20 +164,21 @@
* seems not to vary with number of CPUs (beyond 2) so is just
* a constant.
*/
- static final int maxTimedSpins = (NCPUS < 2) ? 0 : 32;
+ static final int MAX_TIMED_SPINS =
+ (Runtime.getRuntime().availableProcessors() < 2) ? 0 : 32;
/**
* The number of times to spin before blocking in untimed waits.
* This is greater than timed value because untimed waits spin
* faster since they don't need to check times on each spin.
*/
- static final int maxUntimedSpins = maxTimedSpins * 16;
+ static final int MAX_UNTIMED_SPINS = MAX_TIMED_SPINS * 16;
/**
* The number of nanoseconds for which it is faster to spin
* rather than to use timed park. A rough estimate suffices.
*/
- static final long spinForTimeoutThreshold = 1000L;
+ static final long SPIN_FOR_TIMEOUT_THRESHOLD = 1000L;
/** Dual stack */
static final class TransferStack<E> extends Transferer<E> {
@@ -215,7 +218,7 @@
boolean casNext(SNode cmp, SNode val) {
return cmp == next &&
- UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
+ U.compareAndSwapObject(this, NEXT, cmp, val);
}
/**
@@ -228,7 +231,7 @@
*/
boolean tryMatch(SNode s) {
if (match == null &&
- UNSAFE.compareAndSwapObject(this, matchOffset, null, s)) {
+ U.compareAndSwapObject(this, MATCH, null, s)) {
Thread w = waiter;
if (w != null) { // waiters need at most one unpark
waiter = null;
@@ -243,7 +246,7 @@
* Tries to cancel a wait by matching node to itself.
*/
void tryCancel() {
- UNSAFE.compareAndSwapObject(this, matchOffset, null, this);
+ U.compareAndSwapObject(this, MATCH, null, this);
}
boolean isCancelled() {
@@ -251,19 +254,17 @@
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE;
- private static final long matchOffset;
- private static final long nextOffset;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long MATCH;
+ private static final long NEXT;
static {
try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- Class<?> k = SNode.class;
- matchOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("match"));
- nextOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("next"));
- } catch (Exception e) {
+ MATCH = U.objectFieldOffset
+ (SNode.class.getDeclaredField("match"));
+ NEXT = U.objectFieldOffset
+ (SNode.class.getDeclaredField("next"));
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
@@ -274,7 +275,7 @@
boolean casHead(SNode h, SNode nh) {
return h == head &&
- UNSAFE.compareAndSwapObject(this, headOffset, h, nh);
+ U.compareAndSwapObject(this, HEAD, h, nh);
}
/**
@@ -323,7 +324,7 @@
for (;;) {
SNode h = head;
if (h == null || h.mode == mode) { // empty or same-mode
- if (timed && nanos <= 0) { // can't wait
+ if (timed && nanos <= 0L) { // can't wait
if (h != null && h.isCancelled())
casHead(h, h.next); // pop cancelled node
else
@@ -405,8 +406,9 @@
*/
final long deadline = timed ? System.nanoTime() + nanos : 0L;
Thread w = Thread.currentThread();
- int spins = (shouldSpin(s) ?
- (timed ? maxTimedSpins : maxUntimedSpins) : 0);
+ int spins = shouldSpin(s)
+ ? (timed ? MAX_TIMED_SPINS : MAX_UNTIMED_SPINS)
+ : 0;
for (;;) {
if (w.isInterrupted())
s.tryCancel();
@@ -421,12 +423,12 @@
}
}
if (spins > 0)
- spins = shouldSpin(s) ? (spins-1) : 0;
+ spins = shouldSpin(s) ? (spins - 1) : 0;
else if (s.waiter == null)
s.waiter = w; // establish waiter so can park next iter
else if (!timed)
LockSupport.park(this);
- else if (nanos > spinForTimeoutThreshold)
+ else if (nanos > SPIN_FOR_TIMEOUT_THRESHOLD)
LockSupport.parkNanos(this, nanos);
}
}
@@ -478,15 +480,13 @@
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE;
- private static final long headOffset;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long HEAD;
static {
try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- Class<?> k = TransferStack.class;
- headOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("head"));
- } catch (Exception e) {
+ HEAD = U.objectFieldOffset
+ (TransferStack.class.getDeclaredField("head"));
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
@@ -517,19 +517,19 @@
boolean casNext(QNode cmp, QNode val) {
return next == cmp &&
- UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
+ U.compareAndSwapObject(this, NEXT, cmp, val);
}
boolean casItem(Object cmp, Object val) {
return item == cmp &&
- UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
+ U.compareAndSwapObject(this, ITEM, cmp, val);
}
/**
* Tries to cancel by CAS'ing ref to this as item.
*/
void tryCancel(Object cmp) {
- UNSAFE.compareAndSwapObject(this, itemOffset, cmp, this);
+ U.compareAndSwapObject(this, ITEM, cmp, this);
}
boolean isCancelled() {
@@ -546,19 +546,17 @@
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE;
- private static final long itemOffset;
- private static final long nextOffset;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long ITEM;
+ private static final long NEXT;
static {
try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- Class<?> k = QNode.class;
- itemOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("item"));
- nextOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("next"));
- } catch (Exception e) {
+ ITEM = U.objectFieldOffset
+ (QNode.class.getDeclaredField("item"));
+ NEXT = U.objectFieldOffset
+ (QNode.class.getDeclaredField("next"));
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
@@ -587,7 +585,7 @@
*/
void advanceHead(QNode h, QNode nh) {
if (h == head &&
- UNSAFE.compareAndSwapObject(this, headOffset, h, nh))
+ U.compareAndSwapObject(this, HEAD, h, nh))
h.next = h; // forget old next
}
@@ -596,7 +594,7 @@
*/
void advanceTail(QNode t, QNode nt) {
if (tail == t)
- UNSAFE.compareAndSwapObject(this, tailOffset, t, nt);
+ U.compareAndSwapObject(this, TAIL, t, nt);
}
/**
@@ -604,7 +602,7 @@
*/
boolean casCleanMe(QNode cmp, QNode val) {
return cleanMe == cmp &&
- UNSAFE.compareAndSwapObject(this, cleanMeOffset, cmp, val);
+ U.compareAndSwapObject(this, CLEANME, cmp, val);
}
/**
@@ -654,7 +652,7 @@
advanceTail(t, tn);
continue;
}
- if (timed && nanos <= 0) // can't wait
+ if (timed && nanos <= 0L) // can't wait
return null;
if (s == null)
s = new QNode(e, isData);
@@ -709,8 +707,9 @@
/* Same idea as TransferStack.awaitFulfill */
final long deadline = timed ? System.nanoTime() + nanos : 0L;
Thread w = Thread.currentThread();
- int spins = ((head.next == s) ?
- (timed ? maxTimedSpins : maxUntimedSpins) : 0);
+ int spins = (head.next == s)
+ ? (timed ? MAX_TIMED_SPINS : MAX_UNTIMED_SPINS)
+ : 0;
for (;;) {
if (w.isInterrupted())
s.tryCancel(e);
@@ -730,7 +729,7 @@
s.waiter = w;
else if (!timed)
LockSupport.park(this);
- else if (nanos > spinForTimeoutThreshold)
+ else if (nanos > SPIN_FOR_TIMEOUT_THRESHOLD)
LockSupport.parkNanos(this, nanos);
}
}
@@ -789,21 +788,19 @@
}
}
- private static final sun.misc.Unsafe UNSAFE;
- private static final long headOffset;
- private static final long tailOffset;
- private static final long cleanMeOffset;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long HEAD;
+ private static final long TAIL;
+ private static final long CLEANME;
static {
try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- Class<?> k = TransferQueue.class;
- headOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("head"));
- tailOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("tail"));
- cleanMeOffset = UNSAFE.objectFieldOffset
- (k.getDeclaredField("cleanMe"));
- } catch (Exception e) {
+ HEAD = U.objectFieldOffset
+ (TransferQueue.class.getDeclaredField("head"));
+ TAIL = U.objectFieldOffset
+ (TransferQueue.class.getDeclaredField("tail"));
+ CLEANME = U.objectFieldOffset
+ (TransferQueue.class.getDeclaredField("cleanMe"));
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
@@ -1034,19 +1031,19 @@
*
* @return an empty iterator
*/
- @SuppressWarnings("unchecked")
public Iterator<E> iterator() {
- return (Iterator<E>) EmptyIterator.EMPTY_ITERATOR;
+ return Collections.emptyIterator();
}
- // Replicated from a previous version of Collections
- private static class EmptyIterator<E> implements Iterator<E> {
- static final EmptyIterator<Object> EMPTY_ITERATOR
- = new EmptyIterator<Object>();
-
- public boolean hasNext() { return false; }
- public E next() { throw new NoSuchElementException(); }
- public void remove() { throw new IllegalStateException(); }
+ /**
+ * Returns an empty spliterator in which calls to
+ * {@link java.util.Spliterator#trySplit()} always return {@code null}.
+ *
+ * @return an empty spliterator
+ * @since 1.8
+ */
+ public Spliterator<E> spliterator() {
+ return Spliterators.emptySpliterator();
}
/**
@@ -1072,6 +1069,14 @@
}
/**
+ * Always returns {@code "[]"}.
+ * @return {@code "[]"}
+ */
+ public String toString() {
+ return "[]";
+ }
+
+ /**
* @throws UnsupportedOperationException {@inheritDoc}
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
@@ -1131,6 +1136,8 @@
/**
* Saves this queue to a stream (that is, serializes it).
+ * @param s the stream
+ * @throws java.io.IOException if an I/O error occurs
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
@@ -1150,6 +1157,10 @@
/**
* Reconstitutes this queue from a stream (that is, deserializes it).
+ * @param s the stream
+ * @throws ClassNotFoundException if the class of a serialized object
+ * could not be found
+ * @throws java.io.IOException if an I/O error occurs
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
@@ -1160,19 +1171,6 @@
transferer = new TransferStack<E>();
}
- // Unsafe mechanics
- static long objectFieldOffset(sun.misc.Unsafe UNSAFE,
- String field, Class<?> klazz) {
- try {
- return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field));
- } catch (NoSuchFieldException e) {
- // Convert Exception to corresponding Error
- NoSuchFieldError error = new NoSuchFieldError(field);
- error.initCause(e);
- throw error;
- }
- }
-
static {
// Reduce the risk of rare disastrous classloading in first call to
// LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773
diff --git a/luni/src/main/java/java/util/concurrent/ThreadFactory.java b/luni/src/main/java/java/util/concurrent/ThreadFactory.java
index d1a4eb6..fdedea3 100644
--- a/luni/src/main/java/java/util/concurrent/ThreadFactory.java
+++ b/luni/src/main/java/java/util/concurrent/ThreadFactory.java
@@ -13,7 +13,7 @@
*
* <p>
* The simplest implementation of this interface is just:
- * <pre> {@code
+ * <pre> {@code
* class SimpleThreadFactory implements ThreadFactory {
* public Thread newThread(Runnable r) {
* return new Thread(r);
diff --git a/luni/src/main/java/java/util/concurrent/ThreadLocalRandom.java b/luni/src/main/java/java/util/concurrent/ThreadLocalRandom.java
index b007af2..8c65bde 100644
--- a/luni/src/main/java/java/util/concurrent/ThreadLocalRandom.java
+++ b/luni/src/main/java/java/util/concurrent/ThreadLocalRandom.java
@@ -6,7 +6,19 @@
package java.util.concurrent;
+import java.io.ObjectStreamField;
import java.util.Random;
+import java.util.Spliterator;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+// TODO(streams):
+// import java.util.stream.DoubleStream;
+// import java.util.stream.IntStream;
+// import java.util.stream.LongStream;
+// import java.util.stream.StreamSupport;
/**
* A random number generator isolated to the current thread. Like the
@@ -29,50 +41,98 @@
* <p>This class also provides additional commonly used bounded random
* generation methods.
*
+ * <p>Instances of {@code ThreadLocalRandom} are not cryptographically
+ * secure. Consider instead using {@link java.security.SecureRandom}
+ * in security-sensitive applications. Additionally,
+ * default-constructed instances do not use a cryptographically random
+ * seed unless the {@linkplain System#getProperty system property}
+ * {@code java.util.secureRandomSeed} is set to {@code true}.
+ *
* @since 1.7
* @author Doug Lea
*/
public class ThreadLocalRandom extends Random {
- // same constants as Random, but must be redeclared because private
- private static final long multiplier = 0x5DEECE66DL;
- private static final long addend = 0xBL;
- private static final long mask = (1L << 48) - 1;
-
- /**
- * The random seed. We can't use super.seed.
+ /*
+ * This class implements the java.util.Random API (and subclasses
+ * Random) using a single static instance that accesses random
+ * number state held in class Thread (primarily, field
+ * threadLocalRandomSeed). In doing so, it also provides a home
+ * for managing package-private utilities that rely on exactly the
+ * same state as needed to maintain the ThreadLocalRandom
+ * instances. We leverage the need for an initialization flag
+ * field to also use it as a "probe" -- a self-adjusting thread
+ * hash used for contention avoidance, as well as a secondary
+ * simpler (xorShift) random seed that is conservatively used to
+ * avoid otherwise surprising users by hijacking the
+ * ThreadLocalRandom sequence. The dual use is a marriage of
+ * convenience, but is a simple and efficient way of reducing
+ * application-level overhead and footprint of most concurrent
+ * programs.
+ *
+ * Even though this class subclasses java.util.Random, it uses the
+ * same basic algorithm as java.util.SplittableRandom. (See its
+ * internal documentation for explanations, which are not repeated
+ * here.) Because ThreadLocalRandoms are not splittable
+ * though, we use only a single 64bit gamma.
+ *
+ * Because this class is in a different package than class Thread,
+ * field access methods use Unsafe to bypass access control rules.
+ * To conform to the requirements of the Random superclass
+ * constructor, the common static ThreadLocalRandom maintains an
+ * "initialized" field for the sake of rejecting user calls to
+ * setSeed while still allowing a call from constructor. Note
+ * that serialization is completely unnecessary because there is
+ * only a static singleton. But we generate a serial form
+ * containing "rnd" and "initialized" fields to ensure
+ * compatibility across versions.
+ *
+ * Implementations of non-core methods are mostly the same as in
+ * SplittableRandom, that were in part derived from a previous
+ * version of this class.
+ *
+ * The nextLocalGaussian ThreadLocal supports the very rarely used
+ * nextGaussian method by providing a holder for the second of a
+ * pair of them. As is true for the base class version of this
+ * method, this time/space tradeoff is probably never worthwhile,
+ * but we provide identical statistical properties.
*/
- private long rnd;
+
+ private static long mix64(long z) {
+ z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL;
+ z = (z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L;
+ return z ^ (z >>> 33);
+ }
+
+ private static int mix32(long z) {
+ z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL;
+ return (int)(((z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L) >>> 32);
+ }
/**
- * Initialization flag to permit calls to setSeed to succeed only
- * while executing the Random constructor. We can't allow others
- * since it would cause setting seed in one part of a program to
- * unintentionally impact other usages by the thread.
+ * Field used only during singleton initialization.
+ * True when constructor completes.
*/
boolean initialized;
- // Padding to help avoid memory contention among seed updates in
- // different TLRs in the common case that they are located near
- // each other.
- private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7;
+ /** Constructor used only for static singleton */
+ private ThreadLocalRandom() {
+ initialized = true; // false during super() call
+ }
/**
- * The actual ThreadLocal
+ * Initialize Thread fields for the current thread. Called only
+ * when Thread.threadLocalRandomProbe is zero, indicating that a
+ * thread local seed value needs to be generated. Note that even
+ * though the initialization is purely thread-local, we need to
+ * rely on (static) atomic generators to initialize the values.
*/
- private static final ThreadLocal<ThreadLocalRandom> localRandom =
- new ThreadLocal<ThreadLocalRandom>() {
- protected ThreadLocalRandom initialValue() {
- return new ThreadLocalRandom();
- }
- };
-
-
- /**
- * Constructor called only by localRandom.initialValue.
- */
- ThreadLocalRandom() {
- super();
- initialized = true;
+ static final void localInit() {
+ int p = probeGenerator.addAndGet(PROBE_INCREMENT);
+ int probe = (p == 0) ? 1 : p; // skip 0
+ long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
+ Thread t = Thread.currentThread();
+ U.putLong(t, SEED, seed);
+ U.putInt(t, PROBE, probe);
}
/**
@@ -81,7 +141,9 @@
* @return the current thread's {@code ThreadLocalRandom}
*/
public static ThreadLocalRandom current() {
- return localRandom.get();
+ if (U.getInt(Thread.currentThread(), PROBE) == 0)
+ localInit();
+ return instance;
}
/**
@@ -91,107 +153,894 @@
* @throws UnsupportedOperationException always
*/
public void setSeed(long seed) {
+ // only allow call from super() constructor
if (initialized)
throw new UnsupportedOperationException();
- rnd = (seed ^ multiplier) & mask;
}
+ final long nextSeed() {
+ Thread t; long r; // read and update per-thread seed
+ U.putLong(t = Thread.currentThread(), SEED,
+ r = U.getLong(t, SEED) + GAMMA);
+ return r;
+ }
+
+ // We must define this, but never use it.
protected int next(int bits) {
- rnd = (rnd * multiplier + addend) & mask;
- return (int) (rnd >>> (48-bits));
+ return (int)(mix64(nextSeed()) >>> (64 - bits));
}
/**
- * Returns a pseudorandom, uniformly distributed value between the
- * given least value (inclusive) and bound (exclusive).
+ * The form of nextLong used by LongStream Spliterators. If
+ * origin is greater than bound, acts as unbounded form of
+ * nextLong, else as bounded form.
*
- * @param least the least value returned
- * @param bound the upper bound (exclusive)
- * @return the next value
- * @throws IllegalArgumentException if least greater than or equal
- * to bound
+ * @param origin the least value, unless greater than bound
+ * @param bound the upper bound (exclusive), must not equal origin
+ * @return a pseudorandom value
*/
- public int nextInt(int least, int bound) {
- if (least >= bound)
- throw new IllegalArgumentException();
- return nextInt(bound - least) + least;
- }
-
- /**
- * Returns a pseudorandom, uniformly distributed value
- * between 0 (inclusive) and the specified value (exclusive).
- *
- * @param n the bound on the random number to be returned. Must be
- * positive.
- * @return the next value
- * @throws IllegalArgumentException if n is not positive
- */
- public long nextLong(long n) {
- if (n <= 0)
- throw new IllegalArgumentException("n must be positive");
- // Divide n by two until small enough for nextInt. On each
- // iteration (at most 31 of them but usually much less),
- // randomly choose both whether to include high bit in result
- // (offset) and whether to continue with the lower vs upper
- // half (which makes a difference only if odd).
- long offset = 0;
- while (n >= Integer.MAX_VALUE) {
- int bits = next(2);
- long half = n >>> 1;
- long nextn = ((bits & 2) == 0) ? half : n - half;
- if ((bits & 1) == 0)
- offset += n - nextn;
- n = nextn;
+ final long internalNextLong(long origin, long bound) {
+ long r = mix64(nextSeed());
+ if (origin < bound) {
+ long n = bound - origin, m = n - 1;
+ if ((n & m) == 0L) // power of two
+ r = (r & m) + origin;
+ else if (n > 0L) { // reject over-represented candidates
+ for (long u = r >>> 1; // ensure nonnegative
+ u + m - (r = u % n) < 0L; // rejection check
+ u = mix64(nextSeed()) >>> 1) // retry
+ ;
+ r += origin;
+ }
+ else { // range not representable as long
+ while (r < origin || r >= bound)
+ r = mix64(nextSeed());
+ }
}
- return offset + nextInt((int) n);
+ return r;
}
/**
- * Returns a pseudorandom, uniformly distributed value between the
- * given least value (inclusive) and bound (exclusive).
+ * The form of nextInt used by IntStream Spliterators.
+ * Exactly the same as long version, except for types.
*
- * @param least the least value returned
+ * @param origin the least value, unless greater than bound
+ * @param bound the upper bound (exclusive), must not equal origin
+ * @return a pseudorandom value
+ */
+ final int internalNextInt(int origin, int bound) {
+ int r = mix32(nextSeed());
+ if (origin < bound) {
+ int n = bound - origin, m = n - 1;
+ if ((n & m) == 0)
+ r = (r & m) + origin;
+ else if (n > 0) {
+ for (int u = r >>> 1;
+ u + m - (r = u % n) < 0;
+ u = mix32(nextSeed()) >>> 1)
+ ;
+ r += origin;
+ }
+ else {
+ while (r < origin || r >= bound)
+ r = mix32(nextSeed());
+ }
+ }
+ return r;
+ }
+
+ /**
+ * The form of nextDouble used by DoubleStream Spliterators.
+ *
+ * @param origin the least value, unless greater than bound
+ * @param bound the upper bound (exclusive), must not equal origin
+ * @return a pseudorandom value
+ */
+ final double internalNextDouble(double origin, double bound) {
+ double r = (nextLong() >>> 11) * DOUBLE_UNIT;
+ if (origin < bound) {
+ r = r * (bound - origin) + origin;
+ if (r >= bound) // correct for rounding
+ r = Double.longBitsToDouble(Double.doubleToLongBits(bound) - 1);
+ }
+ return r;
+ }
+
+ /**
+ * Returns a pseudorandom {@code int} value.
+ *
+ * @return a pseudorandom {@code int} value
+ */
+ public int nextInt() {
+ return mix32(nextSeed());
+ }
+
+ /**
+ * Returns a pseudorandom {@code int} value between zero (inclusive)
+ * and the specified bound (exclusive).
+ *
+ * @param bound the upper bound (exclusive). Must be positive.
+ * @return a pseudorandom {@code int} value between zero
+ * (inclusive) and the bound (exclusive)
+ * @throws IllegalArgumentException if {@code bound} is not positive
+ */
+ public int nextInt(int bound) {
+ if (bound <= 0)
+ throw new IllegalArgumentException(BAD_BOUND);
+ int r = mix32(nextSeed());
+ int m = bound - 1;
+ if ((bound & m) == 0) // power of two
+ r &= m;
+ else { // reject over-represented candidates
+ for (int u = r >>> 1;
+ u + m - (r = u % bound) < 0;
+ u = mix32(nextSeed()) >>> 1)
+ ;
+ }
+ return r;
+ }
+
+ /**
+ * Returns a pseudorandom {@code int} value between the specified
+ * origin (inclusive) and the specified bound (exclusive).
+ *
+ * @param origin the least value returned
* @param bound the upper bound (exclusive)
- * @return the next value
- * @throws IllegalArgumentException if least greater than or equal
- * to bound
+ * @return a pseudorandom {@code int} value between the origin
+ * (inclusive) and the bound (exclusive)
+ * @throws IllegalArgumentException if {@code origin} is greater than
+ * or equal to {@code bound}
*/
- public long nextLong(long least, long bound) {
- if (least >= bound)
- throw new IllegalArgumentException();
- return nextLong(bound - least) + least;
+ public int nextInt(int origin, int bound) {
+ if (origin >= bound)
+ throw new IllegalArgumentException(BAD_RANGE);
+ return internalNextInt(origin, bound);
}
/**
- * Returns a pseudorandom, uniformly distributed {@code double} value
- * between 0 (inclusive) and the specified value (exclusive).
+ * Returns a pseudorandom {@code long} value.
*
- * @param n the bound on the random number to be returned. Must be
- * positive.
- * @return the next value
- * @throws IllegalArgumentException if n is not positive
+ * @return a pseudorandom {@code long} value
*/
- public double nextDouble(double n) {
- if (!(n > 0))
- throw new IllegalArgumentException("n must be positive");
- return nextDouble() * n;
+ public long nextLong() {
+ return mix64(nextSeed());
}
/**
- * Returns a pseudorandom, uniformly distributed value between the
- * given least value (inclusive) and bound (exclusive).
+ * Returns a pseudorandom {@code long} value between zero (inclusive)
+ * and the specified bound (exclusive).
*
- * @param least the least value returned
+ * @param bound the upper bound (exclusive). Must be positive.
+ * @return a pseudorandom {@code long} value between zero
+ * (inclusive) and the bound (exclusive)
+ * @throws IllegalArgumentException if {@code bound} is not positive
+ */
+ public long nextLong(long bound) {
+ if (bound <= 0)
+ throw new IllegalArgumentException(BAD_BOUND);
+ long r = mix64(nextSeed());
+ long m = bound - 1;
+ if ((bound & m) == 0L) // power of two
+ r &= m;
+ else { // reject over-represented candidates
+ for (long u = r >>> 1;
+ u + m - (r = u % bound) < 0L;
+ u = mix64(nextSeed()) >>> 1)
+ ;
+ }
+ return r;
+ }
+
+ /**
+ * Returns a pseudorandom {@code long} value between the specified
+ * origin (inclusive) and the specified bound (exclusive).
+ *
+ * @param origin the least value returned
* @param bound the upper bound (exclusive)
- * @return the next value
- * @throws IllegalArgumentException if least greater than or equal
- * to bound
+ * @return a pseudorandom {@code long} value between the origin
+ * (inclusive) and the bound (exclusive)
+ * @throws IllegalArgumentException if {@code origin} is greater than
+ * or equal to {@code bound}
*/
- public double nextDouble(double least, double bound) {
- if (least >= bound)
- throw new IllegalArgumentException();
- return nextDouble() * (bound - least) + least;
+ public long nextLong(long origin, long bound) {
+ if (origin >= bound)
+ throw new IllegalArgumentException(BAD_RANGE);
+ return internalNextLong(origin, bound);
}
+ /**
+ * Returns a pseudorandom {@code double} value between zero
+ * (inclusive) and one (exclusive).
+ *
+ * @return a pseudorandom {@code double} value between zero
+ * (inclusive) and one (exclusive)
+ */
+ public double nextDouble() {
+ return (mix64(nextSeed()) >>> 11) * DOUBLE_UNIT;
+ }
+
+ /**
+ * Returns a pseudorandom {@code double} value between 0.0
+ * (inclusive) and the specified bound (exclusive).
+ *
+ * @param bound the upper bound (exclusive). Must be positive.
+ * @return a pseudorandom {@code double} value between zero
+ * (inclusive) and the bound (exclusive)
+ * @throws IllegalArgumentException if {@code bound} is not positive
+ */
+ public double nextDouble(double bound) {
+ if (!(bound > 0.0))
+ throw new IllegalArgumentException(BAD_BOUND);
+ double result = (mix64(nextSeed()) >>> 11) * DOUBLE_UNIT * bound;
+ return (result < bound) ? result : // correct for rounding
+ Double.longBitsToDouble(Double.doubleToLongBits(bound) - 1);
+ }
+
+ /**
+ * Returns a pseudorandom {@code double} value between the specified
+ * origin (inclusive) and bound (exclusive).
+ *
+ * @param origin the least value returned
+ * @param bound the upper bound (exclusive)
+ * @return a pseudorandom {@code double} value between the origin
+ * (inclusive) and the bound (exclusive)
+ * @throws IllegalArgumentException if {@code origin} is greater than
+ * or equal to {@code bound}
+ */
+ public double nextDouble(double origin, double bound) {
+ if (!(origin < bound))
+ throw new IllegalArgumentException(BAD_RANGE);
+ return internalNextDouble(origin, bound);
+ }
+
+ /**
+ * Returns a pseudorandom {@code boolean} value.
+ *
+ * @return a pseudorandom {@code boolean} value
+ */
+ public boolean nextBoolean() {
+ return mix32(nextSeed()) < 0;
+ }
+
+ /**
+ * Returns a pseudorandom {@code float} value between zero
+ * (inclusive) and one (exclusive).
+ *
+ * @return a pseudorandom {@code float} value between zero
+ * (inclusive) and one (exclusive)
+ */
+ public float nextFloat() {
+ return (mix32(nextSeed()) >>> 8) * FLOAT_UNIT;
+ }
+
+ public double nextGaussian() {
+ // Use nextLocalGaussian instead of nextGaussian field
+ Double d = nextLocalGaussian.get();
+ if (d != null) {
+ nextLocalGaussian.set(null);
+ return d.doubleValue();
+ }
+ double v1, v2, s;
+ do {
+ v1 = 2 * nextDouble() - 1; // between -1 and 1
+ v2 = 2 * nextDouble() - 1; // between -1 and 1
+ s = v1 * v1 + v2 * v2;
+ } while (s >= 1 || s == 0);
+ double multiplier = StrictMath.sqrt(-2 * StrictMath.log(s)/s);
+ nextLocalGaussian.set(new Double(v2 * multiplier));
+ return v1 * multiplier;
+ }
+
+ // stream methods, coded in a way intended to better isolate for
+ // maintenance purposes the small differences across forms.
+
+ // TODO(streams):
+ // /**
+ // * Returns a stream producing the given {@code streamSize} number of
+ // * pseudorandom {@code int} values.
+ // *
+ // * @param streamSize the number of values to generate
+ // * @return a stream of pseudorandom {@code int} values
+ // * @throws IllegalArgumentException if {@code streamSize} is
+ // * less than zero
+ // * @since 1.8
+ // */
+ // public IntStream ints(long streamSize) {
+ // if (streamSize < 0L)
+ // throw new IllegalArgumentException(BAD_SIZE);
+ // return StreamSupport.intStream
+ // (new RandomIntsSpliterator
+ // (0L, streamSize, Integer.MAX_VALUE, 0),
+ // false);
+ // }
+
+ // /**
+ // * Returns an effectively unlimited stream of pseudorandom {@code int}
+ // * values.
+ // *
+ // * @implNote This method is implemented to be equivalent to {@code
+ // * ints(Long.MAX_VALUE)}.
+ // *
+ // * @return a stream of pseudorandom {@code int} values
+ // * @since 1.8
+ // */
+ // public IntStream ints() {
+ // return StreamSupport.intStream
+ // (new RandomIntsSpliterator
+ // (0L, Long.MAX_VALUE, Integer.MAX_VALUE, 0),
+ // false);
+ // }
+
+ // /**
+ // * Returns a stream producing the given {@code streamSize} number
+ // * of pseudorandom {@code int} values, each conforming to the given
+ // * origin (inclusive) and bound (exclusive).
+ // *
+ // * @param streamSize the number of values to generate
+ // * @param randomNumberOrigin the origin (inclusive) of each random value
+ // * @param randomNumberBound the bound (exclusive) of each random value
+ // * @return a stream of pseudorandom {@code int} values,
+ // * each with the given origin (inclusive) and bound (exclusive)
+ // * @throws IllegalArgumentException if {@code streamSize} is
+ // * less than zero, or {@code randomNumberOrigin}
+ // * is greater than or equal to {@code randomNumberBound}
+ // * @since 1.8
+ // */
+ // public IntStream ints(long streamSize, int randomNumberOrigin,
+ // int randomNumberBound) {
+ // if (streamSize < 0L)
+ // throw new IllegalArgumentException(BAD_SIZE);
+ // if (randomNumberOrigin >= randomNumberBound)
+ // throw new IllegalArgumentException(BAD_RANGE);
+ // return StreamSupport.intStream
+ // (new RandomIntsSpliterator
+ // (0L, streamSize, randomNumberOrigin, randomNumberBound),
+ // false);
+ // }
+
+ // /**
+ // * Returns an effectively unlimited stream of pseudorandom {@code
+ // * int} values, each conforming to the given origin (inclusive) and bound
+ // * (exclusive).
+ // *
+ // * @implNote This method is implemented to be equivalent to {@code
+ // * ints(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}.
+ // *
+ // * @param randomNumberOrigin the origin (inclusive) of each random value
+ // * @param randomNumberBound the bound (exclusive) of each random value
+ // * @return a stream of pseudorandom {@code int} values,
+ // * each with the given origin (inclusive) and bound (exclusive)
+ // * @throws IllegalArgumentException if {@code randomNumberOrigin}
+ // * is greater than or equal to {@code randomNumberBound}
+ // * @since 1.8
+ // */
+ // public IntStream ints(int randomNumberOrigin, int randomNumberBound) {
+ // if (randomNumberOrigin >= randomNumberBound)
+ // throw new IllegalArgumentException(BAD_RANGE);
+ // return StreamSupport.intStream
+ // (new RandomIntsSpliterator
+ // (0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound),
+ // false);
+ // }
+
+ // /**
+ // * Returns a stream producing the given {@code streamSize} number of
+ // * pseudorandom {@code long} values.
+ // *
+ // * @param streamSize the number of values to generate
+ // * @return a stream of pseudorandom {@code long} values
+ // * @throws IllegalArgumentException if {@code streamSize} is
+ // * less than zero
+ // * @since 1.8
+ // */
+ // public LongStream longs(long streamSize) {
+ // if (streamSize < 0L)
+ // throw new IllegalArgumentException(BAD_SIZE);
+ // return StreamSupport.longStream
+ // (new RandomLongsSpliterator
+ // (0L, streamSize, Long.MAX_VALUE, 0L),
+ // false);
+ // }
+
+ // /**
+ // * Returns an effectively unlimited stream of pseudorandom {@code long}
+ // * values.
+ // *
+ // * @implNote This method is implemented to be equivalent to {@code
+ // * longs(Long.MAX_VALUE)}.
+ // *
+ // * @return a stream of pseudorandom {@code long} values
+ // * @since 1.8
+ // */
+ // public LongStream longs() {
+ // return StreamSupport.longStream
+ // (new RandomLongsSpliterator
+ // (0L, Long.MAX_VALUE, Long.MAX_VALUE, 0L),
+ // false);
+ // }
+
+ // /**
+ // * Returns a stream producing the given {@code streamSize} number of
+ // * pseudorandom {@code long}, each conforming to the given origin
+ // * (inclusive) and bound (exclusive).
+ // *
+ // * @param streamSize the number of values to generate
+ // * @param randomNumberOrigin the origin (inclusive) of each random value
+ // * @param randomNumberBound the bound (exclusive) of each random value
+ // * @return a stream of pseudorandom {@code long} values,
+ // * each with the given origin (inclusive) and bound (exclusive)
+ // * @throws IllegalArgumentException if {@code streamSize} is
+ // * less than zero, or {@code randomNumberOrigin}
+ // * is greater than or equal to {@code randomNumberBound}
+ // * @since 1.8
+ // */
+ // public LongStream longs(long streamSize, long randomNumberOrigin,
+ // long randomNumberBound) {
+ // if (streamSize < 0L)
+ // throw new IllegalArgumentException(BAD_SIZE);
+ // if (randomNumberOrigin >= randomNumberBound)
+ // throw new IllegalArgumentException(BAD_RANGE);
+ // return StreamSupport.longStream
+ // (new RandomLongsSpliterator
+ // (0L, streamSize, randomNumberOrigin, randomNumberBound),
+ // false);
+ // }
+
+ // /**
+ // * Returns an effectively unlimited stream of pseudorandom {@code
+ // * long} values, each conforming to the given origin (inclusive) and bound
+ // * (exclusive).
+ // *
+ // * @implNote This method is implemented to be equivalent to {@code
+ // * longs(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}.
+ // *
+ // * @param randomNumberOrigin the origin (inclusive) of each random value
+ // * @param randomNumberBound the bound (exclusive) of each random value
+ // * @return a stream of pseudorandom {@code long} values,
+ // * each with the given origin (inclusive) and bound (exclusive)
+ // * @throws IllegalArgumentException if {@code randomNumberOrigin}
+ // * is greater than or equal to {@code randomNumberBound}
+ // * @since 1.8
+ // */
+ // public LongStream longs(long randomNumberOrigin, long randomNumberBound) {
+ // if (randomNumberOrigin >= randomNumberBound)
+ // throw new IllegalArgumentException(BAD_RANGE);
+ // return StreamSupport.longStream
+ // (new RandomLongsSpliterator
+ // (0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound),
+ // false);
+ // }
+
+ // /**
+ // * Returns a stream producing the given {@code streamSize} number of
+ // * pseudorandom {@code double} values, each between zero
+ // * (inclusive) and one (exclusive).
+ // *
+ // * @param streamSize the number of values to generate
+ // * @return a stream of {@code double} values
+ // * @throws IllegalArgumentException if {@code streamSize} is
+ // * less than zero
+ // * @since 1.8
+ // */
+ // public DoubleStream doubles(long streamSize) {
+ // if (streamSize < 0L)
+ // throw new IllegalArgumentException(BAD_SIZE);
+ // return StreamSupport.doubleStream
+ // (new RandomDoublesSpliterator
+ // (0L, streamSize, Double.MAX_VALUE, 0.0),
+ // false);
+ // }
+
+ // /**
+ // * Returns an effectively unlimited stream of pseudorandom {@code
+ // * double} values, each between zero (inclusive) and one
+ // * (exclusive).
+ // *
+ // * @implNote This method is implemented to be equivalent to {@code
+ // * doubles(Long.MAX_VALUE)}.
+ // *
+ // * @return a stream of pseudorandom {@code double} values
+ // * @since 1.8
+ // */
+ // public DoubleStream doubles() {
+ // return StreamSupport.doubleStream
+ // (new RandomDoublesSpliterator
+ // (0L, Long.MAX_VALUE, Double.MAX_VALUE, 0.0),
+ // false);
+ // }
+
+ // /**
+ // * Returns a stream producing the given {@code streamSize} number of
+ // * pseudorandom {@code double} values, each conforming to the given origin
+ // * (inclusive) and bound (exclusive).
+ // *
+ // * @param streamSize the number of values to generate
+ // * @param randomNumberOrigin the origin (inclusive) of each random value
+ // * @param randomNumberBound the bound (exclusive) of each random value
+ // * @return a stream of pseudorandom {@code double} values,
+ // * each with the given origin (inclusive) and bound (exclusive)
+ // * @throws IllegalArgumentException if {@code streamSize} is
+ // * less than zero
+ // * @throws IllegalArgumentException if {@code randomNumberOrigin}
+ // * is greater than or equal to {@code randomNumberBound}
+ // * @since 1.8
+ // */
+ // public DoubleStream doubles(long streamSize, double randomNumberOrigin,
+ // double randomNumberBound) {
+ // if (streamSize < 0L)
+ // throw new IllegalArgumentException(BAD_SIZE);
+ // if (!(randomNumberOrigin < randomNumberBound))
+ // throw new IllegalArgumentException(BAD_RANGE);
+ // return StreamSupport.doubleStream
+ // (new RandomDoublesSpliterator
+ // (0L, streamSize, randomNumberOrigin, randomNumberBound),
+ // false);
+ // }
+
+ // /**
+ // * Returns an effectively unlimited stream of pseudorandom {@code
+ // * double} values, each conforming to the given origin (inclusive) and bound
+ // * (exclusive).
+ // *
+ // * @implNote This method is implemented to be equivalent to {@code
+ // * doubles(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}.
+ // *
+ // * @param randomNumberOrigin the origin (inclusive) of each random value
+ // * @param randomNumberBound the bound (exclusive) of each random value
+ // * @return a stream of pseudorandom {@code double} values,
+ // * each with the given origin (inclusive) and bound (exclusive)
+ // * @throws IllegalArgumentException if {@code randomNumberOrigin}
+ // * is greater than or equal to {@code randomNumberBound}
+ // * @since 1.8
+ // */
+ // public DoubleStream doubles(double randomNumberOrigin, double randomNumberBound) {
+ // if (!(randomNumberOrigin < randomNumberBound))
+ // throw new IllegalArgumentException(BAD_RANGE);
+ // return StreamSupport.doubleStream
+ // (new RandomDoublesSpliterator
+ // (0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound),
+ // false);
+ // }
+
+ /**
+ * Spliterator for int streams. We multiplex the four int
+ * versions into one class by treating a bound less than origin as
+ * unbounded, and also by treating "infinite" as equivalent to
+ * Long.MAX_VALUE. For splits, it uses the standard divide-by-two
+ * approach. The long and double versions of this class are
+ * identical except for types.
+ */
+ private static final class RandomIntsSpliterator
+ implements Spliterator.OfInt {
+ long index;
+ final long fence;
+ final int origin;
+ final int bound;
+ RandomIntsSpliterator(long index, long fence,
+ int origin, int bound) {
+ this.index = index; this.fence = fence;
+ this.origin = origin; this.bound = bound;
+ }
+
+ public RandomIntsSpliterator trySplit() {
+ long i = index, m = (i + fence) >>> 1;
+ return (m <= i) ? null :
+ new RandomIntsSpliterator(i, index = m, origin, bound);
+ }
+
+ public long estimateSize() {
+ return fence - index;
+ }
+
+ public int characteristics() {
+ return (Spliterator.SIZED | Spliterator.SUBSIZED |
+ Spliterator.NONNULL | Spliterator.IMMUTABLE);
+ }
+
+ public boolean tryAdvance(IntConsumer consumer) {
+ if (consumer == null) throw new NullPointerException();
+ long i = index, f = fence;
+ if (i < f) {
+ consumer.accept(ThreadLocalRandom.current().internalNextInt(origin, bound));
+ index = i + 1;
+ return true;
+ }
+ return false;
+ }
+
+ public void forEachRemaining(IntConsumer consumer) {
+ if (consumer == null) throw new NullPointerException();
+ long i = index, f = fence;
+ if (i < f) {
+ index = f;
+ int o = origin, b = bound;
+ ThreadLocalRandom rng = ThreadLocalRandom.current();
+ do {
+ consumer.accept(rng.internalNextInt(o, b));
+ } while (++i < f);
+ }
+ }
+ }
+
+ /**
+ * Spliterator for long streams.
+ */
+ private static final class RandomLongsSpliterator
+ implements Spliterator.OfLong {
+ long index;
+ final long fence;
+ final long origin;
+ final long bound;
+ RandomLongsSpliterator(long index, long fence,
+ long origin, long bound) {
+ this.index = index; this.fence = fence;
+ this.origin = origin; this.bound = bound;
+ }
+
+ public RandomLongsSpliterator trySplit() {
+ long i = index, m = (i + fence) >>> 1;
+ return (m <= i) ? null :
+ new RandomLongsSpliterator(i, index = m, origin, bound);
+ }
+
+ public long estimateSize() {
+ return fence - index;
+ }
+
+ public int characteristics() {
+ return (Spliterator.SIZED | Spliterator.SUBSIZED |
+ Spliterator.NONNULL | Spliterator.IMMUTABLE);
+ }
+
+ public boolean tryAdvance(LongConsumer consumer) {
+ if (consumer == null) throw new NullPointerException();
+ long i = index, f = fence;
+ if (i < f) {
+ consumer.accept(ThreadLocalRandom.current().internalNextLong(origin, bound));
+ index = i + 1;
+ return true;
+ }
+ return false;
+ }
+
+ public void forEachRemaining(LongConsumer consumer) {
+ if (consumer == null) throw new NullPointerException();
+ long i = index, f = fence;
+ if (i < f) {
+ index = f;
+ long o = origin, b = bound;
+ ThreadLocalRandom rng = ThreadLocalRandom.current();
+ do {
+ consumer.accept(rng.internalNextLong(o, b));
+ } while (++i < f);
+ }
+ }
+
+ }
+
+ /**
+ * Spliterator for double streams.
+ */
+ private static final class RandomDoublesSpliterator
+ implements Spliterator.OfDouble {
+ long index;
+ final long fence;
+ final double origin;
+ final double bound;
+ RandomDoublesSpliterator(long index, long fence,
+ double origin, double bound) {
+ this.index = index; this.fence = fence;
+ this.origin = origin; this.bound = bound;
+ }
+
+ public RandomDoublesSpliterator trySplit() {
+ long i = index, m = (i + fence) >>> 1;
+ return (m <= i) ? null :
+ new RandomDoublesSpliterator(i, index = m, origin, bound);
+ }
+
+ public long estimateSize() {
+ return fence - index;
+ }
+
+ public int characteristics() {
+ return (Spliterator.SIZED | Spliterator.SUBSIZED |
+ Spliterator.NONNULL | Spliterator.IMMUTABLE);
+ }
+
+ public boolean tryAdvance(DoubleConsumer consumer) {
+ if (consumer == null) throw new NullPointerException();
+ long i = index, f = fence;
+ if (i < f) {
+ consumer.accept(ThreadLocalRandom.current().internalNextDouble(origin, bound));
+ index = i + 1;
+ return true;
+ }
+ return false;
+ }
+
+ public void forEachRemaining(DoubleConsumer consumer) {
+ if (consumer == null) throw new NullPointerException();
+ long i = index, f = fence;
+ if (i < f) {
+ index = f;
+ double o = origin, b = bound;
+ ThreadLocalRandom rng = ThreadLocalRandom.current();
+ do {
+ consumer.accept(rng.internalNextDouble(o, b));
+ } while (++i < f);
+ }
+ }
+ }
+
+
+ // Within-package utilities
+
+ /*
+ * Descriptions of the usages of the methods below can be found in
+ * the classes that use them. Briefly, a thread's "probe" value is
+ * a non-zero hash code that (probably) does not collide with
+ * other existing threads with respect to any power of two
+ * collision space. When it does collide, it is pseudo-randomly
+ * adjusted (using a Marsaglia XorShift). The nextSecondarySeed
+ * method is used in the same contexts as ThreadLocalRandom, but
+ * only for transient usages such as random adaptive spin/block
+ * sequences for which a cheap RNG suffices and for which it could
+ * in principle disrupt user-visible statistical properties of the
+ * main ThreadLocalRandom if we were to use it.
+ *
+ * Note: Because of package-protection issues, versions of some
+ * these methods also appear in some subpackage classes.
+ */
+
+ /**
+ * Returns the probe value for the current thread without forcing
+ * initialization. Note that invoking ThreadLocalRandom.current()
+ * can be used to force initialization on zero return.
+ */
+ static final int getProbe() {
+ return U.getInt(Thread.currentThread(), PROBE);
+ }
+
+ /**
+ * Pseudo-randomly advances and records the given probe value for the
+ * given thread.
+ */
+ static final int advanceProbe(int probe) {
+ probe ^= probe << 13; // xorshift
+ probe ^= probe >>> 17;
+ probe ^= probe << 5;
+ U.putInt(Thread.currentThread(), PROBE, probe);
+ return probe;
+ }
+
+ /**
+ * Returns the pseudo-randomly initialized or updated secondary seed.
+ */
+ static final int nextSecondarySeed() {
+ int r;
+ Thread t = Thread.currentThread();
+ if ((r = U.getInt(t, SECONDARY)) != 0) {
+ r ^= r << 13; // xorshift
+ r ^= r >>> 17;
+ r ^= r << 5;
+ }
+ else if ((r = mix32(seeder.getAndAdd(SEEDER_INCREMENT))) == 0)
+ r = 1; // avoid zero
+ U.putInt(t, SECONDARY, r);
+ return r;
+ }
+
+ // Serialization support
+
private static final long serialVersionUID = -5851777807851030925L;
+
+ /**
+ * @serialField rnd long
+ * seed for random computations
+ * @serialField initialized boolean
+ * always true
+ */
+ private static final ObjectStreamField[] serialPersistentFields = {
+ new ObjectStreamField("rnd", long.class),
+ new ObjectStreamField("initialized", boolean.class),
+ };
+
+ /**
+ * Saves the {@code ThreadLocalRandom} to a stream (that is, serializes it).
+ * @param s the stream
+ * @throws java.io.IOException if an I/O error occurs
+ */
+ private void writeObject(java.io.ObjectOutputStream s)
+ throws java.io.IOException {
+
+ java.io.ObjectOutputStream.PutField fields = s.putFields();
+ fields.put("rnd", U.getLong(Thread.currentThread(), SEED));
+ fields.put("initialized", true);
+ s.writeFields();
+ }
+
+ /**
+ * Returns the {@link #current() current} thread's {@code ThreadLocalRandom}.
+ * @return the {@link #current() current} thread's {@code ThreadLocalRandom}
+ */
+ private Object readResolve() {
+ return current();
+ }
+
+ // Static initialization
+
+ /**
+ * The seed increment.
+ */
+ private static final long GAMMA = 0x9e3779b97f4a7c15L;
+
+ /**
+ * The increment for generating probe values.
+ */
+ private static final int PROBE_INCREMENT = 0x9e3779b9;
+
+ /**
+ * The increment of seeder per new instance.
+ */
+ private static final long SEEDER_INCREMENT = 0xbb67ae8584caa73bL;
+
+ // Constants from SplittableRandom
+ private static final double DOUBLE_UNIT = 0x1.0p-53; // 1.0 / (1L << 53)
+ private static final float FLOAT_UNIT = 0x1.0p-24f; // 1.0f / (1 << 24)
+
+ // IllegalArgumentException messages
+ static final String BAD_BOUND = "bound must be positive";
+ static final String BAD_RANGE = "bound must be greater than origin";
+ static final String BAD_SIZE = "size must be non-negative";
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long SEED;
+ private static final long PROBE;
+ private static final long SECONDARY;
+ static {
+ try {
+ SEED = U.objectFieldOffset
+ (Thread.class.getDeclaredField("threadLocalRandomSeed"));
+ PROBE = U.objectFieldOffset
+ (Thread.class.getDeclaredField("threadLocalRandomProbe"));
+ SECONDARY = U.objectFieldOffset
+ (Thread.class.getDeclaredField("threadLocalRandomSecondarySeed"));
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
+ }
+
+ /** Rarely-used holder for the second of a pair of Gaussians */
+ private static final ThreadLocal<Double> nextLocalGaussian =
+ new ThreadLocal<>();
+
+ /** Generates per-thread initialization/probe field */
+ private static final AtomicInteger probeGenerator = new AtomicInteger();
+
+ /** The common ThreadLocalRandom */
+ static final ThreadLocalRandom instance = new ThreadLocalRandom();
+
+ /**
+ * The next seed for default constructors.
+ */
+ private static final AtomicLong seeder
+ = new AtomicLong(mix64(System.currentTimeMillis()) ^
+ mix64(System.nanoTime()));
+
+ // at end of <clinit> to survive static initialization circularity
+ static {
+ if (java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<Boolean>() {
+ public Boolean run() {
+ return Boolean.getBoolean("java.util.secureRandomSeed");
+ }})) {
+ byte[] seedBytes = java.security.SecureRandom.getSeed(8);
+ long s = (long)seedBytes[0] & 0xffL;
+ for (int i = 1; i < 8; ++i)
+ s = (s << 8) | ((long)seedBytes[i] & 0xffL);
+ seeder.set(s);
+ }
+ }
}
diff --git a/luni/src/main/java/java/util/concurrent/ThreadPoolExecutor.java b/luni/src/main/java/java/util/concurrent/ThreadPoolExecutor.java
index c484920..e601b6a 100644
--- a/luni/src/main/java/java/util/concurrent/ThreadPoolExecutor.java
+++ b/luni/src/main/java/java/util/concurrent/ThreadPoolExecutor.java
@@ -6,11 +6,15 @@
package java.util.concurrent;
+import java.util.ArrayList;
+import java.util.ConcurrentModificationException;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.*;
// BEGIN android-note
// removed security manager docs
@@ -45,7 +49,8 @@
*
* <dt>Core and maximum pool sizes</dt>
*
- * <dd>A {@code ThreadPoolExecutor} will automatically adjust the
+ * <dd style="font-family:'DejaVu Sans', Arial, Helvetica, sans-serif">
+ * A {@code ThreadPoolExecutor} will automatically adjust the
* pool size (see {@link #getPoolSize})
* according to the bounds set by
* corePoolSize (see {@link #getCorePoolSize}) and
@@ -67,7 +72,8 @@
*
* <dt>On-demand construction</dt>
*
- * <dd>By default, even core threads are initially created and
+ * <dd style="font-family:'DejaVu Sans', Arial, Helvetica, sans-serif">
+ * By default, even core threads are initially created and
* started only when new tasks arrive, but this can be overridden
* dynamically using method {@link #prestartCoreThread} or {@link
* #prestartAllCoreThreads}. You probably want to prestart threads if
@@ -75,7 +81,8 @@
*
* <dt>Creating new threads</dt>
*
- * <dd>New threads are created using a {@link ThreadFactory}. If not
+ * <dd style="font-family:'DejaVu Sans', Arial, Helvetica, sans-serif">
+ * New threads are created using a {@link ThreadFactory}. If not
* otherwise specified, a {@link Executors#defaultThreadFactory} is
* used, that creates threads to all be in the same {@link
* ThreadGroup} and with the same {@code NORM_PRIORITY} priority and
@@ -83,11 +90,17 @@
* alter the thread's name, thread group, priority, daemon status,
* etc. If a {@code ThreadFactory} fails to create a thread when asked
* by returning null from {@code newThread}, the executor will
- * continue, but might not be able to execute any tasks.</dd>
+ * continue, but might not be able to execute any tasks. Threads
+ * should possess the "modifyThread" {@code RuntimePermission}. If
+ * worker threads or other threads using the pool do not possess this
+ * permission, service may be degraded: configuration changes may not
+ * take effect in a timely manner, and a shutdown pool may remain in a
+ * state in which termination is possible but not completed.</dd>
*
* <dt>Keep-alive times</dt>
*
- * <dd>If the pool currently has more than corePoolSize threads,
+ * <dd style="font-family:'DejaVu Sans', Arial, Helvetica, sans-serif">
+ * If the pool currently has more than corePoolSize threads,
* excess threads will be terminated if they have been idle for more
* than the keepAliveTime (see {@link #getKeepAliveTime(TimeUnit)}).
* This provides a means of reducing resource consumption when the
@@ -97,36 +110,37 @@
* TimeUnit)}. Using a value of {@code Long.MAX_VALUE} {@link
* TimeUnit#NANOSECONDS} effectively disables idle threads from ever
* terminating prior to shut down. By default, the keep-alive policy
- * applies only when there are more than corePoolSize threads. But
+ * applies only when there are more than corePoolSize threads, but
* method {@link #allowCoreThreadTimeOut(boolean)} can be used to
* apply this time-out policy to core threads as well, so long as the
* keepAliveTime value is non-zero. </dd>
*
* <dt>Queuing</dt>
*
- * <dd>Any {@link BlockingQueue} may be used to transfer and hold
+ * <dd style="font-family:'DejaVu Sans', Arial, Helvetica, sans-serif">
+ * Any {@link BlockingQueue} may be used to transfer and hold
* submitted tasks. The use of this queue interacts with pool sizing:
*
* <ul>
*
- * <li> If fewer than corePoolSize threads are running, the Executor
+ * <li>If fewer than corePoolSize threads are running, the Executor
* always prefers adding a new thread
- * rather than queuing.</li>
+ * rather than queuing.
*
- * <li> If corePoolSize or more threads are running, the Executor
+ * <li>If corePoolSize or more threads are running, the Executor
* always prefers queuing a request rather than adding a new
- * thread.</li>
+ * thread.
*
- * <li> If a request cannot be queued, a new thread is created unless
+ * <li>If a request cannot be queued, a new thread is created unless
* this would exceed maximumPoolSize, in which case, the task will be
- * rejected.</li>
+ * rejected.
*
* </ul>
*
* There are three general strategies for queuing:
* <ol>
*
- * <li> <em> Direct handoffs.</em> A good default choice for a work
+ * <li><em> Direct handoffs.</em> A good default choice for a work
* queue is a {@link SynchronousQueue} that hands off tasks to threads
* without otherwise holding them. Here, an attempt to queue a task
* will fail if no threads are immediately available to run it, so a
@@ -135,7 +149,7 @@
* Direct handoffs generally require unbounded maximumPoolSizes to
* avoid rejection of new submitted tasks. This in turn admits the
* possibility of unbounded thread growth when commands continue to
- * arrive on average faster than they can be processed. </li>
+ * arrive on average faster than they can be processed.
*
* <li><em> Unbounded queues.</em> Using an unbounded queue (for
* example a {@link LinkedBlockingQueue} without a predefined
@@ -148,7 +162,7 @@
* While this style of queuing can be useful in smoothing out
* transient bursts of requests, it admits the possibility of
* unbounded work queue growth when commands continue to arrive on
- * average faster than they can be processed. </li>
+ * average faster than they can be processed.
*
* <li><em>Bounded queues.</em> A bounded queue (for example, an
* {@link ArrayBlockingQueue}) helps prevent resource exhaustion when
@@ -161,7 +175,7 @@
* time for more threads than you otherwise allow. Use of small queues
* generally requires larger pool sizes, which keeps CPUs busier but
* may encounter unacceptable scheduling overhead, which also
- * decreases throughput. </li>
+ * decreases throughput.
*
* </ol>
*
@@ -169,7 +183,8 @@
*
* <dt>Rejected tasks</dt>
*
- * <dd>New tasks submitted in method {@link #execute(Runnable)} will be
+ * <dd style="font-family:'DejaVu Sans', Arial, Helvetica, sans-serif">
+ * New tasks submitted in method {@link #execute(Runnable)} will be
* <em>rejected</em> when the Executor has been shut down, and also when
* the Executor uses finite bounds for both maximum threads and work queue
* capacity, and is saturated. In either case, the {@code execute} method
@@ -180,22 +195,22 @@
*
* <ol>
*
- * <li> In the default {@link ThreadPoolExecutor.AbortPolicy}, the
+ * <li>In the default {@link ThreadPoolExecutor.AbortPolicy}, the
* handler throws a runtime {@link RejectedExecutionException} upon
- * rejection. </li>
+ * rejection.
*
- * <li> In {@link ThreadPoolExecutor.CallerRunsPolicy}, the thread
+ * <li>In {@link ThreadPoolExecutor.CallerRunsPolicy}, the thread
* that invokes {@code execute} itself runs the task. This provides a
* simple feedback control mechanism that will slow down the rate that
- * new tasks are submitted. </li>
+ * new tasks are submitted.
*
- * <li> In {@link ThreadPoolExecutor.DiscardPolicy}, a task that
- * cannot be executed is simply dropped. </li>
+ * <li>In {@link ThreadPoolExecutor.DiscardPolicy}, a task that
+ * cannot be executed is simply dropped.
*
* <li>In {@link ThreadPoolExecutor.DiscardOldestPolicy}, if the
* executor is not shut down, the task at the head of the work queue
* is dropped, and then execution is retried (which can fail again,
- * causing this to be repeated.) </li>
+ * causing this to be repeated.)
*
* </ol>
*
@@ -206,7 +221,8 @@
*
* <dt>Hook methods</dt>
*
- * <dd>This class provides {@code protected} overridable
+ * <dd style="font-family:'DejaVu Sans', Arial, Helvetica, sans-serif">
+ * This class provides {@code protected} overridable
* {@link #beforeExecute(Thread, Runnable)} and
* {@link #afterExecute(Runnable, Throwable)} methods that are called
* before and after execution of each task. These can be used to
@@ -216,12 +232,14 @@
* any special processing that needs to be done once the Executor has
* fully terminated.
*
- * <p>If hook or callback methods throw exceptions, internal worker
- * threads may in turn fail and abruptly terminate.</dd>
+ * <p>If hook, callback, or BlockingQueue methods throw exceptions,
+ * internal worker threads may in turn fail, abruptly terminate, and
+ * possibly be replaced.</dd>
*
* <dt>Queue maintenance</dt>
*
- * <dd>Method {@link #getQueue()} allows access to the work queue
+ * <dd style="font-family:'DejaVu Sans', Arial, Helvetica, sans-serif">
+ * Method {@link #getQueue()} allows access to the work queue
* for purposes of monitoring and debugging. Use of this method for
* any other purpose is strongly discouraged. Two supplied methods,
* {@link #remove(Runnable)} and {@link #purge} are available to
@@ -230,7 +248,8 @@
*
* <dt>Finalization</dt>
*
- * <dd>A pool that is no longer referenced in a program <em>AND</em>
+ * <dd style="font-family:'DejaVu Sans', Arial, Helvetica, sans-serif">
+ * A pool that is no longer referenced in a program <em>AND</em>
* has no remaining threads will be {@code shutdown} automatically. If
* you would like to ensure that unreferenced pools are reclaimed even
* if users forget to call {@link #shutdown}, then you must arrange
@@ -244,7 +263,7 @@
* override one or more of the protected hook methods. For example,
* here is a subclass that adds a simple pause/resume feature:
*
- * <pre> {@code
+ * <pre> {@code
* class PausableThreadPoolExecutor extends ThreadPoolExecutor {
* private boolean isPaused;
* private ReentrantLock pauseLock = new ReentrantLock();
@@ -433,10 +452,10 @@
* Set containing all worker threads in pool. Accessed only when
* holding mainLock.
*/
- private final HashSet<Worker> workers = new HashSet<Worker>();
+ private final HashSet<Worker> workers = new HashSet<>();
/**
- * Wait condition to support awaitTermination
+ * Wait condition to support awaitTermination.
*/
private final Condition termination = mainLock.newCondition();
@@ -512,7 +531,7 @@
private volatile int maximumPoolSize;
/**
- * The default rejected execution handler
+ * The default rejected execution handler.
*/
private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();
@@ -639,6 +658,7 @@
* (but not TIDYING or TERMINATED -- use tryTerminate for that)
*/
private void advanceRunState(int targetState) {
+ // assert targetState == SHUTDOWN || targetState == STOP;
for (;;) {
int c = ctl.get();
if (runStateAtLeast(c, targetState) ||
@@ -821,7 +841,7 @@
*/
private List<Runnable> drainQueue() {
BlockingQueue<Runnable> q = workQueue;
- ArrayList<Runnable> taskList = new ArrayList<Runnable>();
+ ArrayList<Runnable> taskList = new ArrayList<>();
q.drainTo(taskList);
if (!q.isEmpty()) {
for (Runnable r : q.toArray(new Runnable[0])) {
@@ -1376,7 +1396,7 @@
*
* <p>There are no guarantees beyond best-effort attempts to stop
* processing actively executing tasks. This implementation
- * cancels tasks via {@link Thread#interrupt}, so any task that
+ * interrupts tasks via {@link Thread#interrupt}; any task that
* fails to respond to interrupts may never terminate.
*/
// android-note: Removed @throws SecurityException
@@ -1426,13 +1446,12 @@
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
- for (;;) {
- if (runStateAtLeast(ctl.get(), TERMINATED))
- return true;
- if (nanos <= 0)
+ while (!runStateAtLeast(ctl.get(), TERMINATED)) {
+ if (nanos <= 0L)
return false;
nanos = termination.awaitNanos(nanos);
}
+ return true;
} finally {
mainLock.unlock();
}
@@ -1501,10 +1520,12 @@
*
* @param corePoolSize the new core size
* @throws IllegalArgumentException if {@code corePoolSize < 0}
+ * or {@code corePoolSize} is greater than the {@linkplain
+ * #getMaximumPoolSize() maximum pool size}
* @see #getCorePoolSize
*/
public void setCorePoolSize(int corePoolSize) {
- if (corePoolSize < 0)
+ if (corePoolSize < 0 || maximumPoolSize < corePoolSize)
throw new IllegalArgumentException();
int delta = corePoolSize - this.corePoolSize;
this.corePoolSize = corePoolSize;
@@ -1647,11 +1668,13 @@
}
/**
- * Sets the time limit for which threads may remain idle before
- * being terminated. If there are more than the core number of
- * threads currently in the pool, after waiting this amount of
- * time without processing a task, excess threads will be
- * terminated. This overrides any value set in the constructor.
+ * Sets the thread keep-alive time, which is the amount of time
+ * that threads may remain idle before being terminated.
+ * Threads that wait this amount of time without processing a
+ * task will be terminated if there are more than the core
+ * number of threads currently in the pool, or if this pool
+ * {@linkplain #allowsCoreThreadTimeOut() allows core thread timeout}.
+ * This overrides any value set in the constructor.
*
* @param time the time to wait. A time value of zero will cause
* excess threads to terminate immediately after executing tasks.
@@ -1674,8 +1697,11 @@
/**
* Returns the thread keep-alive time, which is the amount of time
- * that threads in excess of the core pool size may remain
- * idle before being terminated.
+ * that threads may remain idle before being terminated.
+ * Threads that wait this amount of time without processing a
+ * task will be terminated if there are more than the core
+ * number of threads currently in the pool, or if this pool
+ * {@linkplain #allowsCoreThreadTimeOut() allows core thread timeout}.
*
* @param unit the desired time unit of the result
* @return the time limit
@@ -1706,8 +1732,8 @@
*
* <p>This method may be useful as one part of a cancellation
* scheme. It may fail to remove tasks that have been converted
- * into other forms before being placed on the internal queue. For
- * example, a task entered using {@code submit} might be
+ * into other forms before being placed on the internal queue.
+ * For example, a task entered using {@code submit} might be
* converted into a form that maintains {@code Future} status.
* However, in such cases, method {@link #purge} may be used to
* remove those Futures that have been cancelled.
@@ -1879,11 +1905,12 @@
mainLock.unlock();
}
int c = ctl.get();
- String rs = (runStateLessThan(c, SHUTDOWN) ? "Running" :
- (runStateAtLeast(c, TERMINATED) ? "Terminated" :
- "Shutting down"));
+ String runState =
+ runStateLessThan(c, SHUTDOWN) ? "Running" :
+ runStateAtLeast(c, TERMINATED) ? "Terminated" :
+ "Shutting down";
return super.toString() +
- "[" + rs +
+ "[" + runState +
", pool size = " + nworkers +
", active threads = " + nactive +
", queued tasks = " + workQueue.size() +
@@ -1930,20 +1957,23 @@
* as in this sample subclass that prints either the direct cause
* or the underlying exception if a task has been aborted:
*
- * <pre> {@code
+ * <pre> {@code
* class ExtendedExecutor extends ThreadPoolExecutor {
* // ...
* protected void afterExecute(Runnable r, Throwable t) {
* super.afterExecute(r, t);
- * if (t == null && r instanceof Future<?>) {
+ * if (t == null
+ * && r instanceof Future<?>
+ * && ((Future<?>)r).isDone()) {
* try {
* Object result = ((Future<?>) r).get();
* } catch (CancellationException ce) {
- * t = ce;
+ * t = ce;
* } catch (ExecutionException ee) {
- * t = ee.getCause();
+ * t = ee.getCause();
* } catch (InterruptedException ie) {
- * Thread.currentThread().interrupt(); // ignore/reset
+ * // ignore/reset
+ * Thread.currentThread().interrupt();
* }
* }
* if (t != null)
diff --git a/luni/src/main/java/java/util/concurrent/TimeUnit.java b/luni/src/main/java/java/util/concurrent/TimeUnit.java
index 8e6a5f7..fff02d8 100644
--- a/luni/src/main/java/java/util/concurrent/TimeUnit.java
+++ b/luni/src/main/java/java/util/concurrent/TimeUnit.java
@@ -6,6 +6,12 @@
package java.util.concurrent;
+import java.util.Objects;
+
+// BEGIN android-note
+// removed java 9 ChronoUnit related code
+// END android-note
+
/**
* A {@code TimeUnit} represents time durations at a given unit of
* granularity and provides utility methods to convert across units,
@@ -23,12 +29,12 @@
* the following code will timeout in 50 milliseconds if the {@link
* java.util.concurrent.locks.Lock lock} is not available:
*
- * <pre> {@code
+ * <pre> {@code
* Lock lock = ...;
* if (lock.tryLock(50L, TimeUnit.MILLISECONDS)) ...}</pre>
*
* while this code will timeout in 50 seconds:
- * <pre> {@code
+ * <pre> {@code
* Lock lock = ...;
* if (lock.tryLock(50L, TimeUnit.SECONDS)) ...}</pre>
*
@@ -41,7 +47,7 @@
*/
public enum TimeUnit {
/**
- * Time unit representing one thousandth of a microsecond
+ * Time unit representing one thousandth of a microsecond.
*/
NANOSECONDS {
public long toNanos(long d) { return d; }
@@ -56,7 +62,7 @@
},
/**
- * Time unit representing one thousandth of a millisecond
+ * Time unit representing one thousandth of a millisecond.
*/
MICROSECONDS {
public long toNanos(long d) { return x(d, C1/C0, MAX/(C1/C0)); }
@@ -71,7 +77,7 @@
},
/**
- * Time unit representing one thousandth of a second
+ * Time unit representing one thousandth of a second.
*/
MILLISECONDS {
public long toNanos(long d) { return x(d, C2/C0, MAX/(C2/C0)); }
@@ -86,7 +92,7 @@
},
/**
- * Time unit representing one second
+ * Time unit representing one second.
*/
SECONDS {
public long toNanos(long d) { return x(d, C3/C0, MAX/(C3/C0)); }
@@ -101,7 +107,7 @@
},
/**
- * Time unit representing sixty seconds
+ * Time unit representing sixty seconds.
* @since 1.6
*/
MINUTES {
@@ -117,7 +123,7 @@
},
/**
- * Time unit representing sixty minutes
+ * Time unit representing sixty minutes.
* @since 1.6
*/
HOURS {
@@ -133,7 +139,7 @@
},
/**
- * Time unit representing twenty four hours
+ * Time unit representing twenty four hours.
* @since 1.6
*/
DAYS {
@@ -164,7 +170,7 @@
* This has a short name to make above code more readable.
*/
static long x(long d, long m, long over) {
- if (d > over) return Long.MAX_VALUE;
+ if (d > +over) return Long.MAX_VALUE;
if (d < -over) return Long.MIN_VALUE;
return d * m;
}
@@ -300,7 +306,7 @@
* method (see {@link BlockingQueue#poll BlockingQueue.poll})
* using:
*
- * <pre> {@code
+ * <pre> {@code
* public synchronized Object poll(long timeout, TimeUnit unit)
* throws InterruptedException {
* while (empty) {
@@ -360,5 +366,4 @@
Thread.sleep(ms, ns);
}
}
-
}
diff --git a/luni/src/main/java/java/util/concurrent/TransferQueue.java b/luni/src/main/java/java/util/concurrent/TransferQueue.java
index 4c2be6f..d4166b5 100644
--- a/luni/src/main/java/java/util/concurrent/TransferQueue.java
+++ b/luni/src/main/java/java/util/concurrent/TransferQueue.java
@@ -34,7 +34,7 @@
*
* @since 1.7
* @author Doug Lea
- * @param <E> the type of elements held in this collection
+ * @param <E> the type of elements held in this queue
*/
public interface TransferQueue<E> extends BlockingQueue<E> {
/**
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java
index f51e6af..01e4b07 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java
@@ -6,8 +6,6 @@
package java.util.concurrent.atomic;
-import sun.misc.Unsafe;
-
/**
* A {@code boolean} value that may be updated atomically. See the
* {@link java.util.concurrent.atomic} package specification for
@@ -21,15 +19,17 @@
*/
public class AtomicBoolean implements java.io.Serializable {
private static final long serialVersionUID = 4654671469794556979L;
- // setup to use Unsafe.compareAndSwapInt for updates
- private static final Unsafe unsafe = Unsafe.getUnsafe();
- private static final long valueOffset;
+
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long VALUE;
static {
try {
- valueOffset = unsafe.objectFieldOffset
+ VALUE = U.objectFieldOffset
(AtomicBoolean.class.getDeclaredField("value"));
- } catch (Exception ex) { throw new Error(ex); }
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
}
private volatile int value;
@@ -64,13 +64,13 @@
*
* @param expect the expected value
* @param update the new value
- * @return true if successful. False return indicates that
+ * @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(boolean expect, boolean update) {
- int e = expect ? 1 : 0;
- int u = update ? 1 : 0;
- return unsafe.compareAndSwapInt(this, valueOffset, e, u);
+ return U.compareAndSwapInt(this, VALUE,
+ (expect ? 1 : 0),
+ (update ? 1 : 0));
}
/**
@@ -83,12 +83,12 @@
*
* @param expect the expected value
* @param update the new value
- * @return true if successful
+ * @return {@code true} if successful
*/
public boolean weakCompareAndSet(boolean expect, boolean update) {
- int e = expect ? 1 : 0;
- int u = update ? 1 : 0;
- return unsafe.compareAndSwapInt(this, valueOffset, e, u);
+ return U.compareAndSwapInt(this, VALUE,
+ (expect ? 1 : 0),
+ (update ? 1 : 0));
}
/**
@@ -107,8 +107,7 @@
* @since 1.6
*/
public final void lazySet(boolean newValue) {
- int v = newValue ? 1 : 0;
- unsafe.putOrderedInt(this, valueOffset, v);
+ U.putOrderedInt(this, VALUE, (newValue ? 1 : 0));
}
/**
@@ -118,11 +117,11 @@
* @return the previous value
*/
public final boolean getAndSet(boolean newValue) {
- for (;;) {
- boolean current = get();
- if (compareAndSet(current, newValue))
- return current;
- }
+ boolean prev;
+ do {
+ prev = get();
+ } while (!compareAndSet(prev, newValue));
+ return prev;
}
/**
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicInteger.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicInteger.java
index 8a15298..849fd8a 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicInteger.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicInteger.java
@@ -6,7 +6,8 @@
package java.util.concurrent.atomic;
-import sun.misc.Unsafe;
+import java.util.function.IntBinaryOperator;
+import java.util.function.IntUnaryOperator;
/**
* An {@code int} value that may be updated atomically. See the
@@ -20,19 +21,20 @@
*
* @since 1.5
* @author Doug Lea
-*/
+ */
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
- // setup to use Unsafe.compareAndSwapInt for updates
- private static final Unsafe unsafe = Unsafe.getUnsafe();
- private static final long valueOffset;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long VALUE;
static {
try {
- valueOffset = unsafe.objectFieldOffset
+ VALUE = U.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
- } catch (Exception ex) { throw new Error(ex); }
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
}
private volatile int value;
@@ -77,7 +79,7 @@
* @since 1.6
*/
public final void lazySet(int newValue) {
- unsafe.putOrderedInt(this, valueOffset, newValue);
+ U.putOrderedInt(this, VALUE, newValue);
}
/**
@@ -87,11 +89,7 @@
* @return the previous value
*/
public final int getAndSet(int newValue) {
- for (;;) {
- int current = get();
- if (compareAndSet(current, newValue))
- return current;
- }
+ return U.getAndSetInt(this, VALUE, newValue);
}
/**
@@ -100,11 +98,11 @@
*
* @param expect the expected value
* @param update the new value
- * @return true if successful. False return indicates that
+ * @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
- return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
+ return U.compareAndSwapInt(this, VALUE, expect, update);
}
/**
@@ -117,10 +115,10 @@
*
* @param expect the expected value
* @param update the new value
- * @return true if successful
+ * @return {@code true} if successful
*/
public final boolean weakCompareAndSet(int expect, int update) {
- return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
+ return U.compareAndSwapInt(this, VALUE, expect, update);
}
/**
@@ -129,12 +127,7 @@
* @return the previous value
*/
public final int getAndIncrement() {
- for (;;) {
- int current = get();
- int next = current + 1;
- if (compareAndSet(current, next))
- return current;
- }
+ return U.getAndAddInt(this, VALUE, 1);
}
/**
@@ -143,12 +136,7 @@
* @return the previous value
*/
public final int getAndDecrement() {
- for (;;) {
- int current = get();
- int next = current - 1;
- if (compareAndSet(current, next))
- return current;
- }
+ return U.getAndAddInt(this, VALUE, -1);
}
/**
@@ -158,12 +146,7 @@
* @return the previous value
*/
public final int getAndAdd(int delta) {
- for (;;) {
- int current = get();
- int next = current + delta;
- if (compareAndSet(current, next))
- return current;
- }
+ return U.getAndAddInt(this, VALUE, delta);
}
/**
@@ -172,12 +155,7 @@
* @return the updated value
*/
public final int incrementAndGet() {
- for (;;) {
- int current = get();
- int next = current + 1;
- if (compareAndSet(current, next))
- return next;
- }
+ return U.getAndAddInt(this, VALUE, 1) + 1;
}
/**
@@ -186,12 +164,7 @@
* @return the updated value
*/
public final int decrementAndGet() {
- for (;;) {
- int current = get();
- int next = current - 1;
- if (compareAndSet(current, next))
- return next;
- }
+ return U.getAndAddInt(this, VALUE, -1) - 1;
}
/**
@@ -201,12 +174,93 @@
* @return the updated value
*/
public final int addAndGet(int delta) {
- for (;;) {
- int current = get();
- int next = current + delta;
- if (compareAndSet(current, next))
- return next;
- }
+ return U.getAndAddInt(this, VALUE, delta) + delta;
+ }
+
+ /**
+ * Atomically updates the current value with the results of
+ * applying the given function, returning the previous value. The
+ * function should be side-effect-free, since it may be re-applied
+ * when attempted updates fail due to contention among threads.
+ *
+ * @param updateFunction a side-effect-free function
+ * @return the previous value
+ * @since 1.8
+ */
+ public final int getAndUpdate(IntUnaryOperator updateFunction) {
+ int prev, next;
+ do {
+ prev = get();
+ next = updateFunction.applyAsInt(prev);
+ } while (!compareAndSet(prev, next));
+ return prev;
+ }
+
+ /**
+ * Atomically updates the current value with the results of
+ * applying the given function, returning the updated value. The
+ * function should be side-effect-free, since it may be re-applied
+ * when attempted updates fail due to contention among threads.
+ *
+ * @param updateFunction a side-effect-free function
+ * @return the updated value
+ * @since 1.8
+ */
+ public final int updateAndGet(IntUnaryOperator updateFunction) {
+ int prev, next;
+ do {
+ prev = get();
+ next = updateFunction.applyAsInt(prev);
+ } while (!compareAndSet(prev, next));
+ return next;
+ }
+
+ /**
+ * Atomically updates the current value with the results of
+ * applying the given function to the current and given values,
+ * returning the previous value. The function should be
+ * side-effect-free, since it may be re-applied when attempted
+ * updates fail due to contention among threads. The function
+ * is applied with the current value as its first argument,
+ * and the given update as the second argument.
+ *
+ * @param x the update value
+ * @param accumulatorFunction a side-effect-free function of two arguments
+ * @return the previous value
+ * @since 1.8
+ */
+ public final int getAndAccumulate(int x,
+ IntBinaryOperator accumulatorFunction) {
+ int prev, next;
+ do {
+ prev = get();
+ next = accumulatorFunction.applyAsInt(prev, x);
+ } while (!compareAndSet(prev, next));
+ return prev;
+ }
+
+ /**
+ * Atomically updates the current value with the results of
+ * applying the given function to the current and given values,
+ * returning the updated value. The function should be
+ * side-effect-free, since it may be re-applied when attempted
+ * updates fail due to contention among threads. The function
+ * is applied with the current value as its first argument,
+ * and the given update as the second argument.
+ *
+ * @param x the update value
+ * @param accumulatorFunction a side-effect-free function of two arguments
+ * @return the updated value
+ * @since 1.8
+ */
+ public final int accumulateAndGet(int x,
+ IntBinaryOperator accumulatorFunction) {
+ int prev, next;
+ do {
+ prev = get();
+ next = accumulatorFunction.applyAsInt(prev, x);
+ } while (!compareAndSet(prev, next));
+ return next;
}
/**
@@ -219,6 +273,7 @@
/**
* Returns the value of this {@code AtomicInteger} as an {@code int}.
+ * Equivalent to {@link #get()}.
*/
public int intValue() {
return get();
@@ -227,6 +282,7 @@
/**
* Returns the value of this {@code AtomicInteger} as a {@code long}
* after a widening primitive conversion.
+ * @jls 5.1.2 Widening Primitive Conversions
*/
public long longValue() {
return (long)get();
@@ -235,6 +291,7 @@
/**
* Returns the value of this {@code AtomicInteger} as a {@code float}
* after a widening primitive conversion.
+ * @jls 5.1.2 Widening Primitive Conversions
*/
public float floatValue() {
return (float)get();
@@ -243,6 +300,7 @@
/**
* Returns the value of this {@code AtomicInteger} as a {@code double}
* after a widening primitive conversion.
+ * @jls 5.1.2 Widening Primitive Conversions
*/
public double doubleValue() {
return (double)get();
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java
index fd492d1..7597e53 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java
@@ -6,7 +6,8 @@
package java.util.concurrent.atomic;
-import sun.misc.Unsafe;
+import java.util.function.IntBinaryOperator;
+import java.util.function.IntUnaryOperator;
/**
* An {@code int} array in which elements may be updated atomically.
@@ -19,16 +20,17 @@
public class AtomicIntegerArray implements java.io.Serializable {
private static final long serialVersionUID = 2862133569453604235L;
- private static final Unsafe unsafe = Unsafe.getUnsafe();
- private static final int base = unsafe.arrayBaseOffset(int[].class);
- private static final int shift;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final int ABASE;
+ private static final int ASHIFT;
private final int[] array;
static {
- int scale = unsafe.arrayIndexScale(int[].class);
+ ABASE = U.arrayBaseOffset(int[].class);
+ int scale = U.arrayIndexScale(int[].class);
if ((scale & (scale - 1)) != 0)
- throw new Error("data type scale not a power of two");
- shift = 31 - Integer.numberOfLeadingZeros(scale);
+ throw new Error("array index scale not a power of two");
+ ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
}
private long checkedByteOffset(int i) {
@@ -39,7 +41,7 @@
}
private static long byteOffset(int i) {
- return ((long) i << shift) + base;
+ return ((long) i << ASHIFT) + ABASE;
}
/**
@@ -84,7 +86,7 @@
}
private int getRaw(long offset) {
- return unsafe.getIntVolatile(array, offset);
+ return U.getIntVolatile(array, offset);
}
/**
@@ -94,7 +96,7 @@
* @param newValue the new value
*/
public final void set(int i, int newValue) {
- unsafe.putIntVolatile(array, checkedByteOffset(i), newValue);
+ U.putIntVolatile(array, checkedByteOffset(i), newValue);
}
/**
@@ -105,7 +107,7 @@
* @since 1.6
*/
public final void lazySet(int i, int newValue) {
- unsafe.putOrderedInt(array, checkedByteOffset(i), newValue);
+ U.putOrderedInt(array, checkedByteOffset(i), newValue);
}
/**
@@ -117,12 +119,7 @@
* @return the previous value
*/
public final int getAndSet(int i, int newValue) {
- long offset = checkedByteOffset(i);
- while (true) {
- int current = getRaw(offset);
- if (compareAndSetRaw(offset, current, newValue))
- return current;
- }
+ return U.getAndSetInt(array, checkedByteOffset(i), newValue);
}
/**
@@ -132,7 +129,7 @@
* @param i the index
* @param expect the expected value
* @param update the new value
- * @return true if successful. False return indicates that
+ * @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int i, int expect, int update) {
@@ -140,7 +137,7 @@
}
private boolean compareAndSetRaw(long offset, int expect, int update) {
- return unsafe.compareAndSwapInt(array, offset, expect, update);
+ return U.compareAndSwapInt(array, offset, expect, update);
}
/**
@@ -154,7 +151,7 @@
* @param i the index
* @param expect the expected value
* @param update the new value
- * @return true if successful
+ * @return {@code true} if successful
*/
public final boolean weakCompareAndSet(int i, int expect, int update) {
return compareAndSet(i, expect, update);
@@ -188,12 +185,7 @@
* @return the previous value
*/
public final int getAndAdd(int i, int delta) {
- long offset = checkedByteOffset(i);
- while (true) {
- int current = getRaw(offset);
- if (compareAndSetRaw(offset, current, current + delta))
- return current;
- }
+ return U.getAndAddInt(array, checkedByteOffset(i), delta);
}
/**
@@ -203,7 +195,7 @@
* @return the updated value
*/
public final int incrementAndGet(int i) {
- return addAndGet(i, 1);
+ return getAndAdd(i, 1) + 1;
}
/**
@@ -213,7 +205,7 @@
* @return the updated value
*/
public final int decrementAndGet(int i) {
- return addAndGet(i, -1);
+ return getAndAdd(i, -1) - 1;
}
/**
@@ -224,13 +216,101 @@
* @return the updated value
*/
public final int addAndGet(int i, int delta) {
+ return getAndAdd(i, delta) + delta;
+ }
+
+ /**
+ * Atomically updates the element at index {@code i} with the results
+ * of applying the given function, returning the previous value. The
+ * function should be side-effect-free, since it may be re-applied
+ * when attempted updates fail due to contention among threads.
+ *
+ * @param i the index
+ * @param updateFunction a side-effect-free function
+ * @return the previous value
+ * @since 1.8
+ */
+ public final int getAndUpdate(int i, IntUnaryOperator updateFunction) {
long offset = checkedByteOffset(i);
- while (true) {
- int current = getRaw(offset);
- int next = current + delta;
- if (compareAndSetRaw(offset, current, next))
- return next;
- }
+ int prev, next;
+ do {
+ prev = getRaw(offset);
+ next = updateFunction.applyAsInt(prev);
+ } while (!compareAndSetRaw(offset, prev, next));
+ return prev;
+ }
+
+ /**
+ * Atomically updates the element at index {@code i} with the results
+ * of applying the given function, returning the updated value. The
+ * function should be side-effect-free, since it may be re-applied
+ * when attempted updates fail due to contention among threads.
+ *
+ * @param i the index
+ * @param updateFunction a side-effect-free function
+ * @return the updated value
+ * @since 1.8
+ */
+ public final int updateAndGet(int i, IntUnaryOperator updateFunction) {
+ long offset = checkedByteOffset(i);
+ int prev, next;
+ do {
+ prev = getRaw(offset);
+ next = updateFunction.applyAsInt(prev);
+ } while (!compareAndSetRaw(offset, prev, next));
+ return next;
+ }
+
+ /**
+ * Atomically updates the element at index {@code i} with the
+ * results of applying the given function to the current and
+ * given values, returning the previous value. The function should
+ * be side-effect-free, since it may be re-applied when attempted
+ * updates fail due to contention among threads. The function is
+ * applied with the current value at index {@code i} as its first
+ * argument, and the given update as the second argument.
+ *
+ * @param i the index
+ * @param x the update value
+ * @param accumulatorFunction a side-effect-free function of two arguments
+ * @return the previous value
+ * @since 1.8
+ */
+ public final int getAndAccumulate(int i, int x,
+ IntBinaryOperator accumulatorFunction) {
+ long offset = checkedByteOffset(i);
+ int prev, next;
+ do {
+ prev = getRaw(offset);
+ next = accumulatorFunction.applyAsInt(prev, x);
+ } while (!compareAndSetRaw(offset, prev, next));
+ return prev;
+ }
+
+ /**
+ * Atomically updates the element at index {@code i} with the
+ * results of applying the given function to the current and
+ * given values, returning the updated value. The function should
+ * be side-effect-free, since it may be re-applied when attempted
+ * updates fail due to contention among threads. The function is
+ * applied with the current value at index {@code i} as its first
+ * argument, and the given update as the second argument.
+ *
+ * @param i the index
+ * @param x the update value
+ * @param accumulatorFunction a side-effect-free function of two arguments
+ * @return the updated value
+ * @since 1.8
+ */
+ public final int accumulateAndGet(int i, int x,
+ IntBinaryOperator accumulatorFunction) {
+ long offset = checkedByteOffset(i);
+ int prev, next;
+ do {
+ prev = getRaw(offset);
+ next = accumulatorFunction.applyAsInt(prev, x);
+ } while (!compareAndSetRaw(offset, prev, next));
+ return next;
}
/**
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java
index 15e8840..9c55491 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java
@@ -7,12 +7,15 @@
package java.util.concurrent.atomic;
import dalvik.system.VMStack; // android-added
-import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.security.AccessController;
-import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.function.IntBinaryOperator;
+import java.util.function.IntUnaryOperator;
+import sun.reflect.CallerSensitive;
+import sun.reflect.Reflection;
/**
* A reflection-based utility that enables atomic updates to
@@ -49,8 +52,11 @@
* or the field is inaccessible to the caller according to Java language
* access control
*/
- public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass, String fieldName) {
- return new AtomicIntegerFieldUpdaterImpl<U>(tclass, fieldName);
+ @CallerSensitive
+ public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass,
+ String fieldName) {
+ return new AtomicIntegerFieldUpdaterImpl<U>
+ (tclass, fieldName, VMStack.getStackClass1()); // android-changed
}
/**
@@ -69,7 +75,7 @@
* @param obj An object whose field to conditionally set
* @param expect the expected value
* @param update the new value
- * @return true if successful
+ * @return {@code true} if successful
* @throws ClassCastException if {@code obj} is not an instance
* of the class possessing the field established in the constructor
*/
@@ -89,7 +95,7 @@
* @param obj An object whose field to conditionally set
* @param expect the expected value
* @param update the new value
- * @return true if successful
+ * @return {@code true} if successful
* @throws ClassCastException if {@code obj} is not an instance
* of the class possessing the field established in the constructor
*/
@@ -133,11 +139,11 @@
* @return the previous value
*/
public int getAndSet(T obj, int newValue) {
- for (;;) {
- int current = get(obj);
- if (compareAndSet(obj, current, newValue))
- return current;
- }
+ int prev;
+ do {
+ prev = get(obj);
+ } while (!compareAndSet(obj, prev, newValue));
+ return prev;
}
/**
@@ -148,12 +154,12 @@
* @return the previous value
*/
public int getAndIncrement(T obj) {
- for (;;) {
- int current = get(obj);
- int next = current + 1;
- if (compareAndSet(obj, current, next))
- return current;
- }
+ int prev, next;
+ do {
+ prev = get(obj);
+ next = prev + 1;
+ } while (!compareAndSet(obj, prev, next));
+ return prev;
}
/**
@@ -164,12 +170,12 @@
* @return the previous value
*/
public int getAndDecrement(T obj) {
- for (;;) {
- int current = get(obj);
- int next = current - 1;
- if (compareAndSet(obj, current, next))
- return current;
- }
+ int prev, next;
+ do {
+ prev = get(obj);
+ next = prev - 1;
+ } while (!compareAndSet(obj, prev, next));
+ return prev;
}
/**
@@ -181,12 +187,12 @@
* @return the previous value
*/
public int getAndAdd(T obj, int delta) {
- for (;;) {
- int current = get(obj);
- int next = current + delta;
- if (compareAndSet(obj, current, next))
- return current;
- }
+ int prev, next;
+ do {
+ prev = get(obj);
+ next = prev + delta;
+ } while (!compareAndSet(obj, prev, next));
+ return prev;
}
/**
@@ -197,12 +203,12 @@
* @return the updated value
*/
public int incrementAndGet(T obj) {
- for (;;) {
- int current = get(obj);
- int next = current + 1;
- if (compareAndSet(obj, current, next))
- return next;
- }
+ int prev, next;
+ do {
+ prev = get(obj);
+ next = prev + 1;
+ } while (!compareAndSet(obj, prev, next));
+ return next;
}
/**
@@ -213,12 +219,12 @@
* @return the updated value
*/
public int decrementAndGet(T obj) {
- for (;;) {
- int current = get(obj);
- int next = current - 1;
- if (compareAndSet(obj, current, next))
- return next;
- }
+ int prev, next;
+ do {
+ prev = get(obj);
+ next = prev - 1;
+ } while (!compareAndSet(obj, prev, next));
+ return next;
}
/**
@@ -230,31 +236,131 @@
* @return the updated value
*/
public int addAndGet(T obj, int delta) {
- for (;;) {
- int current = get(obj);
- int next = current + delta;
- if (compareAndSet(obj, current, next))
- return next;
- }
+ int prev, next;
+ do {
+ prev = get(obj);
+ next = prev + delta;
+ } while (!compareAndSet(obj, prev, next));
+ return next;
}
/**
- * Standard hotspot implementation using intrinsics
+ * Atomically updates the field of the given object managed by this updater
+ * with the results of applying the given function, returning the previous
+ * value. The function should be side-effect-free, since it may be
+ * re-applied when attempted updates fail due to contention among threads.
+ *
+ * @param obj An object whose field to get and set
+ * @param updateFunction a side-effect-free function
+ * @return the previous value
+ * @since 1.8
*/
- private static class AtomicIntegerFieldUpdaterImpl<T> extends AtomicIntegerFieldUpdater<T> {
- private static final Unsafe unsafe = Unsafe.getUnsafe();
- private final long offset;
- private final Class<T> tclass;
- private final Class<?> cclass;
+ public final int getAndUpdate(T obj, IntUnaryOperator updateFunction) {
+ int prev, next;
+ do {
+ prev = get(obj);
+ next = updateFunction.applyAsInt(prev);
+ } while (!compareAndSet(obj, prev, next));
+ return prev;
+ }
- AtomicIntegerFieldUpdaterImpl(final Class<T> tclass, final String fieldName) {
+ /**
+ * Atomically updates the field of the given object managed by this updater
+ * with the results of applying the given function, returning the updated
+ * value. The function should be side-effect-free, since it may be
+ * re-applied when attempted updates fail due to contention among threads.
+ *
+ * @param obj An object whose field to get and set
+ * @param updateFunction a side-effect-free function
+ * @return the updated value
+ * @since 1.8
+ */
+ public final int updateAndGet(T obj, IntUnaryOperator updateFunction) {
+ int prev, next;
+ do {
+ prev = get(obj);
+ next = updateFunction.applyAsInt(prev);
+ } while (!compareAndSet(obj, prev, next));
+ return next;
+ }
+
+ /**
+ * Atomically updates the field of the given object managed by this
+ * updater with the results of applying the given function to the
+ * current and given values, returning the previous value. The
+ * function should be side-effect-free, since it may be re-applied
+ * when attempted updates fail due to contention among threads. The
+ * function is applied with the current value as its first argument,
+ * and the given update as the second argument.
+ *
+ * @param obj An object whose field to get and set
+ * @param x the update value
+ * @param accumulatorFunction a side-effect-free function of two arguments
+ * @return the previous value
+ * @since 1.8
+ */
+ public final int getAndAccumulate(T obj, int x,
+ IntBinaryOperator accumulatorFunction) {
+ int prev, next;
+ do {
+ prev = get(obj);
+ next = accumulatorFunction.applyAsInt(prev, x);
+ } while (!compareAndSet(obj, prev, next));
+ return prev;
+ }
+
+ /**
+ * Atomically updates the field of the given object managed by this
+ * updater with the results of applying the given function to the
+ * current and given values, returning the updated value. The
+ * function should be side-effect-free, since it may be re-applied
+ * when attempted updates fail due to contention among threads. The
+ * function is applied with the current value as its first argument,
+ * and the given update as the second argument.
+ *
+ * @param obj An object whose field to get and set
+ * @param x the update value
+ * @param accumulatorFunction a side-effect-free function of two arguments
+ * @return the updated value
+ * @since 1.8
+ */
+ public final int accumulateAndGet(T obj, int x,
+ IntBinaryOperator accumulatorFunction) {
+ int prev, next;
+ do {
+ prev = get(obj);
+ next = accumulatorFunction.applyAsInt(prev, x);
+ } while (!compareAndSet(obj, prev, next));
+ return next;
+ }
+
+ /**
+ * Standard hotspot implementation using intrinsics.
+ */
+ private static final class AtomicIntegerFieldUpdaterImpl<T>
+ extends AtomicIntegerFieldUpdater<T> {
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private final long offset;
+ /**
+ * if field is protected, the subclass constructing updater, else
+ * the same as tclass
+ */
+ private final Class<?> cclass;
+ /** class holding the field */
+ private final Class<T> tclass;
+
+ AtomicIntegerFieldUpdaterImpl(final Class<T> tclass,
+ final String fieldName,
+ final Class<?> caller) {
final Field field;
- final Class<?> caller;
final int modifiers;
try {
- field = tclass.getDeclaredField(fieldName); // android-changed
- caller = VMStack.getStackClass2(); // android-changed
-
+ field = AccessController.doPrivileged(
+ new PrivilegedExceptionAction<Field>() {
+ public Field run() throws NoSuchFieldException {
+ return tclass.getDeclaredField(fieldName);
+ }
+ });
modifiers = field.getModifiers();
// BEGIN android-removed
// sun.reflect.misc.ReflectUtil.ensureMemberAccess(
@@ -263,7 +369,7 @@
// ClassLoader ccl = caller.getClassLoader();
// if ((ccl != null) && (ccl != cl) &&
// ((cl == null) || !isAncestor(cl, ccl))) {
- // sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
+ // sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
// }
// END android-removed
// BEGIN android-removed
@@ -274,17 +380,15 @@
throw new RuntimeException(ex);
}
- Class<?> fieldt = field.getType();
- if (fieldt != int.class)
+ if (field.getType() != int.class)
throw new IllegalArgumentException("Must be integer type");
if (!Modifier.isVolatile(modifiers))
throw new IllegalArgumentException("Must be volatile type");
- this.cclass = (Modifier.isProtected(modifiers) &&
- caller != tclass) ? caller : null;
+ this.cclass = (Modifier.isProtected(modifiers)) ? caller : tclass;
this.tclass = tclass;
- offset = unsafe.objectFieldOffset(field);
+ this.offset = U.objectFieldOffset(field);
}
// BEGIN android-removed
@@ -293,7 +397,7 @@
// * classloader's delegation chain.
// * Equivalent to the inaccessible: first.isAncestor(second).
// */
- // private static boolean isAncestor(ClassLoader first, ClassLoader second) {
+ // private static boolean isAncestor(ClassLoader first, ClassLoader second) {
// ClassLoader acl = first;
// do {
// acl = acl.getParent();
@@ -304,51 +408,88 @@
// return false;
// }
// END android-removed
- private void fullCheck(T obj) {
- if (!tclass.isInstance(obj))
+
+ /**
+ * Checks that target argument is instance of cclass. On
+ * failure, throws cause.
+ */
+ private final void accessCheck(T obj) {
+ if (!cclass.isInstance(obj))
+ throwAccessCheckException(obj);
+ }
+
+ /**
+ * Throws access exception if accessCheck failed due to
+ * protected access, else ClassCastException.
+ */
+ private final void throwAccessCheckException(T obj) {
+ if (cclass == tclass)
throw new ClassCastException();
- if (cclass != null)
- ensureProtectedAccess(obj);
+ else
+ throw new RuntimeException(
+ new IllegalAccessException(
+ "Class " +
+ cclass.getName() +
+ " can not access a protected member of class " +
+ tclass.getName() +
+ " using an instance of " +
+ obj.getClass().getName()));
}
- public boolean compareAndSet(T obj, int expect, int update) {
- if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
- return unsafe.compareAndSwapInt(obj, offset, expect, update);
+ public final boolean compareAndSet(T obj, int expect, int update) {
+ accessCheck(obj);
+ return U.compareAndSwapInt(obj, offset, expect, update);
}
- public boolean weakCompareAndSet(T obj, int expect, int update) {
- if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
- return unsafe.compareAndSwapInt(obj, offset, expect, update);
+ public final boolean weakCompareAndSet(T obj, int expect, int update) {
+ accessCheck(obj);
+ return U.compareAndSwapInt(obj, offset, expect, update);
}
- public void set(T obj, int newValue) {
- if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
- unsafe.putIntVolatile(obj, offset, newValue);
+ public final void set(T obj, int newValue) {
+ accessCheck(obj);
+ U.putIntVolatile(obj, offset, newValue);
}
- public void lazySet(T obj, int newValue) {
- if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
- unsafe.putOrderedInt(obj, offset, newValue);
+ public final void lazySet(T obj, int newValue) {
+ accessCheck(obj);
+ U.putOrderedInt(obj, offset, newValue);
}
public final int get(T obj) {
- if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
- return unsafe.getIntVolatile(obj, offset);
+ accessCheck(obj);
+ return U.getIntVolatile(obj, offset);
}
- private void ensureProtectedAccess(T obj) {
- if (cclass.isInstance(obj)) {
- return;
- }
- throw new RuntimeException(
- new IllegalAccessException("Class " +
- cclass.getName() +
- " can not access a protected member of class " +
- tclass.getName() +
- " using an instance of " +
- obj.getClass().getName()
- )
- );
+ public final int getAndSet(T obj, int newValue) {
+ accessCheck(obj);
+ return U.getAndSetInt(obj, offset, newValue);
}
+
+ public final int getAndAdd(T obj, int delta) {
+ accessCheck(obj);
+ return U.getAndAddInt(obj, offset, delta);
+ }
+
+ public final int getAndIncrement(T obj) {
+ return getAndAdd(obj, 1);
+ }
+
+ public final int getAndDecrement(T obj) {
+ return getAndAdd(obj, -1);
+ }
+
+ public final int incrementAndGet(T obj) {
+ return getAndAdd(obj, 1) + 1;
+ }
+
+ public final int decrementAndGet(T obj) {
+ return getAndAdd(obj, -1) - 1;
+ }
+
+ public final int addAndGet(T obj, int delta) {
+ return getAndAdd(obj, delta) + delta;
+ }
+
}
}
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicLong.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicLong.java
index ab2961a..fdb5f55 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicLong.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicLong.java
@@ -6,7 +6,8 @@
package java.util.concurrent.atomic;
-import sun.misc.Unsafe;
+import java.util.function.LongBinaryOperator;
+import java.util.function.LongUnaryOperator;
/**
* A {@code long} value that may be updated atomically. See the
@@ -24,9 +25,8 @@
public class AtomicLong extends Number implements java.io.Serializable {
private static final long serialVersionUID = 1927816293512124184L;
- // setup to use Unsafe.compareAndSwapLong for updates
- private static final Unsafe unsafe = Unsafe.getUnsafe();
- private static final long valueOffset;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long VALUE;
/**
* Records whether the underlying JVM supports lockless
@@ -44,9 +44,11 @@
static {
try {
- valueOffset = unsafe.objectFieldOffset
+ VALUE = U.objectFieldOffset
(AtomicLong.class.getDeclaredField("value"));
- } catch (Exception ex) { throw new Error(ex); }
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
}
private volatile long value;
@@ -81,7 +83,9 @@
* @param newValue the new value
*/
public final void set(long newValue) {
- value = newValue;
+ // Use putLongVolatile instead of ordinary volatile store when
+ // using compareAndSwapLong, for sake of some 32bit systems.
+ U.putLongVolatile(this, VALUE, newValue);
}
/**
@@ -91,7 +95,7 @@
* @since 1.6
*/
public final void lazySet(long newValue) {
- unsafe.putOrderedLong(this, valueOffset, newValue);
+ U.putOrderedLong(this, VALUE, newValue);
}
/**
@@ -101,11 +105,7 @@
* @return the previous value
*/
public final long getAndSet(long newValue) {
- while (true) {
- long current = get();
- if (compareAndSet(current, newValue))
- return current;
- }
+ return U.getAndSetLong(this, VALUE, newValue);
}
/**
@@ -114,11 +114,11 @@
*
* @param expect the expected value
* @param update the new value
- * @return true if successful. False return indicates that
+ * @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(long expect, long update) {
- return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
+ return U.compareAndSwapLong(this, VALUE, expect, update);
}
/**
@@ -131,10 +131,10 @@
*
* @param expect the expected value
* @param update the new value
- * @return true if successful
+ * @return {@code true} if successful
*/
public final boolean weakCompareAndSet(long expect, long update) {
- return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
+ return U.compareAndSwapLong(this, VALUE, expect, update);
}
/**
@@ -143,12 +143,7 @@
* @return the previous value
*/
public final long getAndIncrement() {
- while (true) {
- long current = get();
- long next = current + 1;
- if (compareAndSet(current, next))
- return current;
- }
+ return U.getAndAddLong(this, VALUE, 1L);
}
/**
@@ -157,12 +152,7 @@
* @return the previous value
*/
public final long getAndDecrement() {
- while (true) {
- long current = get();
- long next = current - 1;
- if (compareAndSet(current, next))
- return current;
- }
+ return U.getAndAddLong(this, VALUE, -1L);
}
/**
@@ -172,12 +162,7 @@
* @return the previous value
*/
public final long getAndAdd(long delta) {
- while (true) {
- long current = get();
- long next = current + delta;
- if (compareAndSet(current, next))
- return current;
- }
+ return U.getAndAddLong(this, VALUE, delta);
}
/**
@@ -186,12 +171,7 @@
* @return the updated value
*/
public final long incrementAndGet() {
- for (;;) {
- long current = get();
- long next = current + 1;
- if (compareAndSet(current, next))
- return next;
- }
+ return U.getAndAddLong(this, VALUE, 1L) + 1L;
}
/**
@@ -200,12 +180,7 @@
* @return the updated value
*/
public final long decrementAndGet() {
- for (;;) {
- long current = get();
- long next = current - 1;
- if (compareAndSet(current, next))
- return next;
- }
+ return U.getAndAddLong(this, VALUE, -1L) - 1L;
}
/**
@@ -215,12 +190,93 @@
* @return the updated value
*/
public final long addAndGet(long delta) {
- for (;;) {
- long current = get();
- long next = current + delta;
- if (compareAndSet(current, next))
- return next;
- }
+ return U.getAndAddLong(this, VALUE, delta) + delta;
+ }
+
+ /**
+ * Atomically updates the current value with the results of
+ * applying the given function, returning the previous value. The
+ * function should be side-effect-free, since it may be re-applied
+ * when attempted updates fail due to contention among threads.
+ *
+ * @param updateFunction a side-effect-free function
+ * @return the previous value
+ * @since 1.8
+ */
+ public final long getAndUpdate(LongUnaryOperator updateFunction) {
+ long prev, next;
+ do {
+ prev = get();
+ next = updateFunction.applyAsLong(prev);
+ } while (!compareAndSet(prev, next));
+ return prev;
+ }
+
+ /**
+ * Atomically updates the current value with the results of
+ * applying the given function, returning the updated value. The
+ * function should be side-effect-free, since it may be re-applied
+ * when attempted updates fail due to contention among threads.
+ *
+ * @param updateFunction a side-effect-free function
+ * @return the updated value
+ * @since 1.8
+ */
+ public final long updateAndGet(LongUnaryOperator updateFunction) {
+ long prev, next;
+ do {
+ prev = get();
+ next = updateFunction.applyAsLong(prev);
+ } while (!compareAndSet(prev, next));
+ return next;
+ }
+
+ /**
+ * Atomically updates the current value with the results of
+ * applying the given function to the current and given values,
+ * returning the previous value. The function should be
+ * side-effect-free, since it may be re-applied when attempted
+ * updates fail due to contention among threads. The function
+ * is applied with the current value as its first argument,
+ * and the given update as the second argument.
+ *
+ * @param x the update value
+ * @param accumulatorFunction a side-effect-free function of two arguments
+ * @return the previous value
+ * @since 1.8
+ */
+ public final long getAndAccumulate(long x,
+ LongBinaryOperator accumulatorFunction) {
+ long prev, next;
+ do {
+ prev = get();
+ next = accumulatorFunction.applyAsLong(prev, x);
+ } while (!compareAndSet(prev, next));
+ return prev;
+ }
+
+ /**
+ * Atomically updates the current value with the results of
+ * applying the given function to the current and given values,
+ * returning the updated value. The function should be
+ * side-effect-free, since it may be re-applied when attempted
+ * updates fail due to contention among threads. The function
+ * is applied with the current value as its first argument,
+ * and the given update as the second argument.
+ *
+ * @param x the update value
+ * @param accumulatorFunction a side-effect-free function of two arguments
+ * @return the updated value
+ * @since 1.8
+ */
+ public final long accumulateAndGet(long x,
+ LongBinaryOperator accumulatorFunction) {
+ long prev, next;
+ do {
+ prev = get();
+ next = accumulatorFunction.applyAsLong(prev, x);
+ } while (!compareAndSet(prev, next));
+ return next;
}
/**
@@ -234,6 +290,7 @@
/**
* Returns the value of this {@code AtomicLong} as an {@code int}
* after a narrowing primitive conversion.
+ * @jls 5.1.3 Narrowing Primitive Conversions
*/
public int intValue() {
return (int)get();
@@ -241,6 +298,7 @@
/**
* Returns the value of this {@code AtomicLong} as a {@code long}.
+ * Equivalent to {@link #get()}.
*/
public long longValue() {
return get();
@@ -249,6 +307,7 @@
/**
* Returns the value of this {@code AtomicLong} as a {@code float}
* after a widening primitive conversion.
+ * @jls 5.1.2 Widening Primitive Conversions
*/
public float floatValue() {
return (float)get();
@@ -257,6 +316,7 @@
/**
* Returns the value of this {@code AtomicLong} as a {@code double}
* after a widening primitive conversion.
+ * @jls 5.1.2 Widening Primitive Conversions
*/
public double doubleValue() {
return (double)get();
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java
index b7f3d1e..8da1501 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java
@@ -6,7 +6,8 @@
package java.util.concurrent.atomic;
-import sun.misc.Unsafe;
+import java.util.function.LongBinaryOperator;
+import java.util.function.LongUnaryOperator;
/**
* A {@code long} array in which elements may be updated atomically.
@@ -18,16 +19,17 @@
public class AtomicLongArray implements java.io.Serializable {
private static final long serialVersionUID = -2308431214976778248L;
- private static final Unsafe unsafe = Unsafe.getUnsafe();
- private static final int base = unsafe.arrayBaseOffset(long[].class);
- private static final int shift;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final int ABASE;
+ private static final int ASHIFT;
private final long[] array;
static {
- int scale = unsafe.arrayIndexScale(long[].class);
+ ABASE = U.arrayBaseOffset(long[].class);
+ int scale = U.arrayIndexScale(long[].class);
if ((scale & (scale - 1)) != 0)
- throw new Error("data type scale not a power of two");
- shift = 31 - Integer.numberOfLeadingZeros(scale);
+ throw new Error("array index scale not a power of two");
+ ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
}
private long checkedByteOffset(int i) {
@@ -38,7 +40,7 @@
}
private static long byteOffset(int i) {
- return ((long) i << shift) + base;
+ return ((long) i << ASHIFT) + ABASE;
}
/**
@@ -83,7 +85,7 @@
}
private long getRaw(long offset) {
- return unsafe.getLongVolatile(array, offset);
+ return U.getLongVolatile(array, offset);
}
/**
@@ -93,7 +95,7 @@
* @param newValue the new value
*/
public final void set(int i, long newValue) {
- unsafe.putLongVolatile(array, checkedByteOffset(i), newValue);
+ U.putLongVolatile(array, checkedByteOffset(i), newValue);
}
/**
@@ -104,7 +106,7 @@
* @since 1.6
*/
public final void lazySet(int i, long newValue) {
- unsafe.putOrderedLong(array, checkedByteOffset(i), newValue);
+ U.putOrderedLong(array, checkedByteOffset(i), newValue);
}
/**
@@ -116,12 +118,7 @@
* @return the previous value
*/
public final long getAndSet(int i, long newValue) {
- long offset = checkedByteOffset(i);
- while (true) {
- long current = getRaw(offset);
- if (compareAndSetRaw(offset, current, newValue))
- return current;
- }
+ return U.getAndSetLong(array, checkedByteOffset(i), newValue);
}
/**
@@ -131,7 +128,7 @@
* @param i the index
* @param expect the expected value
* @param update the new value
- * @return true if successful. False return indicates that
+ * @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int i, long expect, long update) {
@@ -139,7 +136,7 @@
}
private boolean compareAndSetRaw(long offset, long expect, long update) {
- return unsafe.compareAndSwapLong(array, offset, expect, update);
+ return U.compareAndSwapLong(array, offset, expect, update);
}
/**
@@ -153,7 +150,7 @@
* @param i the index
* @param expect the expected value
* @param update the new value
- * @return true if successful
+ * @return {@code true} if successful
*/
public final boolean weakCompareAndSet(int i, long expect, long update) {
return compareAndSet(i, expect, update);
@@ -187,12 +184,7 @@
* @return the previous value
*/
public final long getAndAdd(int i, long delta) {
- long offset = checkedByteOffset(i);
- while (true) {
- long current = getRaw(offset);
- if (compareAndSetRaw(offset, current, current + delta))
- return current;
- }
+ return U.getAndAddLong(array, checkedByteOffset(i), delta);
}
/**
@@ -202,7 +194,7 @@
* @return the updated value
*/
public final long incrementAndGet(int i) {
- return addAndGet(i, 1);
+ return getAndAdd(i, 1) + 1;
}
/**
@@ -212,7 +204,7 @@
* @return the updated value
*/
public final long decrementAndGet(int i) {
- return addAndGet(i, -1);
+ return getAndAdd(i, -1) - 1;
}
/**
@@ -223,13 +215,101 @@
* @return the updated value
*/
public long addAndGet(int i, long delta) {
+ return getAndAdd(i, delta) + delta;
+ }
+
+ /**
+ * Atomically updates the element at index {@code i} with the results
+ * of applying the given function, returning the previous value. The
+ * function should be side-effect-free, since it may be re-applied
+ * when attempted updates fail due to contention among threads.
+ *
+ * @param i the index
+ * @param updateFunction a side-effect-free function
+ * @return the previous value
+ * @since 1.8
+ */
+ public final long getAndUpdate(int i, LongUnaryOperator updateFunction) {
long offset = checkedByteOffset(i);
- while (true) {
- long current = getRaw(offset);
- long next = current + delta;
- if (compareAndSetRaw(offset, current, next))
- return next;
- }
+ long prev, next;
+ do {
+ prev = getRaw(offset);
+ next = updateFunction.applyAsLong(prev);
+ } while (!compareAndSetRaw(offset, prev, next));
+ return prev;
+ }
+
+ /**
+ * Atomically updates the element at index {@code i} with the results
+ * of applying the given function, returning the updated value. The
+ * function should be side-effect-free, since it may be re-applied
+ * when attempted updates fail due to contention among threads.
+ *
+ * @param i the index
+ * @param updateFunction a side-effect-free function
+ * @return the updated value
+ * @since 1.8
+ */
+ public final long updateAndGet(int i, LongUnaryOperator updateFunction) {
+ long offset = checkedByteOffset(i);
+ long prev, next;
+ do {
+ prev = getRaw(offset);
+ next = updateFunction.applyAsLong(prev);
+ } while (!compareAndSetRaw(offset, prev, next));
+ return next;
+ }
+
+ /**
+ * Atomically updates the element at index {@code i} with the
+ * results of applying the given function to the current and
+ * given values, returning the previous value. The function should
+ * be side-effect-free, since it may be re-applied when attempted
+ * updates fail due to contention among threads. The function is
+ * applied with the current value at index {@code i} as its first
+ * argument, and the given update as the second argument.
+ *
+ * @param i the index
+ * @param x the update value
+ * @param accumulatorFunction a side-effect-free function of two arguments
+ * @return the previous value
+ * @since 1.8
+ */
+ public final long getAndAccumulate(int i, long x,
+ LongBinaryOperator accumulatorFunction) {
+ long offset = checkedByteOffset(i);
+ long prev, next;
+ do {
+ prev = getRaw(offset);
+ next = accumulatorFunction.applyAsLong(prev, x);
+ } while (!compareAndSetRaw(offset, prev, next));
+ return prev;
+ }
+
+ /**
+ * Atomically updates the element at index {@code i} with the
+ * results of applying the given function to the current and
+ * given values, returning the updated value. The function should
+ * be side-effect-free, since it may be re-applied when attempted
+ * updates fail due to contention among threads. The function is
+ * applied with the current value at index {@code i} as its first
+ * argument, and the given update as the second argument.
+ *
+ * @param i the index
+ * @param x the update value
+ * @param accumulatorFunction a side-effect-free function of two arguments
+ * @return the updated value
+ * @since 1.8
+ */
+ public final long accumulateAndGet(int i, long x,
+ LongBinaryOperator accumulatorFunction) {
+ long offset = checkedByteOffset(i);
+ long prev, next;
+ do {
+ prev = getRaw(offset);
+ next = accumulatorFunction.applyAsLong(prev, x);
+ } while (!compareAndSetRaw(offset, prev, next));
+ return next;
}
/**
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java
index 65bd452..c1a02b5 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java
@@ -7,9 +7,15 @@
package java.util.concurrent.atomic;
import dalvik.system.VMStack; // android-added
-import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.function.LongBinaryOperator;
+import java.util.function.LongUnaryOperator;
+import sun.reflect.CallerSensitive;
+import sun.reflect.Reflection;
/**
* A reflection-based utility that enables atomic updates to
@@ -46,11 +52,14 @@
* or the field is inaccessible to the caller according to Java language
* access control
*/
- public static <U> AtomicLongFieldUpdater<U> newUpdater(Class<U> tclass, String fieldName) {
+ @CallerSensitive
+ public static <U> AtomicLongFieldUpdater<U> newUpdater(Class<U> tclass,
+ String fieldName) {
+ Class<?> caller = VMStack.getStackClass1(); // android-changed
if (AtomicLong.VM_SUPPORTS_LONG_CAS)
- return new CASUpdater<U>(tclass, fieldName);
+ return new CASUpdater<U>(tclass, fieldName, caller);
else
- return new LockedUpdater<U>(tclass, fieldName);
+ return new LockedUpdater<U>(tclass, fieldName, caller);
}
/**
@@ -69,7 +78,7 @@
* @param obj An object whose field to conditionally set
* @param expect the expected value
* @param update the new value
- * @return true if successful
+ * @return {@code true} if successful
* @throws ClassCastException if {@code obj} is not an instance
* of the class possessing the field established in the constructor
*/
@@ -89,7 +98,7 @@
* @param obj An object whose field to conditionally set
* @param expect the expected value
* @param update the new value
- * @return true if successful
+ * @return {@code true} if successful
* @throws ClassCastException if {@code obj} is not an instance
* of the class possessing the field established in the constructor
*/
@@ -133,11 +142,11 @@
* @return the previous value
*/
public long getAndSet(T obj, long newValue) {
- for (;;) {
- long current = get(obj);
- if (compareAndSet(obj, current, newValue))
- return current;
- }
+ long prev;
+ do {
+ prev = get(obj);
+ } while (!compareAndSet(obj, prev, newValue));
+ return prev;
}
/**
@@ -148,12 +157,12 @@
* @return the previous value
*/
public long getAndIncrement(T obj) {
- for (;;) {
- long current = get(obj);
- long next = current + 1;
- if (compareAndSet(obj, current, next))
- return current;
- }
+ long prev, next;
+ do {
+ prev = get(obj);
+ next = prev + 1;
+ } while (!compareAndSet(obj, prev, next));
+ return prev;
}
/**
@@ -164,12 +173,12 @@
* @return the previous value
*/
public long getAndDecrement(T obj) {
- for (;;) {
- long current = get(obj);
- long next = current - 1;
- if (compareAndSet(obj, current, next))
- return current;
- }
+ long prev, next;
+ do {
+ prev = get(obj);
+ next = prev - 1;
+ } while (!compareAndSet(obj, prev, next));
+ return prev;
}
/**
@@ -181,12 +190,12 @@
* @return the previous value
*/
public long getAndAdd(T obj, long delta) {
- for (;;) {
- long current = get(obj);
- long next = current + delta;
- if (compareAndSet(obj, current, next))
- return current;
- }
+ long prev, next;
+ do {
+ prev = get(obj);
+ next = prev + delta;
+ } while (!compareAndSet(obj, prev, next));
+ return prev;
}
/**
@@ -197,12 +206,12 @@
* @return the updated value
*/
public long incrementAndGet(T obj) {
- for (;;) {
- long current = get(obj);
- long next = current + 1;
- if (compareAndSet(obj, current, next))
- return next;
- }
+ long prev, next;
+ do {
+ prev = get(obj);
+ next = prev + 1;
+ } while (!compareAndSet(obj, prev, next));
+ return next;
}
/**
@@ -213,12 +222,12 @@
* @return the updated value
*/
public long decrementAndGet(T obj) {
- for (;;) {
- long current = get(obj);
- long next = current - 1;
- if (compareAndSet(obj, current, next))
- return next;
- }
+ long prev, next;
+ do {
+ prev = get(obj);
+ next = prev - 1;
+ } while (!compareAndSet(obj, prev, next));
+ return next;
}
/**
@@ -230,27 +239,121 @@
* @return the updated value
*/
public long addAndGet(T obj, long delta) {
- for (;;) {
- long current = get(obj);
- long next = current + delta;
- if (compareAndSet(obj, current, next))
- return next;
- }
+ long prev, next;
+ do {
+ prev = get(obj);
+ next = prev + delta;
+ } while (!compareAndSet(obj, prev, next));
+ return next;
}
- private static class CASUpdater<T> extends AtomicLongFieldUpdater<T> {
- private static final Unsafe unsafe = Unsafe.getUnsafe();
- private final long offset;
- private final Class<T> tclass;
- private final Class<?> cclass;
+ /**
+ * Atomically updates the field of the given object managed by this updater
+ * with the results of applying the given function, returning the previous
+ * value. The function should be side-effect-free, since it may be
+ * re-applied when attempted updates fail due to contention among threads.
+ *
+ * @param obj An object whose field to get and set
+ * @param updateFunction a side-effect-free function
+ * @return the previous value
+ * @since 1.8
+ */
+ public final long getAndUpdate(T obj, LongUnaryOperator updateFunction) {
+ long prev, next;
+ do {
+ prev = get(obj);
+ next = updateFunction.applyAsLong(prev);
+ } while (!compareAndSet(obj, prev, next));
+ return prev;
+ }
- CASUpdater(final Class<T> tclass, final String fieldName) {
+ /**
+ * Atomically updates the field of the given object managed by this updater
+ * with the results of applying the given function, returning the updated
+ * value. The function should be side-effect-free, since it may be
+ * re-applied when attempted updates fail due to contention among threads.
+ *
+ * @param obj An object whose field to get and set
+ * @param updateFunction a side-effect-free function
+ * @return the updated value
+ * @since 1.8
+ */
+ public final long updateAndGet(T obj, LongUnaryOperator updateFunction) {
+ long prev, next;
+ do {
+ prev = get(obj);
+ next = updateFunction.applyAsLong(prev);
+ } while (!compareAndSet(obj, prev, next));
+ return next;
+ }
+
+ /**
+ * Atomically updates the field of the given object managed by this
+ * updater with the results of applying the given function to the
+ * current and given values, returning the previous value. The
+ * function should be side-effect-free, since it may be re-applied
+ * when attempted updates fail due to contention among threads. The
+ * function is applied with the current value as its first argument,
+ * and the given update as the second argument.
+ *
+ * @param obj An object whose field to get and set
+ * @param x the update value
+ * @param accumulatorFunction a side-effect-free function of two arguments
+ * @return the previous value
+ * @since 1.8
+ */
+ public final long getAndAccumulate(T obj, long x,
+ LongBinaryOperator accumulatorFunction) {
+ long prev, next;
+ do {
+ prev = get(obj);
+ next = accumulatorFunction.applyAsLong(prev, x);
+ } while (!compareAndSet(obj, prev, next));
+ return prev;
+ }
+
+ /**
+ * Atomically updates the field of the given object managed by this
+ * updater with the results of applying the given function to the
+ * current and given values, returning the updated value. The
+ * function should be side-effect-free, since it may be re-applied
+ * when attempted updates fail due to contention among threads. The
+ * function is applied with the current value as its first argument,
+ * and the given update as the second argument.
+ *
+ * @param obj An object whose field to get and set
+ * @param x the update value
+ * @param accumulatorFunction a side-effect-free function of two arguments
+ * @return the updated value
+ * @since 1.8
+ */
+ public final long accumulateAndGet(T obj, long x,
+ LongBinaryOperator accumulatorFunction) {
+ long prev, next;
+ do {
+ prev = get(obj);
+ next = accumulatorFunction.applyAsLong(prev, x);
+ } while (!compareAndSet(obj, prev, next));
+ return next;
+ }
+
+ private static final class CASUpdater<T> extends AtomicLongFieldUpdater<T> {
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private final long offset;
+ /**
+ * if field is protected, the subclass constructing updater, else
+ * the same as tclass
+ */
+ private final Class<?> cclass;
+ /** class holding the field */
+ private final Class<T> tclass;
+
+ CASUpdater(final Class<T> tclass, final String fieldName,
+ final Class<?> caller) {
final Field field;
- final Class<?> caller;
final int modifiers;
try {
field = tclass.getDeclaredField(fieldName); // android-changed
- caller = VMStack.getStackClass2(); // android-changed
modifiers = field.getModifiers();
// BEGIN android-removed
// sun.reflect.misc.ReflectUtil.ensureMemberAccess(
@@ -259,101 +362,7 @@
// ClassLoader ccl = caller.getClassLoader();
// if ((ccl != null) && (ccl != cl) &&
// ((cl == null) || !isAncestor(cl, ccl))) {
- // sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
- // }
- // END android-removed
- // BEGIN android-removed
- // } catch (PrivilegedActionException pae) {
- // throw new RuntimeException(pae.getException());
- // END android-removed
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
-
- Class<?> fieldt = field.getType();
- if (fieldt != long.class)
- throw new IllegalArgumentException("Must be long type");
-
- if (!Modifier.isVolatile(modifiers))
- throw new IllegalArgumentException("Must be volatile type");
-
- this.cclass = (Modifier.isProtected(modifiers) &&
- caller != tclass) ? caller : null;
- this.tclass = tclass;
- offset = unsafe.objectFieldOffset(field);
- }
-
- private void fullCheck(T obj) {
- if (!tclass.isInstance(obj))
- throw new ClassCastException();
- if (cclass != null)
- ensureProtectedAccess(obj);
- }
-
- public boolean compareAndSet(T obj, long expect, long update) {
- if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
- return unsafe.compareAndSwapLong(obj, offset, expect, update);
- }
-
- public boolean weakCompareAndSet(T obj, long expect, long update) {
- if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
- return unsafe.compareAndSwapLong(obj, offset, expect, update);
- }
-
- public void set(T obj, long newValue) {
- if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
- unsafe.putLongVolatile(obj, offset, newValue);
- }
-
- public void lazySet(T obj, long newValue) {
- if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
- unsafe.putOrderedLong(obj, offset, newValue);
- }
-
- public long get(T obj) {
- if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
- return unsafe.getLongVolatile(obj, offset);
- }
-
- private void ensureProtectedAccess(T obj) {
- if (cclass.isInstance(obj)) {
- return;
- }
- throw new RuntimeException(
- new IllegalAccessException("Class " +
- cclass.getName() +
- " can not access a protected member of class " +
- tclass.getName() +
- " using an instance of " +
- obj.getClass().getName()
- )
- );
- }
- }
-
-
- private static class LockedUpdater<T> extends AtomicLongFieldUpdater<T> {
- private static final Unsafe unsafe = Unsafe.getUnsafe();
- private final long offset;
- private final Class<T> tclass;
- private final Class<?> cclass;
-
- LockedUpdater(final Class<T> tclass, final String fieldName) {
- Field field = null;
- Class<?> caller = null;
- int modifiers = 0;
- try {
- field = tclass.getDeclaredField(fieldName); // android-changed
- caller = VMStack.getStackClass2(); // android-changed
- modifiers = field.getModifiers();
- // BEGIN android-removed
- // sun.reflect.misc.ReflectUtil.ensureMemberAccess(
- // caller, tclass, null, modifiers);
- // ClassLoader cl = tclass.getClassLoader();
- // ClassLoader ccl = caller.getClassLoader();
- // if ((ccl != null) && (ccl != cl) &&
- // ((cl == null) || !isAncestor(cl, ccl))) {
- // sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
+ // sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
// }
// END android-removed
// BEGIN android-removed
@@ -364,73 +373,206 @@
throw new RuntimeException(ex);
}
- Class<?> fieldt = field.getType();
- if (fieldt != long.class)
+ if (field.getType() != long.class)
throw new IllegalArgumentException("Must be long type");
if (!Modifier.isVolatile(modifiers))
throw new IllegalArgumentException("Must be volatile type");
- this.cclass = (Modifier.isProtected(modifiers) &&
- caller != tclass) ? caller : null;
+ this.cclass = (Modifier.isProtected(modifiers)) ? caller : tclass;
this.tclass = tclass;
- offset = unsafe.objectFieldOffset(field);
+ this.offset = U.objectFieldOffset(field);
}
- private void fullCheck(T obj) {
- if (!tclass.isInstance(obj))
+ /**
+ * Checks that target argument is instance of cclass. On
+ * failure, throws cause.
+ */
+ private final void accessCheck(T obj) {
+ if (!cclass.isInstance(obj))
+ throwAccessCheckException(obj);
+ }
+
+ /**
+ * Throws access exception if accessCheck failed due to
+ * protected access, else ClassCastException.
+ */
+ private final void throwAccessCheckException(T obj) {
+ if (cclass == tclass)
throw new ClassCastException();
- if (cclass != null)
- ensureProtectedAccess(obj);
+ else
+ throw new RuntimeException(
+ new IllegalAccessException(
+ "Class " +
+ cclass.getName() +
+ " can not access a protected member of class " +
+ tclass.getName() +
+ " using an instance of " +
+ obj.getClass().getName()));
}
- public boolean compareAndSet(T obj, long expect, long update) {
- if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
+ public final boolean compareAndSet(T obj, long expect, long update) {
+ accessCheck(obj);
+ return U.compareAndSwapLong(obj, offset, expect, update);
+ }
+
+ public final boolean weakCompareAndSet(T obj, long expect, long update) {
+ accessCheck(obj);
+ return U.compareAndSwapLong(obj, offset, expect, update);
+ }
+
+ public final void set(T obj, long newValue) {
+ accessCheck(obj);
+ U.putLongVolatile(obj, offset, newValue);
+ }
+
+ public final void lazySet(T obj, long newValue) {
+ accessCheck(obj);
+ U.putOrderedLong(obj, offset, newValue);
+ }
+
+ public final long get(T obj) {
+ accessCheck(obj);
+ return U.getLongVolatile(obj, offset);
+ }
+
+ public final long getAndSet(T obj, long newValue) {
+ accessCheck(obj);
+ return U.getAndSetLong(obj, offset, newValue);
+ }
+
+ public final long getAndAdd(T obj, long delta) {
+ accessCheck(obj);
+ return U.getAndAddLong(obj, offset, delta);
+ }
+
+ public final long getAndIncrement(T obj) {
+ return getAndAdd(obj, 1);
+ }
+
+ public final long getAndDecrement(T obj) {
+ return getAndAdd(obj, -1);
+ }
+
+ public final long incrementAndGet(T obj) {
+ return getAndAdd(obj, 1) + 1;
+ }
+
+ public final long decrementAndGet(T obj) {
+ return getAndAdd(obj, -1) - 1;
+ }
+
+ public final long addAndGet(T obj, long delta) {
+ return getAndAdd(obj, delta) + delta;
+ }
+ }
+
+ private static final class LockedUpdater<T> extends AtomicLongFieldUpdater<T> {
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private final long offset;
+ /**
+ * if field is protected, the subclass constructing updater, else
+ * the same as tclass
+ */
+ private final Class<?> cclass;
+ /** class holding the field */
+ private final Class<T> tclass;
+
+ LockedUpdater(final Class<T> tclass, final String fieldName,
+ final Class<?> caller) {
+ Field field = null;
+ int modifiers = 0;
+ try {
+ field = tclass.getDeclaredField(fieldName); // android-changed
+ modifiers = field.getModifiers();
+ // BEGIN android-removed
+ // sun.reflect.misc.ReflectUtil.ensureMemberAccess(
+ // caller, tclass, null, modifiers);
+ // ClassLoader cl = tclass.getClassLoader();
+ // ClassLoader ccl = caller.getClassLoader();
+ // if ((ccl != null) && (ccl != cl) &&
+ // ((cl == null) || !isAncestor(cl, ccl))) {
+ // sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
+ // }
+ // END android-removed
+ // BEGIN android-removed
+ // } catch (PrivilegedActionException pae) {
+ // throw new RuntimeException(pae.getException());
+ // END android-removed
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+
+ if (field.getType() != long.class)
+ throw new IllegalArgumentException("Must be long type");
+
+ if (!Modifier.isVolatile(modifiers))
+ throw new IllegalArgumentException("Must be volatile type");
+
+ this.cclass = (Modifier.isProtected(modifiers)) ? caller : tclass;
+ this.tclass = tclass;
+ this.offset = U.objectFieldOffset(field);
+ }
+
+ /**
+ * Checks that target argument is instance of cclass. On
+ * failure, throws cause.
+ */
+ private final void accessCheck(T obj) {
+ if (!cclass.isInstance(obj))
+ throw accessCheckException(obj);
+ }
+
+ /**
+ * Returns access exception if accessCheck failed due to
+ * protected access, else ClassCastException.
+ */
+ private final RuntimeException accessCheckException(T obj) {
+ if (cclass == tclass)
+ return new ClassCastException();
+ else
+ return new RuntimeException(
+ new IllegalAccessException(
+ "Class " +
+ cclass.getName() +
+ " can not access a protected member of class " +
+ tclass.getName() +
+ " using an instance of " +
+ obj.getClass().getName()));
+ }
+
+ public final boolean compareAndSet(T obj, long expect, long update) {
+ accessCheck(obj);
synchronized (this) {
- long v = unsafe.getLong(obj, offset);
+ long v = U.getLong(obj, offset);
if (v != expect)
return false;
- unsafe.putLong(obj, offset, update);
+ U.putLong(obj, offset, update);
return true;
}
}
- public boolean weakCompareAndSet(T obj, long expect, long update) {
+ public final boolean weakCompareAndSet(T obj, long expect, long update) {
return compareAndSet(obj, expect, update);
}
- public void set(T obj, long newValue) {
- if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
+ public final void set(T obj, long newValue) {
+ accessCheck(obj);
synchronized (this) {
- unsafe.putLong(obj, offset, newValue);
+ U.putLong(obj, offset, newValue);
}
}
- public void lazySet(T obj, long newValue) {
+ public final void lazySet(T obj, long newValue) {
set(obj, newValue);
}
- public long get(T obj) {
- if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
+ public final long get(T obj) {
+ accessCheck(obj);
synchronized (this) {
- return unsafe.getLong(obj, offset);
+ return U.getLong(obj, offset);
}
}
-
- private void ensureProtectedAccess(T obj) {
- if (cclass.isInstance(obj)) {
- return;
- }
- throw new RuntimeException(
- new IllegalAccessException("Class " +
- cclass.getName() +
- " can not access a protected member of class " +
- tclass.getName() +
- " using an instance of " +
- obj.getClass().getName()
- )
- );
- }
}
// BEGIN android-removed
@@ -439,13 +581,13 @@
// * classloader's delegation chain.
// * Equivalent to the inaccessible: first.isAncestor(second).
// */
- // private static boolean isAncestor(ClassLoader first, ClassLoader second) {
+ // static boolean isAncestor(ClassLoader first, ClassLoader second) {
// ClassLoader acl = first;
// do {
// acl = acl.getParent();
// if (second == acl) {
// return true;
- // }
+ // }
// } while (acl != null);
// return false;
// }
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java
index 18d148f..d11be10 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java
@@ -91,7 +91,7 @@
* @param newReference the new value for the reference
* @param expectedMark the expected value of the mark
* @param newMark the new value for the mark
- * @return true if successful
+ * @return {@code true} if successful
*/
public boolean weakCompareAndSet(V expectedReference,
V newReference,
@@ -111,7 +111,7 @@
* @param newReference the new value for the reference
* @param expectedMark the expected value of the mark
* @param newMark the new value for the mark
- * @return true if successful
+ * @return {@code true} if successful
*/
public boolean compareAndSet(V expectedReference,
V newReference,
@@ -149,7 +149,7 @@
*
* @param expectedReference the expected value of the reference
* @param newMark the new value for the mark
- * @return true if successful
+ * @return {@code true} if successful
*/
public boolean attemptMark(V expectedReference, boolean newMark) {
Pair<V> current = pair;
@@ -161,23 +161,18 @@
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
- private static final long pairOffset =
- objectFieldOffset(UNSAFE, "pair", AtomicMarkableReference.class);
-
- private boolean casPair(Pair<V> cmp, Pair<V> val) {
- return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long PAIR;
+ static {
+ try {
+ PAIR = U.objectFieldOffset
+ (AtomicMarkableReference.class.getDeclaredField("pair"));
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
}
- static long objectFieldOffset(sun.misc.Unsafe UNSAFE,
- String field, Class<?> klazz) {
- try {
- return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field));
- } catch (NoSuchFieldException e) {
- // Convert Exception to corresponding Error
- NoSuchFieldError error = new NoSuchFieldError(field);
- error.initCause(e);
- throw error;
- }
+ private boolean casPair(Pair<V> cmp, Pair<V> val) {
+ return U.compareAndSwapObject(this, PAIR, cmp, val);
}
}
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicReference.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicReference.java
index 7ea6066..c3a5ede 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicReference.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicReference.java
@@ -6,7 +6,8 @@
package java.util.concurrent.atomic;
-import sun.misc.Unsafe;
+import java.util.function.BinaryOperator;
+import java.util.function.UnaryOperator;
/**
* An object reference that may be updated atomically. See the {@link
@@ -19,14 +20,16 @@
public class AtomicReference<V> implements java.io.Serializable {
private static final long serialVersionUID = -1848883965231344442L;
- private static final Unsafe unsafe = Unsafe.getUnsafe();
- private static final long valueOffset;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long VALUE;
static {
try {
- valueOffset = unsafe.objectFieldOffset
+ VALUE = U.objectFieldOffset
(AtomicReference.class.getDeclaredField("value"));
- } catch (Exception ex) { throw new Error(ex); }
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
}
private volatile V value;
@@ -71,7 +74,7 @@
* @since 1.6
*/
public final void lazySet(V newValue) {
- unsafe.putOrderedObject(this, valueOffset, newValue);
+ U.putOrderedObject(this, VALUE, newValue);
}
/**
@@ -79,11 +82,11 @@
* if the current value {@code ==} the expected value.
* @param expect the expected value
* @param update the new value
- * @return true if successful. False return indicates that
+ * @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(V expect, V update) {
- return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
+ return U.compareAndSwapObject(this, VALUE, expect, update);
}
/**
@@ -96,10 +99,10 @@
*
* @param expect the expected value
* @param update the new value
- * @return true if successful
+ * @return {@code true} if successful
*/
public final boolean weakCompareAndSet(V expect, V update) {
- return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
+ return U.compareAndSwapObject(this, VALUE, expect, update);
}
/**
@@ -108,12 +111,95 @@
* @param newValue the new value
* @return the previous value
*/
+ @SuppressWarnings("unchecked")
public final V getAndSet(V newValue) {
- while (true) {
- V x = get();
- if (compareAndSet(x, newValue))
- return x;
- }
+ return (V)U.getAndSetObject(this, VALUE, newValue);
+ }
+
+ /**
+ * Atomically updates the current value with the results of
+ * applying the given function, returning the previous value. The
+ * function should be side-effect-free, since it may be re-applied
+ * when attempted updates fail due to contention among threads.
+ *
+ * @param updateFunction a side-effect-free function
+ * @return the previous value
+ * @since 1.8
+ */
+ public final V getAndUpdate(UnaryOperator<V> updateFunction) {
+ V prev, next;
+ do {
+ prev = get();
+ next = updateFunction.apply(prev);
+ } while (!compareAndSet(prev, next));
+ return prev;
+ }
+
+ /**
+ * Atomically updates the current value with the results of
+ * applying the given function, returning the updated value. The
+ * function should be side-effect-free, since it may be re-applied
+ * when attempted updates fail due to contention among threads.
+ *
+ * @param updateFunction a side-effect-free function
+ * @return the updated value
+ * @since 1.8
+ */
+ public final V updateAndGet(UnaryOperator<V> updateFunction) {
+ V prev, next;
+ do {
+ prev = get();
+ next = updateFunction.apply(prev);
+ } while (!compareAndSet(prev, next));
+ return next;
+ }
+
+ /**
+ * Atomically updates the current value with the results of
+ * applying the given function to the current and given values,
+ * returning the previous value. The function should be
+ * side-effect-free, since it may be re-applied when attempted
+ * updates fail due to contention among threads. The function
+ * is applied with the current value as its first argument,
+ * and the given update as the second argument.
+ *
+ * @param x the update value
+ * @param accumulatorFunction a side-effect-free function of two arguments
+ * @return the previous value
+ * @since 1.8
+ */
+ public final V getAndAccumulate(V x,
+ BinaryOperator<V> accumulatorFunction) {
+ V prev, next;
+ do {
+ prev = get();
+ next = accumulatorFunction.apply(prev, x);
+ } while (!compareAndSet(prev, next));
+ return prev;
+ }
+
+ /**
+ * Atomically updates the current value with the results of
+ * applying the given function to the current and given values,
+ * returning the updated value. The function should be
+ * side-effect-free, since it may be re-applied when attempted
+ * updates fail due to contention among threads. The function
+ * is applied with the current value as its first argument,
+ * and the given update as the second argument.
+ *
+ * @param x the update value
+ * @param accumulatorFunction a side-effect-free function of two arguments
+ * @return the updated value
+ * @since 1.8
+ */
+ public final V accumulateAndGet(V x,
+ BinaryOperator<V> accumulatorFunction) {
+ V prev, next;
+ do {
+ prev = get();
+ next = accumulatorFunction.apply(prev, x);
+ } while (!compareAndSet(prev, next));
+ return next;
}
/**
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java
index 052b839..f5596e8 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java
@@ -6,9 +6,10 @@
package java.util.concurrent.atomic;
-import java.util.Arrays;
import java.lang.reflect.Array;
-import sun.misc.Unsafe;
+import java.util.Arrays;
+import java.util.function.BinaryOperator;
+import java.util.function.UnaryOperator;
/**
* An array of object references in which elements may be updated
@@ -22,23 +23,22 @@
public class AtomicReferenceArray<E> implements java.io.Serializable {
private static final long serialVersionUID = -6209656149925076980L;
- private static final Unsafe unsafe;
- private static final int base;
- private static final int shift;
- private static final long arrayFieldOffset;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long ARRAY;
+ private static final int ABASE;
+ private static final int ASHIFT;
private final Object[] array; // must have exact type Object[]
static {
try {
- unsafe = Unsafe.getUnsafe();
- arrayFieldOffset = unsafe.objectFieldOffset
+ ARRAY = U.objectFieldOffset
(AtomicReferenceArray.class.getDeclaredField("array"));
- base = unsafe.arrayBaseOffset(Object[].class);
- int scale = unsafe.arrayIndexScale(Object[].class);
+ ABASE = U.arrayBaseOffset(Object[].class);
+ int scale = U.arrayIndexScale(Object[].class);
if ((scale & (scale - 1)) != 0)
- throw new Error("data type scale not a power of two");
- shift = 31 - Integer.numberOfLeadingZeros(scale);
- } catch (Exception e) {
+ throw new Error("array index scale not a power of two");
+ ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
@@ -51,7 +51,7 @@
}
private static long byteOffset(int i) {
- return ((long) i << shift) + base;
+ return ((long) i << ASHIFT) + ABASE;
}
/**
@@ -97,7 +97,7 @@
@SuppressWarnings("unchecked")
private E getRaw(long offset) {
- return (E) unsafe.getObjectVolatile(array, offset);
+ return (E) U.getObjectVolatile(array, offset);
}
/**
@@ -107,7 +107,7 @@
* @param newValue the new value
*/
public final void set(int i, E newValue) {
- unsafe.putObjectVolatile(array, checkedByteOffset(i), newValue);
+ U.putObjectVolatile(array, checkedByteOffset(i), newValue);
}
/**
@@ -118,7 +118,7 @@
* @since 1.6
*/
public final void lazySet(int i, E newValue) {
- unsafe.putOrderedObject(array, checkedByteOffset(i), newValue);
+ U.putOrderedObject(array, checkedByteOffset(i), newValue);
}
/**
@@ -129,13 +129,9 @@
* @param newValue the new value
* @return the previous value
*/
+ @SuppressWarnings("unchecked")
public final E getAndSet(int i, E newValue) {
- long offset = checkedByteOffset(i);
- while (true) {
- E current = getRaw(offset);
- if (compareAndSetRaw(offset, current, newValue))
- return current;
- }
+ return (E)U.getAndSetObject(array, checkedByteOffset(i), newValue);
}
/**
@@ -145,7 +141,7 @@
* @param i the index
* @param expect the expected value
* @param update the new value
- * @return true if successful. False return indicates that
+ * @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int i, E expect, E update) {
@@ -153,7 +149,7 @@
}
private boolean compareAndSetRaw(long offset, E expect, E update) {
- return unsafe.compareAndSwapObject(array, offset, expect, update);
+ return U.compareAndSwapObject(array, offset, expect, update);
}
/**
@@ -167,13 +163,107 @@
* @param i the index
* @param expect the expected value
* @param update the new value
- * @return true if successful
+ * @return {@code true} if successful
*/
public final boolean weakCompareAndSet(int i, E expect, E update) {
return compareAndSet(i, expect, update);
}
/**
+ * Atomically updates the element at index {@code i} with the results
+ * of applying the given function, returning the previous value. The
+ * function should be side-effect-free, since it may be re-applied
+ * when attempted updates fail due to contention among threads.
+ *
+ * @param i the index
+ * @param updateFunction a side-effect-free function
+ * @return the previous value
+ * @since 1.8
+ */
+ public final E getAndUpdate(int i, UnaryOperator<E> updateFunction) {
+ long offset = checkedByteOffset(i);
+ E prev, next;
+ do {
+ prev = getRaw(offset);
+ next = updateFunction.apply(prev);
+ } while (!compareAndSetRaw(offset, prev, next));
+ return prev;
+ }
+
+ /**
+ * Atomically updates the element at index {@code i} with the results
+ * of applying the given function, returning the updated value. The
+ * function should be side-effect-free, since it may be re-applied
+ * when attempted updates fail due to contention among threads.
+ *
+ * @param i the index
+ * @param updateFunction a side-effect-free function
+ * @return the updated value
+ * @since 1.8
+ */
+ public final E updateAndGet(int i, UnaryOperator<E> updateFunction) {
+ long offset = checkedByteOffset(i);
+ E prev, next;
+ do {
+ prev = getRaw(offset);
+ next = updateFunction.apply(prev);
+ } while (!compareAndSetRaw(offset, prev, next));
+ return next;
+ }
+
+ /**
+ * Atomically updates the element at index {@code i} with the
+ * results of applying the given function to the current and
+ * given values, returning the previous value. The function should
+ * be side-effect-free, since it may be re-applied when attempted
+ * updates fail due to contention among threads. The function is
+ * applied with the current value at index {@code i} as its first
+ * argument, and the given update as the second argument.
+ *
+ * @param i the index
+ * @param x the update value
+ * @param accumulatorFunction a side-effect-free function of two arguments
+ * @return the previous value
+ * @since 1.8
+ */
+ public final E getAndAccumulate(int i, E x,
+ BinaryOperator<E> accumulatorFunction) {
+ long offset = checkedByteOffset(i);
+ E prev, next;
+ do {
+ prev = getRaw(offset);
+ next = accumulatorFunction.apply(prev, x);
+ } while (!compareAndSetRaw(offset, prev, next));
+ return prev;
+ }
+
+ /**
+ * Atomically updates the element at index {@code i} with the
+ * results of applying the given function to the current and
+ * given values, returning the updated value. The function should
+ * be side-effect-free, since it may be re-applied when attempted
+ * updates fail due to contention among threads. The function is
+ * applied with the current value at index {@code i} as its first
+ * argument, and the given update as the second argument.
+ *
+ * @param i the index
+ * @param x the update value
+ * @param accumulatorFunction a side-effect-free function of two arguments
+ * @return the updated value
+ * @since 1.8
+ */
+ public final E accumulateAndGet(int i, E x,
+ BinaryOperator<E> accumulatorFunction) {
+ long offset = checkedByteOffset(i);
+ E prev, next;
+ do {
+ prev = getRaw(offset);
+ next = accumulatorFunction.apply(prev, x);
+ } while (!compareAndSetRaw(offset, prev, next));
+ return next;
+ }
+
+ /**
* Returns the String representation of the current values of array.
* @return the String representation of the current values of array
*/
@@ -194,17 +284,20 @@
/**
* Reconstitutes the instance from a stream (that is, deserializes it).
+ * @param s the stream
+ * @throws ClassNotFoundException if the class of a serialized object
+ * could not be found
+ * @throws java.io.IOException if an I/O error occurs
*/
private void readObject(java.io.ObjectInputStream s)
- throws java.io.IOException, ClassNotFoundException,
- java.io.InvalidObjectException {
+ throws java.io.IOException, ClassNotFoundException {
// Note: This must be changed if any additional fields are defined
Object a = s.readFields().get("array", null);
if (a == null || !a.getClass().isArray())
throw new java.io.InvalidObjectException("Not array type");
if (a.getClass() != Object[].class)
a = Arrays.copyOf((Object[])a, Array.getLength(a), Object[].class);
- unsafe.putObjectVolatile(this, arrayFieldOffset, a);
+ U.putObjectVolatile(this, ARRAY, a);
}
}
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java
index 13ad3eb..1dc2eb9 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java
@@ -7,9 +7,15 @@
package java.util.concurrent.atomic;
import dalvik.system.VMStack; // android-added
-import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.function.BinaryOperator;
+import java.util.function.UnaryOperator;
+import sun.reflect.CallerSensitive;
+import sun.reflect.Reflection;
/**
* A reflection-based utility that enables atomic updates to
@@ -19,7 +25,7 @@
* independently subject to atomic updates. For example, a tree node
* might be declared as
*
- * <pre> {@code
+ * <pre> {@code
* class Node {
* private volatile Node left, right;
*
@@ -60,17 +66,19 @@
* @param <U> the type of instances of tclass
* @param <W> the type of instances of vclass
* @return the updater
- * @throws IllegalArgumentException if the field is not a volatile reference type
+ * @throws ClassCastException if the field is of the wrong type
+ * @throws IllegalArgumentException if the field is not volatile
* @throws RuntimeException with a nested reflection-based
* exception if the class does not hold field or is the wrong type,
* or the field is inaccessible to the caller according to Java language
* access control
*/
+ @CallerSensitive
public static <U,W> AtomicReferenceFieldUpdater<U,W> newUpdater(Class<U> tclass,
Class<W> vclass,
String fieldName) {
return new AtomicReferenceFieldUpdaterImpl<U,W>
- (tclass, vclass, fieldName);
+ (tclass, vclass, fieldName, VMStack.getStackClass1()); // android-changed
}
/**
@@ -89,7 +97,7 @@
* @param obj An object whose field to conditionally set
* @param expect the expected value
* @param update the new value
- * @return true if successful
+ * @return {@code true} if successful
*/
public abstract boolean compareAndSet(T obj, V expect, V update);
@@ -107,7 +115,7 @@
* @param obj An object whose field to conditionally set
* @param expect the expected value
* @param update the new value
- * @return true if successful
+ * @return {@code true} if successful
*/
public abstract boolean weakCompareAndSet(T obj, V expect, V update);
@@ -149,20 +157,116 @@
* @return the previous value
*/
public V getAndSet(T obj, V newValue) {
- for (;;) {
- V current = get(obj);
- if (compareAndSet(obj, current, newValue))
- return current;
- }
+ V prev;
+ do {
+ prev = get(obj);
+ } while (!compareAndSet(obj, prev, newValue));
+ return prev;
+ }
+
+ /**
+ * Atomically updates the field of the given object managed by this updater
+ * with the results of applying the given function, returning the previous
+ * value. The function should be side-effect-free, since it may be
+ * re-applied when attempted updates fail due to contention among threads.
+ *
+ * @param obj An object whose field to get and set
+ * @param updateFunction a side-effect-free function
+ * @return the previous value
+ * @since 1.8
+ */
+ public final V getAndUpdate(T obj, UnaryOperator<V> updateFunction) {
+ V prev, next;
+ do {
+ prev = get(obj);
+ next = updateFunction.apply(prev);
+ } while (!compareAndSet(obj, prev, next));
+ return prev;
+ }
+
+ /**
+ * Atomically updates the field of the given object managed by this updater
+ * with the results of applying the given function, returning the updated
+ * value. The function should be side-effect-free, since it may be
+ * re-applied when attempted updates fail due to contention among threads.
+ *
+ * @param obj An object whose field to get and set
+ * @param updateFunction a side-effect-free function
+ * @return the updated value
+ * @since 1.8
+ */
+ public final V updateAndGet(T obj, UnaryOperator<V> updateFunction) {
+ V prev, next;
+ do {
+ prev = get(obj);
+ next = updateFunction.apply(prev);
+ } while (!compareAndSet(obj, prev, next));
+ return next;
+ }
+
+ /**
+ * Atomically updates the field of the given object managed by this
+ * updater with the results of applying the given function to the
+ * current and given values, returning the previous value. The
+ * function should be side-effect-free, since it may be re-applied
+ * when attempted updates fail due to contention among threads. The
+ * function is applied with the current value as its first argument,
+ * and the given update as the second argument.
+ *
+ * @param obj An object whose field to get and set
+ * @param x the update value
+ * @param accumulatorFunction a side-effect-free function of two arguments
+ * @return the previous value
+ * @since 1.8
+ */
+ public final V getAndAccumulate(T obj, V x,
+ BinaryOperator<V> accumulatorFunction) {
+ V prev, next;
+ do {
+ prev = get(obj);
+ next = accumulatorFunction.apply(prev, x);
+ } while (!compareAndSet(obj, prev, next));
+ return prev;
+ }
+
+ /**
+ * Atomically updates the field of the given object managed by this
+ * updater with the results of applying the given function to the
+ * current and given values, returning the updated value. The
+ * function should be side-effect-free, since it may be re-applied
+ * when attempted updates fail due to contention among threads. The
+ * function is applied with the current value as its first argument,
+ * and the given update as the second argument.
+ *
+ * @param obj An object whose field to get and set
+ * @param x the update value
+ * @param accumulatorFunction a side-effect-free function of two arguments
+ * @return the updated value
+ * @since 1.8
+ */
+ public final V accumulateAndGet(T obj, V x,
+ BinaryOperator<V> accumulatorFunction) {
+ V prev, next;
+ do {
+ prev = get(obj);
+ next = accumulatorFunction.apply(prev, x);
+ } while (!compareAndSet(obj, prev, next));
+ return next;
}
private static final class AtomicReferenceFieldUpdaterImpl<T,V>
extends AtomicReferenceFieldUpdater<T,V> {
- private static final Unsafe unsafe = Unsafe.getUnsafe();
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
private final long offset;
- private final Class<T> tclass;
- private final Class<V> vclass;
+ /**
+ * if field is protected, the subclass constructing updater, else
+ * the same as tclass
+ */
private final Class<?> cclass;
+ /** class holding the field */
+ private final Class<T> tclass;
+ /** field value type */
+ private final Class<V> vclass;
/*
* Internal type checks within all update methods contain
@@ -177,26 +281,25 @@
*/
AtomicReferenceFieldUpdaterImpl(final Class<T> tclass,
- Class<V> vclass,
- final String fieldName) {
+ final Class<V> vclass,
+ final String fieldName,
+ final Class<?> caller) {
final Field field;
final Class<?> fieldClass;
- final Class<?> caller;
final int modifiers;
try {
field = tclass.getDeclaredField(fieldName); // android-changed
- caller = VMStack.getStackClass2(); // android-changed
modifiers = field.getModifiers();
- // BEGIN android-removed
- // sun.reflect.misc.ReflectUtil.ensureMemberAccess(
- // caller, tclass, null, modifiers);
- // ClassLoader cl = tclass.getClassLoader();
- // ClassLoader ccl = caller.getClassLoader();
- // if ((ccl != null) && (ccl != cl) &&
- // ((cl == null) || !isAncestor(cl, ccl))) {
- // sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
- // }
- // END android-removed
+ // BEGIN android-removed
+ // sun.reflect.misc.ReflectUtil.ensureMemberAccess(
+ // caller, tclass, null, modifiers);
+ // ClassLoader cl = tclass.getClassLoader();
+ // ClassLoader ccl = caller.getClassLoader();
+ // if ((ccl != null) && (ccl != cl) &&
+ // ((cl == null) || !isAncestor(cl, ccl))) {
+ // sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
+ // }
+ // END android-removed
fieldClass = field.getType();
// BEGIN android-removed
// } catch (PrivilegedActionException pae) {
@@ -208,18 +311,16 @@
if (vclass != fieldClass)
throw new ClassCastException();
+ if (vclass.isPrimitive())
+ throw new IllegalArgumentException("Must be reference type");
if (!Modifier.isVolatile(modifiers))
throw new IllegalArgumentException("Must be volatile type");
- this.cclass = (Modifier.isProtected(modifiers) &&
- caller != tclass) ? caller : null;
+ this.cclass = (Modifier.isProtected(modifiers)) ? caller : tclass;
this.tclass = tclass;
- if (vclass == Object.class)
- this.vclass = null;
- else
- this.vclass = vclass;
- offset = unsafe.objectFieldOffset(field);
+ this.vclass = vclass;
+ this.offset = U.objectFieldOffset(field);
}
// BEGIN android-removed
@@ -228,87 +329,90 @@
// * classloader's delegation chain.
// * Equivalent to the inaccessible: first.isAncestor(second).
// */
- //
// private static boolean isAncestor(ClassLoader first, ClassLoader second) {
// ClassLoader acl = first;
// do {
// acl = acl.getParent();
// if (second == acl) {
// return true;
- // }
+ // }
// } while (acl != null);
// return false;
// }
// END android-removed
- void targetCheck(T obj) {
- if (!tclass.isInstance(obj))
+ /**
+ * Checks that target argument is instance of cclass. On
+ * failure, throws cause.
+ */
+ private final void accessCheck(T obj) {
+ if (!cclass.isInstance(obj))
+ throwAccessCheckException(obj);
+ }
+
+ /**
+ * Throws access exception if accessCheck failed due to
+ * protected access, else ClassCastException.
+ */
+ private final void throwAccessCheckException(T obj) {
+ if (cclass == tclass)
throw new ClassCastException();
- if (cclass != null)
- ensureProtectedAccess(obj);
+ else
+ throw new RuntimeException(
+ new IllegalAccessException(
+ "Class " +
+ cclass.getName() +
+ " can not access a protected member of class " +
+ tclass.getName() +
+ " using an instance of " +
+ obj.getClass().getName()));
}
- void updateCheck(T obj, V update) {
- if (!tclass.isInstance(obj) ||
- (update != null && vclass != null && !vclass.isInstance(update)))
- throw new ClassCastException();
- if (cclass != null)
- ensureProtectedAccess(obj);
+ private final void valueCheck(V v) {
+ if (v != null && !(vclass.isInstance(v)))
+ throwCCE();
}
- public boolean compareAndSet(T obj, V expect, V update) {
- if (obj == null || obj.getClass() != tclass || cclass != null ||
- (update != null && vclass != null &&
- vclass != update.getClass()))
- updateCheck(obj, update);
- return unsafe.compareAndSwapObject(obj, offset, expect, update);
+ static void throwCCE() {
+ throw new ClassCastException();
}
- public boolean weakCompareAndSet(T obj, V expect, V update) {
+ public final boolean compareAndSet(T obj, V expect, V update) {
+ accessCheck(obj);
+ valueCheck(update);
+ return U.compareAndSwapObject(obj, offset, expect, update);
+ }
+
+ public final boolean weakCompareAndSet(T obj, V expect, V update) {
// same implementation as strong form for now
- if (obj == null || obj.getClass() != tclass || cclass != null ||
- (update != null && vclass != null &&
- vclass != update.getClass()))
- updateCheck(obj, update);
- return unsafe.compareAndSwapObject(obj, offset, expect, update);
+ accessCheck(obj);
+ valueCheck(update);
+ return U.compareAndSwapObject(obj, offset, expect, update);
}
- public void set(T obj, V newValue) {
- if (obj == null || obj.getClass() != tclass || cclass != null ||
- (newValue != null && vclass != null &&
- vclass != newValue.getClass()))
- updateCheck(obj, newValue);
- unsafe.putObjectVolatile(obj, offset, newValue);
+ public final void set(T obj, V newValue) {
+ accessCheck(obj);
+ valueCheck(newValue);
+ U.putObjectVolatile(obj, offset, newValue);
}
- public void lazySet(T obj, V newValue) {
- if (obj == null || obj.getClass() != tclass || cclass != null ||
- (newValue != null && vclass != null &&
- vclass != newValue.getClass()))
- updateCheck(obj, newValue);
- unsafe.putOrderedObject(obj, offset, newValue);
+ public final void lazySet(T obj, V newValue) {
+ accessCheck(obj);
+ valueCheck(newValue);
+ U.putOrderedObject(obj, offset, newValue);
}
@SuppressWarnings("unchecked")
- public V get(T obj) {
- if (obj == null || obj.getClass() != tclass || cclass != null)
- targetCheck(obj);
- return (V)unsafe.getObjectVolatile(obj, offset);
+ public final V get(T obj) {
+ accessCheck(obj);
+ return (V)U.getObjectVolatile(obj, offset);
}
- private void ensureProtectedAccess(T obj) {
- if (cclass.isInstance(obj)) {
- return;
- }
- throw new RuntimeException(
- new IllegalAccessException("Class " +
- cclass.getName() +
- " can not access a protected member of class " +
- tclass.getName() +
- " using an instance of " +
- obj.getClass().getName()
- )
- );
+ @SuppressWarnings("unchecked")
+ public final V getAndSet(T obj, V newValue) {
+ accessCheck(obj);
+ valueCheck(newValue);
+ return (V)U.getAndSetObject(obj, offset, newValue);
}
}
}
diff --git a/luni/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java b/luni/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java
index 1449856..69fab23 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java
@@ -91,7 +91,7 @@
* @param newReference the new value for the reference
* @param expectedStamp the expected value of the stamp
* @param newStamp the new value for the stamp
- * @return true if successful
+ * @return {@code true} if successful
*/
public boolean weakCompareAndSet(V expectedReference,
V newReference,
@@ -111,7 +111,7 @@
* @param newReference the new value for the reference
* @param expectedStamp the expected value of the stamp
* @param newStamp the new value for the stamp
- * @return true if successful
+ * @return {@code true} if successful
*/
public boolean compareAndSet(V expectedReference,
V newReference,
@@ -149,7 +149,7 @@
*
* @param expectedReference the expected value of the reference
* @param newStamp the new value for the stamp
- * @return true if successful
+ * @return {@code true} if successful
*/
public boolean attemptStamp(V expectedReference, int newStamp) {
Pair<V> current = pair;
@@ -161,23 +161,18 @@
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
- private static final long pairOffset =
- objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class);
-
- private boolean casPair(Pair<V> cmp, Pair<V> val) {
- return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long PAIR;
+ static {
+ try {
+ PAIR = U.objectFieldOffset
+ (AtomicStampedReference.class.getDeclaredField("pair"));
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
}
- static long objectFieldOffset(sun.misc.Unsafe UNSAFE,
- String field, Class<?> klazz) {
- try {
- return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field));
- } catch (NoSuchFieldException e) {
- // Convert Exception to corresponding Error
- NoSuchFieldError error = new NoSuchFieldError(field);
- error.initCause(e);
- throw error;
- }
+ private boolean casPair(Pair<V> cmp, Pair<V> val) {
+ return U.compareAndSwapObject(this, PAIR, cmp, val);
}
}
diff --git a/luni/src/main/java/java/util/concurrent/atomic/DoubleAccumulator.java b/luni/src/main/java/java/util/concurrent/atomic/DoubleAccumulator.java
new file mode 100644
index 0000000..1ea088d
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/atomic/DoubleAccumulator.java
@@ -0,0 +1,270 @@
+/*
+ * 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/
+ */
+
+package java.util.concurrent.atomic;
+
+import java.io.Serializable;
+import java.util.function.DoubleBinaryOperator;
+
+/**
+ * One or more variables that together maintain a running {@code double}
+ * value updated using a supplied function. When updates (method
+ * {@link #accumulate}) are contended across threads, the set of variables
+ * may grow dynamically to reduce contention. Method {@link #get}
+ * (or, equivalently, {@link #doubleValue}) returns the current value
+ * across the variables maintaining updates.
+ *
+ * <p>This class is usually preferable to alternatives when multiple
+ * threads update a common value that is used for purposes such as
+ * summary statistics that are frequently updated but less frequently
+ * read.
+ *
+ * <p>The supplied accumulator function should be side-effect-free,
+ * since it may be re-applied when attempted updates fail due to
+ * contention among threads. The function is applied with the current
+ * value as its first argument, and the given update as the second
+ * argument. For example, to maintain a running maximum value, you
+ * could supply {@code Double::max} along with {@code
+ * Double.NEGATIVE_INFINITY} as the identity. The order of
+ * accumulation within or across threads is not guaranteed. Thus, this
+ * class may not be applicable if numerical stability is required,
+ * especially when combining values of substantially different orders
+ * of magnitude.
+ *
+ * <p>Class {@link DoubleAdder} provides analogs of the functionality
+ * of this class for the common special case of maintaining sums. The
+ * call {@code new DoubleAdder()} is equivalent to {@code new
+ * DoubleAccumulator((x, y) -> x + y, 0.0)}.
+ *
+ * <p>This class extends {@link Number}, but does <em>not</em> define
+ * methods such as {@code equals}, {@code hashCode} and {@code
+ * compareTo} because instances are expected to be mutated, and so are
+ * not useful as collection keys.
+ *
+ * @since 1.8
+ * @author Doug Lea
+ */
+public class DoubleAccumulator extends Striped64 implements Serializable {
+ private static final long serialVersionUID = 7249069246863182397L;
+
+ private final DoubleBinaryOperator function;
+ private final long identity; // use long representation
+
+ /**
+ * Creates a new instance using the given accumulator function
+ * and identity element.
+ * @param accumulatorFunction a side-effect-free function of two arguments
+ * @param identity identity (initial value) for the accumulator function
+ */
+ public DoubleAccumulator(DoubleBinaryOperator accumulatorFunction,
+ double identity) {
+ this.function = accumulatorFunction;
+ base = this.identity = Double.doubleToRawLongBits(identity);
+ }
+
+ /**
+ * Updates with the given value.
+ *
+ * @param x the value
+ */
+ public void accumulate(double x) {
+ Cell[] as; long b, v, r; int m; Cell a;
+ if ((as = cells) != null ||
+ (r = Double.doubleToRawLongBits
+ (function.applyAsDouble
+ (Double.longBitsToDouble(b = base), x))) != b && !casBase(b, r)) {
+ boolean uncontended = true;
+ if (as == null || (m = as.length - 1) < 0 ||
+ (a = as[getProbe() & m]) == null ||
+ !(uncontended =
+ (r = Double.doubleToRawLongBits
+ (function.applyAsDouble
+ (Double.longBitsToDouble(v = a.value), x))) == v ||
+ a.cas(v, r)))
+ doubleAccumulate(x, function, uncontended);
+ }
+ }
+
+ /**
+ * Returns the current value. The returned value is <em>NOT</em>
+ * an atomic snapshot; invocation in the absence of concurrent
+ * updates returns an accurate result, but concurrent updates that
+ * occur while the value is being calculated might not be
+ * incorporated.
+ *
+ * @return the current value
+ */
+ public double get() {
+ Cell[] as = cells;
+ double result = Double.longBitsToDouble(base);
+ if (as != null) {
+ for (Cell a : as)
+ if (a != null)
+ result = function.applyAsDouble
+ (result, Double.longBitsToDouble(a.value));
+ }
+ return result;
+ }
+
+ /**
+ * Resets variables maintaining updates to the identity value.
+ * This method may be a useful alternative to creating a new
+ * updater, but is only effective if there are no concurrent
+ * updates. Because this method is intrinsically racy, it should
+ * only be used when it is known that no threads are concurrently
+ * updating.
+ */
+ public void reset() {
+ Cell[] as = cells;
+ base = identity;
+ if (as != null) {
+ for (Cell a : as)
+ if (a != null)
+ a.reset(identity);
+ }
+ }
+
+ /**
+ * Equivalent in effect to {@link #get} followed by {@link
+ * #reset}. This method may apply for example during quiescent
+ * points between multithreaded computations. If there are
+ * updates concurrent with this method, the returned value is
+ * <em>not</em> guaranteed to be the final value occurring before
+ * the reset.
+ *
+ * @return the value before reset
+ */
+ public double getThenReset() {
+ Cell[] as = cells;
+ double result = Double.longBitsToDouble(base);
+ base = identity;
+ if (as != null) {
+ for (Cell a : as) {
+ if (a != null) {
+ double v = Double.longBitsToDouble(a.value);
+ a.reset(identity);
+ result = function.applyAsDouble(result, v);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns the String representation of the current value.
+ * @return the String representation of the current value
+ */
+ public String toString() {
+ return Double.toString(get());
+ }
+
+ /**
+ * Equivalent to {@link #get}.
+ *
+ * @return the current value
+ */
+ public double doubleValue() {
+ return get();
+ }
+
+ /**
+ * Returns the {@linkplain #get current value} as a {@code long}
+ * after a narrowing primitive conversion.
+ */
+ public long longValue() {
+ return (long)get();
+ }
+
+ /**
+ * Returns the {@linkplain #get current value} as an {@code int}
+ * after a narrowing primitive conversion.
+ */
+ public int intValue() {
+ return (int)get();
+ }
+
+ /**
+ * Returns the {@linkplain #get current value} as a {@code float}
+ * after a narrowing primitive conversion.
+ */
+ public float floatValue() {
+ return (float)get();
+ }
+
+ /**
+ * Serialization proxy, used to avoid reference to the non-public
+ * Striped64 superclass in serialized forms.
+ * @serial include
+ */
+ private static class SerializationProxy implements Serializable {
+ private static final long serialVersionUID = 7249069246863182397L;
+
+ /**
+ * The current value returned by get().
+ * @serial
+ */
+ private final double value;
+
+ /**
+ * The function used for updates.
+ * @serial
+ */
+ private final DoubleBinaryOperator function;
+
+ /**
+ * The identity value, represented as a long, as converted by
+ * {@link Double#doubleToRawLongBits}. The original identity
+ * can be recovered using {@link Double#longBitsToDouble}.
+ * @serial
+ */
+ private final long identity;
+
+ SerializationProxy(double value,
+ DoubleBinaryOperator function,
+ long identity) {
+ this.value = value;
+ this.function = function;
+ this.identity = identity;
+ }
+
+ /**
+ * Returns a {@code DoubleAccumulator} object with initial state
+ * held by this proxy.
+ *
+ * @return a {@code DoubleAccumulator} object with initial state
+ * held by this proxy
+ */
+ private Object readResolve() {
+ double d = Double.longBitsToDouble(identity);
+ DoubleAccumulator a = new DoubleAccumulator(function, d);
+ a.base = Double.doubleToRawLongBits(value);
+ return a;
+ }
+ }
+
+ /**
+ * Returns a
+ * <a href="../../../../serialized-form.html#java.util.concurrent.atomic.DoubleAccumulator.SerializationProxy">
+ * SerializationProxy</a>
+ * representing the state of this instance.
+ *
+ * @return a {@link SerializationProxy}
+ * representing the state of this instance
+ */
+ private Object writeReplace() {
+ return new SerializationProxy(get(), function, identity);
+ }
+
+ /**
+ * @param s the stream
+ * @throws java.io.InvalidObjectException always
+ */
+ private void readObject(java.io.ObjectInputStream s)
+ throws java.io.InvalidObjectException {
+ throw new java.io.InvalidObjectException("Proxy required");
+ }
+
+}
diff --git a/luni/src/main/java/java/util/concurrent/atomic/DoubleAdder.java b/luni/src/main/java/java/util/concurrent/atomic/DoubleAdder.java
new file mode 100644
index 0000000..94844d5
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/atomic/DoubleAdder.java
@@ -0,0 +1,237 @@
+/*
+ * 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/
+ */
+
+package java.util.concurrent.atomic;
+
+import java.io.Serializable;
+
+/**
+ * One or more variables that together maintain an initially zero
+ * {@code double} sum. When updates (method {@link #add}) are
+ * contended across threads, the set of variables may grow dynamically
+ * to reduce contention. Method {@link #sum} (or, equivalently {@link
+ * #doubleValue}) returns the current total combined across the
+ * variables maintaining the sum. The order of accumulation within or
+ * across threads is not guaranteed. Thus, this class may not be
+ * applicable if numerical stability is required, especially when
+ * combining values of substantially different orders of magnitude.
+ *
+ * <p>This class is usually preferable to alternatives when multiple
+ * threads update a common value that is used for purposes such as
+ * summary statistics that are frequently updated but less frequently
+ * read.
+ *
+ * <p>This class extends {@link Number}, but does <em>not</em> define
+ * methods such as {@code equals}, {@code hashCode} and {@code
+ * compareTo} because instances are expected to be mutated, and so are
+ * not useful as collection keys.
+ *
+ * @since 1.8
+ * @author Doug Lea
+ */
+public class DoubleAdder extends Striped64 implements Serializable {
+ private static final long serialVersionUID = 7249069246863182397L;
+
+ /*
+ * Note that we must use "long" for underlying representations,
+ * because there is no compareAndSet for double, due to the fact
+ * that the bitwise equals used in any CAS implementation is not
+ * the same as double-precision equals. However, we use CAS only
+ * to detect and alleviate contention, for which bitwise equals
+ * works best anyway. In principle, the long/double conversions
+ * used here should be essentially free on most platforms since
+ * they just re-interpret bits.
+ */
+
+ /**
+ * Creates a new adder with initial sum of zero.
+ */
+ public DoubleAdder() {
+ }
+
+ /**
+ * Adds the given value.
+ *
+ * @param x the value to add
+ */
+ public void add(double x) {
+ Cell[] as; long b, v; int m; Cell a;
+ if ((as = cells) != null ||
+ !casBase(b = base,
+ Double.doubleToRawLongBits
+ (Double.longBitsToDouble(b) + x))) {
+ boolean uncontended = true;
+ if (as == null || (m = as.length - 1) < 0 ||
+ (a = as[getProbe() & m]) == null ||
+ !(uncontended = a.cas(v = a.value,
+ Double.doubleToRawLongBits
+ (Double.longBitsToDouble(v) + x))))
+ doubleAccumulate(x, null, uncontended);
+ }
+ }
+
+ /**
+ * Returns the current sum. The returned value is <em>NOT</em> an
+ * atomic snapshot; invocation in the absence of concurrent
+ * updates returns an accurate result, but concurrent updates that
+ * occur while the sum is being calculated might not be
+ * incorporated. Also, because floating-point arithmetic is not
+ * strictly associative, the returned result need not be identical
+ * to the value that would be obtained in a sequential series of
+ * updates to a single variable.
+ *
+ * @return the sum
+ */
+ public double sum() {
+ Cell[] as = cells;
+ double sum = Double.longBitsToDouble(base);
+ if (as != null) {
+ for (Cell a : as)
+ if (a != null)
+ sum += Double.longBitsToDouble(a.value);
+ }
+ return sum;
+ }
+
+ /**
+ * Resets variables maintaining the sum to zero. This method may
+ * be a useful alternative to creating a new adder, but is only
+ * effective if there are no concurrent updates. Because this
+ * method is intrinsically racy, it should only be used when it is
+ * known that no threads are concurrently updating.
+ */
+ public void reset() {
+ Cell[] as = cells;
+ base = 0L; // relies on fact that double 0 must have same rep as long
+ if (as != null) {
+ for (Cell a : as)
+ if (a != null)
+ a.reset();
+ }
+ }
+
+ /**
+ * Equivalent in effect to {@link #sum} followed by {@link
+ * #reset}. This method may apply for example during quiescent
+ * points between multithreaded computations. If there are
+ * updates concurrent with this method, the returned value is
+ * <em>not</em> guaranteed to be the final value occurring before
+ * the reset.
+ *
+ * @return the sum
+ */
+ public double sumThenReset() {
+ Cell[] as = cells;
+ double sum = Double.longBitsToDouble(base);
+ base = 0L;
+ if (as != null) {
+ for (Cell a : as) {
+ if (a != null) {
+ long v = a.value;
+ a.reset();
+ sum += Double.longBitsToDouble(v);
+ }
+ }
+ }
+ return sum;
+ }
+
+ /**
+ * Returns the String representation of the {@link #sum}.
+ * @return the String representation of the {@link #sum}
+ */
+ public String toString() {
+ return Double.toString(sum());
+ }
+
+ /**
+ * Equivalent to {@link #sum}.
+ *
+ * @return the sum
+ */
+ public double doubleValue() {
+ return sum();
+ }
+
+ /**
+ * Returns the {@link #sum} as a {@code long} after a
+ * narrowing primitive conversion.
+ */
+ public long longValue() {
+ return (long)sum();
+ }
+
+ /**
+ * Returns the {@link #sum} as an {@code int} after a
+ * narrowing primitive conversion.
+ */
+ public int intValue() {
+ return (int)sum();
+ }
+
+ /**
+ * Returns the {@link #sum} as a {@code float}
+ * after a narrowing primitive conversion.
+ */
+ public float floatValue() {
+ return (float)sum();
+ }
+
+ /**
+ * Serialization proxy, used to avoid reference to the non-public
+ * Striped64 superclass in serialized forms.
+ * @serial include
+ */
+ private static class SerializationProxy implements Serializable {
+ private static final long serialVersionUID = 7249069246863182397L;
+
+ /**
+ * The current value returned by sum().
+ * @serial
+ */
+ private final double value;
+
+ SerializationProxy(DoubleAdder a) {
+ value = a.sum();
+ }
+
+ /**
+ * Returns a {@code DoubleAdder} object with initial state
+ * held by this proxy.
+ *
+ * @return a {@code DoubleAdder} object with initial state
+ * held by this proxy
+ */
+ private Object readResolve() {
+ DoubleAdder a = new DoubleAdder();
+ a.base = Double.doubleToRawLongBits(value);
+ return a;
+ }
+ }
+
+ /**
+ * Returns a
+ * <a href="../../../../serialized-form.html#java.util.concurrent.atomic.DoubleAdder.SerializationProxy">
+ * SerializationProxy</a>
+ * representing the state of this instance.
+ *
+ * @return a {@link SerializationProxy}
+ * representing the state of this instance
+ */
+ private Object writeReplace() {
+ return new SerializationProxy(this);
+ }
+
+ /**
+ * @param s the stream
+ * @throws java.io.InvalidObjectException always
+ */
+ private void readObject(java.io.ObjectInputStream s)
+ throws java.io.InvalidObjectException {
+ throw new java.io.InvalidObjectException("Proxy required");
+ }
+
+}
diff --git a/luni/src/main/java/java/util/concurrent/atomic/Fences.java b/luni/src/main/java/java/util/concurrent/atomic/Fences.java
deleted file mode 100644
index d907faf..0000000
--- a/luni/src/main/java/java/util/concurrent/atomic/Fences.java
+++ /dev/null
@@ -1,538 +0,0 @@
-/*
- * 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/
- */
-
-package java.util.concurrent.atomic;
-
-/**
- * A set of methods providing fine-grained control over happens-before
- * and synchronization order relations among reads and/or writes. The
- * methods of this class are designed for use in uncommon situations
- * where declaring variables {@code volatile} or {@code final}, using
- * instances of atomic classes, using {@code synchronized} blocks or
- * methods, or using other synchronization facilities are not possible
- * or do not provide the desired control.
- *
- * <p><b>Memory Ordering.</b> There are three methods for controlling
- * ordering relations among memory accesses (i.e., reads and
- * writes). Method {@code orderWrites} is typically used to enforce
- * order between two writes, and {@code orderAccesses} between a write
- * and a read. Method {@code orderReads} is used to enforce order
- * between two reads with respect to other {@code orderWrites} and/or
- * {@code orderAccesses} invocations. The formally specified
- * properties of these methods described below provide
- * platform-independent guarantees that are honored by all levels of a
- * platform (compilers, systems, processors). The use of these
- * methods may result in the suppression of otherwise valid compiler
- * transformations and optimizations that could visibly violate the
- * specified orderings, and may or may not entail the use of
- * processor-level "memory barrier" instructions.
- *
- * <p>Each ordering method accepts a {@code ref} argument, and
- * controls ordering among accesses with respect to this reference.
- * Invocations must be placed <em>between</em> accesses performed in
- * expression evaluations and assignment statements to control the
- * orderings of prior versus subsequent accesses appearing in program
- * order. These methods also return their arguments to simplify
- * correct usage in these contexts.
- *
- * <p>Usages of ordering methods almost always take one of the forms
- * illustrated in the examples below. These idioms arrange some of
- * the ordering properties associated with {@code volatile} and
- * related language-based constructions, but without other
- * compile-time and runtime benefits that make language-based
- * constructions far better choices when they are applicable. Usages
- * should be restricted to the control of strictly internal
- * implementation matters inside a class or package, and must either
- * avoid or document any consequent violations of ordering or safety
- * properties expected by users of a class employing them.
- *
- * <p><b>Reachability.</b> Method {@code reachabilityFence}
- * establishes an ordering for strong reachability (as defined in the
- * {@link java.lang.ref} package specification) with respect to
- * garbage collection. Method {@code reachabilityFence} differs from
- * the others in that it controls relations that are otherwise only
- * implicit in a program -- the reachability conditions triggering
- * garbage collection. As illustrated in the sample usages below,
- * this method is applicable only when reclamation may have visible
- * effects, which is possible for objects with finalizers (see Section
- * 12.6 of the Java Language Specification) that are implemented in
- * ways that rely on ordering control for correctness.
- *
- * <p><b>Sample Usages</b>
- *
- * <p><b>Safe publication.</b> With care, method {@code orderWrites}
- * may be used to obtain the memory safety effects of {@code final}
- * for a field that cannot be declared as {@code final}, because its
- * primary initialization cannot be performed in a constructor, in
- * turn because it is used in a framework requiring that all classes
- * have a no-argument constructor; as in:
- *
- * <pre> {@code
- * class WidgetHolder {
- * private Widget widget;
- * public WidgetHolder() {}
- * public static WidgetHolder newWidgetHolder(Params params) {
- * WidgetHolder h = new WidgetHolder();
- * h.widget = new Widget(params);
- * return Fences.orderWrites(h);
- * }
- * }}</pre>
- *
- * Here, the invocation of {@code orderWrites} ensures that the
- * effects of the widget assignment are ordered before those of any
- * (unknown) subsequent stores of {@code h} in other variables that
- * make {@code h} available for use by other objects. Initialization
- * sequences using {@code orderWrites} require more care than those
- * involving {@code final} fields. When {@code final} is not used,
- * compilers cannot help you to ensure that the field is set correctly
- * across all usages. You must fully initialize objects
- * <em>before</em> the {@code orderWrites} invocation that makes
- * references to them safe to assign to accessible variables. Further,
- * initialization sequences must not internally "leak" the reference
- * by using it as an argument to a callback method or adding it to a
- * static data structure. If less constrained usages were required,
- * it may be possible to cope using more extensive sets of fences, or
- * as a normally better choice, using synchronization (locking).
- * Conversely, if it were possible to do so, the best option would be
- * to rewrite class {@code WidgetHolder} to use {@code final}.
- *
- * <p>An alternative approach is to place similar mechanics in the
- * (sole) method that makes such objects available for use by others.
- * Here is a stripped-down example illustrating the essentials. In
- * practice, among other changes, you would use access methods instead
- * of a public field.
- *
- * <pre> {@code
- * class AnotherWidgetHolder {
- * public Widget widget;
- * void publish(Widget w) {
- * this.widget = Fences.orderWrites(w);
- * }
- * // ...
- * }}</pre>
- *
- * In this case, the {@code orderWrites} invocation occurs before the
- * store making the object available. Correctness again relies on
- * ensuring that there are no leaks prior to invoking this method, and
- * that it really is the <em>only</em> means of accessing the
- * published object. This approach is not often applicable --
- * normally you would publish objects using a thread-safe collection
- * that itself guarantees the expected ordering relations. However, it
- * may come into play in the construction of such classes themselves.
- *
- * <p><b>Safely updating fields.</b> Outside of the initialization
- * idioms illustrated above, Fence methods ordering writes must be
- * paired with those ordering reads. To illustrate, suppose class
- * {@code c} contains an accessible variable {@code data} that should
- * have been declared as {@code volatile} but wasn't:
- *
- * <pre> {@code
- * class C {
- * Object data; // need volatile access but not volatile
- * // ...
- * }
- *
- * class App {
- * Object getData(C c) {
- * return Fences.orderReads(c).data;
- * }
- *
- * void setData(C c) {
- * Object newValue = ...;
- * c.data = Fences.orderWrites(newValue);
- * Fences.orderAccesses(c);
- * }
- * // ...
- * }}</pre>
- *
- * Method {@code getData} provides an emulation of {@code volatile}
- * reads of (non-long/double) fields by ensuring that the read of
- * {@code c} obtained as an argument is ordered before subsequent
- * reads using this reference, and then performs the read of its
- * field. Method {@code setData} provides an emulation of volatile
- * writes, ensuring that all other relevant writes have completed,
- * then performing the assignment, and then ensuring that the write is
- * ordered before any other access. These techniques may apply even
- * when fields are not directly accessible, in which case calls to
- * fence methods would surround calls to methods such as {@code
- * c.getData()}. However, these techniques cannot be applied to
- * {@code long} or {@code double} fields because reads and writes of
- * fields of these types are not guaranteed to be
- * atomic. Additionally, correctness may require that all accesses of
- * such data use these kinds of wrapper methods, which you would need
- * to manually ensure.
- *
- * <p>More generally, Fence methods can be used in this way to achieve
- * the safety properties of {@code volatile}. However their use does
- * not necessarily guarantee the full sequential consistency
- * properties specified in the Java Language Specification chapter 17
- * for programs using {@code volatile}. In particular, emulation using
- * Fence methods is not guaranteed to maintain the property that
- * {@code volatile} operations performed by different threads are
- * observed in the same order by all observer threads.
- *
- * <p><b>Acquire/Release management of threadsafe objects</b>. It may
- * be possible to use weaker conventions for volatile-like variables
- * when they are used to keep track of objects that fully manage their
- * own thread-safety and synchronization. Here, an acquiring read
- * operation remains the same as a volatile-read, but a releasing
- * write differs by virtue of not itself ensuring an ordering of its
- * write with subsequent reads, because the required effects are
- * already ensured by the referenced objects.
- * For example:
- *
- * <pre> {@code
- * class Item {
- * synchronized f(); // ALL methods are synchronized
- * // ...
- * }
- *
- * class ItemHolder {
- * private Item item;
- * Item acquireItem() {
- * return Fences.orderReads(item);
- * }
- *
- * void releaseItem(Item x) {
- * item = Fences.orderWrites(x);
- * }
- *
- * // ...
- * }}</pre>
- *
- * Because this construction avoids use of {@code orderAccesses},
- * which is typically more costly than the other fence methods, it may
- * result in better performance than using {@code volatile} or its
- * emulation. However, as is the case with most applications of fence
- * methods, correctness relies on the usage context -- here, the
- * thread safety of {@code Item}, as well as the lack of need for full
- * volatile semantics inside this class itself. However, the second
- * concern means that it can be difficult to extend the {@code
- * ItemHolder} class in this example to be more useful.
- *
- * <p><b>Avoiding premature finalization.</b> Finalization may occur
- * whenever a Java Virtual Machine detects that no reference to an
- * object will ever be stored in the heap: A garbage collector may
- * reclaim an object even if the fields of that object are still in
- * use, so long as the object has otherwise become unreachable. This
- * may have surprising and undesirable effects in cases such as the
- * following example in which the bookkeeping associated with a class
- * is managed through array indices. Here, method {@code action}
- * uses a {@code reachabilityFence} to ensure that the Resource
- * object is not reclaimed before bookkeeping on an associated
- * ExternalResource has been performed; in particular here, to ensure
- * that the array slot holding the ExternalResource is not nulled out
- * in method {@link Object#finalize}, which may otherwise run
- * concurrently.
- *
- * <pre> {@code
- * class Resource {
- * private static ExternalResource[] externalResourceArray = ...
- *
- * int myIndex;
- * Resource(...) {
- * myIndex = ...
- * externalResourceArray[myIndex] = ...;
- * ...
- * }
- * protected void finalize() {
- * externalResourceArray[myIndex] = null;
- * ...
- * }
- * public void action() {
- * try {
- * // ...
- * int i = myIndex;
- * Resource.update(externalResourceArray[i]);
- * } finally {
- * Fences.reachabilityFence(this);
- * }
- * }
- * private static void update(ExternalResource ext) {
- * ext.status = ...;
- * }
- * }}</pre>
- *
- * Here, the call to {@code reachabilityFence} is nonintuitively
- * placed <em>after</em> the call to {@code update}, to ensure that
- * the array slot is not nulled out by {@link Object#finalize} before
- * the update, even if the call to {@code action} was the last use of
- * this object. This might be the case if for example a usage in a
- * user program had the form {@code new Resource().action();} which
- * retains no other reference to this Resource. While probably
- * overkill here, {@code reachabilityFence} is placed in a {@code
- * finally} block to ensure that it is invoked across all paths in the
- * method. In a method with more complex control paths, you might
- * need further precautions to ensure that {@code reachabilityFence}
- * is encountered along all of them.
- *
- * <p>It is sometimes possible to better encapsulate use of
- * {@code reachabilityFence}. Continuing the above example, if it
- * were OK for the call to method update to proceed even if the
- * finalizer had already executed (nulling out slot), then you could
- * localize use of {@code reachabilityFence}:
- *
- * <pre> {@code
- * public void action2() {
- * // ...
- * Resource.update(getExternalResource());
- * }
- * private ExternalResource getExternalResource() {
- * ExternalResource ext = externalResourceArray[myIndex];
- * Fences.reachabilityFence(this);
- * return ext;
- * }}</pre>
- *
- * <p>Method {@code reachabilityFence} is not required in
- * constructions that themselves ensure reachability. For example,
- * because objects that are locked cannot in general be reclaimed, it
- * would suffice if all accesses of the object, in all methods of
- * class Resource (including {@code finalize}) were enclosed in {@code
- * synchronized (this)} blocks. (Further, such blocks must not include
- * infinite loops, or themselves be unreachable, which fall into the
- * corner case exceptions to the "in general" disclaimer.) However,
- * method {@code reachabilityFence} remains a better option in cases
- * where this approach is not as efficient, desirable, or possible;
- * for example because it would encounter deadlock.
- *
- * <p><b>Formal Properties.</b>
- *
- * <p>Using the terminology of The Java Language Specification chapter
- * 17, the rules governing the semantics of the methods of this class
- * are as follows:
- *
- * <p>The following is still under construction.
- *
- * <dl>
- *
- * <dt><b>[Definitions]</b>
- * <dd>
- * <ul>
- *
- * <li>Define <em>sequenced(a, b)</em> to be true if <em>a</em>
- * occurs before <em>b</em> in <em>program order</em>.
- *
- * <li>Define <em>accesses(a, p)</em> to be true if
- * <em>a</em> is a read or write of a field (or if an array, an
- * element) of the object referenced by <em>p</em>.
- *
- * <li>Define <em>deeplyAccesses(a, p)</em> to be true if either
- * <em>accesses(a, p)</em> or <em>deeplyAccesses(a, q)</em> where
- * <em>q</em> is the value seen by some read <em>r</em>
- * such that <em>accesses(r, p)</em>.
- *
- * </ul>
- * <dt><b>[Matching]</b>
- * <dd>Given:
- *
- * <ul>
- *
- * <li><em>p</em>, a reference to an object
- *
- * <li><em>wf</em>, an invocation of {@code orderWrites(p)} or
- * {@code orderAccesses(p)}
- *
- * <li><em>w</em>, a write of value <em>p</em>
- *
- * <li> <em>rf</em>, an invocation of {@code orderReads(p)} or
- * {@code orderAccesses(p)}
- *
- * <li> <em>r</em>, a read returning value <em>p</em>
- *
- * </ul>
- * If:
- * <ul>
- * <li>sequenced(wf, w)
- * <li>read <em>r</em> sees write <em>w</em>
- * <li>sequenced(r, rf)
- * </ul>
- * Then:
- * <ul>
- *
- * <li> <em>wf happens-before rf</em>
- *
- * <li> <em>wf</em> precedes <em>rf</em> in the
- * <em>synchronization order</em>
- *
- * <li> If (<em>r1</em>, <em>w1</em>) and (<em>r2</em>,
- * <em>w2</em>) are two pairs of reads and writes, both
- * respectively satisfying the above conditions for <em>p</em>,
- * and sequenced(r1, r2) then it is not the case that <em>w2
- * happens-before w1</em>.
- *
- * </ul>
- * <dt><b>[Initial Reads]</b>
- * <dd>Given:
- *
- * <ul>
- *
- * <li><em>p</em>, a reference to an object
- *
- * <li> <em>a</em>, an access where deeplyAccesses(a, p)
- *
- * <li><em>wf</em>, an invocation of {@code orderWrites(p)} or
- * {@code orderAccesses(p)}
- *
- * <li><em>w</em>, a write of value <em>p</em>
- *
- * <li> <em>r</em>, a read returning value <em>p</em>
- *
- * <li> <em>b</em>, an access where accesses(b, p)
- *
- * </ul>
- * If:
- * <ul>
- * <li>sequenced(a, wf);
- * <li>sequenced(wf, w)
- * <li>read <em>r</em> sees write <em>w</em>, and
- * <em>r</em> is the first read by some thread
- * <em>t</em> that sees value <em>p</em>
- * <li>sequenced(r, b)
- * </ul>
- * Then:
- * <ul>
- * <li> the effects of <em>b</em> are constrained
- * by the relation <em>a happens-before b</em>.
- * </ul>
- * <dt><b>[orderAccesses]</b>
- * <dd>Given:
- *
- * <ul>
- * <li><em>p</em>, a reference to an object
- * <li><em>f</em>, an invocation of {@code orderAccesses(p)}
- * </ul>
- * If:
- * <ul>
- * <li>sequenced(f, w)
- * </ul>
- *
- * Then:
- *
- * <ul>
- *
- * <li> <em>f</em> is an element of the <em>synchronization order</em>.
- *
- * </ul>
- * <dt><b>[Reachability]</b>
- * <dd>Given:
- *
- * <ul>
- *
- * <li><em>p</em>, a reference to an object
- *
- * <li><em>f</em>, an invocation of {@code reachabilityFence(p)}
- *
- * <li><em>a</em>, an access where accesses(a, p)
- *
- * <li><em>b</em>, an action (by a garbage collector) taking
- * the form of an invocation of {@code
- * p.finalize()} or of enqueuing any {@link
- * java.lang.ref.Reference} constructed with argument <em>p</em>
- *
- * </ul>
- *
- * If:
- * <ul>
- * <li>sequenced(a, f)
- * </ul>
- *
- * Then:
- *
- * <ul>
- *
- * <li> <em>a happens-before b</em>.
- *
- * </ul>
- *
- * </dl>
- *
- * @hide
- * @author Doug Lea
- */
-public class Fences {
- private Fences() {} // Non-instantiable
-
- /**
- * The methods of this class are intended to be intrinisified by a
- * JVM. However, we provide correct but inefficient Java-level
- * code that simply reads and writes a static volatile
- * variable. Without JVM support, the consistency effects are
- * stronger than necessary, and the memory contention effects can
- * be a serious performance issue.
- */
- private static volatile int theVolatile;
-
- /**
- * Informally: Ensures that a read of the given reference prior to
- * the invocation of this method occurs before a subsequent use of
- * the given reference with the effect of reading or writing a
- * field (or if an array, element) of the referenced object. The
- * use of this method is sensible only when paired with other
- * invocations of {@link #orderWrites} and/or {@link
- * #orderAccesses} for the given reference. For details, see the
- * class documentation for this class.
- *
- * @param ref the reference. If null, this method has no effect.
- * @param <T> the type of the reference
- * @return the given ref, to simplify usage
- */
- public static <T> T orderReads(T ref) {
- int ignore = theVolatile;
- return ref;
- }
-
- /**
- * Informally: Ensures that a use of the given reference with the
- * effect of reading or writing a field (or if an array, element)
- * of the referenced object, prior to the invocation of this
- * method occur before a subsequent write of the reference. For
- * details, see the class documentation for this class.
- *
- * @param ref the reference. If null, this method has no effect.
- * @param <T> the type of the reference
- * @return the given ref, to simplify usage
- */
- public static <T> T orderWrites(T ref) {
- theVolatile = 0;
- return ref;
- }
-
- /**
- * Informally: Ensures that accesses (reads or writes) using the
- * given reference prior to the invocation of this method occur
- * before subsequent accesses. For details, see the class
- * documentation for this class.
- *
- * @param ref the reference. If null, this method has no effect.
- * @param <T> the type of the reference
- * @return the given ref, to simplify usage
- */
- public static <T> T orderAccesses(T ref) {
- theVolatile = 0;
- return ref;
- }
-
- /**
- * Ensures that the object referenced by the given reference
- * remains <em>strongly reachable</em> (as defined in the {@link
- * java.lang.ref} package documentation), regardless of any prior
- * actions of the program that might otherwise cause the object to
- * become unreachable; thus, the referenced object is not
- * reclaimable by garbage collection at least until after the
- * invocation of this method. Invocation of this method does not
- * itself initiate garbage collection or finalization.
- *
- * <p>See the class-level documentation for further explanation
- * and usage examples.
- *
- * @param ref the reference. If null, this method has no effect.
- */
- public static void reachabilityFence(Object ref) {
- if (ref != null) {
- synchronized (ref) {}
- }
- }
-}
diff --git a/luni/src/main/java/java/util/concurrent/atomic/LongAccumulator.java b/luni/src/main/java/java/util/concurrent/atomic/LongAccumulator.java
new file mode 100644
index 0000000..85e3241
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/atomic/LongAccumulator.java
@@ -0,0 +1,264 @@
+/*
+ * 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/
+ */
+
+package java.util.concurrent.atomic;
+
+import java.io.Serializable;
+import java.util.function.LongBinaryOperator;
+
+/**
+ * One or more variables that together maintain a running {@code long}
+ * value updated using a supplied function. When updates (method
+ * {@link #accumulate}) are contended across threads, the set of variables
+ * may grow dynamically to reduce contention. Method {@link #get}
+ * (or, equivalently, {@link #longValue}) returns the current value
+ * across the variables maintaining updates.
+ *
+ * <p>This class is usually preferable to {@link AtomicLong} when
+ * multiple threads update a common value that is used for purposes such
+ * as collecting statistics, not for fine-grained synchronization
+ * control. Under low update contention, the two classes have similar
+ * characteristics. But under high contention, expected throughput of
+ * this class is significantly higher, at the expense of higher space
+ * consumption.
+ *
+ * <p>The order of accumulation within or across threads is not
+ * guaranteed and cannot be depended upon, so this class is only
+ * applicable to functions for which the order of accumulation does
+ * not matter. The supplied accumulator function should be
+ * side-effect-free, since it may be re-applied when attempted updates
+ * fail due to contention among threads. The function is applied with
+ * the current value as its first argument, and the given update as
+ * the second argument. For example, to maintain a running maximum
+ * value, you could supply {@code Long::max} along with {@code
+ * Long.MIN_VALUE} as the identity.
+ *
+ * <p>Class {@link LongAdder} provides analogs of the functionality of
+ * this class for the common special case of maintaining counts and
+ * sums. The call {@code new LongAdder()} is equivalent to {@code new
+ * LongAccumulator((x, y) -> x + y, 0L}.
+ *
+ * <p>This class extends {@link Number}, but does <em>not</em> define
+ * methods such as {@code equals}, {@code hashCode} and {@code
+ * compareTo} because instances are expected to be mutated, and so are
+ * not useful as collection keys.
+ *
+ * @since 1.8
+ * @author Doug Lea
+ */
+public class LongAccumulator extends Striped64 implements Serializable {
+ private static final long serialVersionUID = 7249069246863182397L;
+
+ private final LongBinaryOperator function;
+ private final long identity;
+
+ /**
+ * Creates a new instance using the given accumulator function
+ * and identity element.
+ * @param accumulatorFunction a side-effect-free function of two arguments
+ * @param identity identity (initial value) for the accumulator function
+ */
+ public LongAccumulator(LongBinaryOperator accumulatorFunction,
+ long identity) {
+ this.function = accumulatorFunction;
+ base = this.identity = identity;
+ }
+
+ /**
+ * Updates with the given value.
+ *
+ * @param x the value
+ */
+ public void accumulate(long x) {
+ Cell[] as; long b, v, r; int m; Cell a;
+ if ((as = cells) != null ||
+ (r = function.applyAsLong(b = base, x)) != b && !casBase(b, r)) {
+ boolean uncontended = true;
+ if (as == null || (m = as.length - 1) < 0 ||
+ (a = as[getProbe() & m]) == null ||
+ !(uncontended =
+ (r = function.applyAsLong(v = a.value, x)) == v ||
+ a.cas(v, r)))
+ longAccumulate(x, function, uncontended);
+ }
+ }
+
+ /**
+ * Returns the current value. The returned value is <em>NOT</em>
+ * an atomic snapshot; invocation in the absence of concurrent
+ * updates returns an accurate result, but concurrent updates that
+ * occur while the value is being calculated might not be
+ * incorporated.
+ *
+ * @return the current value
+ */
+ public long get() {
+ Cell[] as = cells;
+ long result = base;
+ if (as != null) {
+ for (Cell a : as)
+ if (a != null)
+ result = function.applyAsLong(result, a.value);
+ }
+ return result;
+ }
+
+ /**
+ * Resets variables maintaining updates to the identity value.
+ * This method may be a useful alternative to creating a new
+ * updater, but is only effective if there are no concurrent
+ * updates. Because this method is intrinsically racy, it should
+ * only be used when it is known that no threads are concurrently
+ * updating.
+ */
+ public void reset() {
+ Cell[] as = cells;
+ base = identity;
+ if (as != null) {
+ for (Cell a : as)
+ if (a != null)
+ a.reset(identity);
+ }
+ }
+
+ /**
+ * Equivalent in effect to {@link #get} followed by {@link
+ * #reset}. This method may apply for example during quiescent
+ * points between multithreaded computations. If there are
+ * updates concurrent with this method, the returned value is
+ * <em>not</em> guaranteed to be the final value occurring before
+ * the reset.
+ *
+ * @return the value before reset
+ */
+ public long getThenReset() {
+ Cell[] as = cells;
+ long result = base;
+ base = identity;
+ if (as != null) {
+ for (Cell a : as) {
+ if (a != null) {
+ long v = a.value;
+ a.reset(identity);
+ result = function.applyAsLong(result, v);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns the String representation of the current value.
+ * @return the String representation of the current value
+ */
+ public String toString() {
+ return Long.toString(get());
+ }
+
+ /**
+ * Equivalent to {@link #get}.
+ *
+ * @return the current value
+ */
+ public long longValue() {
+ return get();
+ }
+
+ /**
+ * Returns the {@linkplain #get current value} as an {@code int}
+ * after a narrowing primitive conversion.
+ */
+ public int intValue() {
+ return (int)get();
+ }
+
+ /**
+ * Returns the {@linkplain #get current value} as a {@code float}
+ * after a widening primitive conversion.
+ */
+ public float floatValue() {
+ return (float)get();
+ }
+
+ /**
+ * Returns the {@linkplain #get current value} as a {@code double}
+ * after a widening primitive conversion.
+ */
+ public double doubleValue() {
+ return (double)get();
+ }
+
+ /**
+ * Serialization proxy, used to avoid reference to the non-public
+ * Striped64 superclass in serialized forms.
+ * @serial include
+ */
+ private static class SerializationProxy implements Serializable {
+ private static final long serialVersionUID = 7249069246863182397L;
+
+ /**
+ * The current value returned by get().
+ * @serial
+ */
+ private final long value;
+
+ /**
+ * The function used for updates.
+ * @serial
+ */
+ private final LongBinaryOperator function;
+
+ /**
+ * The identity value.
+ * @serial
+ */
+ private final long identity;
+
+ SerializationProxy(long value,
+ LongBinaryOperator function,
+ long identity) {
+ this.value = value;
+ this.function = function;
+ this.identity = identity;
+ }
+
+ /**
+ * Returns a {@code LongAccumulator} object with initial state
+ * held by this proxy.
+ *
+ * @return a {@code LongAccumulator} object with initial state
+ * held by this proxy
+ */
+ private Object readResolve() {
+ LongAccumulator a = new LongAccumulator(function, identity);
+ a.base = value;
+ return a;
+ }
+ }
+
+ /**
+ * Returns a
+ * <a href="../../../../serialized-form.html#java.util.concurrent.atomic.LongAccumulator.SerializationProxy">
+ * SerializationProxy</a>
+ * representing the state of this instance.
+ *
+ * @return a {@link SerializationProxy}
+ * representing the state of this instance
+ */
+ private Object writeReplace() {
+ return new SerializationProxy(get(), function, identity);
+ }
+
+ /**
+ * @param s the stream
+ * @throws java.io.InvalidObjectException always
+ */
+ private void readObject(java.io.ObjectInputStream s)
+ throws java.io.InvalidObjectException {
+ throw new java.io.InvalidObjectException("Proxy required");
+ }
+
+}
diff --git a/luni/src/main/java/java/util/concurrent/atomic/LongAdder.java b/luni/src/main/java/java/util/concurrent/atomic/LongAdder.java
new file mode 100644
index 0000000..a17d52a
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/atomic/LongAdder.java
@@ -0,0 +1,238 @@
+/*
+ * 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/
+ */
+
+package java.util.concurrent.atomic;
+
+import java.io.Serializable;
+
+/**
+ * One or more variables that together maintain an initially zero
+ * {@code long} sum. When updates (method {@link #add}) are contended
+ * across threads, the set of variables may grow dynamically to reduce
+ * contention. Method {@link #sum} (or, equivalently, {@link
+ * #longValue}) returns the current total combined across the
+ * variables maintaining the sum.
+ *
+ * <p>This class is usually preferable to {@link AtomicLong} when
+ * multiple threads update a common sum that is used for purposes such
+ * as collecting statistics, not for fine-grained synchronization
+ * control. Under low update contention, the two classes have similar
+ * characteristics. But under high contention, expected throughput of
+ * this class is significantly higher, at the expense of higher space
+ * consumption.
+ *
+ * <p>LongAdders can be used with a {@link
+ * java.util.concurrent.ConcurrentHashMap} to maintain a scalable
+ * frequency map (a form of histogram or multiset). For example, to
+ * add a count to a {@code ConcurrentHashMap<String,LongAdder> freqs},
+ * initializing if not already present, you can use {@code
+ * freqs.computeIfAbsent(key, k -> new LongAdder()).increment();}
+ *
+ * <p>This class extends {@link Number}, but does <em>not</em> define
+ * methods such as {@code equals}, {@code hashCode} and {@code
+ * compareTo} because instances are expected to be mutated, and so are
+ * not useful as collection keys.
+ *
+ * @since 1.8
+ * @author Doug Lea
+ */
+public class LongAdder extends Striped64 implements Serializable {
+ private static final long serialVersionUID = 7249069246863182397L;
+
+ /**
+ * Creates a new adder with initial sum of zero.
+ */
+ public LongAdder() {
+ }
+
+ /**
+ * Adds the given value.
+ *
+ * @param x the value to add
+ */
+ public void add(long x) {
+ Cell[] as; long b, v; int m; Cell a;
+ if ((as = cells) != null || !casBase(b = base, b + x)) {
+ boolean uncontended = true;
+ if (as == null || (m = as.length - 1) < 0 ||
+ (a = as[getProbe() & m]) == null ||
+ !(uncontended = a.cas(v = a.value, v + x)))
+ longAccumulate(x, null, uncontended);
+ }
+ }
+
+ /**
+ * Equivalent to {@code add(1)}.
+ */
+ public void increment() {
+ add(1L);
+ }
+
+ /**
+ * Equivalent to {@code add(-1)}.
+ */
+ public void decrement() {
+ add(-1L);
+ }
+
+ /**
+ * Returns the current sum. The returned value is <em>NOT</em> an
+ * atomic snapshot; invocation in the absence of concurrent
+ * updates returns an accurate result, but concurrent updates that
+ * occur while the sum is being calculated might not be
+ * incorporated.
+ *
+ * @return the sum
+ */
+ public long sum() {
+ Cell[] as = cells;
+ long sum = base;
+ if (as != null) {
+ for (Cell a : as)
+ if (a != null)
+ sum += a.value;
+ }
+ return sum;
+ }
+
+ /**
+ * Resets variables maintaining the sum to zero. This method may
+ * be a useful alternative to creating a new adder, but is only
+ * effective if there are no concurrent updates. Because this
+ * method is intrinsically racy, it should only be used when it is
+ * known that no threads are concurrently updating.
+ */
+ public void reset() {
+ Cell[] as = cells;
+ base = 0L;
+ if (as != null) {
+ for (Cell a : as)
+ if (a != null)
+ a.reset();
+ }
+ }
+
+ /**
+ * Equivalent in effect to {@link #sum} followed by {@link
+ * #reset}. This method may apply for example during quiescent
+ * points between multithreaded computations. If there are
+ * updates concurrent with this method, the returned value is
+ * <em>not</em> guaranteed to be the final value occurring before
+ * the reset.
+ *
+ * @return the sum
+ */
+ public long sumThenReset() {
+ Cell[] as = cells;
+ long sum = base;
+ base = 0L;
+ if (as != null) {
+ for (Cell a : as) {
+ if (a != null) {
+ sum += a.value;
+ a.reset();
+ }
+ }
+ }
+ return sum;
+ }
+
+ /**
+ * Returns the String representation of the {@link #sum}.
+ * @return the String representation of the {@link #sum}
+ */
+ public String toString() {
+ return Long.toString(sum());
+ }
+
+ /**
+ * Equivalent to {@link #sum}.
+ *
+ * @return the sum
+ */
+ public long longValue() {
+ return sum();
+ }
+
+ /**
+ * Returns the {@link #sum} as an {@code int} after a narrowing
+ * primitive conversion.
+ */
+ public int intValue() {
+ return (int)sum();
+ }
+
+ /**
+ * Returns the {@link #sum} as a {@code float}
+ * after a widening primitive conversion.
+ */
+ public float floatValue() {
+ return (float)sum();
+ }
+
+ /**
+ * Returns the {@link #sum} as a {@code double} after a widening
+ * primitive conversion.
+ */
+ public double doubleValue() {
+ return (double)sum();
+ }
+
+ /**
+ * Serialization proxy, used to avoid reference to the non-public
+ * Striped64 superclass in serialized forms.
+ * @serial include
+ */
+ private static class SerializationProxy implements Serializable {
+ private static final long serialVersionUID = 7249069246863182397L;
+
+ /**
+ * The current value returned by sum().
+ * @serial
+ */
+ private final long value;
+
+ SerializationProxy(LongAdder a) {
+ value = a.sum();
+ }
+
+ /**
+ * Returns a {@code LongAdder} object with initial state
+ * held by this proxy.
+ *
+ * @return a {@code LongAdder} object with initial state
+ * held by this proxy
+ */
+ private Object readResolve() {
+ LongAdder a = new LongAdder();
+ a.base = value;
+ return a;
+ }
+ }
+
+ /**
+ * Returns a
+ * <a href="../../../../serialized-form.html#java.util.concurrent.atomic.LongAdder.SerializationProxy">
+ * SerializationProxy</a>
+ * representing the state of this instance.
+ *
+ * @return a {@link SerializationProxy}
+ * representing the state of this instance
+ */
+ private Object writeReplace() {
+ return new SerializationProxy(this);
+ }
+
+ /**
+ * @param s the stream
+ * @throws java.io.InvalidObjectException always
+ */
+ private void readObject(java.io.ObjectInputStream s)
+ throws java.io.InvalidObjectException {
+ throw new java.io.InvalidObjectException("Proxy required");
+ }
+
+}
diff --git a/luni/src/main/java/java/util/concurrent/atomic/Striped64.java b/luni/src/main/java/java/util/concurrent/atomic/Striped64.java
new file mode 100644
index 0000000..fc88849
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/atomic/Striped64.java
@@ -0,0 +1,365 @@
+/*
+ * 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/
+ */
+
+package java.util.concurrent.atomic;
+
+import java.util.Arrays;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.function.DoubleBinaryOperator;
+import java.util.function.LongBinaryOperator;
+
+/**
+ * A package-local class holding common representation and mechanics
+ * for classes supporting dynamic striping on 64bit values. The class
+ * extends Number so that concrete subclasses must publicly do so.
+ */
+@SuppressWarnings("serial")
+abstract class Striped64 extends Number {
+ /*
+ * This class maintains a lazily-initialized table of atomically
+ * updated variables, plus an extra "base" field. The table size
+ * is a power of two. Indexing uses masked per-thread hash codes.
+ * Nearly all declarations in this class are package-private,
+ * accessed directly by subclasses.
+ *
+ * Table entries are of class Cell; a variant of AtomicLong padded
+ * (via @Contended) to reduce cache contention. Padding is
+ * overkill for most Atomics because they are usually irregularly
+ * scattered in memory and thus don't interfere much with each
+ * other. But Atomic objects residing in arrays will tend to be
+ * placed adjacent to each other, and so will most often share
+ * cache lines (with a huge negative performance impact) without
+ * this precaution.
+ *
+ * In part because Cells are relatively large, we avoid creating
+ * them until they are needed. When there is no contention, all
+ * updates are made to the base field. Upon first contention (a
+ * failed CAS on base update), the table is initialized to size 2.
+ * The table size is doubled upon further contention until
+ * reaching the nearest power of two greater than or equal to the
+ * number of CPUS. Table slots remain empty (null) until they are
+ * needed.
+ *
+ * A single spinlock ("cellsBusy") is used for initializing and
+ * resizing the table, as well as populating slots with new Cells.
+ * There is no need for a blocking lock; when the lock is not
+ * available, threads try other slots (or the base). During these
+ * retries, there is increased contention and reduced locality,
+ * which is still better than alternatives.
+ *
+ * The Thread probe fields maintained via ThreadLocalRandom serve
+ * as per-thread hash codes. We let them remain uninitialized as
+ * zero (if they come in this way) until they contend at slot
+ * 0. They are then initialized to values that typically do not
+ * often conflict with others. Contention and/or table collisions
+ * are indicated by failed CASes when performing an update
+ * operation. Upon a collision, if the table size is less than
+ * the capacity, it is doubled in size unless some other thread
+ * holds the lock. If a hashed slot is empty, and lock is
+ * available, a new Cell is created. Otherwise, if the slot
+ * exists, a CAS is tried. Retries proceed by "double hashing",
+ * using a secondary hash (Marsaglia XorShift) to try to find a
+ * free slot.
+ *
+ * The table size is capped because, when there are more threads
+ * than CPUs, supposing that each thread were bound to a CPU,
+ * there would exist a perfect hash function mapping threads to
+ * slots that eliminates collisions. When we reach capacity, we
+ * search for this mapping by randomly varying the hash codes of
+ * colliding threads. Because search is random, and collisions
+ * only become known via CAS failures, convergence can be slow,
+ * and because threads are typically not bound to CPUS forever,
+ * may not occur at all. However, despite these limitations,
+ * observed contention rates are typically low in these cases.
+ *
+ * It is possible for a Cell to become unused when threads that
+ * once hashed to it terminate, as well as in the case where
+ * doubling the table causes no thread to hash to it under
+ * expanded mask. We do not try to detect or remove such cells,
+ * under the assumption that for long-running instances, observed
+ * contention levels will recur, so the cells will eventually be
+ * needed again; and for short-lived ones, it does not matter.
+ */
+
+ /**
+ * Padded variant of AtomicLong supporting only raw accesses plus CAS.
+ *
+ * JVM intrinsics note: It would be possible to use a release-only
+ * form of CAS here, if it were provided.
+ */
+ // @jdk.internal.vm.annotation.Contended // android-removed
+ static final class Cell {
+ volatile long value;
+ Cell(long x) { value = x; }
+ final boolean cas(long cmp, long val) {
+ return U.compareAndSwapLong(this, VALUE, cmp, val);
+ }
+ final void reset() {
+ U.putLongVolatile(this, VALUE, 0L);
+ }
+ final void reset(long identity) {
+ U.putLongVolatile(this, VALUE, identity);
+ }
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long VALUE;
+ static {
+ try {
+ VALUE = U.objectFieldOffset
+ (Cell.class.getDeclaredField("value"));
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
+ }
+ }
+
+ /** Number of CPUS, to place bound on table size */
+ static final int NCPU = Runtime.getRuntime().availableProcessors();
+
+ /**
+ * Table of cells. When non-null, size is a power of 2.
+ */
+ transient volatile Cell[] cells;
+
+ /**
+ * Base value, used mainly when there is no contention, but also as
+ * a fallback during table initialization races. Updated via CAS.
+ */
+ transient volatile long base;
+
+ /**
+ * Spinlock (locked via CAS) used when resizing and/or creating Cells.
+ */
+ transient volatile int cellsBusy;
+
+ /**
+ * Package-private default constructor.
+ */
+ Striped64() {
+ }
+
+ /**
+ * CASes the base field.
+ */
+ final boolean casBase(long cmp, long val) {
+ return U.compareAndSwapLong(this, BASE, cmp, val);
+ }
+
+ /**
+ * CASes the cellsBusy field from 0 to 1 to acquire lock.
+ */
+ final boolean casCellsBusy() {
+ return U.compareAndSwapInt(this, CELLSBUSY, 0, 1);
+ }
+
+ /**
+ * Returns the probe value for the current thread.
+ * Duplicated from ThreadLocalRandom because of packaging restrictions.
+ */
+ static final int getProbe() {
+ return U.getInt(Thread.currentThread(), PROBE);
+ }
+
+ /**
+ * Pseudo-randomly advances and records the given probe value for the
+ * given thread.
+ * Duplicated from ThreadLocalRandom because of packaging restrictions.
+ */
+ static final int advanceProbe(int probe) {
+ probe ^= probe << 13; // xorshift
+ probe ^= probe >>> 17;
+ probe ^= probe << 5;
+ U.putInt(Thread.currentThread(), PROBE, probe);
+ return probe;
+ }
+
+ /**
+ * Handles cases of updates involving initialization, resizing,
+ * creating new Cells, and/or contention. See above for
+ * explanation. This method suffers the usual non-modularity
+ * problems of optimistic retry code, relying on rechecked sets of
+ * reads.
+ *
+ * @param x the value
+ * @param fn the update function, or null for add (this convention
+ * avoids the need for an extra field or function in LongAdder).
+ * @param wasUncontended false if CAS failed before call
+ */
+ final void longAccumulate(long x, LongBinaryOperator fn,
+ boolean wasUncontended) {
+ int h;
+ if ((h = getProbe()) == 0) {
+ ThreadLocalRandom.current(); // force initialization
+ h = getProbe();
+ wasUncontended = true;
+ }
+ boolean collide = false; // True if last slot nonempty
+ done: for (;;) {
+ Cell[] as; Cell a; int n; long v;
+ if ((as = cells) != null && (n = as.length) > 0) {
+ if ((a = as[(n - 1) & h]) == null) {
+ if (cellsBusy == 0) { // Try to attach new Cell
+ Cell r = new Cell(x); // Optimistically create
+ if (cellsBusy == 0 && casCellsBusy()) {
+ try { // Recheck under lock
+ Cell[] rs; int m, j;
+ if ((rs = cells) != null &&
+ (m = rs.length) > 0 &&
+ rs[j = (m - 1) & h] == null) {
+ rs[j] = r;
+ break done;
+ }
+ } finally {
+ cellsBusy = 0;
+ }
+ continue; // Slot is now non-empty
+ }
+ }
+ collide = false;
+ }
+ else if (!wasUncontended) // CAS already known to fail
+ wasUncontended = true; // Continue after rehash
+ else if (a.cas(v = a.value,
+ (fn == null) ? v + x : fn.applyAsLong(v, x)))
+ break;
+ else if (n >= NCPU || cells != as)
+ collide = false; // At max size or stale
+ else if (!collide)
+ collide = true;
+ else if (cellsBusy == 0 && casCellsBusy()) {
+ try {
+ if (cells == as) // Expand table unless stale
+ cells = Arrays.copyOf(as, n << 1);
+ } finally {
+ cellsBusy = 0;
+ }
+ collide = false;
+ continue; // Retry with expanded table
+ }
+ h = advanceProbe(h);
+ }
+ else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
+ try { // Initialize table
+ if (cells == as) {
+ Cell[] rs = new Cell[2];
+ rs[h & 1] = new Cell(x);
+ cells = rs;
+ break done;
+ }
+ } finally {
+ cellsBusy = 0;
+ }
+ }
+ // Fall back on using base
+ else if (casBase(v = base,
+ (fn == null) ? v + x : fn.applyAsLong(v, x)))
+ break done;
+ }
+ }
+
+ private static long apply(DoubleBinaryOperator fn, long v, double x) {
+ double d = Double.longBitsToDouble(v);
+ d = (fn == null) ? d + x : fn.applyAsDouble(d, x);
+ return Double.doubleToRawLongBits(d);
+ }
+
+ /**
+ * Same as longAccumulate, but injecting long/double conversions
+ * in too many places to sensibly merge with long version, given
+ * the low-overhead requirements of this class. So must instead be
+ * maintained by copy/paste/adapt.
+ */
+ final void doubleAccumulate(double x, DoubleBinaryOperator fn,
+ boolean wasUncontended) {
+ int h;
+ if ((h = getProbe()) == 0) {
+ ThreadLocalRandom.current(); // force initialization
+ h = getProbe();
+ wasUncontended = true;
+ }
+ boolean collide = false; // True if last slot nonempty
+ done: for (;;) {
+ Cell[] as; Cell a; int n; long v;
+ if ((as = cells) != null && (n = as.length) > 0) {
+ if ((a = as[(n - 1) & h]) == null) {
+ if (cellsBusy == 0) { // Try to attach new Cell
+ Cell r = new Cell(Double.doubleToRawLongBits(x));
+ if (cellsBusy == 0 && casCellsBusy()) {
+ try { // Recheck under lock
+ Cell[] rs; int m, j;
+ if ((rs = cells) != null &&
+ (m = rs.length) > 0 &&
+ rs[j = (m - 1) & h] == null) {
+ rs[j] = r;
+ break done;
+ }
+ } finally {
+ cellsBusy = 0;
+ }
+ continue; // Slot is now non-empty
+ }
+ }
+ collide = false;
+ }
+ else if (!wasUncontended) // CAS already known to fail
+ wasUncontended = true; // Continue after rehash
+ else if (a.cas(v = a.value, apply(fn, v, x)))
+ break;
+ else if (n >= NCPU || cells != as)
+ collide = false; // At max size or stale
+ else if (!collide)
+ collide = true;
+ else if (cellsBusy == 0 && casCellsBusy()) {
+ try {
+ if (cells == as) // Expand table unless stale
+ cells = Arrays.copyOf(as, n << 1);
+ } finally {
+ cellsBusy = 0;
+ }
+ collide = false;
+ continue; // Retry with expanded table
+ }
+ h = advanceProbe(h);
+ }
+ else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
+ try { // Initialize table
+ if (cells == as) {
+ Cell[] rs = new Cell[2];
+ rs[h & 1] = new Cell(Double.doubleToRawLongBits(x));
+ cells = rs;
+ break done;
+ }
+ } finally {
+ cellsBusy = 0;
+ }
+ }
+ // Fall back on using base
+ else if (casBase(v = base, apply(fn, v, x)))
+ break done;
+ }
+ }
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long BASE;
+ private static final long CELLSBUSY;
+ private static final long PROBE;
+ static {
+ try {
+ BASE = U.objectFieldOffset
+ (Striped64.class.getDeclaredField("base"));
+ CELLSBUSY = U.objectFieldOffset
+ (Striped64.class.getDeclaredField("cellsBusy"));
+
+ PROBE = U.objectFieldOffset
+ (Thread.class.getDeclaredField("threadLocalRandomProbe"));
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
+ }
+
+}
diff --git a/luni/src/main/java/java/util/concurrent/atomic/package-info.java b/luni/src/main/java/java/util/concurrent/atomic/package-info.java
index 568d2c6..b19dd49 100644
--- a/luni/src/main/java/java/util/concurrent/atomic/package-info.java
+++ b/luni/src/main/java/java/util/concurrent/atomic/package-info.java
@@ -11,7 +11,7 @@
* array elements to those that also provide an atomic conditional update
* operation of the form:
*
- * <pre> {@code boolean compareAndSet(expectedValue, updateValue);}</pre>
+ * <pre> {@code boolean compareAndSet(expectedValue, updateValue);}</pre>
*
* <p>This method (which varies in argument types across different
* classes) atomically sets a variable to the {@code updateValue} if it
@@ -38,7 +38,7 @@
* {@code AtomicInteger} provide atomic increment methods. One
* application is to generate sequence numbers, as in:
*
- * <pre> {@code
+ * <pre> {@code
* class Sequencer {
* private final AtomicLong sequenceNumber
* = new AtomicLong(0);
@@ -53,31 +53,31 @@
* <pre> {@code long transform(long input)}</pre>
*
* write your utility method as follows:
- * <pre> {@code
+ * <pre> {@code
* long getAndTransform(AtomicLong var) {
- * while (true) {
- * long current = var.get();
- * long next = transform(current);
- * if (var.compareAndSet(current, next))
- * return current;
- * // return next; for transformAndGet
- * }
+ * long prev, next;
+ * do {
+ * prev = var.get();
+ * next = transform(prev);
+ * } while (!var.compareAndSet(prev, next));
+ * return prev; // return next; for transformAndGet
* }}</pre>
*
* <p>The memory effects for accesses and updates of atomics generally
* follow the rules for volatiles, as stated in
- * <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4">
- * The Java Language Specification (17.4 Memory Model)</a>:
+ * <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4">
+ * Chapter 17 of
+ * <cite>The Java™ Language Specification</cite></a>:
*
* <ul>
*
- * <li> {@code get} has the memory effects of reading a
+ * <li>{@code get} has the memory effects of reading a
* {@code volatile} variable.
*
- * <li> {@code set} has the memory effects of writing (assigning) a
+ * <li>{@code set} has the memory effects of writing (assigning) a
* {@code volatile} variable.
*
- * <li> {@code lazySet} has the memory effects of writing (assigning)
+ * <li>{@code lazySet} has the memory effects of writing (assigning)
* a {@code volatile} variable except that it permits reorderings with
* subsequent (but not previous) memory actions that do not themselves
* impose reordering constraints with ordinary non-{@code volatile}
@@ -91,7 +91,7 @@
* with respect to previous or subsequent reads and writes of any
* variables other than the target of the {@code weakCompareAndSet}.
*
- * <li> {@code compareAndSet}
+ * <li>{@code compareAndSet}
* and all other read-and-update operations such as {@code getAndIncrement}
* have the memory effects of both reading and
* writing {@code volatile} variables.
diff --git a/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java b/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java
index a74fb24..3c10805 100644
--- a/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java
+++ b/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java
@@ -6,11 +6,11 @@
package java.util.concurrent.locks;
-import java.util.concurrent.TimeUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
-import sun.misc.Unsafe;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.AbstractQueuedSynchronizer.Node;
/**
* A version of {@link AbstractQueuedSynchronizer} in
@@ -49,221 +49,6 @@
protected AbstractQueuedLongSynchronizer() { }
/**
- * Wait queue node class.
- *
- * <p>The wait queue is a variant of a "CLH" (Craig, Landin, and
- * Hagersten) lock queue. CLH locks are normally used for
- * spinlocks. We instead use them for blocking synchronizers, but
- * use the same basic tactic of holding some of the control
- * information about a thread in the predecessor of its node. A
- * "status" field in each node keeps track of whether a thread
- * should block. A node is signalled when its predecessor
- * releases. Each node of the queue otherwise serves as a
- * specific-notification-style monitor holding a single waiting
- * thread. The status field does NOT control whether threads are
- * granted locks etc though. A thread may try to acquire if it is
- * first in the queue. But being first does not guarantee success;
- * it only gives the right to contend. So the currently released
- * contender thread may need to rewait.
- *
- * <p>To enqueue into a CLH lock, you atomically splice it in as new
- * tail. To dequeue, you just set the head field.
- * <pre>
- * +------+ prev +-----+ +-----+
- * head | | <---- | | <---- | | tail
- * +------+ +-----+ +-----+
- * </pre>
- *
- * <p>Insertion into a CLH queue requires only a single atomic
- * operation on "tail", so there is a simple atomic point of
- * demarcation from unqueued to queued. Similarly, dequeuing
- * involves only updating the "head". However, it takes a bit
- * more work for nodes to determine who their successors are,
- * in part to deal with possible cancellation due to timeouts
- * and interrupts.
- *
- * <p>The "prev" links (not used in original CLH locks), are mainly
- * needed to handle cancellation. If a node is cancelled, its
- * successor is (normally) relinked to a non-cancelled
- * predecessor. For explanation of similar mechanics in the case
- * of spin locks, see the papers by Scott and Scherer at
- * http://www.cs.rochester.edu/u/scott/synchronization/
- *
- * <p>We also use "next" links to implement blocking mechanics.
- * The thread id for each node is kept in its own node, so a
- * predecessor signals the next node to wake up by traversing
- * next link to determine which thread it is. Determination of
- * successor must avoid races with newly queued nodes to set
- * the "next" fields of their predecessors. This is solved
- * when necessary by checking backwards from the atomically
- * updated "tail" when a node's successor appears to be null.
- * (Or, said differently, the next-links are an optimization
- * so that we don't usually need a backward scan.)
- *
- * <p>Cancellation introduces some conservatism to the basic
- * algorithms. Since we must poll for cancellation of other
- * nodes, we can miss noticing whether a cancelled node is
- * ahead or behind us. This is dealt with by always unparking
- * successors upon cancellation, allowing them to stabilize on
- * a new predecessor, unless we can identify an uncancelled
- * predecessor who will carry this responsibility.
- *
- * <p>CLH queues need a dummy header node to get started. But
- * we don't create them on construction, because it would be wasted
- * effort if there is never contention. Instead, the node
- * is constructed and head and tail pointers are set upon first
- * contention.
- *
- * <p>Threads waiting on Conditions use the same nodes, but
- * use an additional link. Conditions only need to link nodes
- * in simple (non-concurrent) linked queues because they are
- * only accessed when exclusively held. Upon await, a node is
- * inserted into a condition queue. Upon signal, the node is
- * transferred to the main queue. A special value of status
- * field is used to mark which queue a node is on.
- *
- * <p>Thanks go to Dave Dice, Mark Moir, Victor Luchangco, Bill
- * Scherer and Michael Scott, along with members of JSR-166
- * expert group, for helpful ideas, discussions, and critiques
- * on the design of this class.
- */
- static final class Node {
- /** Marker to indicate a node is waiting in shared mode */
- static final Node SHARED = new Node();
- /** Marker to indicate a node is waiting in exclusive mode */
- static final Node EXCLUSIVE = null;
-
- /** waitStatus value to indicate thread has cancelled */
- static final int CANCELLED = 1;
- /** waitStatus value to indicate successor's thread needs unparking */
- static final int SIGNAL = -1;
- /** waitStatus value to indicate thread is waiting on condition */
- static final int CONDITION = -2;
- /**
- * waitStatus value to indicate the next acquireShared should
- * unconditionally propagate
- */
- static final int PROPAGATE = -3;
-
- /**
- * Status field, taking on only the values:
- * SIGNAL: The successor of this node is (or will soon be)
- * blocked (via park), so the current node must
- * unpark its successor when it releases or
- * cancels. To avoid races, acquire methods must
- * first indicate they need a signal,
- * then retry the atomic acquire, and then,
- * on failure, block.
- * CANCELLED: This node is cancelled due to timeout or interrupt.
- * Nodes never leave this state. In particular,
- * a thread with cancelled node never again blocks.
- * CONDITION: This node is currently on a condition queue.
- * It will not be used as a sync queue node
- * until transferred, at which time the status
- * will be set to 0. (Use of this value here has
- * nothing to do with the other uses of the
- * field, but simplifies mechanics.)
- * PROPAGATE: A releaseShared should be propagated to other
- * nodes. This is set (for head node only) in
- * doReleaseShared to ensure propagation
- * continues, even if other operations have
- * since intervened.
- * 0: None of the above
- *
- * The values are arranged numerically to simplify use.
- * Non-negative values mean that a node doesn't need to
- * signal. So, most code doesn't need to check for particular
- * values, just for sign.
- *
- * The field is initialized to 0 for normal sync nodes, and
- * CONDITION for condition nodes. It is modified using CAS
- * (or when possible, unconditional volatile writes).
- */
- volatile int waitStatus;
-
- /**
- * Link to predecessor node that current node/thread relies on
- * for checking waitStatus. Assigned during enqueuing, and nulled
- * out (for sake of GC) only upon dequeuing. Also, upon
- * cancellation of a predecessor, we short-circuit while
- * finding a non-cancelled one, which will always exist
- * because the head node is never cancelled: A node becomes
- * head only as a result of successful acquire. A
- * cancelled thread never succeeds in acquiring, and a thread only
- * cancels itself, not any other node.
- */
- volatile Node prev;
-
- /**
- * Link to the successor node that the current node/thread
- * unparks upon release. Assigned during enqueuing, adjusted
- * when bypassing cancelled predecessors, and nulled out (for
- * sake of GC) when dequeued. The enq operation does not
- * assign next field of a predecessor until after attachment,
- * so seeing a null next field does not necessarily mean that
- * node is at end of queue. However, if a next field appears
- * to be null, we can scan prev's from the tail to
- * double-check. The next field of cancelled nodes is set to
- * point to the node itself instead of null, to make life
- * easier for isOnSyncQueue.
- */
- volatile Node next;
-
- /**
- * The thread that enqueued this node. Initialized on
- * construction and nulled out after use.
- */
- volatile Thread thread;
-
- /**
- * Link to next node waiting on condition, or the special
- * value SHARED. Because condition queues are accessed only
- * when holding in exclusive mode, we just need a simple
- * linked queue to hold nodes while they are waiting on
- * conditions. They are then transferred to the queue to
- * re-acquire. And because conditions can only be exclusive,
- * we save a field by using special value to indicate shared
- * mode.
- */
- Node nextWaiter;
-
- /**
- * Returns true if node is waiting in shared mode.
- */
- final boolean isShared() {
- return nextWaiter == SHARED;
- }
-
- /**
- * Returns previous node, or throws NullPointerException if null.
- * Use when predecessor cannot be null. The null check could
- * be elided, but is present to help the VM.
- *
- * @return the predecessor of this node
- */
- final Node predecessor() throws NullPointerException {
- Node p = prev;
- if (p == null)
- throw new NullPointerException();
- else
- return p;
- }
-
- Node() { // Used to establish initial head or SHARED marker
- }
-
- Node(Thread thread, Node mode) { // Used by addWaiter
- this.nextWaiter = mode;
- this.thread = thread;
- }
-
- Node(Thread thread, int waitStatus) { // Used by Condition
- this.waitStatus = waitStatus;
- this.thread = thread;
- }
- }
-
- /**
* Head of the wait queue, lazily initialized. Except for
* initialization, it is modified only via method setHead. Note:
* If head exists, its waitStatus is guaranteed not to be
@@ -297,7 +82,9 @@
* @param newState the new state value
*/
protected final void setState(long newState) {
- state = newState;
+ // Use putLongVolatile instead of ordinary volatile store when
+ // using compareAndSwapLong, for sake of some 32bit systems.
+ U.putLongVolatile(this, STATE, newState);
}
/**
@@ -308,12 +95,11 @@
*
* @param expect the expected value
* @param update the new value
- * @return true if successful. False return indicates that the actual
+ * @return {@code true} if successful. False return indicates that the actual
* value was not equal to the expected value.
*/
protected final boolean compareAndSetState(long expect, long update) {
- // See below for intrinsics setup to support this
- return unsafe.compareAndSwapLong(this, stateOffset, expect, update);
+ return U.compareAndSwapLong(this, STATE, expect, update);
}
// Queuing utilities
@@ -323,25 +109,24 @@
* rather than to use timed park. A rough estimate suffices
* to improve responsiveness with very short timeouts.
*/
- static final long spinForTimeoutThreshold = 1000L;
+ static final long SPIN_FOR_TIMEOUT_THRESHOLD = 1000L;
/**
* Inserts node into queue, initializing if necessary. See picture above.
* @param node the node to insert
* @return node's predecessor
*/
- private Node enq(final Node node) {
+ private Node enq(Node node) {
for (;;) {
- Node t = tail;
- if (t == null) { // Must initialize
- if (compareAndSetHead(new Node()))
- tail = head;
- } else {
- node.prev = t;
- if (compareAndSetTail(t, node)) {
- t.next = node;
- return t;
+ Node oldTail = tail;
+ if (oldTail != null) {
+ U.putObject(node, Node.PREV, oldTail);
+ if (compareAndSetTail(oldTail, node)) {
+ oldTail.next = node;
+ return oldTail;
}
+ } else {
+ initializeSyncQueue();
}
}
}
@@ -353,18 +138,20 @@
* @return the new node
*/
private Node addWaiter(Node mode) {
- Node node = new Node(Thread.currentThread(), mode);
- // Try the fast path of enq; backup to full enq on failure
- Node pred = tail;
- if (pred != null) {
- node.prev = pred;
- if (compareAndSetTail(pred, node)) {
- pred.next = node;
- return node;
+ Node node = new Node(mode);
+
+ for (;;) {
+ Node oldTail = tail;
+ if (oldTail != null) {
+ U.putObject(node, Node.PREV, oldTail);
+ if (compareAndSetTail(oldTail, node)) {
+ oldTail.next = node;
+ return node;
+ }
+ } else {
+ initializeSyncQueue();
}
}
- enq(node);
- return node;
}
/**
@@ -393,7 +180,7 @@
*/
int ws = node.waitStatus;
if (ws < 0)
- compareAndSetWaitStatus(node, ws, 0);
+ node.compareAndSetWaitStatus(ws, 0);
/*
* Thread to unpark is held in successor, which is normally
@@ -404,7 +191,7 @@
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
- for (Node p = tail; p != null && p != node; p = p.prev)
+ for (Node p = tail; p != node && p != null; p = p.prev)
if (p.waitStatus <= 0)
s = p;
}
@@ -434,12 +221,12 @@
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
- if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
+ if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
- !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
+ !h.compareAndSetWaitStatus(0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
@@ -461,7 +248,8 @@
/*
* Try to signal next queued node if:
* Propagation was indicated by caller,
- * or was recorded (as h.waitStatus) by a previous operation
+ * or was recorded (as h.waitStatus either before
+ * or after setHead) by a previous operation
* (note: this uses sign-check of waitStatus because
* PROPAGATE status may transition to SIGNAL.)
* and
@@ -473,7 +261,8 @@
* racing acquires/releases, so most need signals now or soon
* anyway.
*/
- if (propagate > 0 || h == null || h.waitStatus < 0) {
+ if (propagate > 0 || h == null || h.waitStatus < 0 ||
+ (h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
@@ -511,18 +300,18 @@
// If we are the tail, remove ourselves.
if (node == tail && compareAndSetTail(node, pred)) {
- compareAndSetNext(pred, predNext, null);
+ pred.compareAndSetNext(predNext, null);
} else {
// If successor needs signal, try to set pred's next-link
// so it will get one. Otherwise wake it up to propagate.
int ws;
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
- (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
+ (ws <= 0 && pred.compareAndSetWaitStatus(ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
if (next != null && next.waitStatus <= 0)
- compareAndSetNext(pred, predNext, next);
+ pred.compareAndSetNext(predNext, next);
} else {
unparkSuccessor(node);
}
@@ -563,7 +352,7 @@
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
- compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
+ pred.compareAndSetWaitStatus(ws, Node.SIGNAL);
}
return false;
}
@@ -576,7 +365,7 @@
}
/**
- * Convenience method to park and then check if interrupted
+ * Convenience method to park and then check if interrupted.
*
* @return {@code true} if interrupted
*/
@@ -603,7 +392,6 @@
* @return {@code true} if interrupted while waiting
*/
final boolean acquireQueued(final Node node, long arg) {
- boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
@@ -611,16 +399,15 @@
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
- failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
- } finally {
- if (failed)
- cancelAcquire(node);
+ } catch (Throwable t) {
+ cancelAcquire(node);
+ throw t;
}
}
@@ -631,23 +418,21 @@
private void doAcquireInterruptibly(long arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
- boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
- failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
- } finally {
- if (failed)
- cancelAcquire(node);
+ } catch (Throwable t) {
+ cancelAcquire(node);
+ throw t;
}
}
@@ -664,28 +449,28 @@
return false;
final long deadline = System.nanoTime() + nanosTimeout;
final Node node = addWaiter(Node.EXCLUSIVE);
- boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
- failed = false;
return true;
}
nanosTimeout = deadline - System.nanoTime();
- if (nanosTimeout <= 0L)
+ if (nanosTimeout <= 0L) {
+ cancelAcquire(node);
return false;
+ }
if (shouldParkAfterFailedAcquire(p, node) &&
- nanosTimeout > spinForTimeoutThreshold)
+ nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
- } finally {
- if (failed)
- cancelAcquire(node);
+ } catch (Throwable t) {
+ cancelAcquire(node);
+ throw t;
}
}
@@ -695,7 +480,6 @@
*/
private void doAcquireShared(long arg) {
final Node node = addWaiter(Node.SHARED);
- boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
@@ -707,7 +491,6 @@
p.next = null; // help GC
if (interrupted)
selfInterrupt();
- failed = false;
return;
}
}
@@ -715,9 +498,9 @@
parkAndCheckInterrupt())
interrupted = true;
}
- } finally {
- if (failed)
- cancelAcquire(node);
+ } catch (Throwable t) {
+ cancelAcquire(node);
+ throw t;
}
}
@@ -728,7 +511,6 @@
private void doAcquireSharedInterruptibly(long arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
- boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
@@ -737,7 +519,6 @@
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
- failed = false;
return;
}
}
@@ -745,9 +526,9 @@
parkAndCheckInterrupt())
throw new InterruptedException();
}
- } finally {
- if (failed)
- cancelAcquire(node);
+ } catch (Throwable t) {
+ cancelAcquire(node);
+ throw t;
}
}
@@ -764,7 +545,6 @@
return false;
final long deadline = System.nanoTime() + nanosTimeout;
final Node node = addWaiter(Node.SHARED);
- boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
@@ -773,22 +553,23 @@
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
- failed = false;
return true;
}
}
nanosTimeout = deadline - System.nanoTime();
- if (nanosTimeout <= 0L)
+ if (nanosTimeout <= 0L) {
+ cancelAcquire(node);
return false;
+ }
if (shouldParkAfterFailedAcquire(p, node) &&
- nanosTimeout > spinForTimeoutThreshold)
+ nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
- } finally {
- if (failed)
- cancelAcquire(node);
+ } catch (Throwable t) {
+ cancelAcquire(node);
+ throw t;
}
}
@@ -1112,7 +893,7 @@
/**
* Queries whether any threads have ever contended to acquire this
- * synchronizer; that is if an acquire method has ever blocked.
+ * synchronizer; that is, if an acquire method has ever blocked.
*
* <p>In this implementation, this operation returns in
* constant time.
@@ -1140,7 +921,7 @@
}
/**
- * Version of getFirstQueuedThread called when fastpath fails
+ * Version of getFirstQueuedThread called when fastpath fails.
*/
private Thread fullGetFirstQueuedThread() {
/*
@@ -1167,13 +948,11 @@
* guaranteeing termination.
*/
- Node t = tail;
Thread firstThread = null;
- while (t != null && t != head) {
- Thread tt = t.thread;
- if (tt != null)
- firstThread = tt;
- t = t.prev;
+ for (Node p = tail; p != null && p != head; p = p.prev) {
+ Thread t = p.thread;
+ if (t != null)
+ firstThread = t;
}
return firstThread;
}
@@ -1220,9 +999,9 @@
*
* <p>An invocation of this method is equivalent to (but may be
* more efficient than):
- * <pre> {@code
- * getFirstQueuedThread() != Thread.currentThread() &&
- * hasQueuedThreads()}</pre>
+ * <pre> {@code
+ * getFirstQueuedThread() != Thread.currentThread()
+ * && hasQueuedThreads()}</pre>
*
* <p>Note that because cancellations due to interrupts and
* timeouts may occur at any time, a {@code true} return does not
@@ -1240,7 +1019,7 @@
* tryAcquire} method for a fair, reentrant, exclusive mode
* synchronizer might look like this:
*
- * <pre> {@code
+ * <pre> {@code
* protected boolean tryAcquire(int arg) {
* if (isHeldExclusively()) {
* // A reentrant acquire; increment hold count
@@ -1276,8 +1055,7 @@
* acquire. The value is only an estimate because the number of
* threads may change dynamically while this method traverses
* internal data structures. This method is designed for use in
- * monitoring system state, not for synchronization
- * control.
+ * monitoring system state, not for synchronization control.
*
* @return the estimated number of threads waiting to acquire
*/
@@ -1302,7 +1080,7 @@
* @return the collection of threads
*/
public final Collection<Thread> getQueuedThreads() {
- ArrayList<Thread> list = new ArrayList<Thread>();
+ ArrayList<Thread> list = new ArrayList<>();
for (Node p = tail; p != null; p = p.prev) {
Thread t = p.thread;
if (t != null)
@@ -1320,7 +1098,7 @@
* @return the collection of threads
*/
public final Collection<Thread> getExclusiveQueuedThreads() {
- ArrayList<Thread> list = new ArrayList<Thread>();
+ ArrayList<Thread> list = new ArrayList<>();
for (Node p = tail; p != null; p = p.prev) {
if (!p.isShared()) {
Thread t = p.thread;
@@ -1340,7 +1118,7 @@
* @return the collection of threads
*/
public final Collection<Thread> getSharedQueuedThreads() {
- ArrayList<Thread> list = new ArrayList<Thread>();
+ ArrayList<Thread> list = new ArrayList<>();
for (Node p = tail; p != null; p = p.prev) {
if (p.isShared()) {
Thread t = p.thread;
@@ -1361,10 +1139,9 @@
* @return a string identifying this synchronizer, as well as its state
*/
public String toString() {
- long s = getState();
- String q = hasQueuedThreads() ? "non" : "";
- return super.toString() +
- "[State = " + s + ", " + q + "empty queue]";
+ return super.toString()
+ + "[State = " + getState() + ", "
+ + (hasQueuedThreads() ? "non" : "") + "empty queue]";
}
@@ -1398,8 +1175,10 @@
* @return true if present
*/
private boolean findNodeFromTail(Node node) {
- Node p = tail;
- for (;;) {
+ // We check for node first, since it's likely to be at or near tail.
+ // tail is known to be non-null, so we could re-order to "save"
+ // one null check, but we leave it this way to help the VM.
+ for (Node p = tail;;) {
if (p == node)
return true;
if (p == null)
@@ -1419,7 +1198,7 @@
/*
* If cannot change waitStatus, the node has been cancelled.
*/
- if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
+ if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))
return false;
/*
@@ -1430,7 +1209,7 @@
*/
Node p = enq(node);
int ws = p.waitStatus;
- if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
+ if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
@@ -1443,7 +1222,7 @@
* @return true if cancelled before the node was signalled
*/
final boolean transferAfterCancelledWait(Node node) {
- if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
+ if (node.compareAndSetWaitStatus(Node.CONDITION, 0)) {
enq(node);
return true;
}
@@ -1465,18 +1244,14 @@
* @return previous sync state
*/
final long fullyRelease(Node node) {
- boolean failed = true;
try {
long savedState = getState();
- if (release(savedState)) {
- failed = false;
+ if (release(savedState))
return savedState;
- } else {
- throw new IllegalMonitorStateException();
- }
- } finally {
- if (failed)
- node.waitStatus = Node.CANCELLED;
+ throw new IllegalMonitorStateException();
+ } catch (Throwable t) {
+ node.waitStatus = Node.CANCELLED;
+ throw t;
}
}
@@ -1521,8 +1296,8 @@
* given condition associated with this synchronizer. Note that
* because timeouts and interrupts may occur at any time, the
* estimate serves only as an upper bound on the actual number of
- * waiters. This method is designed for use in monitoring of the
- * system state, not for synchronization control.
+ * waiters. This method is designed for use in monitoring system
+ * state, not for synchronization control.
*
* @param condition the condition
* @return the estimated number of waiting threads
@@ -1602,7 +1377,9 @@
unlinkCancelledWaiters();
t = lastWaiter;
}
- Node node = new Node(Thread.currentThread(), Node.CONDITION);
+
+ Node node = new Node(Node.CONDITION);
+
if (t == null)
firstWaiter = node;
else
@@ -1710,12 +1487,12 @@
/**
* Implements uninterruptible condition wait.
* <ol>
- * <li> Save lock state returned by {@link #getState}.
- * <li> Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- * <li> Block until signalled.
- * <li> Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
+ * <li>Save lock state returned by {@link #getState}.
+ * <li>Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ * <li>Block until signalled.
+ * <li>Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
* </ol>
*/
public final void awaitUninterruptibly() {
@@ -1769,14 +1546,14 @@
/**
* Implements interruptible condition wait.
* <ol>
- * <li> If current thread is interrupted, throw InterruptedException.
- * <li> Save lock state returned by {@link #getState}.
- * <li> Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- * <li> Block until signalled or interrupted.
- * <li> Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
- * <li> If interrupted while blocked in step 4, throw InterruptedException.
+ * <li>If current thread is interrupted, throw InterruptedException.
+ * <li>Save lock state returned by {@link #getState}.
+ * <li>Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ * <li>Block until signalled or interrupted.
+ * <li>Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
+ * <li>If interrupted while blocked in step 4, throw InterruptedException.
* </ol>
*/
public final void await() throws InterruptedException {
@@ -1801,31 +1578,33 @@
/**
* Implements timed condition wait.
* <ol>
- * <li> If current thread is interrupted, throw InterruptedException.
- * <li> Save lock state returned by {@link #getState}.
- * <li> Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- * <li> Block until signalled, interrupted, or timed out.
- * <li> Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
- * <li> If interrupted while blocked in step 4, throw InterruptedException.
+ * <li>If current thread is interrupted, throw InterruptedException.
+ * <li>Save lock state returned by {@link #getState}.
+ * <li>Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ * <li>Block until signalled, interrupted, or timed out.
+ * <li>Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
+ * <li>If interrupted while blocked in step 4, throw InterruptedException.
* </ol>
*/
public final long awaitNanos(long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
+ // We don't check for nanosTimeout <= 0L here, to allow
+ // awaitNanos(0) as a way to "yield the lock".
+ final long deadline = System.nanoTime() + nanosTimeout;
long initialNanos = nanosTimeout;
Node node = addConditionWaiter();
long savedState = fullyRelease(node);
- final long deadline = System.nanoTime() + nanosTimeout;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
if (nanosTimeout <= 0L) {
transferAfterCancelledWait(node);
break;
}
- if (nanosTimeout >= spinForTimeoutThreshold)
+ if (nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
LockSupport.parkNanos(this, nanosTimeout);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
@@ -1838,24 +1617,21 @@
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
long remaining = deadline - System.nanoTime(); // avoid overflow
- // BEGIN android-note Changed from < to <= http://b/24284239
- // return (remaining < initialNanos) ? remaining : Long.MIN_VALUE;
return (remaining <= initialNanos) ? remaining : Long.MIN_VALUE;
- // END android-note
}
/**
* Implements absolute timed condition wait.
* <ol>
- * <li> If current thread is interrupted, throw InterruptedException.
- * <li> Save lock state returned by {@link #getState}.
- * <li> Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- * <li> Block until signalled, interrupted, or timed out.
- * <li> Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
- * <li> If interrupted while blocked in step 4, throw InterruptedException.
- * <li> If timed out while blocked in step 4, return false, else true.
+ * <li>If current thread is interrupted, throw InterruptedException.
+ * <li>Save lock state returned by {@link #getState}.
+ * <li>Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ * <li>Block until signalled, interrupted, or timed out.
+ * <li>Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
+ * <li>If interrupted while blocked in step 4, throw InterruptedException.
+ * <li>If timed out while blocked in step 4, return false, else true.
* </ol>
*/
public final boolean awaitUntil(Date deadline)
@@ -1868,7 +1644,7 @@
boolean timedout = false;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
- if (System.currentTimeMillis() > abstime) {
+ if (System.currentTimeMillis() >= abstime) {
timedout = transferAfterCancelledWait(node);
break;
}
@@ -1888,15 +1664,15 @@
/**
* Implements timed condition wait.
* <ol>
- * <li> If current thread is interrupted, throw InterruptedException.
- * <li> Save lock state returned by {@link #getState}.
- * <li> Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- * <li> Block until signalled, interrupted, or timed out.
- * <li> Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
- * <li> If interrupted while blocked in step 4, throw InterruptedException.
- * <li> If timed out while blocked in step 4, return false, else true.
+ * <li>If current thread is interrupted, throw InterruptedException.
+ * <li>Save lock state returned by {@link #getState}.
+ * <li>Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ * <li>Block until signalled, interrupted, or timed out.
+ * <li>Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
+ * <li>If interrupted while blocked in step 4, throw InterruptedException.
+ * <li>If timed out while blocked in step 4, return false, else true.
* </ol>
*/
public final boolean await(long time, TimeUnit unit)
@@ -1904,9 +1680,11 @@
long nanosTimeout = unit.toNanos(time);
if (Thread.interrupted())
throw new InterruptedException();
+ // We don't check for nanosTimeout <= 0L here, to allow
+ // await(0, unit) as a way to "yield the lock".
+ final long deadline = System.nanoTime() + nanosTimeout;
Node node = addConditionWaiter();
long savedState = fullyRelease(node);
- final long deadline = System.nanoTime() + nanosTimeout;
boolean timedout = false;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
@@ -1914,7 +1692,7 @@
timedout = transferAfterCancelledWait(node);
break;
}
- if (nanosTimeout >= spinForTimeoutThreshold)
+ if (nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
LockSupport.parkNanos(this, nanosTimeout);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
@@ -1943,7 +1721,7 @@
/**
* Queries whether any threads are waiting on this condition.
- * Implements {@link AbstractQueuedLongSynchronizer#hasWaiters}.
+ * Implements {@link AbstractQueuedLongSynchronizer#hasWaiters(ConditionObject)}.
*
* @return {@code true} if there are any waiting threads
* @throws IllegalMonitorStateException if {@link #isHeldExclusively}
@@ -1962,7 +1740,7 @@
/**
* Returns an estimate of the number of threads waiting on
* this condition.
- * Implements {@link AbstractQueuedLongSynchronizer#getWaitQueueLength}.
+ * Implements {@link AbstractQueuedLongSynchronizer#getWaitQueueLength(ConditionObject)}.
*
* @return the estimated number of waiting threads
* @throws IllegalMonitorStateException if {@link #isHeldExclusively}
@@ -1982,7 +1760,7 @@
/**
* Returns a collection containing those threads that may be
* waiting on this Condition.
- * Implements {@link AbstractQueuedLongSynchronizer#getWaitingThreads}.
+ * Implements {@link AbstractQueuedLongSynchronizer#getWaitingThreads(ConditionObject)}.
*
* @return the collection of threads
* @throws IllegalMonitorStateException if {@link #isHeldExclusively}
@@ -1991,7 +1769,7 @@
protected final Collection<Thread> getWaitingThreads() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
- ArrayList<Thread> list = new ArrayList<Thread>();
+ ArrayList<Thread> list = new ArrayList<>();
for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
if (w.waitStatus == Node.CONDITION) {
Thread t = w.thread;
@@ -2012,27 +1790,22 @@
* are at it, we do the same for other CASable fields (which could
* otherwise be done with atomic field updaters).
*/
- private static final Unsafe unsafe = Unsafe.getUnsafe();
- private static final long stateOffset;
- private static final long headOffset;
- private static final long tailOffset;
- private static final long waitStatusOffset;
- private static final long nextOffset;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long STATE;
+ private static final long HEAD;
+ private static final long TAIL;
static {
try {
- stateOffset = unsafe.objectFieldOffset
+ STATE = U.objectFieldOffset
(AbstractQueuedLongSynchronizer.class.getDeclaredField("state"));
- headOffset = unsafe.objectFieldOffset
+ HEAD = U.objectFieldOffset
(AbstractQueuedLongSynchronizer.class.getDeclaredField("head"));
- tailOffset = unsafe.objectFieldOffset
+ TAIL = U.objectFieldOffset
(AbstractQueuedLongSynchronizer.class.getDeclaredField("tail"));
- waitStatusOffset = unsafe.objectFieldOffset
- (Node.class.getDeclaredField("waitStatus"));
- nextOffset = unsafe.objectFieldOffset
- (Node.class.getDeclaredField("next"));
-
- } catch (Exception ex) { throw new Error(ex); }
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
// Reduce the risk of rare disastrous classloading in first call to
// LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773
@@ -2040,35 +1813,18 @@
}
/**
- * CAS head field. Used only by enq.
+ * Initializes head and tail fields on first contention.
*/
- private final boolean compareAndSetHead(Node update) {
- return unsafe.compareAndSwapObject(this, headOffset, null, update);
+ private final void initializeSyncQueue() {
+ Node h;
+ if (U.compareAndSwapObject(this, HEAD, null, (h = new Node())))
+ tail = h;
}
/**
- * CAS tail field. Used only by enq.
+ * CASes tail field.
*/
private final boolean compareAndSetTail(Node expect, Node update) {
- return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
- }
-
- /**
- * CAS waitStatus field of a node.
- */
- private static final boolean compareAndSetWaitStatus(Node node,
- int expect,
- int update) {
- return unsafe.compareAndSwapInt(node, waitStatusOffset,
- expect, update);
- }
-
- /**
- * CAS next field of a node.
- */
- private static final boolean compareAndSetNext(Node node,
- Node expect,
- Node update) {
- return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
+ return U.compareAndSwapObject(this, TAIL, expect, update);
}
}
diff --git a/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java b/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
index 8823b6f..2c41000 100644
--- a/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
+++ b/luni/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
@@ -6,15 +6,11 @@
package java.util.concurrent.locks;
-import java.util.concurrent.TimeUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
-import sun.misc.Unsafe;
-
-// BEGIN android-note
-// Use older class level documentation to not @link to hasQueuedPredecessors
-// END android-changed
+import java.util.concurrent.TimeUnit;
+/// OPENJDK-9 import jdk.internal.vm.annotation.ReservedStackAccess;
/**
* Provides a framework for implementing blocking locks and related
@@ -86,11 +82,11 @@
* #setState} and/or {@link #compareAndSetState}:
*
* <ul>
- * <li> {@link #tryAcquire}
- * <li> {@link #tryRelease}
- * <li> {@link #tryAcquireShared}
- * <li> {@link #tryReleaseShared}
- * <li> {@link #isHeldExclusively}
+ * <li>{@link #tryAcquire}
+ * <li>{@link #tryRelease}
+ * <li>{@link #tryAcquireShared}
+ * <li>{@link #tryReleaseShared}
+ * <li>{@link #isHeldExclusively}
* </ul>
*
* Each of these methods by default throws {@link
@@ -131,7 +127,7 @@
* disable barging by internally invoking one or more of the inspection
* methods, thereby providing a <em>fair</em> FIFO acquisition order.
* In particular, most fair synchronizers can define {@code tryAcquire}
- * to return {@code false} if {@code hasQueuedPredecessors} (a method
+ * to return {@code false} if {@link #hasQueuedPredecessors} (a method
* specifically designed to be used by fair synchronizers) returns
* {@code true}. Other variations are possible.
*
@@ -171,7 +167,7 @@
* It also supports conditions and exposes
* one of the instrumentation methods:
*
- * <pre> {@code
+ * <pre> {@code
* class Mutex implements Lock, java.io.Serializable {
*
* // Our internal helper class
@@ -235,7 +231,7 @@
* fire. Because a latch is non-exclusive, it uses the {@code shared}
* acquire and release methods.
*
- * <pre> {@code
+ * <pre> {@code
* class BooleanLatch {
*
* private static class Sync extends AbstractQueuedSynchronizer {
@@ -359,15 +355,15 @@
/** Marker to indicate a node is waiting in exclusive mode */
static final Node EXCLUSIVE = null;
- /** waitStatus value to indicate thread has cancelled */
+ /** waitStatus value to indicate thread has cancelled. */
static final int CANCELLED = 1;
- /** waitStatus value to indicate successor's thread needs unparking */
+ /** waitStatus value to indicate successor's thread needs unparking. */
static final int SIGNAL = -1;
- /** waitStatus value to indicate thread is waiting on condition */
+ /** waitStatus value to indicate thread is waiting on condition. */
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
- * unconditionally propagate
+ * unconditionally propagate.
*/
static final int PROPAGATE = -3;
@@ -475,17 +471,49 @@
return p;
}
- Node() { // Used to establish initial head or SHARED marker
+ /** Establishes initial head or SHARED marker. */
+ Node() {}
+
+ /** Constructor used by addWaiter. */
+ Node(Node nextWaiter) {
+ this.nextWaiter = nextWaiter;
+ U.putObject(this, THREAD, Thread.currentThread());
}
- Node(Thread thread, Node mode) { // Used by addWaiter
- this.nextWaiter = mode;
- this.thread = thread;
+ /** Constructor used by addConditionWaiter. */
+ Node(int waitStatus) {
+ U.putInt(this, WAITSTATUS, waitStatus);
+ U.putObject(this, THREAD, Thread.currentThread());
}
- Node(Thread thread, int waitStatus) { // Used by Condition
- this.waitStatus = waitStatus;
- this.thread = thread;
+ /** CASes waitStatus field. */
+ final boolean compareAndSetWaitStatus(int expect, int update) {
+ return U.compareAndSwapInt(this, WAITSTATUS, expect, update);
+ }
+
+ /** CASes next field. */
+ final boolean compareAndSetNext(Node expect, Node update) {
+ return U.compareAndSwapObject(this, NEXT, expect, update);
+ }
+
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long NEXT;
+ static final long PREV;
+ private static final long THREAD;
+ private static final long WAITSTATUS;
+ static {
+ try {
+ NEXT = U.objectFieldOffset
+ (Node.class.getDeclaredField("next"));
+ PREV = U.objectFieldOffset
+ (Node.class.getDeclaredField("prev"));
+ THREAD = U.objectFieldOffset
+ (Node.class.getDeclaredField("thread"));
+ WAITSTATUS = U.objectFieldOffset
+ (Node.class.getDeclaredField("waitStatus"));
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
}
}
@@ -534,12 +562,11 @@
*
* @param expect the expected value
* @param update the new value
- * @return true if successful. False return indicates that the actual
+ * @return {@code true} if successful. False return indicates that the actual
* value was not equal to the expected value.
*/
protected final boolean compareAndSetState(int expect, int update) {
- // See below for intrinsics setup to support this
- return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
+ return U.compareAndSwapInt(this, STATE, expect, update);
}
// Queuing utilities
@@ -549,25 +576,24 @@
* rather than to use timed park. A rough estimate suffices
* to improve responsiveness with very short timeouts.
*/
- static final long spinForTimeoutThreshold = 1000L;
+ static final long SPIN_FOR_TIMEOUT_THRESHOLD = 1000L;
/**
* Inserts node into queue, initializing if necessary. See picture above.
* @param node the node to insert
* @return node's predecessor
*/
- private Node enq(final Node node) {
+ private Node enq(Node node) {
for (;;) {
- Node t = tail;
- if (t == null) { // Must initialize
- if (compareAndSetHead(new Node()))
- tail = head;
- } else {
- node.prev = t;
- if (compareAndSetTail(t, node)) {
- t.next = node;
- return t;
+ Node oldTail = tail;
+ if (oldTail != null) {
+ U.putObject(node, Node.PREV, oldTail);
+ if (compareAndSetTail(oldTail, node)) {
+ oldTail.next = node;
+ return oldTail;
}
+ } else {
+ initializeSyncQueue();
}
}
}
@@ -579,18 +605,20 @@
* @return the new node
*/
private Node addWaiter(Node mode) {
- Node node = new Node(Thread.currentThread(), mode);
- // Try the fast path of enq; backup to full enq on failure
- Node pred = tail;
- if (pred != null) {
- node.prev = pred;
- if (compareAndSetTail(pred, node)) {
- pred.next = node;
- return node;
+ Node node = new Node(mode);
+
+ for (;;) {
+ Node oldTail = tail;
+ if (oldTail != null) {
+ U.putObject(node, Node.PREV, oldTail);
+ if (compareAndSetTail(oldTail, node)) {
+ oldTail.next = node;
+ return node;
+ }
+ } else {
+ initializeSyncQueue();
}
}
- enq(node);
- return node;
}
/**
@@ -619,7 +647,7 @@
*/
int ws = node.waitStatus;
if (ws < 0)
- compareAndSetWaitStatus(node, ws, 0);
+ node.compareAndSetWaitStatus(ws, 0);
/*
* Thread to unpark is held in successor, which is normally
@@ -630,9 +658,9 @@
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
- for (Node t = tail; t != null && t != node; t = t.prev)
- if (t.waitStatus <= 0)
- s = t;
+ for (Node p = tail; p != node && p != null; p = p.prev)
+ if (p.waitStatus <= 0)
+ s = p;
}
if (s != null)
LockSupport.unpark(s.thread);
@@ -660,12 +688,12 @@
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
- if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
+ if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
- !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
+ !h.compareAndSetWaitStatus(0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
@@ -687,7 +715,8 @@
/*
* Try to signal next queued node if:
* Propagation was indicated by caller,
- * or was recorded (as h.waitStatus) by a previous operation
+ * or was recorded (as h.waitStatus either before
+ * or after setHead) by a previous operation
* (note: this uses sign-check of waitStatus because
* PROPAGATE status may transition to SIGNAL.)
* and
@@ -699,7 +728,8 @@
* racing acquires/releases, so most need signals now or soon
* anyway.
*/
- if (propagate > 0 || h == null || h.waitStatus < 0) {
+ if (propagate > 0 || h == null || h.waitStatus < 0 ||
+ (h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
@@ -737,18 +767,18 @@
// If we are the tail, remove ourselves.
if (node == tail && compareAndSetTail(node, pred)) {
- compareAndSetNext(pred, predNext, null);
+ pred.compareAndSetNext(predNext, null);
} else {
// If successor needs signal, try to set pred's next-link
// so it will get one. Otherwise wake it up to propagate.
int ws;
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
- (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
+ (ws <= 0 && pred.compareAndSetWaitStatus(ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
if (next != null && next.waitStatus <= 0)
- compareAndSetNext(pred, predNext, next);
+ pred.compareAndSetNext(predNext, next);
} else {
unparkSuccessor(node);
}
@@ -789,7 +819,7 @@
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
- compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
+ pred.compareAndSetWaitStatus(ws, Node.SIGNAL);
}
return false;
}
@@ -802,7 +832,7 @@
}
/**
- * Convenience method to park and then check if interrupted
+ * Convenience method to park and then check if interrupted.
*
* @return {@code true} if interrupted
*/
@@ -828,8 +858,8 @@
* @param arg the acquire argument
* @return {@code true} if interrupted while waiting
*/
+/// OPENJDK-9 @ReservedStackAccess
final boolean acquireQueued(final Node node, int arg) {
- boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
@@ -837,16 +867,15 @@
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
- failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
- } finally {
- if (failed)
- cancelAcquire(node);
+ } catch (Throwable t) {
+ cancelAcquire(node);
+ throw t;
}
}
@@ -857,23 +886,21 @@
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
- boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
- failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
- } finally {
- if (failed)
- cancelAcquire(node);
+ } catch (Throwable t) {
+ cancelAcquire(node);
+ throw t;
}
}
@@ -890,28 +917,28 @@
return false;
final long deadline = System.nanoTime() + nanosTimeout;
final Node node = addWaiter(Node.EXCLUSIVE);
- boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
- failed = false;
return true;
}
nanosTimeout = deadline - System.nanoTime();
- if (nanosTimeout <= 0L)
+ if (nanosTimeout <= 0L) {
+ cancelAcquire(node);
return false;
+ }
if (shouldParkAfterFailedAcquire(p, node) &&
- nanosTimeout > spinForTimeoutThreshold)
+ nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
- } finally {
- if (failed)
- cancelAcquire(node);
+ } catch (Throwable t) {
+ cancelAcquire(node);
+ throw t;
}
}
@@ -921,7 +948,6 @@
*/
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);
- boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
@@ -933,7 +959,6 @@
p.next = null; // help GC
if (interrupted)
selfInterrupt();
- failed = false;
return;
}
}
@@ -941,9 +966,9 @@
parkAndCheckInterrupt())
interrupted = true;
}
- } finally {
- if (failed)
- cancelAcquire(node);
+ } catch (Throwable t) {
+ cancelAcquire(node);
+ throw t;
}
}
@@ -954,7 +979,6 @@
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
- boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
@@ -963,7 +987,6 @@
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
- failed = false;
return;
}
}
@@ -971,9 +994,9 @@
parkAndCheckInterrupt())
throw new InterruptedException();
}
- } finally {
- if (failed)
- cancelAcquire(node);
+ } catch (Throwable t) {
+ cancelAcquire(node);
+ throw t;
}
}
@@ -990,7 +1013,6 @@
return false;
final long deadline = System.nanoTime() + nanosTimeout;
final Node node = addWaiter(Node.SHARED);
- boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
@@ -999,22 +1021,23 @@
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
- failed = false;
return true;
}
}
nanosTimeout = deadline - System.nanoTime();
- if (nanosTimeout <= 0L)
+ if (nanosTimeout <= 0L) {
+ cancelAcquire(node);
return false;
+ }
if (shouldParkAfterFailedAcquire(p, node) &&
- nanosTimeout > spinForTimeoutThreshold)
+ nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
- } finally {
- if (failed)
- cancelAcquire(node);
+ } catch (Throwable t) {
+ cancelAcquire(node);
+ throw t;
}
}
@@ -1168,6 +1191,7 @@
* {@link #tryAcquire} but is otherwise uninterpreted and
* can represent anything you like.
*/
+/// OPENJDK-9 @ReservedStackAccess
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
@@ -1231,6 +1255,7 @@
* can represent anything you like.
* @return the value returned from {@link #tryRelease}
*/
+/// OPENJDK-9 @ReservedStackAccess
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
@@ -1311,6 +1336,7 @@
* and can represent anything you like.
* @return the value returned from {@link #tryReleaseShared}
*/
+/// OPENJDK-9 @ReservedStackAccess
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
@@ -1338,7 +1364,7 @@
/**
* Queries whether any threads have ever contended to acquire this
- * synchronizer; that is if an acquire method has ever blocked.
+ * synchronizer; that is, if an acquire method has ever blocked.
*
* <p>In this implementation, this operation returns in
* constant time.
@@ -1366,7 +1392,7 @@
}
/**
- * Version of getFirstQueuedThread called when fastpath fails
+ * Version of getFirstQueuedThread called when fastpath fails.
*/
private Thread fullGetFirstQueuedThread() {
/*
@@ -1393,13 +1419,11 @@
* guaranteeing termination.
*/
- Node t = tail;
Thread firstThread = null;
- while (t != null && t != head) {
- Thread tt = t.thread;
- if (tt != null)
- firstThread = tt;
- t = t.prev;
+ for (Node p = tail; p != null && p != head; p = p.prev) {
+ Thread t = p.thread;
+ if (t != null)
+ firstThread = t;
}
return firstThread;
}
@@ -1446,9 +1470,9 @@
*
* <p>An invocation of this method is equivalent to (but may be
* more efficient than):
- * <pre> {@code
- * getFirstQueuedThread() != Thread.currentThread() &&
- * hasQueuedThreads()}</pre>
+ * <pre> {@code
+ * getFirstQueuedThread() != Thread.currentThread()
+ * && hasQueuedThreads()}</pre>
*
* <p>Note that because cancellations due to interrupts and
* timeouts may occur at any time, a {@code true} return does not
@@ -1466,7 +1490,7 @@
* tryAcquire} method for a fair, reentrant, exclusive mode
* synchronizer might look like this:
*
- * <pre> {@code
+ * <pre> {@code
* protected boolean tryAcquire(int arg) {
* if (isHeldExclusively()) {
* // A reentrant acquire; increment hold count
@@ -1502,8 +1526,7 @@
* acquire. The value is only an estimate because the number of
* threads may change dynamically while this method traverses
* internal data structures. This method is designed for use in
- * monitoring system state, not for synchronization
- * control.
+ * monitoring system state, not for synchronization control.
*
* @return the estimated number of threads waiting to acquire
*/
@@ -1528,7 +1551,7 @@
* @return the collection of threads
*/
public final Collection<Thread> getQueuedThreads() {
- ArrayList<Thread> list = new ArrayList<Thread>();
+ ArrayList<Thread> list = new ArrayList<>();
for (Node p = tail; p != null; p = p.prev) {
Thread t = p.thread;
if (t != null)
@@ -1546,7 +1569,7 @@
* @return the collection of threads
*/
public final Collection<Thread> getExclusiveQueuedThreads() {
- ArrayList<Thread> list = new ArrayList<Thread>();
+ ArrayList<Thread> list = new ArrayList<>();
for (Node p = tail; p != null; p = p.prev) {
if (!p.isShared()) {
Thread t = p.thread;
@@ -1566,7 +1589,7 @@
* @return the collection of threads
*/
public final Collection<Thread> getSharedQueuedThreads() {
- ArrayList<Thread> list = new ArrayList<Thread>();
+ ArrayList<Thread> list = new ArrayList<>();
for (Node p = tail; p != null; p = p.prev) {
if (p.isShared()) {
Thread t = p.thread;
@@ -1587,10 +1610,9 @@
* @return a string identifying this synchronizer, as well as its state
*/
public String toString() {
- int s = getState();
- String q = hasQueuedThreads() ? "non" : "";
- return super.toString() +
- "[State = " + s + ", " + q + "empty queue]";
+ return super.toString()
+ + "[State = " + getState() + ", "
+ + (hasQueuedThreads() ? "non" : "") + "empty queue]";
}
@@ -1624,13 +1646,15 @@
* @return true if present
*/
private boolean findNodeFromTail(Node node) {
- Node t = tail;
- for (;;) {
- if (t == node)
+ // We check for node first, since it's likely to be at or near tail.
+ // tail is known to be non-null, so we could re-order to "save"
+ // one null check, but we leave it this way to help the VM.
+ for (Node p = tail;;) {
+ if (p == node)
return true;
- if (t == null)
+ if (p == null)
return false;
- t = t.prev;
+ p = p.prev;
}
}
@@ -1645,7 +1669,7 @@
/*
* If cannot change waitStatus, the node has been cancelled.
*/
- if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
+ if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))
return false;
/*
@@ -1656,7 +1680,7 @@
*/
Node p = enq(node);
int ws = p.waitStatus;
- if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
+ if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
@@ -1669,7 +1693,7 @@
* @return true if cancelled before the node was signalled
*/
final boolean transferAfterCancelledWait(Node node) {
- if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
+ if (node.compareAndSetWaitStatus(Node.CONDITION, 0)) {
enq(node);
return true;
}
@@ -1691,18 +1715,14 @@
* @return previous sync state
*/
final int fullyRelease(Node node) {
- boolean failed = true;
try {
int savedState = getState();
- if (release(savedState)) {
- failed = false;
+ if (release(savedState))
return savedState;
- } else {
- throw new IllegalMonitorStateException();
- }
- } finally {
- if (failed)
- node.waitStatus = Node.CANCELLED;
+ throw new IllegalMonitorStateException();
+ } catch (Throwable t) {
+ node.waitStatus = Node.CANCELLED;
+ throw t;
}
}
@@ -1747,8 +1767,8 @@
* given condition associated with this synchronizer. Note that
* because timeouts and interrupts may occur at any time, the
* estimate serves only as an upper bound on the actual number of
- * waiters. This method is designed for use in monitoring of the
- * system state, not for synchronization control.
+ * waiters. This method is designed for use in monitoring system
+ * state, not for synchronization control.
*
* @param condition the condition
* @return the estimated number of waiting threads
@@ -1826,7 +1846,9 @@
unlinkCancelledWaiters();
t = lastWaiter;
}
- Node node = new Node(Thread.currentThread(), Node.CONDITION);
+
+ Node node = new Node(Node.CONDITION);
+
if (t == null)
firstWaiter = node;
else
@@ -1934,12 +1956,12 @@
/**
* Implements uninterruptible condition wait.
* <ol>
- * <li> Save lock state returned by {@link #getState}.
- * <li> Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- * <li> Block until signalled.
- * <li> Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
+ * <li>Save lock state returned by {@link #getState}.
+ * <li>Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ * <li>Block until signalled.
+ * <li>Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
* </ol>
*/
public final void awaitUninterruptibly() {
@@ -1993,14 +2015,14 @@
/**
* Implements interruptible condition wait.
* <ol>
- * <li> If current thread is interrupted, throw InterruptedException.
- * <li> Save lock state returned by {@link #getState}.
- * <li> Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- * <li> Block until signalled or interrupted.
- * <li> Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
- * <li> If interrupted while blocked in step 4, throw InterruptedException.
+ * <li>If current thread is interrupted, throw InterruptedException.
+ * <li>Save lock state returned by {@link #getState}.
+ * <li>Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ * <li>Block until signalled or interrupted.
+ * <li>Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
+ * <li>If interrupted while blocked in step 4, throw InterruptedException.
* </ol>
*/
public final void await() throws InterruptedException {
@@ -2025,31 +2047,33 @@
/**
* Implements timed condition wait.
* <ol>
- * <li> If current thread is interrupted, throw InterruptedException.
- * <li> Save lock state returned by {@link #getState}.
- * <li> Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- * <li> Block until signalled, interrupted, or timed out.
- * <li> Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
- * <li> If interrupted while blocked in step 4, throw InterruptedException.
+ * <li>If current thread is interrupted, throw InterruptedException.
+ * <li>Save lock state returned by {@link #getState}.
+ * <li>Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ * <li>Block until signalled, interrupted, or timed out.
+ * <li>Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
+ * <li>If interrupted while blocked in step 4, throw InterruptedException.
* </ol>
*/
public final long awaitNanos(long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
+ // We don't check for nanosTimeout <= 0L here, to allow
+ // awaitNanos(0) as a way to "yield the lock".
+ final long deadline = System.nanoTime() + nanosTimeout;
long initialNanos = nanosTimeout;
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
- final long deadline = System.nanoTime() + nanosTimeout;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
if (nanosTimeout <= 0L) {
transferAfterCancelledWait(node);
break;
}
- if (nanosTimeout >= spinForTimeoutThreshold)
+ if (nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
LockSupport.parkNanos(this, nanosTimeout);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
@@ -2062,24 +2086,21 @@
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
long remaining = deadline - System.nanoTime(); // avoid overflow
- // BEGIN android-note Changed from < to <= http://b/24284239
- // return (remaining < initialNanos) ? remaining : Long.MIN_VALUE;
return (remaining <= initialNanos) ? remaining : Long.MIN_VALUE;
- // END android-note
}
/**
* Implements absolute timed condition wait.
* <ol>
- * <li> If current thread is interrupted, throw InterruptedException.
- * <li> Save lock state returned by {@link #getState}.
- * <li> Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- * <li> Block until signalled, interrupted, or timed out.
- * <li> Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
- * <li> If interrupted while blocked in step 4, throw InterruptedException.
- * <li> If timed out while blocked in step 4, return false, else true.
+ * <li>If current thread is interrupted, throw InterruptedException.
+ * <li>Save lock state returned by {@link #getState}.
+ * <li>Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ * <li>Block until signalled, interrupted, or timed out.
+ * <li>Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
+ * <li>If interrupted while blocked in step 4, throw InterruptedException.
+ * <li>If timed out while blocked in step 4, return false, else true.
* </ol>
*/
public final boolean awaitUntil(Date deadline)
@@ -2092,7 +2113,7 @@
boolean timedout = false;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
- if (System.currentTimeMillis() > abstime) {
+ if (System.currentTimeMillis() >= abstime) {
timedout = transferAfterCancelledWait(node);
break;
}
@@ -2112,15 +2133,15 @@
/**
* Implements timed condition wait.
* <ol>
- * <li> If current thread is interrupted, throw InterruptedException.
- * <li> Save lock state returned by {@link #getState}.
- * <li> Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- * <li> Block until signalled, interrupted, or timed out.
- * <li> Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
- * <li> If interrupted while blocked in step 4, throw InterruptedException.
- * <li> If timed out while blocked in step 4, return false, else true.
+ * <li>If current thread is interrupted, throw InterruptedException.
+ * <li>Save lock state returned by {@link #getState}.
+ * <li>Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ * <li>Block until signalled, interrupted, or timed out.
+ * <li>Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
+ * <li>If interrupted while blocked in step 4, throw InterruptedException.
+ * <li>If timed out while blocked in step 4, return false, else true.
* </ol>
*/
public final boolean await(long time, TimeUnit unit)
@@ -2128,9 +2149,11 @@
long nanosTimeout = unit.toNanos(time);
if (Thread.interrupted())
throw new InterruptedException();
+ // We don't check for nanosTimeout <= 0L here, to allow
+ // await(0, unit) as a way to "yield the lock".
+ final long deadline = System.nanoTime() + nanosTimeout;
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
- final long deadline = System.nanoTime() + nanosTimeout;
boolean timedout = false;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
@@ -2138,7 +2161,7 @@
timedout = transferAfterCancelledWait(node);
break;
}
- if (nanosTimeout >= spinForTimeoutThreshold)
+ if (nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
LockSupport.parkNanos(this, nanosTimeout);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
@@ -2167,7 +2190,7 @@
/**
* Queries whether any threads are waiting on this condition.
- * Implements {@link AbstractQueuedSynchronizer#hasWaiters}.
+ * Implements {@link AbstractQueuedSynchronizer#hasWaiters(ConditionObject)}.
*
* @return {@code true} if there are any waiting threads
* @throws IllegalMonitorStateException if {@link #isHeldExclusively}
@@ -2186,7 +2209,7 @@
/**
* Returns an estimate of the number of threads waiting on
* this condition.
- * Implements {@link AbstractQueuedSynchronizer#getWaitQueueLength}.
+ * Implements {@link AbstractQueuedSynchronizer#getWaitQueueLength(ConditionObject)}.
*
* @return the estimated number of waiting threads
* @throws IllegalMonitorStateException if {@link #isHeldExclusively}
@@ -2206,7 +2229,7 @@
/**
* Returns a collection containing those threads that may be
* waiting on this Condition.
- * Implements {@link AbstractQueuedSynchronizer#getWaitingThreads}.
+ * Implements {@link AbstractQueuedSynchronizer#getWaitingThreads(ConditionObject)}.
*
* @return the collection of threads
* @throws IllegalMonitorStateException if {@link #isHeldExclusively}
@@ -2215,7 +2238,7 @@
protected final Collection<Thread> getWaitingThreads() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
- ArrayList<Thread> list = new ArrayList<Thread>();
+ ArrayList<Thread> list = new ArrayList<>();
for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
if (w.waitStatus == Node.CONDITION) {
Thread t = w.thread;
@@ -2236,27 +2259,22 @@
* are at it, we do the same for other CASable fields (which could
* otherwise be done with atomic field updaters).
*/
- private static final Unsafe unsafe = Unsafe.getUnsafe();
- private static final long stateOffset;
- private static final long headOffset;
- private static final long tailOffset;
- private static final long waitStatusOffset;
- private static final long nextOffset;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long STATE;
+ private static final long HEAD;
+ private static final long TAIL;
static {
try {
- stateOffset = unsafe.objectFieldOffset
+ STATE = U.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
- headOffset = unsafe.objectFieldOffset
+ HEAD = U.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("head"));
- tailOffset = unsafe.objectFieldOffset
+ TAIL = U.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
- waitStatusOffset = unsafe.objectFieldOffset
- (Node.class.getDeclaredField("waitStatus"));
- nextOffset = unsafe.objectFieldOffset
- (Node.class.getDeclaredField("next"));
-
- } catch (Exception ex) { throw new Error(ex); }
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
// Reduce the risk of rare disastrous classloading in first call to
// LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773
@@ -2264,35 +2282,18 @@
}
/**
- * CAS head field. Used only by enq.
+ * Initializes head and tail fields on first contention.
*/
- private final boolean compareAndSetHead(Node update) {
- return unsafe.compareAndSwapObject(this, headOffset, null, update);
+ private final void initializeSyncQueue() {
+ Node h;
+ if (U.compareAndSwapObject(this, HEAD, null, (h = new Node())))
+ tail = h;
}
/**
- * CAS tail field. Used only by enq.
+ * CASes tail field.
*/
private final boolean compareAndSetTail(Node expect, Node update) {
- return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
- }
-
- /**
- * CAS waitStatus field of a node.
- */
- private static final boolean compareAndSetWaitStatus(Node node,
- int expect,
- int update) {
- return unsafe.compareAndSwapInt(node, waitStatusOffset,
- expect, update);
- }
-
- /**
- * CAS next field of a node.
- */
- private static final boolean compareAndSetNext(Node node,
- Node expect,
- Node update) {
- return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
+ return U.compareAndSwapObject(this, TAIL, expect, update);
}
}
diff --git a/luni/src/main/java/java/util/concurrent/locks/Condition.java b/luni/src/main/java/java/util/concurrent/locks/Condition.java
index 11a7090..b3132e7 100644
--- a/luni/src/main/java/java/util/concurrent/locks/Condition.java
+++ b/luni/src/main/java/java/util/concurrent/locks/Condition.java
@@ -6,8 +6,8 @@
package java.util.concurrent.locks;
-import java.util.concurrent.TimeUnit;
import java.util.Date;
+import java.util.concurrent.TimeUnit;
/**
* {@code Condition} factors out the {@code Object} monitor
@@ -98,7 +98,7 @@
* <p>Note that {@code Condition} instances are just normal objects and can
* themselves be used as the target in a {@code synchronized} statement,
* and can have their own monitor {@link Object#wait wait} and
- * {@link Object#notify notification} methods invoked.
+ * {@link Object#notify notify} methods invoked.
* Acquiring the monitor lock of a {@code Condition} instance, or using its
* monitor methods, has no specified relationship with acquiring the
* {@link Lock} associated with that {@code Condition} or the use of its
@@ -280,7 +280,7 @@
* condition still does not hold. Typical uses of this method take
* the following form:
*
- * <pre> {@code
+ * <pre> {@code
* boolean aMethod(long timeout, TimeUnit unit) {
* long nanos = unit.toNanos(timeout);
* lock.lock();
@@ -333,7 +333,7 @@
* Causes the current thread to wait until it is signalled or interrupted,
* or the specified waiting time elapses. This method is behaviorally
* equivalent to:
- * <pre> {@code awaitNanos(unit.toNanos(time)) > 0}</pre>
+ * <pre> {@code awaitNanos(unit.toNanos(time)) > 0}</pre>
*
* @param time the maximum time to wait
* @param unit the time unit of the {@code time} argument
@@ -382,7 +382,7 @@
*
* <p>The return value indicates whether the deadline has elapsed,
* which can be used as follows:
- * <pre> {@code
+ * <pre> {@code
* boolean aMethod(Date deadline) {
* boolean stillWaiting = true;
* lock.lock();
diff --git a/luni/src/main/java/java/util/concurrent/locks/Lock.java b/luni/src/main/java/java/util/concurrent/locks/Lock.java
index a7ca001..a2d7b48 100644
--- a/luni/src/main/java/java/util/concurrent/locks/Lock.java
+++ b/luni/src/main/java/java/util/concurrent/locks/Lock.java
@@ -49,7 +49,7 @@
* methods and statements. In most cases, the following idiom
* should be used:
*
- * <pre> {@code
+ * <pre> {@code
* Lock l = ...;
* l.lock();
* try {
@@ -93,8 +93,9 @@
* <p>All {@code Lock} implementations <em>must</em> enforce the same
* memory synchronization semantics as provided by the built-in monitor
* lock, as described in
- * <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4">
- * The Java Language Specification (17.4 Memory Model)</a>:
+ * <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4">
+ * Chapter 17 of
+ * <cite>The Java™ Language Specification</cite></a>:
* <ul>
* <li>A successful {@code lock} operation has the same memory
* synchronization effects as a successful <em>Lock</em> action.
@@ -212,7 +213,7 @@
* immediately with the value {@code false}.
*
* <p>A typical usage idiom for this method would be:
- * <pre> {@code
+ * <pre> {@code
* Lock lock = ...;
* if (lock.tryLock()) {
* try {
diff --git a/luni/src/main/java/java/util/concurrent/locks/LockSupport.java b/luni/src/main/java/java/util/concurrent/locks/LockSupport.java
index 089d818..694f4ca 100644
--- a/luni/src/main/java/java/util/concurrent/locks/LockSupport.java
+++ b/luni/src/main/java/java/util/concurrent/locks/LockSupport.java
@@ -6,8 +6,6 @@
package java.util.concurrent.locks;
-import sun.misc.Unsafe;
-
/**
* Basic thread blocking primitives for creating locks and other
* synchronization classes.
@@ -19,6 +17,10 @@
* it <em>may</em> block. A call to {@code unpark} makes the permit
* available, if it was not already available. (Unlike with Semaphores
* though, permits do not accumulate. There is at most one.)
+ * Reliable usage requires the use of volatile (or atomic) variables
+ * to control when to park or unpark. Orderings of calls to these
+ * methods are maintained with respect to volatile variable accesses,
+ * but not necessarily non-volatile variable accesses.
*
* <p>Methods {@code park} and {@code unpark} provide efficient
* means of blocking and unblocking threads that do not encounter the
@@ -39,73 +41,74 @@
* {@code blocker} object parameter. This object is recorded while
* the thread is blocked to permit monitoring and diagnostic tools to
* identify the reasons that threads are blocked. (Such tools may
- * access blockers using method {@link #getBlocker}.) The use of these
- * forms rather than the original forms without this parameter is
- * strongly encouraged. The normal argument to supply as a
- * {@code blocker} within a lock implementation is {@code this}.
+ * access blockers using method {@link #getBlocker(Thread)}.)
+ * The use of these forms rather than the original forms without this
+ * parameter is strongly encouraged. The normal argument to supply as
+ * a {@code blocker} within a lock implementation is {@code this}.
*
* <p>These methods are designed to be used as tools for creating
* higher-level synchronization utilities, and are not in themselves
* useful for most concurrency control applications. The {@code park}
* method is designed for use only in constructions of the form:
*
- * <pre> {@code
- * while (!canProceed()) { ... LockSupport.park(this); }}</pre>
+ * <pre> {@code
+ * while (!canProceed()) {
+ * // ensure request to unpark is visible to other threads
+ * ...
+ * LockSupport.park(this);
+ * }}</pre>
*
- * where neither {@code canProceed} nor any other actions prior to the
- * call to {@code park} entail locking or blocking. Because only one
- * permit is associated with each thread, any intermediary uses of
- * {@code park} could interfere with its intended effects.
+ * where no actions by the thread publishing a request to unpark,
+ * prior to the call to {@code park}, entail locking or blocking.
+ * Because only one permit is associated with each thread, any
+ * intermediary uses of {@code park}, including implicitly via class
+ * loading, could lead to an unresponsive thread (a "lost unpark").
*
* <p><b>Sample Usage.</b> Here is a sketch of a first-in-first-out
* non-reentrant lock class:
- * <pre> {@code
+ * <pre> {@code
* class FIFOMutex {
* private final AtomicBoolean locked = new AtomicBoolean(false);
* private final Queue<Thread> waiters
- * = new ConcurrentLinkedQueue<Thread>();
+ * = new ConcurrentLinkedQueue<>();
*
* public void lock() {
* boolean wasInterrupted = false;
- * Thread current = Thread.currentThread();
- * waiters.add(current);
+ * // publish current thread for unparkers
+ * waiters.add(Thread.currentThread());
*
* // Block while not first in queue or cannot acquire lock
- * while (waiters.peek() != current ||
+ * while (waiters.peek() != Thread.currentThread() ||
* !locked.compareAndSet(false, true)) {
* LockSupport.park(this);
- * if (Thread.interrupted()) // ignore interrupts while waiting
+ * // ignore interrupts while waiting
+ * if (Thread.interrupted())
* wasInterrupted = true;
* }
*
* waiters.remove();
- * if (wasInterrupted) // reassert interrupt status on exit
- * current.interrupt();
+ * // ensure correct interrupt status on return
+ * if (wasInterrupted)
+ * Thread.currentThread().interrupt();
* }
*
* public void unlock() {
* locked.set(false);
* LockSupport.unpark(waiters.peek());
* }
+ *
+ * static {
+ * // Reduce the risk of "lost unpark" due to classloading
+ * Class<?> ensureLoaded = LockSupport.class;
+ * }
* }}</pre>
*/
public class LockSupport {
private LockSupport() {} // Cannot be instantiated.
- // Hotspot implementation via intrinsics API
- private static final Unsafe unsafe = Unsafe.getUnsafe();
- private static final long parkBlockerOffset;
-
- static {
- try {
- parkBlockerOffset = unsafe.objectFieldOffset
- (java.lang.Thread.class.getDeclaredField("parkBlocker"));
- } catch (Exception ex) { throw new Error(ex); }
- }
-
private static void setBlocker(Thread t, Object arg) {
// Even though volatile, hotspot doesn't need a write barrier here.
- unsafe.putObject(t, parkBlockerOffset, arg);
+ U.putObject(t, PARKBLOCKER, arg);
}
/**
@@ -121,7 +124,7 @@
*/
public static void unpark(Thread thread) {
if (thread != null)
- unsafe.unpark(thread);
+ U.unpark(thread);
}
/**
@@ -155,7 +158,7 @@
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
- unsafe.park(false, 0L);
+ U.park(false, 0L);
setBlocker(t, null);
}
@@ -195,7 +198,7 @@
if (nanos > 0) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
- unsafe.park(false, nanos);
+ U.park(false, nanos);
setBlocker(t, null);
}
}
@@ -236,7 +239,7 @@
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
- unsafe.park(true, deadline);
+ U.park(true, deadline);
setBlocker(t, null);
}
@@ -255,7 +258,7 @@
public static Object getBlocker(Thread t) {
if (t == null)
throw new NullPointerException();
- return unsafe.getObjectVolatile(t, parkBlockerOffset);
+ return U.getObjectVolatile(t, PARKBLOCKER);
}
/**
@@ -284,7 +287,7 @@
* for example, the interrupt status of the thread upon return.
*/
public static void park() {
- unsafe.park(false, 0L);
+ U.park(false, 0L);
}
/**
@@ -318,7 +321,7 @@
*/
public static void parkNanos(long nanos) {
if (nanos > 0)
- unsafe.park(false, nanos);
+ U.park(false, nanos);
}
/**
@@ -352,6 +355,40 @@
* to wait until
*/
public static void parkUntil(long deadline) {
- unsafe.park(true, deadline);
+ U.park(true, deadline);
}
+
+ /**
+ * Returns the pseudo-randomly initialized or updated secondary seed.
+ * Copied from ThreadLocalRandom due to package access restrictions.
+ */
+ static final int nextSecondarySeed() {
+ int r;
+ Thread t = Thread.currentThread();
+ if ((r = U.getInt(t, SECONDARY)) != 0) {
+ r ^= r << 13; // xorshift
+ r ^= r >>> 17;
+ r ^= r << 5;
+ }
+ else if ((r = java.util.concurrent.ThreadLocalRandom.current().nextInt()) == 0)
+ r = 1; // avoid zero
+ U.putInt(t, SECONDARY, r);
+ return r;
+ }
+
+ // Hotspot implementation via intrinsics API
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long PARKBLOCKER;
+ private static final long SECONDARY;
+ static {
+ try {
+ PARKBLOCKER = U.objectFieldOffset
+ (Thread.class.getDeclaredField("parkBlocker"));
+ SECONDARY = U.objectFieldOffset
+ (Thread.class.getDeclaredField("threadLocalRandomSecondarySeed"));
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
+ }
+
}
diff --git a/luni/src/main/java/java/util/concurrent/locks/ReadWriteLock.java b/luni/src/main/java/java/util/concurrent/locks/ReadWriteLock.java
index 8690355..a1a211a 100644
--- a/luni/src/main/java/java/util/concurrent/locks/ReadWriteLock.java
+++ b/luni/src/main/java/java/util/concurrent/locks/ReadWriteLock.java
@@ -9,9 +9,9 @@
/**
* A {@code ReadWriteLock} maintains a pair of associated {@link
* Lock locks}, one for read-only operations and one for writing.
- * The {@link #readLock read lock} may be held simultaneously by
- * multiple reader threads, so long as there are no writers. The
- * {@link #writeLock write lock} is exclusive.
+ * The {@linkplain #readLock read lock} may be held simultaneously
+ * by multiple reader threads, so long as there are no writers.
+ * The {@linkplain #writeLock write lock} is exclusive.
*
* <p>All {@code ReadWriteLock} implementations must guarantee that
* the memory synchronization effects of {@code writeLock} operations
diff --git a/luni/src/main/java/java/util/concurrent/locks/ReentrantLock.java b/luni/src/main/java/java/util/concurrent/locks/ReentrantLock.java
index 3654248..5fedcaa 100644
--- a/luni/src/main/java/java/util/concurrent/locks/ReentrantLock.java
+++ b/luni/src/main/java/java/util/concurrent/locks/ReentrantLock.java
@@ -6,8 +6,9 @@
package java.util.concurrent.locks;
-import java.util.concurrent.TimeUnit;
import java.util.Collection;
+import java.util.concurrent.TimeUnit;
+/// OPENJDK-9 import jdk.internal.vm.annotation.ReservedStackAccess;
/**
* A reentrant mutual exclusion {@link Lock} with the same basic
@@ -44,7 +45,7 @@
* follow a call to {@code lock} with a {@code try} block, most
* typically in a before/after construction such as:
*
- * <pre> {@code
+ * <pre> {@code
* class X {
* private final ReentrantLock lock = new ReentrantLock();
* // ...
@@ -98,6 +99,7 @@
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
+/// OPENJDK-9 @ReservedStackAccess
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
@@ -117,6 +119,7 @@
return false;
}
+/// OPENJDK-9 @ReservedStackAccess
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
@@ -174,6 +177,7 @@
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
+/// OPENJDK-9 @ReservedStackAccess
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
@@ -200,6 +204,7 @@
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
+/// OPENJDK-9 @ReservedStackAccess
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
@@ -350,7 +355,7 @@
* method. If you want a timed {@code tryLock} that does permit barging on
* a fair lock then combine the timed and un-timed forms together:
*
- * <pre> {@code
+ * <pre> {@code
* if (lock.tryLock() ||
* lock.tryLock(timeout, unit)) {
* ...
@@ -456,7 +461,7 @@
* InterruptedException} will be thrown, and the thread's
* interrupted status will be cleared.
*
- * <li> Waiting threads are signalled in FIFO order.
+ * <li>Waiting threads are signalled in FIFO order.
*
* <li>The ordering of lock reacquisition for threads returning
* from waiting methods is the same as for threads initially
@@ -483,7 +488,7 @@
* not be entered with the lock already held then we can assert that
* fact:
*
- * <pre> {@code
+ * <pre> {@code
* class X {
* ReentrantLock lock = new ReentrantLock();
* // ...
@@ -508,12 +513,12 @@
/**
* Queries if this lock is held by the current thread.
*
- * <p>Analogous to the {@link Thread#holdsLock} method for built-in
- * monitor locks, this method is typically used for debugging and
- * testing. For example, a method that should only be called while
- * a lock is held can assert that this is the case:
+ * <p>Analogous to the {@link Thread#holdsLock(Object)} method for
+ * built-in monitor locks, this method is typically used for
+ * debugging and testing. For example, a method that should only be
+ * called while a lock is held can assert that this is the case:
*
- * <pre> {@code
+ * <pre> {@code
* class X {
* ReentrantLock lock = new ReentrantLock();
* // ...
@@ -527,7 +532,7 @@
* <p>It can also be used to ensure that a reentrant lock is used
* in a non-reentrant manner, for example:
*
- * <pre> {@code
+ * <pre> {@code
* class X {
* ReentrantLock lock = new ReentrantLock();
* // ...
@@ -618,12 +623,11 @@
}
/**
- * Returns an estimate of the number of threads waiting to
- * acquire this lock. The value is only an estimate because the number of
+ * Returns an estimate of the number of threads waiting to acquire
+ * this lock. The value is only an estimate because the number of
* threads may change dynamically while this method traverses
* internal data structures. This method is designed for use in
- * monitoring of the system state, not for synchronization
- * control.
+ * monitoring system state, not for synchronization control.
*
* @return the estimated number of threads waiting for this lock
*/
diff --git a/luni/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java b/luni/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java
index cc7ba4c..8969a54 100644
--- a/luni/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java
+++ b/luni/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java
@@ -6,8 +6,8 @@
package java.util.concurrent.locks;
-import java.util.concurrent.TimeUnit;
import java.util.Collection;
+import java.util.concurrent.TimeUnit;
/**
* An implementation of {@link ReadWriteLock} supporting similar
@@ -23,15 +23,16 @@
*
* <dl>
* <dt><b><i>Non-fair mode (default)</i></b>
- * <dd>When constructed as non-fair (the default), the order of entry
+ * <dd style="font-family:'DejaVu Sans', Arial, Helvetica, sans-serif">
+ * When constructed as non-fair (the default), the order of entry
* to the read and write lock is unspecified, subject to reentrancy
* constraints. A nonfair lock that is continuously contended may
* indefinitely postpone one or more reader or writer threads, but
* will normally have higher throughput than a fair lock.
- * <p>
*
* <dt><b><i>Fair mode</i></b>
- * <dd>When constructed as fair, threads contend for entry using an
+ * <dd style="font-family:'DejaVu Sans', Arial, Helvetica, sans-serif">
+ * When constructed as fair, threads contend for entry using an
* approximately arrival-order policy. When the currently held lock
* is released, either the longest-waiting single writer thread will
* be assigned the write lock, or if there is a group of reader threads
@@ -53,7 +54,6 @@
* {@link ReadLock#tryLock()} and {@link WriteLock#tryLock()} methods
* do not honor this fair setting and will immediately acquire the lock
* if it is possible, regardless of waiting threads.)
- * <p>
* </dl>
*
* <li><b>Reentrancy</b>
@@ -147,9 +147,9 @@
* is a class using a TreeMap that is expected to be large and
* concurrently accessed.
*
- * <pre> {@code
+ * <pre> {@code
* class RWDictionary {
- * private final Map<String, Data> m = new TreeMap<String, Data>();
+ * private final Map<String, Data> m = new TreeMap<>();
* private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
* private final Lock r = rwl.readLock();
* private final Lock w = rwl.writeLock();
@@ -159,9 +159,9 @@
* try { return m.get(key); }
* finally { r.unlock(); }
* }
- * public String[] allKeys() {
+ * public List<String> allKeys() {
* r.lock();
- * try { return m.keySet().toArray(); }
+ * try { return new ArrayList<>(m.keySet()); }
* finally { r.unlock(); }
* }
* public Data put(String key, Data value) {
@@ -247,9 +247,9 @@
* Maintained as a ThreadLocal; cached in cachedHoldCounter.
*/
static final class HoldCounter {
- int count = 0;
+ int count; // initially 0
// Use id, not reference, to avoid garbage retention
- final long tid = Thread.currentThread().getId();
+ final long tid = getThreadId(Thread.currentThread());
}
/**
@@ -392,7 +392,7 @@
firstReaderHoldCount--;
} else {
HoldCounter rh = cachedHoldCounter;
- if (rh == null || rh.tid != current.getId())
+ if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
int count = rh.count;
if (count <= 1) {
@@ -450,7 +450,7 @@
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;
- if (rh == null || rh.tid != current.getId())
+ if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
@@ -487,7 +487,7 @@
} else {
if (rh == null) {
rh = cachedHoldCounter;
- if (rh == null || rh.tid != current.getId()) {
+ if (rh == null || rh.tid != getThreadId(current)) {
rh = readHolds.get();
if (rh.count == 0)
readHolds.remove();
@@ -508,7 +508,7 @@
} else {
if (rh == null)
rh = cachedHoldCounter;
- if (rh == null || rh.tid != current.getId())
+ if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
@@ -564,7 +564,7 @@
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;
- if (rh == null || rh.tid != current.getId())
+ if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
@@ -615,7 +615,7 @@
return firstReaderHoldCount;
HoldCounter rh = cachedHoldCounter;
- if (rh != null && rh.tid == current.getId())
+ if (rh != null && rh.tid == getThreadId(current))
return rh.count;
int count = readHolds.get().count;
@@ -677,7 +677,7 @@
private final Sync sync;
/**
- * Constructor for use by subclasses
+ * Constructor for use by subclasses.
*
* @param lock the outer lock object
* @throws NullPointerException if the lock is null
@@ -788,7 +788,7 @@
* permit barging on a fair lock then combine the timed and
* un-timed forms together:
*
- * <pre> {@code
+ * <pre> {@code
* if (lock.tryLock() ||
* lock.tryLock(timeout, unit)) {
* ...
@@ -848,7 +848,12 @@
* Attempts to release this lock.
*
* <p>If the number of readers is now zero then the lock
- * is made available for write lock attempts.
+ * is made available for write lock attempts. If the current
+ * thread does not hold this lock then {@link
+ * IllegalMonitorStateException} is thrown.
+ *
+ * @throws IllegalMonitorStateException if the current thread
+ * does not hold this lock
*/
public void unlock() {
sync.releaseShared(1);
@@ -886,7 +891,7 @@
private final Sync sync;
/**
- * Constructor for use by subclasses
+ * Constructor for use by subclasses.
*
* @param lock the outer lock object
* @throws NullPointerException if the lock is null
@@ -1000,7 +1005,7 @@
* by the current thread, or the write lock was already held
* by the current thread; and {@code false} otherwise.
*/
- public boolean tryLock( ) {
+ public boolean tryLock() {
return sync.tryWriteLock();
}
@@ -1020,7 +1025,7 @@
* that does permit barging on a fair lock then combine the
* timed and un-timed forms together:
*
- * <pre> {@code
+ * <pre> {@code
* if (lock.tryLock() ||
* lock.tryLock(timeout, unit)) {
* ...
@@ -1135,7 +1140,7 @@
* InterruptedException} will be thrown, and the thread's
* interrupted status will be cleared.
*
- * <li> Waiting threads are signalled in FIFO order.
+ * <li>Waiting threads are signalled in FIFO order.
*
* <li>The ordering of lock reacquisition for threads returning
* from waiting methods is the same as for threads initially
@@ -1343,7 +1348,7 @@
* either the read or write lock. The value is only an estimate
* because the number of threads may change dynamically while this
* method traverses internal data structures. This method is
- * designed for use in monitoring of the system state, not for
+ * designed for use in monitoring system state, not for
* synchronization control.
*
* @return the estimated number of threads waiting for this lock
@@ -1456,4 +1461,26 @@
"[Write locks = " + w + ", Read locks = " + r + "]";
}
+ /**
+ * Returns the thread id for the given thread. We must access
+ * this directly rather than via method Thread.getId() because
+ * getId() is not final, and has been known to be overridden in
+ * ways that do not preserve unique mappings.
+ */
+ static final long getThreadId(Thread thread) {
+ return U.getLongVolatile(thread, TID);
+ }
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long TID;
+ static {
+ try {
+ TID = U.objectFieldOffset
+ (Thread.class.getDeclaredField("tid"));
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
+ }
+
}
diff --git a/luni/src/main/java/java/util/concurrent/locks/StampedLock.java b/luni/src/main/java/java/util/concurrent/locks/StampedLock.java
new file mode 100644
index 0000000..1f4d54f
--- /dev/null
+++ b/luni/src/main/java/java/util/concurrent/locks/StampedLock.java
@@ -0,0 +1,1403 @@
+/*
+ * 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/
+ */
+
+package java.util.concurrent.locks;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A capability-based lock with three modes for controlling read/write
+ * access. The state of a StampedLock consists of a version and mode.
+ * Lock acquisition methods return a stamp that represents and
+ * controls access with respect to a lock state; "try" versions of
+ * these methods may instead return the special value zero to
+ * represent failure to acquire access. Lock release and conversion
+ * methods require stamps as arguments, and fail if they do not match
+ * the state of the lock. The three modes are:
+ *
+ * <ul>
+ *
+ * <li><b>Writing.</b> Method {@link #writeLock} possibly blocks
+ * waiting for exclusive access, returning a stamp that can be used
+ * in method {@link #unlockWrite} to release the lock. Untimed and
+ * timed versions of {@code tryWriteLock} are also provided. When
+ * the lock is held in write mode, no read locks may be obtained,
+ * and all optimistic read validations will fail.
+ *
+ * <li><b>Reading.</b> Method {@link #readLock} possibly blocks
+ * waiting for non-exclusive access, returning a stamp that can be
+ * used in method {@link #unlockRead} to release the lock. Untimed
+ * and timed versions of {@code tryReadLock} are also provided.
+ *
+ * <li><b>Optimistic Reading.</b> Method {@link #tryOptimisticRead}
+ * returns a non-zero stamp only if the lock is not currently held
+ * in write mode. Method {@link #validate} returns true if the lock
+ * has not been acquired in write mode since obtaining a given
+ * stamp. This mode can be thought of as an extremely weak version
+ * of a read-lock, that can be broken by a writer at any time. The
+ * use of optimistic mode for short read-only code segments often
+ * reduces contention and improves throughput. However, its use is
+ * inherently fragile. Optimistic read sections should only read
+ * fields and hold them in local variables for later use after
+ * validation. Fields read while in optimistic mode may be wildly
+ * inconsistent, so usage applies only when you are familiar enough
+ * with data representations to check consistency and/or repeatedly
+ * invoke method {@code validate()}. For example, such steps are
+ * typically required when first reading an object or array
+ * reference, and then accessing one of its fields, elements or
+ * methods.
+ *
+ * </ul>
+ *
+ * <p>This class also supports methods that conditionally provide
+ * conversions across the three modes. For example, method {@link
+ * #tryConvertToWriteLock} attempts to "upgrade" a mode, returning
+ * a valid write stamp if (1) already in writing mode (2) in reading
+ * mode and there are no other readers or (3) in optimistic mode and
+ * the lock is available. The forms of these methods are designed to
+ * help reduce some of the code bloat that otherwise occurs in
+ * retry-based designs.
+ *
+ * <p>StampedLocks are designed for use as internal utilities in the
+ * development of thread-safe components. Their use relies on
+ * knowledge of the internal properties of the data, objects, and
+ * methods they are protecting. They are not reentrant, so locked
+ * bodies should not call other unknown methods that may try to
+ * re-acquire locks (although you may pass a stamp to other methods
+ * that can use or convert it). The use of read lock modes relies on
+ * the associated code sections being side-effect-free. Unvalidated
+ * optimistic read sections cannot call methods that are not known to
+ * tolerate potential inconsistencies. Stamps use finite
+ * representations, and are not cryptographically secure (i.e., a
+ * valid stamp may be guessable). Stamp values may recycle after (no
+ * sooner than) one year of continuous operation. A stamp held without
+ * use or validation for longer than this period may fail to validate
+ * correctly. StampedLocks are serializable, but always deserialize
+ * into initial unlocked state, so they are not useful for remote
+ * locking.
+ *
+ * <p>The scheduling policy of StampedLock does not consistently
+ * prefer readers over writers or vice versa. All "try" methods are
+ * best-effort and do not necessarily conform to any scheduling or
+ * fairness policy. A zero return from any "try" method for acquiring
+ * or converting locks does not carry any information about the state
+ * of the lock; a subsequent invocation may succeed.
+ *
+ * <p>Because it supports coordinated usage across multiple lock
+ * modes, this class does not directly implement the {@link Lock} or
+ * {@link ReadWriteLock} interfaces. However, a StampedLock may be
+ * viewed {@link #asReadLock()}, {@link #asWriteLock()}, or {@link
+ * #asReadWriteLock()} in applications requiring only the associated
+ * set of functionality.
+ *
+ * <p><b>Sample Usage.</b> The following illustrates some usage idioms
+ * in a class that maintains simple two-dimensional points. The sample
+ * code illustrates some try/catch conventions even though they are
+ * not strictly needed here because no exceptions can occur in their
+ * bodies.<br>
+ *
+ * <pre> {@code
+ * class Point {
+ * private double x, y;
+ * private final StampedLock sl = new StampedLock();
+ *
+ * void move(double deltaX, double deltaY) { // an exclusively locked method
+ * long stamp = sl.writeLock();
+ * try {
+ * x += deltaX;
+ * y += deltaY;
+ * } finally {
+ * sl.unlockWrite(stamp);
+ * }
+ * }
+ *
+ * double distanceFromOrigin() { // A read-only method
+ * long stamp = sl.tryOptimisticRead();
+ * double currentX = x, currentY = y;
+ * if (!sl.validate(stamp)) {
+ * stamp = sl.readLock();
+ * try {
+ * currentX = x;
+ * currentY = y;
+ * } finally {
+ * sl.unlockRead(stamp);
+ * }
+ * }
+ * return Math.sqrt(currentX * currentX + currentY * currentY);
+ * }
+ *
+ * void moveIfAtOrigin(double newX, double newY) { // upgrade
+ * // Could instead start with optimistic, not read mode
+ * long stamp = sl.readLock();
+ * try {
+ * while (x == 0.0 && y == 0.0) {
+ * long ws = sl.tryConvertToWriteLock(stamp);
+ * if (ws != 0L) {
+ * stamp = ws;
+ * x = newX;
+ * y = newY;
+ * break;
+ * }
+ * else {
+ * sl.unlockRead(stamp);
+ * stamp = sl.writeLock();
+ * }
+ * }
+ * } finally {
+ * sl.unlock(stamp);
+ * }
+ * }
+ * }}</pre>
+ *
+ * @since 1.8
+ * @author Doug Lea
+ */
+public class StampedLock implements java.io.Serializable {
+ /*
+ * Algorithmic notes:
+ *
+ * The design employs elements of Sequence locks
+ * (as used in linux kernels; see Lameter's
+ * http://www.lameter.com/gelato2005.pdf
+ * and elsewhere; see
+ * Boehm's http://www.hpl.hp.com/techreports/2012/HPL-2012-68.html)
+ * and Ordered RW locks (see Shirako et al
+ * http://dl.acm.org/citation.cfm?id=2312015)
+ *
+ * Conceptually, the primary state of the lock includes a sequence
+ * number that is odd when write-locked and even otherwise.
+ * However, this is offset by a reader count that is non-zero when
+ * read-locked. The read count is ignored when validating
+ * "optimistic" seqlock-reader-style stamps. Because we must use
+ * a small finite number of bits (currently 7) for readers, a
+ * supplementary reader overflow word is used when the number of
+ * readers exceeds the count field. We do this by treating the max
+ * reader count value (RBITS) as a spinlock protecting overflow
+ * updates.
+ *
+ * Waiters use a modified form of CLH lock used in
+ * AbstractQueuedSynchronizer (see its internal documentation for
+ * a fuller account), where each node is tagged (field mode) as
+ * either a reader or writer. Sets of waiting readers are grouped
+ * (linked) under a common node (field cowait) so act as a single
+ * node with respect to most CLH mechanics. By virtue of the
+ * queue structure, wait nodes need not actually carry sequence
+ * numbers; we know each is greater than its predecessor. This
+ * simplifies the scheduling policy to a mainly-FIFO scheme that
+ * incorporates elements of Phase-Fair locks (see Brandenburg &
+ * Anderson, especially http://www.cs.unc.edu/~bbb/diss/). In
+ * particular, we use the phase-fair anti-barging rule: If an
+ * incoming reader arrives while read lock is held but there is a
+ * queued writer, this incoming reader is queued. (This rule is
+ * responsible for some of the complexity of method acquireRead,
+ * but without it, the lock becomes highly unfair.) Method release
+ * does not (and sometimes cannot) itself wake up cowaiters. This
+ * is done by the primary thread, but helped by any other threads
+ * with nothing better to do in methods acquireRead and
+ * acquireWrite.
+ *
+ * These rules apply to threads actually queued. All tryLock forms
+ * opportunistically try to acquire locks regardless of preference
+ * rules, and so may "barge" their way in. Randomized spinning is
+ * used in the acquire methods to reduce (increasingly expensive)
+ * context switching while also avoiding sustained memory
+ * thrashing among many threads. We limit spins to the head of
+ * queue. A thread spin-waits up to SPINS times (where each
+ * iteration decreases spin count with 50% probability) before
+ * blocking. If, upon wakening it fails to obtain lock, and is
+ * still (or becomes) the first waiting thread (which indicates
+ * that some other thread barged and obtained lock), it escalates
+ * spins (up to MAX_HEAD_SPINS) to reduce the likelihood of
+ * continually losing to barging threads.
+ *
+ * Nearly all of these mechanics are carried out in methods
+ * acquireWrite and acquireRead, that, as typical of such code,
+ * sprawl out because actions and retries rely on consistent sets
+ * of locally cached reads.
+ *
+ * As noted in Boehm's paper (above), sequence validation (mainly
+ * method validate()) requires stricter ordering rules than apply
+ * to normal volatile reads (of "state"). To force orderings of
+ * reads before a validation and the validation itself in those
+ * cases where this is not already forced, we use
+ * Unsafe.loadFence.
+ *
+ * The memory layout keeps lock state and queue pointers together
+ * (normally on the same cache line). This usually works well for
+ * read-mostly loads. In most other cases, the natural tendency of
+ * adaptive-spin CLH locks to reduce memory contention lessens
+ * motivation to further spread out contended locations, but might
+ * be subject to future improvements.
+ */
+
+ private static final long serialVersionUID = -6001602636862214147L;
+
+ /** Number of processors, for spin control */
+ private static final int NCPU = Runtime.getRuntime().availableProcessors();
+
+ /** Maximum number of retries before enqueuing on acquisition */
+ private static final int SPINS = (NCPU > 1) ? 1 << 6 : 0;
+
+ /** Maximum number of retries before blocking at head on acquisition */
+ private static final int HEAD_SPINS = (NCPU > 1) ? 1 << 10 : 0;
+
+ /** Maximum number of retries before re-blocking */
+ private static final int MAX_HEAD_SPINS = (NCPU > 1) ? 1 << 16 : 0;
+
+ /** The period for yielding when waiting for overflow spinlock */
+ private static final int OVERFLOW_YIELD_RATE = 7; // must be power 2 - 1
+
+ /** The number of bits to use for reader count before overflowing */
+ private static final int LG_READERS = 7;
+
+ // Values for lock state and stamp operations
+ private static final long RUNIT = 1L;
+ private static final long WBIT = 1L << LG_READERS;
+ private static final long RBITS = WBIT - 1L;
+ private static final long RFULL = RBITS - 1L;
+ private static final long ABITS = RBITS | WBIT;
+ private static final long SBITS = ~RBITS; // note overlap with ABITS
+
+ // Initial value for lock state; avoid failure value zero
+ private static final long ORIGIN = WBIT << 1;
+
+ // Special value from cancelled acquire methods so caller can throw IE
+ private static final long INTERRUPTED = 1L;
+
+ // Values for node status; order matters
+ private static final int WAITING = -1;
+ private static final int CANCELLED = 1;
+
+ // Modes for nodes (int not boolean to allow arithmetic)
+ private static final int RMODE = 0;
+ private static final int WMODE = 1;
+
+ /** Wait nodes */
+ static final class WNode {
+ volatile WNode prev;
+ volatile WNode next;
+ volatile WNode cowait; // list of linked readers
+ volatile Thread thread; // non-null while possibly parked
+ volatile int status; // 0, WAITING, or CANCELLED
+ final int mode; // RMODE or WMODE
+ WNode(int m, WNode p) { mode = m; prev = p; }
+ }
+
+ /** Head of CLH queue */
+ private transient volatile WNode whead;
+ /** Tail (last) of CLH queue */
+ private transient volatile WNode wtail;
+
+ // views
+ transient ReadLockView readLockView;
+ transient WriteLockView writeLockView;
+ transient ReadWriteLockView readWriteLockView;
+
+ /** Lock sequence/state */
+ private transient volatile long state;
+ /** extra reader count when state read count saturated */
+ private transient int readerOverflow;
+
+ /**
+ * Creates a new lock, initially in unlocked state.
+ */
+ public StampedLock() {
+ state = ORIGIN;
+ }
+
+ /**
+ * Exclusively acquires the lock, blocking if necessary
+ * until available.
+ *
+ * @return a stamp that can be used to unlock or convert mode
+ */
+ public long writeLock() {
+ long s, next; // bypass acquireWrite in fully unlocked case only
+ return ((((s = state) & ABITS) == 0L &&
+ U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
+ next : acquireWrite(false, 0L));
+ }
+
+ /**
+ * Exclusively acquires the lock if it is immediately available.
+ *
+ * @return a stamp that can be used to unlock or convert mode,
+ * or zero if the lock is not available
+ */
+ public long tryWriteLock() {
+ long s, next;
+ return ((((s = state) & ABITS) == 0L &&
+ U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
+ next : 0L);
+ }
+
+ /**
+ * Exclusively acquires the lock if it is available within the
+ * given time and the current thread has not been interrupted.
+ * Behavior under timeout and interruption matches that specified
+ * for method {@link Lock#tryLock(long,TimeUnit)}.
+ *
+ * @param time the maximum time to wait for the lock
+ * @param unit the time unit of the {@code time} argument
+ * @return a stamp that can be used to unlock or convert mode,
+ * or zero if the lock is not available
+ * @throws InterruptedException if the current thread is interrupted
+ * before acquiring the lock
+ */
+ public long tryWriteLock(long time, TimeUnit unit)
+ throws InterruptedException {
+ long nanos = unit.toNanos(time);
+ if (!Thread.interrupted()) {
+ long next, deadline;
+ if ((next = tryWriteLock()) != 0L)
+ return next;
+ if (nanos <= 0L)
+ return 0L;
+ if ((deadline = System.nanoTime() + nanos) == 0L)
+ deadline = 1L;
+ if ((next = acquireWrite(true, deadline)) != INTERRUPTED)
+ return next;
+ }
+ throw new InterruptedException();
+ }
+
+ /**
+ * Exclusively acquires the lock, blocking if necessary
+ * until available or the current thread is interrupted.
+ * Behavior under interruption matches that specified
+ * for method {@link Lock#lockInterruptibly()}.
+ *
+ * @return a stamp that can be used to unlock or convert mode
+ * @throws InterruptedException if the current thread is interrupted
+ * before acquiring the lock
+ */
+ public long writeLockInterruptibly() throws InterruptedException {
+ long next;
+ if (!Thread.interrupted() &&
+ (next = acquireWrite(true, 0L)) != INTERRUPTED)
+ return next;
+ throw new InterruptedException();
+ }
+
+ /**
+ * Non-exclusively acquires the lock, blocking if necessary
+ * until available.
+ *
+ * @return a stamp that can be used to unlock or convert mode
+ */
+ public long readLock() {
+ long s = state, next; // bypass acquireRead on common uncontended case
+ return ((whead == wtail && (s & ABITS) < RFULL &&
+ U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) ?
+ next : acquireRead(false, 0L));
+ }
+
+ /**
+ * Non-exclusively acquires the lock if it is immediately available.
+ *
+ * @return a stamp that can be used to unlock or convert mode,
+ * or zero if the lock is not available
+ */
+ public long tryReadLock() {
+ for (;;) {
+ long s, m, next;
+ if ((m = (s = state) & ABITS) == WBIT)
+ return 0L;
+ else if (m < RFULL) {
+ if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
+ return next;
+ }
+ else if ((next = tryIncReaderOverflow(s)) != 0L)
+ return next;
+ }
+ }
+
+ /**
+ * Non-exclusively acquires the lock if it is available within the
+ * given time and the current thread has not been interrupted.
+ * Behavior under timeout and interruption matches that specified
+ * for method {@link Lock#tryLock(long,TimeUnit)}.
+ *
+ * @param time the maximum time to wait for the lock
+ * @param unit the time unit of the {@code time} argument
+ * @return a stamp that can be used to unlock or convert mode,
+ * or zero if the lock is not available
+ * @throws InterruptedException if the current thread is interrupted
+ * before acquiring the lock
+ */
+ public long tryReadLock(long time, TimeUnit unit)
+ throws InterruptedException {
+ long s, m, next, deadline;
+ long nanos = unit.toNanos(time);
+ if (!Thread.interrupted()) {
+ if ((m = (s = state) & ABITS) != WBIT) {
+ if (m < RFULL) {
+ if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
+ return next;
+ }
+ else if ((next = tryIncReaderOverflow(s)) != 0L)
+ return next;
+ }
+ if (nanos <= 0L)
+ return 0L;
+ if ((deadline = System.nanoTime() + nanos) == 0L)
+ deadline = 1L;
+ if ((next = acquireRead(true, deadline)) != INTERRUPTED)
+ return next;
+ }
+ throw new InterruptedException();
+ }
+
+ /**
+ * Non-exclusively acquires the lock, blocking if necessary
+ * until available or the current thread is interrupted.
+ * Behavior under interruption matches that specified
+ * for method {@link Lock#lockInterruptibly()}.
+ *
+ * @return a stamp that can be used to unlock or convert mode
+ * @throws InterruptedException if the current thread is interrupted
+ * before acquiring the lock
+ */
+ public long readLockInterruptibly() throws InterruptedException {
+ long next;
+ if (!Thread.interrupted() &&
+ (next = acquireRead(true, 0L)) != INTERRUPTED)
+ return next;
+ throw new InterruptedException();
+ }
+
+ /**
+ * Returns a stamp that can later be validated, or zero
+ * if exclusively locked.
+ *
+ * @return a stamp, or zero if exclusively locked
+ */
+ public long tryOptimisticRead() {
+ long s;
+ return (((s = state) & WBIT) == 0L) ? (s & SBITS) : 0L;
+ }
+
+ /**
+ * Returns true if the lock has not been exclusively acquired
+ * since issuance of the given stamp. Always returns false if the
+ * stamp is zero. Always returns true if the stamp represents a
+ * currently held lock. Invoking this method with a value not
+ * obtained from {@link #tryOptimisticRead} or a locking method
+ * for this lock has no defined effect or result.
+ *
+ * @param stamp a stamp
+ * @return {@code true} if the lock has not been exclusively acquired
+ * since issuance of the given stamp; else false
+ */
+ public boolean validate(long stamp) {
+ U.loadFence();
+ return (stamp & SBITS) == (state & SBITS);
+ }
+
+ /**
+ * If the lock state matches the given stamp, releases the
+ * exclusive lock.
+ *
+ * @param stamp a stamp returned by a write-lock operation
+ * @throws IllegalMonitorStateException if the stamp does
+ * not match the current state of this lock
+ */
+ public void unlockWrite(long stamp) {
+ WNode h;
+ if (state != stamp || (stamp & WBIT) == 0L)
+ throw new IllegalMonitorStateException();
+ U.putLongVolatile(this, STATE, (stamp += WBIT) == 0L ? ORIGIN : stamp);
+ if ((h = whead) != null && h.status != 0)
+ release(h);
+ }
+
+ /**
+ * If the lock state matches the given stamp, releases the
+ * non-exclusive lock.
+ *
+ * @param stamp a stamp returned by a read-lock operation
+ * @throws IllegalMonitorStateException if the stamp does
+ * not match the current state of this lock
+ */
+ public void unlockRead(long stamp) {
+ long s, m; WNode h;
+ for (;;) {
+ if (((s = state) & SBITS) != (stamp & SBITS) ||
+ (stamp & ABITS) == 0L || (m = s & ABITS) == 0L || m == WBIT)
+ throw new IllegalMonitorStateException();
+ if (m < RFULL) {
+ if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
+ if (m == RUNIT && (h = whead) != null && h.status != 0)
+ release(h);
+ break;
+ }
+ }
+ else if (tryDecReaderOverflow(s) != 0L)
+ break;
+ }
+ }
+
+ /**
+ * If the lock state matches the given stamp, releases the
+ * corresponding mode of the lock.
+ *
+ * @param stamp a stamp returned by a lock operation
+ * @throws IllegalMonitorStateException if the stamp does
+ * not match the current state of this lock
+ */
+ public void unlock(long stamp) {
+ long a = stamp & ABITS, m, s; WNode h;
+ while (((s = state) & SBITS) == (stamp & SBITS)) {
+ if ((m = s & ABITS) == 0L)
+ break;
+ else if (m == WBIT) {
+ if (a != m)
+ break;
+ U.putLongVolatile(this, STATE, (s += WBIT) == 0L ? ORIGIN : s);
+ if ((h = whead) != null && h.status != 0)
+ release(h);
+ return;
+ }
+ else if (a == 0L || a >= WBIT)
+ break;
+ else if (m < RFULL) {
+ if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
+ if (m == RUNIT && (h = whead) != null && h.status != 0)
+ release(h);
+ return;
+ }
+ }
+ else if (tryDecReaderOverflow(s) != 0L)
+ return;
+ }
+ throw new IllegalMonitorStateException();
+ }
+
+ /**
+ * If the lock state matches the given stamp, atomically performs one of
+ * the following actions. If the stamp represents holding a write
+ * lock, returns it. Or, if a read lock, if the write lock is
+ * available, releases the read lock and returns a write stamp.
+ * Or, if an optimistic read, returns a write stamp only if
+ * immediately available. This method returns zero in all other
+ * cases.
+ *
+ * @param stamp a stamp
+ * @return a valid write stamp, or zero on failure
+ */
+ public long tryConvertToWriteLock(long stamp) {
+ long a = stamp & ABITS, m, s, next;
+ while (((s = state) & SBITS) == (stamp & SBITS)) {
+ if ((m = s & ABITS) == 0L) {
+ if (a != 0L)
+ break;
+ if (U.compareAndSwapLong(this, STATE, s, next = s + WBIT))
+ return next;
+ }
+ else if (m == WBIT) {
+ if (a != m)
+ break;
+ return stamp;
+ }
+ else if (m == RUNIT && a != 0L) {
+ if (U.compareAndSwapLong(this, STATE, s,
+ next = s - RUNIT + WBIT))
+ return next;
+ }
+ else
+ break;
+ }
+ return 0L;
+ }
+
+ /**
+ * If the lock state matches the given stamp, atomically performs one of
+ * the following actions. If the stamp represents holding a write
+ * lock, releases it and obtains a read lock. Or, if a read lock,
+ * returns it. Or, if an optimistic read, acquires a read lock and
+ * returns a read stamp only if immediately available. This method
+ * returns zero in all other cases.
+ *
+ * @param stamp a stamp
+ * @return a valid read stamp, or zero on failure
+ */
+ public long tryConvertToReadLock(long stamp) {
+ long a = stamp & ABITS, m, s, next; WNode h;
+ while (((s = state) & SBITS) == (stamp & SBITS)) {
+ if ((m = s & ABITS) == 0L) {
+ if (a != 0L)
+ break;
+ else if (m < RFULL) {
+ if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
+ return next;
+ }
+ else if ((next = tryIncReaderOverflow(s)) != 0L)
+ return next;
+ }
+ else if (m == WBIT) {
+ if (a != m)
+ break;
+ U.putLongVolatile(this, STATE, next = s + (WBIT + RUNIT));
+ if ((h = whead) != null && h.status != 0)
+ release(h);
+ return next;
+ }
+ else if (a != 0L && a < WBIT)
+ return stamp;
+ else
+ break;
+ }
+ return 0L;
+ }
+
+ /**
+ * If the lock state matches the given stamp then, atomically, if the stamp
+ * represents holding a lock, releases it and returns an
+ * observation stamp. Or, if an optimistic read, returns it if
+ * validated. This method returns zero in all other cases, and so
+ * may be useful as a form of "tryUnlock".
+ *
+ * @param stamp a stamp
+ * @return a valid optimistic read stamp, or zero on failure
+ */
+ public long tryConvertToOptimisticRead(long stamp) {
+ long a = stamp & ABITS, m, s, next; WNode h;
+ U.loadFence();
+ for (;;) {
+ if (((s = state) & SBITS) != (stamp & SBITS))
+ break;
+ if ((m = s & ABITS) == 0L) {
+ if (a != 0L)
+ break;
+ return s;
+ }
+ else if (m == WBIT) {
+ if (a != m)
+ break;
+ U.putLongVolatile(this, STATE,
+ next = (s += WBIT) == 0L ? ORIGIN : s);
+ if ((h = whead) != null && h.status != 0)
+ release(h);
+ return next;
+ }
+ else if (a == 0L || a >= WBIT)
+ break;
+ else if (m < RFULL) {
+ if (U.compareAndSwapLong(this, STATE, s, next = s - RUNIT)) {
+ if (m == RUNIT && (h = whead) != null && h.status != 0)
+ release(h);
+ return next & SBITS;
+ }
+ }
+ else if ((next = tryDecReaderOverflow(s)) != 0L)
+ return next & SBITS;
+ }
+ return 0L;
+ }
+
+ /**
+ * Releases the write lock if it is held, without requiring a
+ * stamp value. This method may be useful for recovery after
+ * errors.
+ *
+ * @return {@code true} if the lock was held, else false
+ */
+ public boolean tryUnlockWrite() {
+ long s; WNode h;
+ if (((s = state) & WBIT) != 0L) {
+ U.putLongVolatile(this, STATE, (s += WBIT) == 0L ? ORIGIN : s);
+ if ((h = whead) != null && h.status != 0)
+ release(h);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Releases one hold of the read lock if it is held, without
+ * requiring a stamp value. This method may be useful for recovery
+ * after errors.
+ *
+ * @return {@code true} if the read lock was held, else false
+ */
+ public boolean tryUnlockRead() {
+ long s, m; WNode h;
+ while ((m = (s = state) & ABITS) != 0L && m < WBIT) {
+ if (m < RFULL) {
+ if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
+ if (m == RUNIT && (h = whead) != null && h.status != 0)
+ release(h);
+ return true;
+ }
+ }
+ else if (tryDecReaderOverflow(s) != 0L)
+ return true;
+ }
+ return false;
+ }
+
+ // status monitoring methods
+
+ /**
+ * Returns combined state-held and overflow read count for given
+ * state s.
+ */
+ private int getReadLockCount(long s) {
+ long readers;
+ if ((readers = s & RBITS) >= RFULL)
+ readers = RFULL + readerOverflow;
+ return (int) readers;
+ }
+
+ /**
+ * Returns {@code true} if the lock is currently held exclusively.
+ *
+ * @return {@code true} if the lock is currently held exclusively
+ */
+ public boolean isWriteLocked() {
+ return (state & WBIT) != 0L;
+ }
+
+ /**
+ * Returns {@code true} if the lock is currently held non-exclusively.
+ *
+ * @return {@code true} if the lock is currently held non-exclusively
+ */
+ public boolean isReadLocked() {
+ return (state & RBITS) != 0L;
+ }
+
+ /**
+ * Queries the number of read locks held for this lock. This
+ * method is designed for use in monitoring system state, not for
+ * synchronization control.
+ * @return the number of read locks held
+ */
+ public int getReadLockCount() {
+ return getReadLockCount(state);
+ }
+
+ /**
+ * Returns a string identifying this lock, as well as its lock
+ * state. The state, in brackets, includes the String {@code
+ * "Unlocked"} or the String {@code "Write-locked"} or the String
+ * {@code "Read-locks:"} followed by the current number of
+ * read-locks held.
+ *
+ * @return a string identifying this lock, as well as its lock state
+ */
+ public String toString() {
+ long s = state;
+ return super.toString() +
+ ((s & ABITS) == 0L ? "[Unlocked]" :
+ (s & WBIT) != 0L ? "[Write-locked]" :
+ "[Read-locks:" + getReadLockCount(s) + "]");
+ }
+
+ // views
+
+ /**
+ * Returns a plain {@link Lock} view of this StampedLock in which
+ * the {@link Lock#lock} method is mapped to {@link #readLock},
+ * and similarly for other methods. The returned Lock does not
+ * support a {@link Condition}; method {@link
+ * Lock#newCondition()} throws {@code
+ * UnsupportedOperationException}.
+ *
+ * @return the lock
+ */
+ public Lock asReadLock() {
+ ReadLockView v;
+ return ((v = readLockView) != null ? v :
+ (readLockView = new ReadLockView()));
+ }
+
+ /**
+ * Returns a plain {@link Lock} view of this StampedLock in which
+ * the {@link Lock#lock} method is mapped to {@link #writeLock},
+ * and similarly for other methods. The returned Lock does not
+ * support a {@link Condition}; method {@link
+ * Lock#newCondition()} throws {@code
+ * UnsupportedOperationException}.
+ *
+ * @return the lock
+ */
+ public Lock asWriteLock() {
+ WriteLockView v;
+ return ((v = writeLockView) != null ? v :
+ (writeLockView = new WriteLockView()));
+ }
+
+ /**
+ * Returns a {@link ReadWriteLock} view of this StampedLock in
+ * which the {@link ReadWriteLock#readLock()} method is mapped to
+ * {@link #asReadLock()}, and {@link ReadWriteLock#writeLock()} to
+ * {@link #asWriteLock()}.
+ *
+ * @return the lock
+ */
+ public ReadWriteLock asReadWriteLock() {
+ ReadWriteLockView v;
+ return ((v = readWriteLockView) != null ? v :
+ (readWriteLockView = new ReadWriteLockView()));
+ }
+
+ // view classes
+
+ final class ReadLockView implements Lock {
+ public void lock() { readLock(); }
+ public void lockInterruptibly() throws InterruptedException {
+ readLockInterruptibly();
+ }
+ public boolean tryLock() { return tryReadLock() != 0L; }
+ public boolean tryLock(long time, TimeUnit unit)
+ throws InterruptedException {
+ return tryReadLock(time, unit) != 0L;
+ }
+ public void unlock() { unstampedUnlockRead(); }
+ public Condition newCondition() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ final class WriteLockView implements Lock {
+ public void lock() { writeLock(); }
+ public void lockInterruptibly() throws InterruptedException {
+ writeLockInterruptibly();
+ }
+ public boolean tryLock() { return tryWriteLock() != 0L; }
+ public boolean tryLock(long time, TimeUnit unit)
+ throws InterruptedException {
+ return tryWriteLock(time, unit) != 0L;
+ }
+ public void unlock() { unstampedUnlockWrite(); }
+ public Condition newCondition() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ final class ReadWriteLockView implements ReadWriteLock {
+ public Lock readLock() { return asReadLock(); }
+ public Lock writeLock() { return asWriteLock(); }
+ }
+
+ // Unlock methods without stamp argument checks for view classes.
+ // Needed because view-class lock methods throw away stamps.
+
+ final void unstampedUnlockWrite() {
+ WNode h; long s;
+ if (((s = state) & WBIT) == 0L)
+ throw new IllegalMonitorStateException();
+ U.putLongVolatile(this, STATE, (s += WBIT) == 0L ? ORIGIN : s);
+ if ((h = whead) != null && h.status != 0)
+ release(h);
+ }
+
+ final void unstampedUnlockRead() {
+ for (;;) {
+ long s, m; WNode h;
+ if ((m = (s = state) & ABITS) == 0L || m >= WBIT)
+ throw new IllegalMonitorStateException();
+ else if (m < RFULL) {
+ if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
+ if (m == RUNIT && (h = whead) != null && h.status != 0)
+ release(h);
+ break;
+ }
+ }
+ else if (tryDecReaderOverflow(s) != 0L)
+ break;
+ }
+ }
+
+ private void readObject(java.io.ObjectInputStream s)
+ throws java.io.IOException, ClassNotFoundException {
+ s.defaultReadObject();
+ U.putLongVolatile(this, STATE, ORIGIN); // reset to unlocked state
+ }
+
+ // internals
+
+ /**
+ * Tries to increment readerOverflow by first setting state
+ * access bits value to RBITS, indicating hold of spinlock,
+ * then updating, then releasing.
+ *
+ * @param s a reader overflow stamp: (s & ABITS) >= RFULL
+ * @return new stamp on success, else zero
+ */
+ private long tryIncReaderOverflow(long s) {
+ // assert (s & ABITS) >= RFULL;
+ if ((s & ABITS) == RFULL) {
+ if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
+ ++readerOverflow;
+ U.putLongVolatile(this, STATE, s);
+ return s;
+ }
+ }
+ else if ((LockSupport.nextSecondarySeed() &
+ OVERFLOW_YIELD_RATE) == 0)
+ Thread.yield();
+ return 0L;
+ }
+
+ /**
+ * Tries to decrement readerOverflow.
+ *
+ * @param s a reader overflow stamp: (s & ABITS) >= RFULL
+ * @return new stamp on success, else zero
+ */
+ private long tryDecReaderOverflow(long s) {
+ // assert (s & ABITS) >= RFULL;
+ if ((s & ABITS) == RFULL) {
+ if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
+ int r; long next;
+ if ((r = readerOverflow) > 0) {
+ readerOverflow = r - 1;
+ next = s;
+ }
+ else
+ next = s - RUNIT;
+ U.putLongVolatile(this, STATE, next);
+ return next;
+ }
+ }
+ else if ((LockSupport.nextSecondarySeed() &
+ OVERFLOW_YIELD_RATE) == 0)
+ Thread.yield();
+ return 0L;
+ }
+
+ /**
+ * Wakes up the successor of h (normally whead). This is normally
+ * just h.next, but may require traversal from wtail if next
+ * pointers are lagging. This may fail to wake up an acquiring
+ * thread when one or more have been cancelled, but the cancel
+ * methods themselves provide extra safeguards to ensure liveness.
+ */
+ private void release(WNode h) {
+ if (h != null) {
+ WNode q; Thread w;
+ U.compareAndSwapInt(h, WSTATUS, WAITING, 0);
+ if ((q = h.next) == null || q.status == CANCELLED) {
+ for (WNode t = wtail; t != null && t != h; t = t.prev)
+ if (t.status <= 0)
+ q = t;
+ }
+ if (q != null && (w = q.thread) != null)
+ U.unpark(w);
+ }
+ }
+
+ /**
+ * See above for explanation.
+ *
+ * @param interruptible true if should check interrupts and if so
+ * return INTERRUPTED
+ * @param deadline if nonzero, the System.nanoTime value to timeout
+ * at (and return zero)
+ * @return next state, or INTERRUPTED
+ */
+ private long acquireWrite(boolean interruptible, long deadline) {
+ WNode node = null, p;
+ for (int spins = -1;;) { // spin while enqueuing
+ long m, s, ns;
+ if ((m = (s = state) & ABITS) == 0L) {
+ if (U.compareAndSwapLong(this, STATE, s, ns = s + WBIT))
+ return ns;
+ }
+ else if (spins < 0)
+ spins = (m == WBIT && wtail == whead) ? SPINS : 0;
+ else if (spins > 0) {
+ if (LockSupport.nextSecondarySeed() >= 0)
+ --spins;
+ }
+ else if ((p = wtail) == null) { // initialize queue
+ WNode hd = new WNode(WMODE, null);
+ if (U.compareAndSwapObject(this, WHEAD, null, hd))
+ wtail = hd;
+ }
+ else if (node == null)
+ node = new WNode(WMODE, p);
+ else if (node.prev != p)
+ node.prev = p;
+ else if (U.compareAndSwapObject(this, WTAIL, p, node)) {
+ p.next = node;
+ break;
+ }
+ }
+
+ boolean wasInterrupted = false;
+ for (int spins = -1;;) {
+ WNode h, np, pp; int ps;
+ if ((h = whead) == p) {
+ if (spins < 0)
+ spins = HEAD_SPINS;
+ else if (spins < MAX_HEAD_SPINS)
+ spins <<= 1;
+ for (int k = spins;;) { // spin at head
+ long s, ns;
+ if (((s = state) & ABITS) == 0L) {
+ if (U.compareAndSwapLong(this, STATE, s,
+ ns = s + WBIT)) {
+ whead = node;
+ node.prev = null;
+ if (wasInterrupted)
+ Thread.currentThread().interrupt();
+ return ns;
+ }
+ }
+ else if (LockSupport.nextSecondarySeed() >= 0 &&
+ --k <= 0)
+ break;
+ }
+ }
+ else if (h != null) { // help release stale waiters
+ WNode c; Thread w;
+ while ((c = h.cowait) != null) {
+ if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
+ (w = c.thread) != null)
+ U.unpark(w);
+ }
+ }
+ if (whead == h) {
+ if ((np = node.prev) != p) {
+ if (np != null)
+ (p = np).next = node; // stale
+ }
+ else if ((ps = p.status) == 0)
+ U.compareAndSwapInt(p, WSTATUS, 0, WAITING);
+ else if (ps == CANCELLED) {
+ if ((pp = p.prev) != null) {
+ node.prev = pp;
+ pp.next = node;
+ }
+ }
+ else {
+ long time; // 0 argument to park means no timeout
+ if (deadline == 0L)
+ time = 0L;
+ else if ((time = deadline - System.nanoTime()) <= 0L)
+ return cancelWaiter(node, node, false);
+ Thread wt = Thread.currentThread();
+ U.putObject(wt, PARKBLOCKER, this);
+ node.thread = wt;
+ if (p.status < 0 && (p != h || (state & ABITS) != 0L) &&
+ whead == h && node.prev == p)
+ U.park(false, time); // emulate LockSupport.park
+ node.thread = null;
+ U.putObject(wt, PARKBLOCKER, null);
+ if (Thread.interrupted()) {
+ if (interruptible)
+ return cancelWaiter(node, node, true);
+ wasInterrupted = true;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * See above for explanation.
+ *
+ * @param interruptible true if should check interrupts and if so
+ * return INTERRUPTED
+ * @param deadline if nonzero, the System.nanoTime value to timeout
+ * at (and return zero)
+ * @return next state, or INTERRUPTED
+ */
+ private long acquireRead(boolean interruptible, long deadline) {
+ boolean wasInterrupted = false;
+ WNode node = null, p;
+ for (int spins = -1;;) {
+ WNode h;
+ if ((h = whead) == (p = wtail)) {
+ for (long m, s, ns;;) {
+ if ((m = (s = state) & ABITS) < RFULL ?
+ U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
+ (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) {
+ if (wasInterrupted)
+ Thread.currentThread().interrupt();
+ return ns;
+ }
+ else if (m >= WBIT) {
+ if (spins > 0) {
+ if (LockSupport.nextSecondarySeed() >= 0)
+ --spins;
+ }
+ else {
+ if (spins == 0) {
+ WNode nh = whead, np = wtail;
+ if ((nh == h && np == p) || (h = nh) != (p = np))
+ break;
+ }
+ spins = SPINS;
+ }
+ }
+ }
+ }
+ if (p == null) { // initialize queue
+ WNode hd = new WNode(WMODE, null);
+ if (U.compareAndSwapObject(this, WHEAD, null, hd))
+ wtail = hd;
+ }
+ else if (node == null)
+ node = new WNode(RMODE, p);
+ else if (h == p || p.mode != RMODE) {
+ if (node.prev != p)
+ node.prev = p;
+ else if (U.compareAndSwapObject(this, WTAIL, p, node)) {
+ p.next = node;
+ break;
+ }
+ }
+ else if (!U.compareAndSwapObject(p, WCOWAIT,
+ node.cowait = p.cowait, node))
+ node.cowait = null;
+ else {
+ for (;;) {
+ WNode pp, c; Thread w;
+ if ((h = whead) != null && (c = h.cowait) != null &&
+ U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
+ (w = c.thread) != null) // help release
+ U.unpark(w);
+ if (h == (pp = p.prev) || h == p || pp == null) {
+ long m, s, ns;
+ do {
+ if ((m = (s = state) & ABITS) < RFULL ?
+ U.compareAndSwapLong(this, STATE, s,
+ ns = s + RUNIT) :
+ (m < WBIT &&
+ (ns = tryIncReaderOverflow(s)) != 0L)) {
+ if (wasInterrupted)
+ Thread.currentThread().interrupt();
+ return ns;
+ }
+ } while (m < WBIT);
+ }
+ if (whead == h && p.prev == pp) {
+ long time;
+ if (pp == null || h == p || p.status > 0) {
+ node = null; // throw away
+ break;
+ }
+ if (deadline == 0L)
+ time = 0L;
+ else if ((time = deadline - System.nanoTime()) <= 0L) {
+ if (wasInterrupted)
+ Thread.currentThread().interrupt();
+ return cancelWaiter(node, p, false);
+ }
+ Thread wt = Thread.currentThread();
+ U.putObject(wt, PARKBLOCKER, this);
+ node.thread = wt;
+ if ((h != pp || (state & ABITS) == WBIT) &&
+ whead == h && p.prev == pp)
+ U.park(false, time);
+ node.thread = null;
+ U.putObject(wt, PARKBLOCKER, null);
+ if (Thread.interrupted()) {
+ if (interruptible)
+ return cancelWaiter(node, p, true);
+ wasInterrupted = true;
+ }
+ }
+ }
+ }
+ }
+
+ for (int spins = -1;;) {
+ WNode h, np, pp; int ps;
+ if ((h = whead) == p) {
+ if (spins < 0)
+ spins = HEAD_SPINS;
+ else if (spins < MAX_HEAD_SPINS)
+ spins <<= 1;
+ for (int k = spins;;) { // spin at head
+ long m, s, ns;
+ if ((m = (s = state) & ABITS) < RFULL ?
+ U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
+ (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) {
+ WNode c; Thread w;
+ whead = node;
+ node.prev = null;
+ while ((c = node.cowait) != null) {
+ if (U.compareAndSwapObject(node, WCOWAIT,
+ c, c.cowait) &&
+ (w = c.thread) != null)
+ U.unpark(w);
+ }
+ if (wasInterrupted)
+ Thread.currentThread().interrupt();
+ return ns;
+ }
+ else if (m >= WBIT &&
+ LockSupport.nextSecondarySeed() >= 0 && --k <= 0)
+ break;
+ }
+ }
+ else if (h != null) {
+ WNode c; Thread w;
+ while ((c = h.cowait) != null) {
+ if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
+ (w = c.thread) != null)
+ U.unpark(w);
+ }
+ }
+ if (whead == h) {
+ if ((np = node.prev) != p) {
+ if (np != null)
+ (p = np).next = node; // stale
+ }
+ else if ((ps = p.status) == 0)
+ U.compareAndSwapInt(p, WSTATUS, 0, WAITING);
+ else if (ps == CANCELLED) {
+ if ((pp = p.prev) != null) {
+ node.prev = pp;
+ pp.next = node;
+ }
+ }
+ else {
+ long time;
+ if (deadline == 0L)
+ time = 0L;
+ else if ((time = deadline - System.nanoTime()) <= 0L)
+ return cancelWaiter(node, node, false);
+ Thread wt = Thread.currentThread();
+ U.putObject(wt, PARKBLOCKER, this);
+ node.thread = wt;
+ if (p.status < 0 &&
+ (p != h || (state & ABITS) == WBIT) &&
+ whead == h && node.prev == p)
+ U.park(false, time);
+ node.thread = null;
+ U.putObject(wt, PARKBLOCKER, null);
+ if (Thread.interrupted()) {
+ if (interruptible)
+ return cancelWaiter(node, node, true);
+ wasInterrupted = true;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * If node non-null, forces cancel status and unsplices it from
+ * queue if possible and wakes up any cowaiters (of the node, or
+ * group, as applicable), and in any case helps release current
+ * first waiter if lock is free. (Calling with null arguments
+ * serves as a conditional form of release, which is not currently
+ * needed but may be needed under possible future cancellation
+ * policies). This is a variant of cancellation methods in
+ * AbstractQueuedSynchronizer (see its detailed explanation in AQS
+ * internal documentation).
+ *
+ * @param node if nonnull, the waiter
+ * @param group either node or the group node is cowaiting with
+ * @param interrupted if already interrupted
+ * @return INTERRUPTED if interrupted or Thread.interrupted, else zero
+ */
+ private long cancelWaiter(WNode node, WNode group, boolean interrupted) {
+ if (node != null && group != null) {
+ Thread w;
+ node.status = CANCELLED;
+ // unsplice cancelled nodes from group
+ for (WNode p = group, q; (q = p.cowait) != null;) {
+ if (q.status == CANCELLED) {
+ U.compareAndSwapObject(p, WCOWAIT, q, q.cowait);
+ p = group; // restart
+ }
+ else
+ p = q;
+ }
+ if (group == node) {
+ for (WNode r = group.cowait; r != null; r = r.cowait) {
+ if ((w = r.thread) != null)
+ U.unpark(w); // wake up uncancelled co-waiters
+ }
+ for (WNode pred = node.prev; pred != null; ) { // unsplice
+ WNode succ, pp; // find valid successor
+ while ((succ = node.next) == null ||
+ succ.status == CANCELLED) {
+ WNode q = null; // find successor the slow way
+ for (WNode t = wtail; t != null && t != node; t = t.prev)
+ if (t.status != CANCELLED)
+ q = t; // don't link if succ cancelled
+ if (succ == q || // ensure accurate successor
+ U.compareAndSwapObject(node, WNEXT,
+ succ, succ = q)) {
+ if (succ == null && node == wtail)
+ U.compareAndSwapObject(this, WTAIL, node, pred);
+ break;
+ }
+ }
+ if (pred.next == node) // unsplice pred link
+ U.compareAndSwapObject(pred, WNEXT, node, succ);
+ if (succ != null && (w = succ.thread) != null) {
+ succ.thread = null;
+ U.unpark(w); // wake up succ to observe new pred
+ }
+ if (pred.status != CANCELLED || (pp = pred.prev) == null)
+ break;
+ node.prev = pp; // repeat if new pred wrong/cancelled
+ U.compareAndSwapObject(pp, WNEXT, pred, succ);
+ pred = pp;
+ }
+ }
+ }
+ WNode h; // Possibly release first waiter
+ while ((h = whead) != null) {
+ long s; WNode q; // similar to release() but check eligibility
+ if ((q = h.next) == null || q.status == CANCELLED) {
+ for (WNode t = wtail; t != null && t != h; t = t.prev)
+ if (t.status <= 0)
+ q = t;
+ }
+ if (h == whead) {
+ if (q != null && h.status == 0 &&
+ ((s = state) & ABITS) != WBIT && // waiter is eligible
+ (s == 0L || q.mode == RMODE))
+ release(h);
+ break;
+ }
+ }
+ return (interrupted || Thread.interrupted()) ? INTERRUPTED : 0L;
+ }
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long STATE;
+ private static final long WHEAD;
+ private static final long WTAIL;
+ private static final long WNEXT;
+ private static final long WSTATUS;
+ private static final long WCOWAIT;
+ private static final long PARKBLOCKER;
+
+ static {
+ try {
+ STATE = U.objectFieldOffset
+ (StampedLock.class.getDeclaredField("state"));
+ WHEAD = U.objectFieldOffset
+ (StampedLock.class.getDeclaredField("whead"));
+ WTAIL = U.objectFieldOffset
+ (StampedLock.class.getDeclaredField("wtail"));
+
+ WSTATUS = U.objectFieldOffset
+ (WNode.class.getDeclaredField("status"));
+ WNEXT = U.objectFieldOffset
+ (WNode.class.getDeclaredField("next"));
+ WCOWAIT = U.objectFieldOffset
+ (WNode.class.getDeclaredField("cowait"));
+
+ PARKBLOCKER = U.objectFieldOffset
+ (Thread.class.getDeclaredField("parkBlocker"));
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
+ }
+}
diff --git a/luni/src/main/java/java/util/concurrent/package-info.java b/luni/src/main/java/java/util/concurrent/package-info.java
index afc8ca4..5dc1228 100644
--- a/luni/src/main/java/java/util/concurrent/package-info.java
+++ b/luni/src/main/java/java/util/concurrent/package-info.java
@@ -181,18 +181,25 @@
* collections are unshared, or are accessible only when
* holding other locks.
*
- * <p>Most concurrent Collection implementations (including most
- * Queues) also differ from the usual java.util conventions in that
- * their Iterators provide <em>weakly consistent</em> rather than
- * fast-fail traversal. A weakly consistent iterator is thread-safe,
- * but does not necessarily freeze the collection while iterating, so
- * it may (or may not) reflect any updates since the iterator was
- * created.
+ * <p id="Weakly">Most concurrent Collection implementations
+ * (including most Queues) also differ from the usual {@code java.util}
+ * conventions in that their {@linkplain java.util.Iterator Iterators}
+ * and {@linkplain java.util.Spliterator Spliterators} provide
+ * <em>weakly consistent</em> rather than fast-fail traversal:
+ * <ul>
+ * <li>they may proceed concurrently with other operations
+ * <li>they will never throw {@link java.util.ConcurrentModificationException
+ * ConcurrentModificationException}
+ * <li>they are guaranteed to traverse elements as they existed upon
+ * construction exactly once, and may (but are not guaranteed to)
+ * reflect any modifications subsequent to construction.
+ * </ul>
*
* <h2 id="MemoryVisibility">Memory Consistency Properties</h2>
*
- * <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.5">
- * Chapter 17 of the Java Language Specification</a> defines the
+ * <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.5">
+ * Chapter 17 of
+ * <cite>The Java™ Language Specification</cite></a> defines the
* <i>happens-before</i> relation on memory operations such as reads and
* writes of shared variables. The results of a write by one thread are
* guaranteed to be visible to a read by another thread only if the write
diff --git a/non_openjdk_java_files.mk b/non_openjdk_java_files.mk
index 0c5502b..104a6f3 100644
--- a/non_openjdk_java_files.mk
+++ b/non_openjdk_java_files.mk
@@ -88,7 +88,10 @@
luni/src/main/java/java/util/concurrent/BrokenBarrierException.java \
luni/src/main/java/java/util/concurrent/Callable.java \
luni/src/main/java/java/util/concurrent/CancellationException.java \
+ luni/src/main/java/java/util/concurrent/CompletableFuture.java \
+ luni/src/main/java/java/util/concurrent/CompletionException.java \
luni/src/main/java/java/util/concurrent/CompletionService.java \
+ luni/src/main/java/java/util/concurrent/CompletionStage.java \
luni/src/main/java/java/util/concurrent/ConcurrentHashMap.java \
luni/src/main/java/java/util/concurrent/ConcurrentLinkedDeque.java \
luni/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java \
@@ -114,6 +117,7 @@
luni/src/main/java/java/util/concurrent/ForkJoinWorkerThread.java \
luni/src/main/java/java/util/concurrent/Future.java \
luni/src/main/java/java/util/concurrent/FutureTask.java \
+ luni/src/main/java/java/util/concurrent/Helpers.java \
luni/src/main/java/java/util/concurrent/LinkedBlockingDeque.java \
luni/src/main/java/java/util/concurrent/LinkedBlockingQueue.java \
luni/src/main/java/java/util/concurrent/LinkedTransferQueue.java \
@@ -148,7 +152,11 @@
luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java \
luni/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java \
luni/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java \
- luni/src/main/java/java/util/concurrent/atomic/Fences.java \
+ luni/src/main/java/java/util/concurrent/atomic/DoubleAccumulator.java \
+ luni/src/main/java/java/util/concurrent/atomic/DoubleAdder.java \
+ luni/src/main/java/java/util/concurrent/atomic/LongAccumulator.java \
+ luni/src/main/java/java/util/concurrent/atomic/LongAdder.java \
+ luni/src/main/java/java/util/concurrent/atomic/Striped64.java \
luni/src/main/java/java/util/concurrent/atomic/package-info.java \
luni/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java \
luni/src/main/java/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java \
@@ -159,6 +167,7 @@
luni/src/main/java/java/util/concurrent/locks/ReadWriteLock.java \
luni/src/main/java/java/util/concurrent/locks/ReentrantLock.java \
luni/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java \
+ luni/src/main/java/java/util/concurrent/locks/StampedLock.java \
luni/src/main/java/java/util/concurrent/locks/package-info.java \
luni/src/main/java/java/util/concurrent/package-info.java \
luni/src/main/java/javax/xml/XMLConstants.java \
diff --git a/ojluni/src/main/java/java/lang/Thread.java b/ojluni/src/main/java/java/lang/Thread.java
index 2afeccf..c14022b 100755
--- a/ojluni/src/main/java/java/lang/Thread.java
+++ b/ojluni/src/main/java/java/lang/Thread.java
@@ -2050,6 +2050,25 @@
}
}
+
+ // The following three initially uninitialized fields are exclusively
+ // managed by class java.util.concurrent.ThreadLocalRandom. These
+ // fields are used to build the high-performance PRNGs in the
+ // concurrent code, and we can not risk accidental false sharing.
+ // Hence, the fields are isolated with @Contended.
+
+ /** The current seed for a ThreadLocalRandom */
+ // @sun.misc.Contended("tlr")
+ long threadLocalRandomSeed;
+
+ /** Probe hash value; nonzero if threadLocalRandomSeed initialized */
+ // @sun.misc.Contended("tlr")
+ int threadLocalRandomProbe;
+
+ /** Secondary seed isolated from public ThreadLocalRandom sequence */
+ // @sun.misc.Contended("tlr")
+ int threadLocalRandomSecondarySeed;
+
/* Some private helper methods */
private native void nativeSetName(String newName);
diff --git a/ojluni/src/main/java/java/util/AbstractQueue.java b/ojluni/src/main/java/java/util/AbstractQueue.java
index ebe2700..44e11f7 100644
--- a/ojluni/src/main/java/java/util/AbstractQueue.java
+++ b/ojluni/src/main/java/java/util/AbstractQueue.java
@@ -58,7 +58,7 @@
*
* @since 1.5
* @author Doug Lea
- * @param <E> the type of elements held in this collection
+ * @param <E> the type of elements held in this queue
*/
public abstract class AbstractQueue<E>
extends AbstractCollection<E>
diff --git a/ojluni/src/main/java/java/util/ArrayDeque.java b/ojluni/src/main/java/java/util/ArrayDeque.java
index eb31bcc..83ae076 100644
--- a/ojluni/src/main/java/java/util/ArrayDeque.java
+++ b/ojluni/src/main/java/java/util/ArrayDeque.java
@@ -33,7 +33,7 @@
package java.util;
-import java.io.*;
+import java.io.Serializable;
import java.util.function.Consumer;
// BEGIN android-note
@@ -81,10 +81,10 @@
*
* @author Josh Bloch and Doug Lea
* @since 1.6
- * @param <E> the type of elements held in this collection
+ * @param <E> the type of elements held in this deque
*/
public class ArrayDeque<E> extends AbstractCollection<E>
- implements Deque<E>, Cloneable, java.io.Serializable
+ implements Deque<E>, Cloneable, Serializable
{
/**
* The array in which the elements of the deque are stored.
@@ -96,20 +96,20 @@
* other. We also guarantee that all array cells not holding
* deque elements are always null.
*/
- private transient Object[] elements;
+ transient Object[] elements; // non-private to simplify nested class access
/**
* The index of the element at the head of the deque (which is the
* element that would be removed by remove() or pop()); or an
* arbitrary number equal to tail if the deque is empty.
*/
- private transient int head;
+ transient int head;
/**
* The index at which the next element would be added to the tail
* of the deque (via addLast(E), add(E), or push(E)).
*/
- private transient int tail;
+ transient int tail;
/**
* The minimum capacity that we'll use for a newly created deque.
@@ -137,8 +137,8 @@
initialCapacity |= (initialCapacity >>> 16);
initialCapacity++;
- if (initialCapacity < 0) // Too many elements, must back off
- initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
+ if (initialCapacity < 0) // Too many elements, must back off
+ initialCapacity >>>= 1; // Good luck allocating 2^30 elements
}
elements = new Object[initialCapacity];
}
@@ -275,25 +275,27 @@
}
public E pollFirst() {
- int h = head;
+ final Object[] elements = this.elements;
+ final int h = head;
@SuppressWarnings("unchecked")
E result = (E) elements[h];
// Element is null if deque empty
- if (result == null)
- return null;
- elements[h] = null; // Must null out slot
- head = (h + 1) & (elements.length - 1);
+ if (result != null) {
+ elements[h] = null; // Must null out slot
+ head = (h + 1) & (elements.length - 1);
+ }
return result;
}
public E pollLast() {
- int t = (tail - 1) & (elements.length - 1);
+ final Object[] elements = this.elements;
+ final int t = (tail - 1) & (elements.length - 1);
@SuppressWarnings("unchecked")
E result = (E) elements[t];
- if (result == null)
- return null;
- elements[t] = null;
- tail = t;
+ if (result != null) {
+ elements[t] = null;
+ tail = t;
+ }
return result;
}
@@ -343,17 +345,15 @@
* @return {@code true} if the deque contained the specified element
*/
public boolean removeFirstOccurrence(Object o) {
- if (o == null)
- return false;
- int mask = elements.length - 1;
- int i = head;
- Object x;
- while ( (x = elements[i]) != null) {
- if (o.equals(x)) {
- delete(i);
- return true;
+ if (o != null) {
+ int mask = elements.length - 1;
+ int i = head;
+ for (Object x; (x = elements[i]) != null; i = (i + 1) & mask) {
+ if (o.equals(x)) {
+ delete(i);
+ return true;
+ }
}
- i = (i + 1) & mask;
}
return false;
}
@@ -371,17 +371,15 @@
* @return {@code true} if the deque contained the specified element
*/
public boolean removeLastOccurrence(Object o) {
- if (o == null)
- return false;
- int mask = elements.length - 1;
- int i = (tail - 1) & mask;
- Object x;
- while ( (x = elements[i]) != null) {
- if (o.equals(x)) {
- delete(i);
- return true;
+ if (o != null) {
+ int mask = elements.length - 1;
+ int i = (tail - 1) & mask;
+ for (Object x; (x = elements[i]) != null; i = (i - 1) & mask) {
+ if (o.equals(x)) {
+ delete(i);
+ return true;
+ }
}
- i = (i - 1) & mask;
}
return false;
}
@@ -501,11 +499,11 @@
}
private void checkInvariants() {
- // assert elements[tail] == null;
- // assert head == tail ? elements[head] == null :
- // (elements[head] != null &&
- // elements[(tail - 1) & (elements.length - 1)] != null);
- // assert elements[(head - 1) & (elements.length - 1)] == null;
+ assert elements[tail] == null;
+ assert head == tail ? elements[head] == null :
+ (elements[head] != null &&
+ elements[(tail - 1) & (elements.length - 1)] != null);
+ assert elements[(head - 1) & (elements.length - 1)] == null;
}
/**
@@ -518,8 +516,8 @@
*
* @return true if elements moved backwards
*/
- private boolean delete(int i) {
- //checkInvariants();
+ boolean delete(int i) {
+ checkInvariants();
final Object[] elements = this.elements;
final int mask = elements.length - 1;
final int h = head;
@@ -704,15 +702,13 @@
* @return {@code true} if this deque contains the specified element
*/
public boolean contains(Object o) {
- if (o == null)
- return false;
- int mask = elements.length - 1;
- int i = head;
- Object x;
- while ( (x = elements[i]) != null) {
- if (o.equals(x))
- return true;
- i = (i + 1) & mask;
+ if (o != null) {
+ int mask = elements.length - 1;
+ int i = head;
+ for (Object x; (x = elements[i]) != null; i = (i + 1) & mask) {
+ if (o.equals(x))
+ return true;
+ }
}
return false;
}
@@ -725,7 +721,7 @@
* Returns {@code true} if this deque contained the specified element
* (or equivalently, if this deque changed as a result of the call).
*
- * <p>This method is equivalent to {@link #removeFirstOccurrence}.
+ * <p>This method is equivalent to {@link #removeFirstOccurrence(Object)}.
*
* @param o element to be removed from this deque, if present
* @return {@code true} if this deque contained the specified element
@@ -798,7 +794,7 @@
* The following code can be used to dump the deque into a newly
* allocated array of {@code String}:
*
- * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
@@ -856,6 +852,8 @@
/**
* Saves this deque to a stream (that is, serializes it).
*
+ * @param s the stream
+ * @throws java.io.IOException if an I/O error occurs
* @serialData The current size ({@code int}) of the deque,
* followed by all of its elements (each an object reference) in
* first-to-last order.
@@ -875,6 +873,10 @@
/**
* Reconstitutes this deque from a stream (that is, deserializes it).
+ * @param s the stream
+ * @throws ClassNotFoundException if the class of a serialized object
+ * could not be found
+ * @throws java.io.IOException if an I/O error occurs
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
@@ -913,7 +915,7 @@
private int fence; // -1 until first use
private int index; // current index, modified on traverse/split
- /** Creates new spliterator covering the given array and range */
+ /** Creates new spliterator covering the given array and range. */
DeqSpliterator(ArrayDeque<E> deq, int origin, int fence) {
this.deq = deq;
this.index = origin;
@@ -935,7 +937,7 @@
if (h > t)
t += n;
int m = ((h + t) >>> 1) & (n - 1);
- return new DeqSpliterator<>(deq, h, index = m);
+ return new DeqSpliterator<E>(deq, h, index = m);
}
return null;
}
@@ -960,7 +962,7 @@
throw new NullPointerException();
Object[] a = deq.elements;
int m = a.length - 1, f = getFence(), i = index;
- if (i != fence) {
+ if (i != f) {
@SuppressWarnings("unchecked") E e = (E)a[i];
index = (i + 1) & m;
if (e == null)
diff --git a/ojluni/src/main/java/java/util/ArrayPrefixHelpers.java b/ojluni/src/main/java/java/util/ArrayPrefixHelpers.java
new file mode 100644
index 0000000..d3b5614
--- /dev/null
+++ b/ojluni/src/main/java/java/util/ArrayPrefixHelpers.java
@@ -0,0 +1,677 @@
+/*
+ * 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/
+ */
+
+package java.util;
+
+import java.util.concurrent.CountedCompleter;
+import java.util.concurrent.ForkJoinPool;
+import java.util.function.BinaryOperator;
+import java.util.function.DoubleBinaryOperator;
+import java.util.function.IntBinaryOperator;
+import java.util.function.LongBinaryOperator;
+
+/**
+ * ForkJoin tasks to perform Arrays.parallelPrefix operations.
+ *
+ * @author Doug Lea
+ * @since 1.8
+ */
+class ArrayPrefixHelpers {
+ private ArrayPrefixHelpers() {} // non-instantiable
+
+ /*
+ * Parallel prefix (aka cumulate, scan) task classes
+ * are based loosely on Guy Blelloch's original
+ * algorithm (http://www.cs.cmu.edu/~scandal/alg/scan.html):
+ * Keep dividing by two to threshold segment size, and then:
+ * Pass 1: Create tree of partial sums for each segment
+ * Pass 2: For each segment, cumulate with offset of left sibling
+ *
+ * This version improves performance within FJ framework mainly by
+ * allowing the second pass of ready left-hand sides to proceed
+ * even if some right-hand side first passes are still executing.
+ * It also combines first and second pass for leftmost segment,
+ * and skips the first pass for rightmost segment (whose result is
+ * not needed for second pass). It similarly manages to avoid
+ * requiring that users supply an identity basis for accumulations
+ * by tracking those segments/subtasks for which the first
+ * existing element is used as base.
+ *
+ * Managing this relies on ORing some bits in the pendingCount for
+ * phases/states: CUMULATE, SUMMED, and FINISHED. CUMULATE is the
+ * main phase bit. When false, segments compute only their sum.
+ * When true, they cumulate array elements. CUMULATE is set at
+ * root at beginning of second pass and then propagated down. But
+ * it may also be set earlier for subtrees with lo==0 (the left
+ * spine of tree). SUMMED is a one bit join count. For leafs, it
+ * is set when summed. For internal nodes, it becomes true when
+ * one child is summed. When the second child finishes summing,
+ * we then moves up tree to trigger the cumulate phase. FINISHED
+ * is also a one bit join count. For leafs, it is set when
+ * cumulated. For internal nodes, it becomes true when one child
+ * is cumulated. When the second child finishes cumulating, it
+ * then moves up tree, completing at the root.
+ *
+ * To better exploit locality and reduce overhead, the compute
+ * method loops starting with the current task, moving if possible
+ * to one of its subtasks rather than forking.
+ *
+ * As usual for this sort of utility, there are 4 versions, that
+ * are simple copy/paste/adapt variants of each other. (The
+ * double and int versions differ from long version solely by
+ * replacing "long" (with case-matching)).
+ */
+
+ // see above
+ static final int CUMULATE = 1;
+ static final int SUMMED = 2;
+ static final int FINISHED = 4;
+
+ /** The smallest subtask array partition size to use as threshold */
+ static final int MIN_PARTITION = 16;
+
+ static final class CumulateTask<T> extends CountedCompleter<Void> {
+ final T[] array;
+ final BinaryOperator<T> function;
+ CumulateTask<T> left, right;
+ T in, out;
+ final int lo, hi, origin, fence, threshold;
+
+ /** Root task constructor */
+ public CumulateTask(CumulateTask<T> parent,
+ BinaryOperator<T> function,
+ T[] array, int lo, int hi) {
+ super(parent);
+ this.function = function; this.array = array;
+ this.lo = this.origin = lo; this.hi = this.fence = hi;
+ int p;
+ this.threshold =
+ (p = (hi - lo) / (ForkJoinPool.getCommonPoolParallelism() << 3))
+ <= MIN_PARTITION ? MIN_PARTITION : p;
+ }
+
+ /** Subtask constructor */
+ CumulateTask(CumulateTask<T> parent, BinaryOperator<T> function,
+ T[] array, int origin, int fence, int threshold,
+ int lo, int hi) {
+ super(parent);
+ this.function = function; this.array = array;
+ this.origin = origin; this.fence = fence;
+ this.threshold = threshold;
+ this.lo = lo; this.hi = hi;
+ }
+
+ public final void compute() {
+ final BinaryOperator<T> fn;
+ final T[] a;
+ if ((fn = this.function) == null || (a = this.array) == null)
+ throw new NullPointerException(); // hoist checks
+ int th = threshold, org = origin, fnc = fence, l, h;
+ CumulateTask<T> t = this;
+ outer: while ((l = t.lo) >= 0 && (h = t.hi) <= a.length) {
+ if (h - l > th) {
+ CumulateTask<T> lt = t.left, rt = t.right, f;
+ if (lt == null) { // first pass
+ int mid = (l + h) >>> 1;
+ f = rt = t.right =
+ new CumulateTask<T>(t, fn, a, org, fnc, th, mid, h);
+ t = lt = t.left =
+ new CumulateTask<T>(t, fn, a, org, fnc, th, l, mid);
+ }
+ else { // possibly refork
+ T pin = t.in;
+ lt.in = pin;
+ f = t = null;
+ if (rt != null) {
+ T lout = lt.out;
+ rt.in = (l == org ? lout :
+ fn.apply(pin, lout));
+ for (int c;;) {
+ if (((c = rt.getPendingCount()) & CUMULATE) != 0)
+ break;
+ if (rt.compareAndSetPendingCount(c, c|CUMULATE)){
+ t = rt;
+ break;
+ }
+ }
+ }
+ for (int c;;) {
+ if (((c = lt.getPendingCount()) & CUMULATE) != 0)
+ break;
+ if (lt.compareAndSetPendingCount(c, c|CUMULATE)) {
+ if (t != null)
+ f = t;
+ t = lt;
+ break;
+ }
+ }
+ if (t == null)
+ break;
+ }
+ if (f != null)
+ f.fork();
+ }
+ else {
+ int state; // Transition to sum, cumulate, or both
+ for (int b;;) {
+ if (((b = t.getPendingCount()) & FINISHED) != 0)
+ break outer; // already done
+ state = ((b & CUMULATE) != 0 ? FINISHED :
+ (l > org) ? SUMMED : (SUMMED|FINISHED));
+ if (t.compareAndSetPendingCount(b, b|state))
+ break;
+ }
+
+ T sum;
+ if (state != SUMMED) {
+ int first;
+ if (l == org) { // leftmost; no in
+ sum = a[org];
+ first = org + 1;
+ }
+ else {
+ sum = t.in;
+ first = l;
+ }
+ for (int i = first; i < h; ++i) // cumulate
+ a[i] = sum = fn.apply(sum, a[i]);
+ }
+ else if (h < fnc) { // skip rightmost
+ sum = a[l];
+ for (int i = l + 1; i < h; ++i) // sum only
+ sum = fn.apply(sum, a[i]);
+ }
+ else
+ sum = t.in;
+ t.out = sum;
+ for (CumulateTask<T> par;;) { // propagate
+ @SuppressWarnings("unchecked") CumulateTask<T> partmp
+ = (CumulateTask<T>)t.getCompleter();
+ if ((par = partmp) == null) {
+ if ((state & FINISHED) != 0) // enable join
+ t.quietlyComplete();
+ break outer;
+ }
+ int b = par.getPendingCount();
+ if ((b & state & FINISHED) != 0)
+ t = par; // both done
+ else if ((b & state & SUMMED) != 0) { // both summed
+ int nextState; CumulateTask<T> lt, rt;
+ if ((lt = par.left) != null &&
+ (rt = par.right) != null) {
+ T lout = lt.out;
+ par.out = (rt.hi == fnc ? lout :
+ fn.apply(lout, rt.out));
+ }
+ int refork = (((b & CUMULATE) == 0 &&
+ par.lo == org) ? CUMULATE : 0);
+ if ((nextState = b|state|refork) == b ||
+ par.compareAndSetPendingCount(b, nextState)) {
+ state = SUMMED; // drop finished
+ t = par;
+ if (refork != 0)
+ par.fork();
+ }
+ }
+ else if (par.compareAndSetPendingCount(b, b|state))
+ break outer; // sib not ready
+ }
+ }
+ }
+ }
+ private static final long serialVersionUID = 5293554502939613543L;
+ }
+
+ static final class LongCumulateTask extends CountedCompleter<Void> {
+ final long[] array;
+ final LongBinaryOperator function;
+ LongCumulateTask left, right;
+ long in, out;
+ final int lo, hi, origin, fence, threshold;
+
+ /** Root task constructor */
+ public LongCumulateTask(LongCumulateTask parent,
+ LongBinaryOperator function,
+ long[] array, int lo, int hi) {
+ super(parent);
+ this.function = function; this.array = array;
+ this.lo = this.origin = lo; this.hi = this.fence = hi;
+ int p;
+ this.threshold =
+ (p = (hi - lo) / (ForkJoinPool.getCommonPoolParallelism() << 3))
+ <= MIN_PARTITION ? MIN_PARTITION : p;
+ }
+
+ /** Subtask constructor */
+ LongCumulateTask(LongCumulateTask parent, LongBinaryOperator function,
+ long[] array, int origin, int fence, int threshold,
+ int lo, int hi) {
+ super(parent);
+ this.function = function; this.array = array;
+ this.origin = origin; this.fence = fence;
+ this.threshold = threshold;
+ this.lo = lo; this.hi = hi;
+ }
+
+ public final void compute() {
+ final LongBinaryOperator fn;
+ final long[] a;
+ if ((fn = this.function) == null || (a = this.array) == null)
+ throw new NullPointerException(); // hoist checks
+ int th = threshold, org = origin, fnc = fence, l, h;
+ LongCumulateTask t = this;
+ outer: while ((l = t.lo) >= 0 && (h = t.hi) <= a.length) {
+ if (h - l > th) {
+ LongCumulateTask lt = t.left, rt = t.right, f;
+ if (lt == null) { // first pass
+ int mid = (l + h) >>> 1;
+ f = rt = t.right =
+ new LongCumulateTask(t, fn, a, org, fnc, th, mid, h);
+ t = lt = t.left =
+ new LongCumulateTask(t, fn, a, org, fnc, th, l, mid);
+ }
+ else { // possibly refork
+ long pin = t.in;
+ lt.in = pin;
+ f = t = null;
+ if (rt != null) {
+ long lout = lt.out;
+ rt.in = (l == org ? lout :
+ fn.applyAsLong(pin, lout));
+ for (int c;;) {
+ if (((c = rt.getPendingCount()) & CUMULATE) != 0)
+ break;
+ if (rt.compareAndSetPendingCount(c, c|CUMULATE)){
+ t = rt;
+ break;
+ }
+ }
+ }
+ for (int c;;) {
+ if (((c = lt.getPendingCount()) & CUMULATE) != 0)
+ break;
+ if (lt.compareAndSetPendingCount(c, c|CUMULATE)) {
+ if (t != null)
+ f = t;
+ t = lt;
+ break;
+ }
+ }
+ if (t == null)
+ break;
+ }
+ if (f != null)
+ f.fork();
+ }
+ else {
+ int state; // Transition to sum, cumulate, or both
+ for (int b;;) {
+ if (((b = t.getPendingCount()) & FINISHED) != 0)
+ break outer; // already done
+ state = ((b & CUMULATE) != 0 ? FINISHED :
+ (l > org) ? SUMMED : (SUMMED|FINISHED));
+ if (t.compareAndSetPendingCount(b, b|state))
+ break;
+ }
+
+ long sum;
+ if (state != SUMMED) {
+ int first;
+ if (l == org) { // leftmost; no in
+ sum = a[org];
+ first = org + 1;
+ }
+ else {
+ sum = t.in;
+ first = l;
+ }
+ for (int i = first; i < h; ++i) // cumulate
+ a[i] = sum = fn.applyAsLong(sum, a[i]);
+ }
+ else if (h < fnc) { // skip rightmost
+ sum = a[l];
+ for (int i = l + 1; i < h; ++i) // sum only
+ sum = fn.applyAsLong(sum, a[i]);
+ }
+ else
+ sum = t.in;
+ t.out = sum;
+ for (LongCumulateTask par;;) { // propagate
+ if ((par = (LongCumulateTask)t.getCompleter()) == null) {
+ if ((state & FINISHED) != 0) // enable join
+ t.quietlyComplete();
+ break outer;
+ }
+ int b = par.getPendingCount();
+ if ((b & state & FINISHED) != 0)
+ t = par; // both done
+ else if ((b & state & SUMMED) != 0) { // both summed
+ int nextState; LongCumulateTask lt, rt;
+ if ((lt = par.left) != null &&
+ (rt = par.right) != null) {
+ long lout = lt.out;
+ par.out = (rt.hi == fnc ? lout :
+ fn.applyAsLong(lout, rt.out));
+ }
+ int refork = (((b & CUMULATE) == 0 &&
+ par.lo == org) ? CUMULATE : 0);
+ if ((nextState = b|state|refork) == b ||
+ par.compareAndSetPendingCount(b, nextState)) {
+ state = SUMMED; // drop finished
+ t = par;
+ if (refork != 0)
+ par.fork();
+ }
+ }
+ else if (par.compareAndSetPendingCount(b, b|state))
+ break outer; // sib not ready
+ }
+ }
+ }
+ }
+ private static final long serialVersionUID = -5074099945909284273L;
+ }
+
+ static final class DoubleCumulateTask extends CountedCompleter<Void> {
+ final double[] array;
+ final DoubleBinaryOperator function;
+ DoubleCumulateTask left, right;
+ double in, out;
+ final int lo, hi, origin, fence, threshold;
+
+ /** Root task constructor */
+ public DoubleCumulateTask(DoubleCumulateTask parent,
+ DoubleBinaryOperator function,
+ double[] array, int lo, int hi) {
+ super(parent);
+ this.function = function; this.array = array;
+ this.lo = this.origin = lo; this.hi = this.fence = hi;
+ int p;
+ this.threshold =
+ (p = (hi - lo) / (ForkJoinPool.getCommonPoolParallelism() << 3))
+ <= MIN_PARTITION ? MIN_PARTITION : p;
+ }
+
+ /** Subtask constructor */
+ DoubleCumulateTask(DoubleCumulateTask parent, DoubleBinaryOperator function,
+ double[] array, int origin, int fence, int threshold,
+ int lo, int hi) {
+ super(parent);
+ this.function = function; this.array = array;
+ this.origin = origin; this.fence = fence;
+ this.threshold = threshold;
+ this.lo = lo; this.hi = hi;
+ }
+
+ public final void compute() {
+ final DoubleBinaryOperator fn;
+ final double[] a;
+ if ((fn = this.function) == null || (a = this.array) == null)
+ throw new NullPointerException(); // hoist checks
+ int th = threshold, org = origin, fnc = fence, l, h;
+ DoubleCumulateTask t = this;
+ outer: while ((l = t.lo) >= 0 && (h = t.hi) <= a.length) {
+ if (h - l > th) {
+ DoubleCumulateTask lt = t.left, rt = t.right, f;
+ if (lt == null) { // first pass
+ int mid = (l + h) >>> 1;
+ f = rt = t.right =
+ new DoubleCumulateTask(t, fn, a, org, fnc, th, mid, h);
+ t = lt = t.left =
+ new DoubleCumulateTask(t, fn, a, org, fnc, th, l, mid);
+ }
+ else { // possibly refork
+ double pin = t.in;
+ lt.in = pin;
+ f = t = null;
+ if (rt != null) {
+ double lout = lt.out;
+ rt.in = (l == org ? lout :
+ fn.applyAsDouble(pin, lout));
+ for (int c;;) {
+ if (((c = rt.getPendingCount()) & CUMULATE) != 0)
+ break;
+ if (rt.compareAndSetPendingCount(c, c|CUMULATE)){
+ t = rt;
+ break;
+ }
+ }
+ }
+ for (int c;;) {
+ if (((c = lt.getPendingCount()) & CUMULATE) != 0)
+ break;
+ if (lt.compareAndSetPendingCount(c, c|CUMULATE)) {
+ if (t != null)
+ f = t;
+ t = lt;
+ break;
+ }
+ }
+ if (t == null)
+ break;
+ }
+ if (f != null)
+ f.fork();
+ }
+ else {
+ int state; // Transition to sum, cumulate, or both
+ for (int b;;) {
+ if (((b = t.getPendingCount()) & FINISHED) != 0)
+ break outer; // already done
+ state = ((b & CUMULATE) != 0 ? FINISHED :
+ (l > org) ? SUMMED : (SUMMED|FINISHED));
+ if (t.compareAndSetPendingCount(b, b|state))
+ break;
+ }
+
+ double sum;
+ if (state != SUMMED) {
+ int first;
+ if (l == org) { // leftmost; no in
+ sum = a[org];
+ first = org + 1;
+ }
+ else {
+ sum = t.in;
+ first = l;
+ }
+ for (int i = first; i < h; ++i) // cumulate
+ a[i] = sum = fn.applyAsDouble(sum, a[i]);
+ }
+ else if (h < fnc) { // skip rightmost
+ sum = a[l];
+ for (int i = l + 1; i < h; ++i) // sum only
+ sum = fn.applyAsDouble(sum, a[i]);
+ }
+ else
+ sum = t.in;
+ t.out = sum;
+ for (DoubleCumulateTask par;;) { // propagate
+ if ((par = (DoubleCumulateTask)t.getCompleter()) == null) {
+ if ((state & FINISHED) != 0) // enable join
+ t.quietlyComplete();
+ break outer;
+ }
+ int b = par.getPendingCount();
+ if ((b & state & FINISHED) != 0)
+ t = par; // both done
+ else if ((b & state & SUMMED) != 0) { // both summed
+ int nextState; DoubleCumulateTask lt, rt;
+ if ((lt = par.left) != null &&
+ (rt = par.right) != null) {
+ double lout = lt.out;
+ par.out = (rt.hi == fnc ? lout :
+ fn.applyAsDouble(lout, rt.out));
+ }
+ int refork = (((b & CUMULATE) == 0 &&
+ par.lo == org) ? CUMULATE : 0);
+ if ((nextState = b|state|refork) == b ||
+ par.compareAndSetPendingCount(b, nextState)) {
+ state = SUMMED; // drop finished
+ t = par;
+ if (refork != 0)
+ par.fork();
+ }
+ }
+ else if (par.compareAndSetPendingCount(b, b|state))
+ break outer; // sib not ready
+ }
+ }
+ }
+ }
+ private static final long serialVersionUID = -586947823794232033L;
+ }
+
+ static final class IntCumulateTask extends CountedCompleter<Void> {
+ final int[] array;
+ final IntBinaryOperator function;
+ IntCumulateTask left, right;
+ int in, out;
+ final int lo, hi, origin, fence, threshold;
+
+ /** Root task constructor */
+ public IntCumulateTask(IntCumulateTask parent,
+ IntBinaryOperator function,
+ int[] array, int lo, int hi) {
+ super(parent);
+ this.function = function; this.array = array;
+ this.lo = this.origin = lo; this.hi = this.fence = hi;
+ int p;
+ this.threshold =
+ (p = (hi - lo) / (ForkJoinPool.getCommonPoolParallelism() << 3))
+ <= MIN_PARTITION ? MIN_PARTITION : p;
+ }
+
+ /** Subtask constructor */
+ IntCumulateTask(IntCumulateTask parent, IntBinaryOperator function,
+ int[] array, int origin, int fence, int threshold,
+ int lo, int hi) {
+ super(parent);
+ this.function = function; this.array = array;
+ this.origin = origin; this.fence = fence;
+ this.threshold = threshold;
+ this.lo = lo; this.hi = hi;
+ }
+
+ public final void compute() {
+ final IntBinaryOperator fn;
+ final int[] a;
+ if ((fn = this.function) == null || (a = this.array) == null)
+ throw new NullPointerException(); // hoist checks
+ int th = threshold, org = origin, fnc = fence, l, h;
+ IntCumulateTask t = this;
+ outer: while ((l = t.lo) >= 0 && (h = t.hi) <= a.length) {
+ if (h - l > th) {
+ IntCumulateTask lt = t.left, rt = t.right, f;
+ if (lt == null) { // first pass
+ int mid = (l + h) >>> 1;
+ f = rt = t.right =
+ new IntCumulateTask(t, fn, a, org, fnc, th, mid, h);
+ t = lt = t.left =
+ new IntCumulateTask(t, fn, a, org, fnc, th, l, mid);
+ }
+ else { // possibly refork
+ int pin = t.in;
+ lt.in = pin;
+ f = t = null;
+ if (rt != null) {
+ int lout = lt.out;
+ rt.in = (l == org ? lout :
+ fn.applyAsInt(pin, lout));
+ for (int c;;) {
+ if (((c = rt.getPendingCount()) & CUMULATE) != 0)
+ break;
+ if (rt.compareAndSetPendingCount(c, c|CUMULATE)){
+ t = rt;
+ break;
+ }
+ }
+ }
+ for (int c;;) {
+ if (((c = lt.getPendingCount()) & CUMULATE) != 0)
+ break;
+ if (lt.compareAndSetPendingCount(c, c|CUMULATE)) {
+ if (t != null)
+ f = t;
+ t = lt;
+ break;
+ }
+ }
+ if (t == null)
+ break;
+ }
+ if (f != null)
+ f.fork();
+ }
+ else {
+ int state; // Transition to sum, cumulate, or both
+ for (int b;;) {
+ if (((b = t.getPendingCount()) & FINISHED) != 0)
+ break outer; // already done
+ state = ((b & CUMULATE) != 0 ? FINISHED :
+ (l > org) ? SUMMED : (SUMMED|FINISHED));
+ if (t.compareAndSetPendingCount(b, b|state))
+ break;
+ }
+
+ int sum;
+ if (state != SUMMED) {
+ int first;
+ if (l == org) { // leftmost; no in
+ sum = a[org];
+ first = org + 1;
+ }
+ else {
+ sum = t.in;
+ first = l;
+ }
+ for (int i = first; i < h; ++i) // cumulate
+ a[i] = sum = fn.applyAsInt(sum, a[i]);
+ }
+ else if (h < fnc) { // skip rightmost
+ sum = a[l];
+ for (int i = l + 1; i < h; ++i) // sum only
+ sum = fn.applyAsInt(sum, a[i]);
+ }
+ else
+ sum = t.in;
+ t.out = sum;
+ for (IntCumulateTask par;;) { // propagate
+ if ((par = (IntCumulateTask)t.getCompleter()) == null) {
+ if ((state & FINISHED) != 0) // enable join
+ t.quietlyComplete();
+ break outer;
+ }
+ int b = par.getPendingCount();
+ if ((b & state & FINISHED) != 0)
+ t = par; // both done
+ else if ((b & state & SUMMED) != 0) { // both summed
+ int nextState; IntCumulateTask lt, rt;
+ if ((lt = par.left) != null &&
+ (rt = par.right) != null) {
+ int lout = lt.out;
+ par.out = (rt.hi == fnc ? lout :
+ fn.applyAsInt(lout, rt.out));
+ }
+ int refork = (((b & CUMULATE) == 0 &&
+ par.lo == org) ? CUMULATE : 0);
+ if ((nextState = b|state|refork) == b ||
+ par.compareAndSetPendingCount(b, nextState)) {
+ state = SUMMED; // drop finished
+ t = par;
+ if (refork != 0)
+ par.fork();
+ }
+ }
+ else if (par.compareAndSetPendingCount(b, b|state))
+ break outer; // sib not ready
+ }
+ }
+ }
+ }
+ private static final long serialVersionUID = 3731755594596840961L;
+ }
+}
diff --git a/ojluni/src/main/java/java/util/Deque.java b/ojluni/src/main/java/java/util/Deque.java
index f230677..9fec73b 100644
--- a/ojluni/src/main/java/java/util/Deque.java
+++ b/ojluni/src/main/java/java/util/Deque.java
@@ -59,7 +59,6 @@
* <p>The twelve methods described above are summarized in the
* following table:
*
- * <p>
* <table BORDER CELLPADDING=3 CELLSPACING=1>
* <caption>Summary of Deque methods</caption>
* <tr>
@@ -103,7 +102,6 @@
* inherited from the {@code Queue} interface are precisely equivalent to
* {@code Deque} methods as indicated in the following table:
*
- * <p>
* <table BORDER CELLPADDING=3 CELLSPACING=1>
* <caption>Comparison of Queue and Deque methods</caption>
* <tr>
@@ -142,7 +140,6 @@
* beginning of the deque. Stack methods are precisely equivalent to
* {@code Deque} methods as indicated in the table below:
*
- * <p>
* <table BORDER CELLPADDING=3 CELLSPACING=1>
* <caption>Comparison of Stack and Deque methods</caption>
* <tr>
@@ -190,7 +187,7 @@
* @author Doug Lea
* @author Josh Bloch
* @since 1.6
- * @param <E> the type of elements held in this collection
+ * @param <E> the type of elements held in this deque
*/
public interface Deque<E> extends Queue<E> {
/**
@@ -346,17 +343,18 @@
* Removes the first occurrence of the specified element from this deque.
* If the deque does not contain the element, it is unchanged.
* More formally, removes the first element {@code e} such that
- * <tt>(o==null ? e==null : o.equals(e))</tt>
- * (if such an element exists).
+ * {@code Objects.equals(o, e)} (if such an element exists).
* Returns {@code true} if this deque contained the specified element
* (or equivalently, if this deque changed as a result of the call).
*
* @param o element to be removed from this deque, if present
* @return {@code true} if an element was removed as a result of this call
* @throws ClassCastException if the class of the specified element
- * is incompatible with this deque (optional)
+ * is incompatible with this deque
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements (optional)
+ * deque does not permit null elements
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
*/
boolean removeFirstOccurrence(Object o);
@@ -364,17 +362,18 @@
* Removes the last occurrence of the specified element from this deque.
* If the deque does not contain the element, it is unchanged.
* More formally, removes the last element {@code e} such that
- * <tt>(o==null ? e==null : o.equals(e))</tt>
- * (if such an element exists).
+ * {@code Objects.equals(o, e)} (if such an element exists).
* Returns {@code true} if this deque contained the specified element
* (or equivalently, if this deque changed as a result of the call).
*
* @param o element to be removed from this deque, if present
* @return {@code true} if an element was removed as a result of this call
* @throws ClassCastException if the class of the specified element
- * is incompatible with this deque (optional)
+ * is incompatible with this deque
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements (optional)
+ * deque does not permit null elements
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
*/
boolean removeLastOccurrence(Object o);
@@ -519,8 +518,7 @@
* Removes the first occurrence of the specified element from this deque.
* If the deque does not contain the element, it is unchanged.
* More formally, removes the first element {@code e} such that
- * <tt>(o==null ? e==null : o.equals(e))</tt>
- * (if such an element exists).
+ * {@code Objects.equals(o, e)} (if such an element exists).
* Returns {@code true} if this deque contained the specified element
* (or equivalently, if this deque changed as a result of the call).
*
@@ -529,24 +527,27 @@
* @param o element to be removed from this deque, if present
* @return {@code true} if an element was removed as a result of this call
* @throws ClassCastException if the class of the specified element
- * is incompatible with this deque (optional)
+ * is incompatible with this deque
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements (optional)
+ * deque does not permit null elements
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
*/
boolean remove(Object o);
/**
* Returns {@code true} if this deque contains the specified element.
* More formally, returns {@code true} if and only if this deque contains
- * at least one element {@code e} such that
- * <tt>(o==null ? e==null : o.equals(e))</tt>.
+ * at least one element {@code e} such that {@code Objects.equals(o, e)}.
*
* @param o element whose presence in this deque is to be tested
* @return {@code true} if this deque contains the specified element
- * @throws ClassCastException if the type of the specified element
- * is incompatible with this deque (optional)
+ * @throws ClassCastException if the class of the specified element
+ * is incompatible with this deque
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null and this
- * deque does not permit null elements (optional)
+ * deque does not permit null elements
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
*/
boolean contains(Object o);
@@ -555,7 +556,7 @@
*
* @return the number of elements in this deque
*/
- public int size();
+ int size();
/**
* Returns an iterator over the elements in this deque in proper sequence.
diff --git a/ojluni/src/main/java/java/util/Map.java b/ojluni/src/main/java/java/util/Map.java
old mode 100755
new mode 100644
index f59b5a9..74ba701
--- a/ojluni/src/main/java/java/util/Map.java
+++ b/ojluni/src/main/java/java/util/Map.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -25,40 +25,43 @@
package java.util;
-
-import java.io.Serializable;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
+import java.io.Serializable;
+// BEGIN android-note
+// removed link to collections framework docs
+// removed java 9 methods
+// END android-note
/**
* An object that maps keys to values. A map cannot contain duplicate keys;
* each key can map to at most one value.
*
- * <p>This interface takes the place of the <tt>Dictionary</tt> class, which
+ * <p>This interface takes the place of the {@code Dictionary} class, which
* was a totally abstract class rather than an interface.
*
- * <p>The <tt>Map</tt> interface provides three <i>collection views</i>, which
+ * <p>The {@code Map} interface provides three <i>collection views</i>, which
* allow a map's contents to be viewed as a set of keys, collection of values,
* or set of key-value mappings. The <i>order</i> of a map is defined as
* the order in which the iterators on the map's collection views return their
- * elements. Some map implementations, like the <tt>TreeMap</tt> class, make
- * specific guarantees as to their order; others, like the <tt>HashMap</tt>
+ * elements. Some map implementations, like the {@code TreeMap} class, make
+ * specific guarantees as to their order; others, like the {@code HashMap}
* class, do not.
*
* <p>Note: great care must be exercised if mutable objects are used as map
* keys. The behavior of a map is not specified if the value of an object is
- * changed in a manner that affects <tt>equals</tt> comparisons while the
+ * changed in a manner that affects {@code equals} comparisons while the
* object is a key in the map. A special case of this prohibition is that it
* is not permissible for a map to contain itself as a key. While it is
* permissible for a map to contain itself as a value, extreme caution is
- * advised: the <tt>equals</tt> and <tt>hashCode</tt> methods are no longer
+ * advised: the {@code equals} and {@code hashCode} methods are no longer
* well defined on such a map.
*
* <p>All general-purpose map implementation classes should provide two
* "standard" constructors: a void (no arguments) constructor which creates an
- * empty map, and a constructor with a single argument of type <tt>Map</tt>,
+ * empty map, and a constructor with a single argument of type {@code Map},
* which creates a new map with the same key-value mappings as its argument.
* In effect, the latter constructor allows the user to copy any map,
* producing an equivalent map of the desired class. There is no way to
@@ -67,9 +70,9 @@
*
* <p>The "destructive" methods contained in this interface, that is, the
* methods that modify the map on which they operate, are specified to throw
- * <tt>UnsupportedOperationException</tt> if this map does not support the
+ * {@code UnsupportedOperationException} if this map does not support the
* operation. If this is the case, these methods may, but are not required
- * to, throw an <tt>UnsupportedOperationException</tt> if the invocation would
+ * to, throw an {@code UnsupportedOperationException} if the invocation would
* have no effect on the map. For example, invoking the {@link #putAll(Map)}
* method on an unmodifiable map may, but is not required to, throw the
* exception if the map whose mappings are to be "superimposed" is empty.
@@ -78,7 +81,7 @@
* may contain. For example, some implementations prohibit null keys and
* values, and some have restrictions on the types of their keys. Attempting
* to insert an ineligible key or value throws an unchecked exception,
- * typically <tt>NullPointerException</tt> or <tt>ClassCastException</tt>.
+ * typically {@code NullPointerException} or {@code ClassCastException}.
* Attempting to query the presence of an ineligible key or value may throw an
* exception, or it may simply return false; some implementations will exhibit
* the former behavior and some will exhibit the latter. More generally,
@@ -88,20 +91,16 @@
* Such exceptions are marked as "optional" in the specification for this
* interface.
*
- * <p>This interface is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
- * Java Collections Framework</a>.
- *
* <p>Many methods in Collections Framework interfaces are defined
* in terms of the {@link Object#equals(Object) equals} method. For
* example, the specification for the {@link #containsKey(Object)
- * containsKey(Object key)} method says: "returns <tt>true</tt> if and
- * only if this map contains a mapping for a key <tt>k</tt> such that
- * <tt>(key==null ? k==null : key.equals(k))</tt>." This specification should
- * <i>not</i> be construed to imply that invoking <tt>Map.containsKey</tt>
- * with a non-null argument <tt>key</tt> will cause <tt>key.equals(k)</tt> to
- * be invoked for any key <tt>k</tt>. Implementations are free to
- * implement optimizations whereby the <tt>equals</tt> invocation is avoided,
+ * containsKey(Object key)} method says: "returns {@code true} if and
+ * only if this map contains a mapping for a key {@code k} such that
+ * {@code (key==null ? k==null : key.equals(k))}." This specification should
+ * <i>not</i> be construed to imply that invoking {@code Map.containsKey}
+ * with a non-null argument {@code key} will cause {@code key.equals(k)} to
+ * be invoked for any key {@code k}. Implementations are free to
+ * implement optimizations whereby the {@code equals} invocation is avoided,
* for example, by first comparing the hash codes of the two keys. (The
* {@link Object#hashCode()} specification guarantees that two objects with
* unequal hash codes cannot be equal.) More generally, implementations of
@@ -109,6 +108,13 @@
* the specified behavior of underlying {@link Object} methods wherever the
* implementor deems it appropriate.
*
+ * <p>Some map operations which perform recursive traversal of the map may fail
+ * with an exception for self-referential instances where the map directly or
+ * indirectly contains itself. This includes the {@code clone()},
+ * {@code equals()}, {@code hashCode()} and {@code toString()} methods.
+ * Implementations may optionally handle the self-referential scenario, however
+ * most current implementations do not do so.
+ *
* @param <K> the type of keys maintained by this map
* @param <V> the type of mapped values
*
@@ -121,34 +127,34 @@
* @see Set
* @since 1.2
*/
-public interface Map<K,V> {
+public interface Map<K, V> {
// Query Operations
/**
* Returns the number of key-value mappings in this map. If the
- * map contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
- * <tt>Integer.MAX_VALUE</tt>.
+ * map contains more than {@code Integer.MAX_VALUE} elements, returns
+ * {@code Integer.MAX_VALUE}.
*
* @return the number of key-value mappings in this map
*/
int size();
/**
- * Returns <tt>true</tt> if this map contains no key-value mappings.
+ * Returns {@code true} if this map contains no key-value mappings.
*
- * @return <tt>true</tt> if this map contains no key-value mappings
+ * @return {@code true} if this map contains no key-value mappings
*/
boolean isEmpty();
/**
- * Returns <tt>true</tt> if this map contains a mapping for the specified
- * key. More formally, returns <tt>true</tt> if and only if
- * this map contains a mapping for a key <tt>k</tt> such that
- * <tt>(key==null ? k==null : key.equals(k))</tt>. (There can be
+ * Returns {@code true} if this map contains a mapping for the specified
+ * key. More formally, returns {@code true} if and only if
+ * this map contains a mapping for a key {@code k} such that
+ * {@code Objects.equals(key, k)}. (There can be
* at most one such mapping.)
*
* @param key key whose presence in this map is to be tested
- * @return <tt>true</tt> if this map contains a mapping for the specified
+ * @return {@code true} if this map contains a mapping for the specified
* key
* @throws ClassCastException if the key is of an inappropriate type for
* this map
@@ -160,15 +166,15 @@
boolean containsKey(Object key);
/**
- * Returns <tt>true</tt> if this map maps one or more keys to the
- * specified value. More formally, returns <tt>true</tt> if and only if
- * this map contains at least one mapping to a value <tt>v</tt> such that
- * <tt>(value==null ? v==null : value.equals(v))</tt>. This operation
+ * Returns {@code true} if this map maps one or more keys to the
+ * specified value. More formally, returns {@code true} if and only if
+ * this map contains at least one mapping to a value {@code v} such that
+ * {@code Objects.equals(value, v)}. This operation
* will probably require time linear in the map size for most
- * implementations of the <tt>Map</tt> interface.
+ * implementations of the {@code Map} interface.
*
* @param value value whose presence in this map is to be tested
- * @return <tt>true</tt> if this map maps one or more keys to the
+ * @return {@code true} if this map maps one or more keys to the
* specified value
* @throws ClassCastException if the value is of an inappropriate type for
* this map
@@ -184,8 +190,9 @@
* or {@code null} if this map contains no mapping for the key.
*
* <p>More formally, if this map contains a mapping from a key
- * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
- * key.equals(k))}, then this method returns {@code v}; otherwise
+ * {@code k} to a value {@code v} such that
+ * {@code Objects.equals(key, k)},
+ * then this method returns {@code v}; otherwise
* it returns {@code null}. (There can be at most one such mapping.)
*
* <p>If this map permits null values, then a return value of
@@ -212,18 +219,18 @@
* Associates the specified value with the specified key in this map
* (optional operation). If the map previously contained a mapping for
* the key, the old value is replaced by the specified value. (A map
- * <tt>m</tt> is said to contain a mapping for a key <tt>k</tt> if and only
+ * {@code m} is said to contain a mapping for a key {@code k} if and only
* if {@link #containsKey(Object) m.containsKey(k)} would return
- * <tt>true</tt>.)
+ * {@code true}.)
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
- * @return the previous value associated with <tt>key</tt>, or
- * <tt>null</tt> if there was no mapping for <tt>key</tt>.
- * (A <tt>null</tt> return can also indicate that the map
- * previously associated <tt>null</tt> with <tt>key</tt>,
- * if the implementation supports <tt>null</tt> values.)
- * @throws UnsupportedOperationException if the <tt>put</tt> operation
+ * @return the previous value associated with {@code key}, or
+ * {@code null} if there was no mapping for {@code key}.
+ * (A {@code null} return can also indicate that the map
+ * previously associated {@code null} with {@code key},
+ * if the implementation supports {@code null} values.)
+ * @throws UnsupportedOperationException if the {@code put} operation
* is not supported by this map
* @throws ClassCastException if the class of the specified key or value
* prevents it from being stored in this map
@@ -237,25 +244,25 @@
/**
* Removes the mapping for a key from this map if it is present
* (optional operation). More formally, if this map contains a mapping
- * from key <tt>k</tt> to value <tt>v</tt> such that
- * <code>(key==null ? k==null : key.equals(k))</code>, that mapping
+ * from key {@code k} to value {@code v} such that
+ * {@code Objects.equals(key, k)}, that mapping
* is removed. (The map can contain at most one such mapping.)
*
* <p>Returns the value to which this map previously associated the key,
- * or <tt>null</tt> if the map contained no mapping for the key.
+ * or {@code null} if the map contained no mapping for the key.
*
* <p>If this map permits null values, then a return value of
- * <tt>null</tt> does not <i>necessarily</i> indicate that the map
+ * {@code null} does not <i>necessarily</i> indicate that the map
* contained no mapping for the key; it's also possible that the map
- * explicitly mapped the key to <tt>null</tt>.
+ * explicitly mapped the key to {@code null}.
*
* <p>The map will not contain a mapping for the specified key once the
* call returns.
*
* @param key key whose mapping is to be removed from the map
- * @return the previous value associated with <tt>key</tt>, or
- * <tt>null</tt> if there was no mapping for <tt>key</tt>.
- * @throws UnsupportedOperationException if the <tt>remove</tt> operation
+ * @return the previous value associated with {@code key}, or
+ * {@code null} if there was no mapping for {@code key}.
+ * @throws UnsupportedOperationException if the {@code remove} operation
* is not supported by this map
* @throws ClassCastException if the key is of an inappropriate type for
* this map
@@ -273,12 +280,12 @@
* Copies all of the mappings from the specified map to this map
* (optional operation). The effect of this call is equivalent to that
* of calling {@link #put(Object,Object) put(k, v)} on this map once
- * for each mapping from key <tt>k</tt> to value <tt>v</tt> in the
+ * for each mapping from key {@code k} to value {@code v} in the
* specified map. The behavior of this operation is undefined if the
* specified map is modified while the operation is in progress.
*
* @param m mappings to be stored in this map
- * @throws UnsupportedOperationException if the <tt>putAll</tt> operation
+ * @throws UnsupportedOperationException if the {@code putAll} operation
* is not supported by this map
* @throws ClassCastException if the class of a key or value in the
* specified map prevents it from being stored in this map
@@ -294,7 +301,7 @@
* Removes all of the mappings from this map (optional operation).
* The map will be empty after this call returns.
*
- * @throws UnsupportedOperationException if the <tt>clear</tt> operation
+ * @throws UnsupportedOperationException if the {@code clear} operation
* is not supported by this map
*/
void clear();
@@ -307,12 +314,12 @@
* The set is backed by the map, so changes to the map are
* reflected in the set, and vice-versa. If the map is modified
* while an iteration over the set is in progress (except through
- * the iterator's own <tt>remove</tt> operation), the results of
+ * the iterator's own {@code remove} operation), the results of
* the iteration are undefined. The set supports element removal,
* which removes the corresponding mapping from the map, via the
- * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
- * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
- * operations. It does not support the <tt>add</tt> or <tt>addAll</tt>
+ * {@code Iterator.remove}, {@code Set.remove},
+ * {@code removeAll}, {@code retainAll}, and {@code clear}
+ * operations. It does not support the {@code add} or {@code addAll}
* operations.
*
* @return a set view of the keys contained in this map
@@ -324,13 +331,13 @@
* The collection is backed by the map, so changes to the map are
* reflected in the collection, and vice-versa. If the map is
* modified while an iteration over the collection is in progress
- * (except through the iterator's own <tt>remove</tt> operation),
+ * (except through the iterator's own {@code remove} operation),
* the results of the iteration are undefined. The collection
* supports element removal, which removes the corresponding
- * mapping from the map, via the <tt>Iterator.remove</tt>,
- * <tt>Collection.remove</tt>, <tt>removeAll</tt>,
- * <tt>retainAll</tt> and <tt>clear</tt> operations. It does not
- * support the <tt>add</tt> or <tt>addAll</tt> operations.
+ * mapping from the map, via the {@code Iterator.remove},
+ * {@code Collection.remove}, {@code removeAll},
+ * {@code retainAll} and {@code clear} operations. It does not
+ * support the {@code add} or {@code addAll} operations.
*
* @return a collection view of the values contained in this map
*/
@@ -341,33 +348,33 @@
* The set is backed by the map, so changes to the map are
* reflected in the set, and vice-versa. If the map is modified
* while an iteration over the set is in progress (except through
- * the iterator's own <tt>remove</tt> operation, or through the
- * <tt>setValue</tt> operation on a map entry returned by the
+ * the iterator's own {@code remove} operation, or through the
+ * {@code setValue} operation on a map entry returned by the
* iterator) the results of the iteration are undefined. The set
* supports element removal, which removes the corresponding
- * mapping from the map, via the <tt>Iterator.remove</tt>,
- * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and
- * <tt>clear</tt> operations. It does not support the
- * <tt>add</tt> or <tt>addAll</tt> operations.
+ * mapping from the map, via the {@code Iterator.remove},
+ * {@code Set.remove}, {@code removeAll}, {@code retainAll} and
+ * {@code clear} operations. It does not support the
+ * {@code add} or {@code addAll} operations.
*
* @return a set view of the mappings contained in this map
*/
Set<Map.Entry<K, V>> entrySet();
/**
- * A map entry (key-value pair). The <tt>Map.entrySet</tt> method returns
+ * A map entry (key-value pair). The {@code Map.entrySet} method returns
* a collection-view of the map, whose elements are of this class. The
* <i>only</i> way to obtain a reference to a map entry is from the
- * iterator of this collection-view. These <tt>Map.Entry</tt> objects are
+ * iterator of this collection-view. These {@code Map.Entry} objects are
* valid <i>only</i> for the duration of the iteration; more formally,
* the behavior of a map entry is undefined if the backing map has been
* modified after the entry was returned by the iterator, except through
- * the <tt>setValue</tt> operation on the map entry.
+ * the {@code setValue} operation on the map entry.
*
* @see Map#entrySet()
* @since 1.2
*/
- interface Entry<K,V> {
+ interface Entry<K, V> {
/**
* Returns the key corresponding to this entry.
*
@@ -381,7 +388,7 @@
/**
* Returns the value corresponding to this entry. If the mapping
* has been removed from the backing map (by the iterator's
- * <tt>remove</tt> operation), the results of this call are undefined.
+ * {@code remove} operation), the results of this call are undefined.
*
* @return the value corresponding to this entry
* @throws IllegalStateException implementations may, but are not
@@ -394,11 +401,11 @@
* Replaces the value corresponding to this entry with the specified
* value (optional operation). (Writes through to the map.) The
* behavior of this call is undefined if the mapping has already been
- * removed from the map (by the iterator's <tt>remove</tt> operation).
+ * removed from the map (by the iterator's {@code remove} operation).
*
* @param value new value to be stored in this entry
* @return old value corresponding to the entry
- * @throws UnsupportedOperationException if the <tt>put</tt> operation
+ * @throws UnsupportedOperationException if the {@code put} operation
* is not supported by the backing map
* @throws ClassCastException if the class of the specified value
* prevents it from being stored in the backing map
@@ -414,34 +421,34 @@
/**
* Compares the specified object with this entry for equality.
- * Returns <tt>true</tt> if the given object is also a map entry and
+ * Returns {@code true} if the given object is also a map entry and
* the two entries represent the same mapping. More formally, two
- * entries <tt>e1</tt> and <tt>e2</tt> represent the same mapping
+ * entries {@code e1} and {@code e2} represent the same mapping
* if<pre>
* (e1.getKey()==null ?
* e2.getKey()==null : e1.getKey().equals(e2.getKey())) &&
* (e1.getValue()==null ?
* e2.getValue()==null : e1.getValue().equals(e2.getValue()))
* </pre>
- * This ensures that the <tt>equals</tt> method works properly across
- * different implementations of the <tt>Map.Entry</tt> interface.
+ * This ensures that the {@code equals} method works properly across
+ * different implementations of the {@code Map.Entry} interface.
*
* @param o object to be compared for equality with this map entry
- * @return <tt>true</tt> if the specified object is equal to this map
+ * @return {@code true} if the specified object is equal to this map
* entry
*/
boolean equals(Object o);
/**
* Returns the hash code value for this map entry. The hash code
- * of a map entry <tt>e</tt> is defined to be: <pre>
+ * of a map entry {@code e} is defined to be: <pre>
* (e.getKey()==null ? 0 : e.getKey().hashCode()) ^
* (e.getValue()==null ? 0 : e.getValue().hashCode())
* </pre>
- * This ensures that <tt>e1.equals(e2)</tt> implies that
- * <tt>e1.hashCode()==e2.hashCode()</tt> for any two Entries
- * <tt>e1</tt> and <tt>e2</tt>, as required by the general
- * contract of <tt>Object.hashCode</tt>.
+ * This ensures that {@code e1.equals(e2)} implies that
+ * {@code e1.hashCode()==e2.hashCode()} for any two Entries
+ * {@code e1} and {@code e2}, as required by the general
+ * contract of {@code Object.hashCode}.
*
* @return the hash code value for this map entry
* @see Object#hashCode()
@@ -462,12 +469,29 @@
* @see Comparable
* @since 1.8
*/
- public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {
+ public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K, V>> comparingByKey() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getKey().compareTo(c2.getKey());
}
/**
+ * Returns a comparator that compares {@link Map.Entry} in natural order on value.
+ *
+ * <p>The returned comparator is serializable and throws {@link
+ * NullPointerException} when comparing an entry with null values.
+ *
+ * @param <K> the type of the map keys
+ * @param <V> the {@link Comparable} type of the map values
+ * @return a comparator that compares {@link Map.Entry} in natural order on value.
+ * @see Comparable
+ * @since 1.8
+ */
+ public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K, V>> comparingByValue() {
+ return (Comparator<Map.Entry<K, V>> & Serializable)
+ (c1, c2) -> c1.getValue().compareTo(c2.getValue());
+ }
+
+ /**
* Returns a comparator that compares {@link Map.Entry} by key using the given
* {@link Comparator}.
*
@@ -485,30 +509,49 @@
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
}
+
+ /**
+ * Returns a comparator that compares {@link Map.Entry} by value using the given
+ * {@link Comparator}.
+ *
+ * <p>The returned comparator is serializable if the specified comparator
+ * is also serializable.
+ *
+ * @param <K> the type of the map keys
+ * @param <V> the type of the map values
+ * @param cmp the value {@link Comparator}
+ * @return a comparator that compares {@link Map.Entry} by the value.
+ * @since 1.8
+ */
+ public static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp) {
+ Objects.requireNonNull(cmp);
+ return (Comparator<Map.Entry<K, V>> & Serializable)
+ (c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
+ }
}
// Comparison and hashing
/**
* Compares the specified object with this map for equality. Returns
- * <tt>true</tt> if the given object is also a map and the two maps
- * represent the same mappings. More formally, two maps <tt>m1</tt> and
- * <tt>m2</tt> represent the same mappings if
- * <tt>m1.entrySet().equals(m2.entrySet())</tt>. This ensures that the
- * <tt>equals</tt> method works properly across different implementations
- * of the <tt>Map</tt> interface.
+ * {@code true} if the given object is also a map and the two maps
+ * represent the same mappings. More formally, two maps {@code m1} and
+ * {@code m2} represent the same mappings if
+ * {@code m1.entrySet().equals(m2.entrySet())}. This ensures that the
+ * {@code equals} method works properly across different implementations
+ * of the {@code Map} interface.
*
* @param o object to be compared for equality with this map
- * @return <tt>true</tt> if the specified object is equal to this map
+ * @return {@code true} if the specified object is equal to this map
*/
boolean equals(Object o);
/**
* Returns the hash code value for this map. The hash code of a map is
* defined to be the sum of the hash codes of each entry in the map's
- * <tt>entrySet()</tt> view. This ensures that <tt>m1.equals(m2)</tt>
- * implies that <tt>m1.hashCode()==m2.hashCode()</tt> for any two maps
- * <tt>m1</tt> and <tt>m2</tt>, as required by the general contract of
+ * {@code entrySet()} view. This ensures that {@code m1.equals(m2)}
+ * implies that {@code m1.hashCode()==m2.hashCode()} for any two maps
+ * {@code m1} and {@code m2}, as required by the general contract of
* {@link Object#hashCode}.
*
* @return the hash code value for this map
@@ -518,6 +561,37 @@
*/
int hashCode();
+ // Defaultable methods
+
+ /**
+ * Returns the value to which the specified key is mapped, or
+ * {@code defaultValue} if this map contains no mapping for the key.
+ *
+ * @implSpec
+ * The default implementation makes no guarantees about synchronization
+ * or atomicity properties of this method. Any implementation providing
+ * atomicity guarantees must override this method and document its
+ * concurrency properties.
+ *
+ * @param key the key whose associated value is to be returned
+ * @param defaultValue the default mapping of the key
+ * @return the value to which the specified key is mapped, or
+ * {@code defaultValue} if this map contains no mapping for the key
+ * @throws ClassCastException if the key is of an inappropriate type for
+ * this map
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @throws NullPointerException if the specified key is null and this map
+ * does not permit null keys
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @since 1.8
+ */
+ default V getOrDefault(Object key, V defaultValue) {
+ V v;
+ return (((v = get(key)) != null) || containsKey(key))
+ ? v
+ : defaultValue;
+ }
+
/**
* Performs the given action for each entry in this map until all entries
* have been processed or the action throws an exception. Unless
@@ -551,11 +625,625 @@
try {
k = entry.getKey();
v = entry.getValue();
- } catch(IllegalStateException ise) {
+ } catch (IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
action.accept(k, v);
}
}
+
+ /**
+ * Replaces each entry's value with the result of invoking the given
+ * function on that entry until all entries have been processed or the
+ * function throws an exception. Exceptions thrown by the function are
+ * relayed to the caller.
+ *
+ * @implSpec
+ * <p>The default implementation is equivalent to, for this {@code map}:
+ * <pre> {@code
+ * for (Map.Entry<K, V> entry : map.entrySet())
+ * entry.setValue(function.apply(entry.getKey(), entry.getValue()));
+ * }</pre>
+ *
+ * <p>The default implementation makes no guarantees about synchronization
+ * or atomicity properties of this method. Any implementation providing
+ * atomicity guarantees must override this method and document its
+ * concurrency properties.
+ *
+ * @param function the function to apply to each entry
+ * @throws UnsupportedOperationException if the {@code set} operation
+ * is not supported by this map's entry set iterator.
+ * @throws ClassCastException if the class of a replacement value
+ * prevents it from being stored in this map
+ * @throws NullPointerException if the specified function is null, or the
+ * specified replacement value is null, and this map does not permit null
+ * values
+ * @throws ClassCastException if a replacement value is of an inappropriate
+ * type for this map
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @throws NullPointerException if function or a replacement value is null,
+ * and this map does not permit null keys or values
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @throws IllegalArgumentException if some property of a replacement value
+ * prevents it from being stored in this map
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @throws ConcurrentModificationException if an entry is found to be
+ * removed during iteration
+ * @since 1.8
+ */
+ default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
+ Objects.requireNonNull(function);
+ for (Map.Entry<K, V> entry : entrySet()) {
+ K k;
+ V v;
+ try {
+ k = entry.getKey();
+ v = entry.getValue();
+ } catch (IllegalStateException ise) {
+ // this usually means the entry is no longer in the map.
+ throw new ConcurrentModificationException(ise);
+ }
+
+ // ise thrown from function is not a cme.
+ v = function.apply(k, v);
+
+ try {
+ entry.setValue(v);
+ } catch (IllegalStateException ise) {
+ // this usually means the entry is no longer in the map.
+ throw new ConcurrentModificationException(ise);
+ }
+ }
+ }
+
+ /**
+ * If the specified key is not already associated with a value (or is mapped
+ * to {@code null}) associates it with the given value and returns
+ * {@code null}, else returns the current value.
+ *
+ * @implSpec
+ * The default implementation is equivalent to, for this {@code
+ * map}:
+ *
+ * <pre> {@code
+ * V v = map.get(key);
+ * if (v == null)
+ * v = map.put(key, value);
+ *
+ * return v;
+ * }</pre>
+ *
+ * <p>The default implementation makes no guarantees about synchronization
+ * or atomicity properties of this method. Any implementation providing
+ * atomicity guarantees must override this method and document its
+ * concurrency properties.
+ *
+ * @param key key with which the specified value is to be associated
+ * @param value value to be associated with the specified key
+ * @return the previous value associated with the specified key, or
+ * {@code null} if there was no mapping for the key.
+ * (A {@code null} return can also indicate that the map
+ * previously associated {@code null} with the key,
+ * if the implementation supports null values.)
+ * @throws UnsupportedOperationException if the {@code put} operation
+ * is not supported by this map
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @throws ClassCastException if the key or value is of an inappropriate
+ * type for this map
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @throws NullPointerException if the specified key or value is null,
+ * and this map does not permit null keys or values
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @throws IllegalArgumentException if some property of the specified key
+ * or value prevents it from being stored in this map
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @since 1.8
+ */
+ default V putIfAbsent(K key, V value) {
+ V v = get(key);
+ if (v == null) {
+ v = put(key, value);
+ }
+
+ return v;
+ }
+
+ /**
+ * Removes the entry for the specified key only if it is currently
+ * mapped to the specified value.
+ *
+ * @implSpec
+ * The default implementation is equivalent to, for this {@code map}:
+ *
+ * <pre> {@code
+ * if (map.containsKey(key) && Objects.equals(map.get(key), value)) {
+ * map.remove(key);
+ * return true;
+ * } else
+ * return false;
+ * }</pre>
+ *
+ * <p>The default implementation makes no guarantees about synchronization
+ * or atomicity properties of this method. Any implementation providing
+ * atomicity guarantees must override this method and document its
+ * concurrency properties.
+ *
+ * @param key key with which the specified value is associated
+ * @param value value expected to be associated with the specified key
+ * @return {@code true} if the value was removed
+ * @throws UnsupportedOperationException if the {@code remove} operation
+ * is not supported by this map
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @throws ClassCastException if the key or value is of an inappropriate
+ * type for this map
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @throws NullPointerException if the specified key or value is null,
+ * and this map does not permit null keys or values
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @since 1.8
+ */
+ default boolean remove(Object key, Object value) {
+ Object curValue = get(key);
+ if (!Objects.equals(curValue, value) ||
+ (curValue == null && !containsKey(key))) {
+ return false;
+ }
+ remove(key);
+ return true;
+ }
+
+ /**
+ * Replaces the entry for the specified key only if currently
+ * mapped to the specified value.
+ *
+ * @implSpec
+ * The default implementation is equivalent to, for this {@code map}:
+ *
+ * <pre> {@code
+ * if (map.containsKey(key) && Objects.equals(map.get(key), value)) {
+ * map.put(key, newValue);
+ * return true;
+ * } else
+ * return false;
+ * }</pre>
+ *
+ * The default implementation does not throw NullPointerException
+ * for maps that do not support null values if oldValue is null unless
+ * newValue is also null.
+ *
+ * <p>The default implementation makes no guarantees about synchronization
+ * or atomicity properties of this method. Any implementation providing
+ * atomicity guarantees must override this method and document its
+ * concurrency properties.
+ *
+ * @param key key with which the specified value is associated
+ * @param oldValue value expected to be associated with the specified key
+ * @param newValue value to be associated with the specified key
+ * @return {@code true} if the value was replaced
+ * @throws UnsupportedOperationException if the {@code put} operation
+ * is not supported by this map
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @throws ClassCastException if the class of a specified key or value
+ * prevents it from being stored in this map
+ * @throws NullPointerException if a specified key or newValue is null,
+ * and this map does not permit null keys or values
+ * @throws NullPointerException if oldValue is null and this map does not
+ * permit null values
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @throws IllegalArgumentException if some property of a specified key
+ * or value prevents it from being stored in this map
+ * @since 1.8
+ */
+ default boolean replace(K key, V oldValue, V newValue) {
+ Object curValue = get(key);
+ if (!Objects.equals(curValue, oldValue) ||
+ (curValue == null && !containsKey(key))) {
+ return false;
+ }
+ put(key, newValue);
+ return true;
+ }
+
+ /**
+ * Replaces the entry for the specified key only if it is
+ * currently mapped to some value.
+ *
+ * @implSpec
+ * The default implementation is equivalent to, for this {@code map}:
+ *
+ * <pre> {@code
+ * if (map.containsKey(key)) {
+ * return map.put(key, value);
+ * } else
+ * return null;
+ * }</pre>
+ *
+ * <p>The default implementation makes no guarantees about synchronization
+ * or atomicity properties of this method. Any implementation providing
+ * atomicity guarantees must override this method and document its
+ * concurrency properties.
+ *
+ * @param key key with which the specified value is associated
+ * @param value value to be associated with the specified key
+ * @return the previous value associated with the specified key, or
+ * {@code null} if there was no mapping for the key.
+ * (A {@code null} return can also indicate that the map
+ * previously associated {@code null} with the key,
+ * if the implementation supports null values.)
+ * @throws UnsupportedOperationException if the {@code put} operation
+ * is not supported by this map
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @throws ClassCastException if the class of the specified key or value
+ * prevents it from being stored in this map
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @throws NullPointerException if the specified key or value is null,
+ * and this map does not permit null keys or values
+ * @throws IllegalArgumentException if some property of the specified key
+ * or value prevents it from being stored in this map
+ * @since 1.8
+ */
+ default V replace(K key, V value) {
+ V curValue;
+ if (((curValue = get(key)) != null) || containsKey(key)) {
+ curValue = put(key, value);
+ }
+ return curValue;
+ }
+
+ /**
+ * If the specified key is not already associated with a value (or is mapped
+ * to {@code null}), attempts to compute its value using the given mapping
+ * function and enters it into this map unless {@code null}.
+ *
+ * <p>If the mapping function returns {@code null}, no mapping is recorded.
+ * If the mapping function itself throws an (unchecked) exception, the
+ * exception is rethrown, and no mapping is recorded. The most
+ * common usage is to construct a new object serving as an initial
+ * mapped value or memoized result, as in:
+ *
+ * <pre> {@code
+ * map.computeIfAbsent(key, k -> new Value(f(k)));
+ * }</pre>
+ *
+ * <p>Or to implement a multi-value map, {@code Map<K,Collection<V>>},
+ * supporting multiple values per key:
+ *
+ * <pre> {@code
+ * map.computeIfAbsent(key, k -> new HashSet<V>()).add(v);
+ * }</pre>
+ *
+ * <p>The mapping function should not modify this map during computation.
+ *
+ * @implSpec
+ * The default implementation is equivalent to the following steps for this
+ * {@code map}, then returning the current value or {@code null} if now
+ * absent:
+ *
+ * <pre> {@code
+ * if (map.get(key) == null) {
+ * V newValue = mappingFunction.apply(key);
+ * if (newValue != null)
+ * map.put(key, newValue);
+ * }
+ * }</pre>
+ *
+ * <p>The default implementation makes no guarantees about detecting if the
+ * mapping function modifies this map during computation and, if
+ * appropriate, reporting an error. Non-concurrent implementations should
+ * override this method and, on a best-effort basis, throw a
+ * {@code ConcurrentModificationException} if it is detected that the
+ * mapping function modifies this map during computation. Concurrent
+ * implementations should override this method and, on a best-effort basis,
+ * throw an {@code IllegalStateException} if it is detected that the
+ * mapping function modifies this map during computation and as a result
+ * computation would never complete.
+ *
+ * <p>The default implementation makes no guarantees about synchronization
+ * or atomicity properties of this method. Any implementation providing
+ * atomicity guarantees must override this method and document its
+ * concurrency properties. In particular, all implementations of
+ * subinterface {@link java.util.concurrent.ConcurrentMap} must document
+ * whether the mapping function is applied once atomically only if the value
+ * is not present.
+ *
+ * @param key key with which the specified value is to be associated
+ * @param mappingFunction the mapping function to compute a value
+ * @return the current (existing or computed) value associated with
+ * the specified key, or null if the computed value is null
+ * @throws NullPointerException if the specified key is null and
+ * this map does not support null keys, or the mappingFunction
+ * is null
+ * @throws UnsupportedOperationException if the {@code put} operation
+ * is not supported by this map
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @throws ClassCastException if the class of the specified key or value
+ * prevents it from being stored in this map
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @throws IllegalArgumentException if some property of the specified key
+ * or value prevents it from being stored in this map
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @since 1.8
+ */
+ default V computeIfAbsent(K key,
+ Function<? super K, ? extends V> mappingFunction) {
+ Objects.requireNonNull(mappingFunction);
+ V v;
+ if ((v = get(key)) == null) {
+ V newValue;
+ if ((newValue = mappingFunction.apply(key)) != null) {
+ put(key, newValue);
+ return newValue;
+ }
+ }
+
+ return v;
+ }
+
+ /**
+ * If the value for the specified key is present and non-null, attempts to
+ * compute a new mapping given the key and its current mapped value.
+ *
+ * <p>If the remapping function returns {@code null}, the mapping is removed.
+ * If the remapping function itself throws an (unchecked) exception, the
+ * exception is rethrown, and the current mapping is left unchanged.
+ *
+ * <p>The remapping function should not modify this map during computation.
+ *
+ * @implSpec
+ * The default implementation is equivalent to performing the following
+ * steps for this {@code map}, then returning the current value or
+ * {@code null} if now absent:
+ *
+ * <pre> {@code
+ * if (map.get(key) != null) {
+ * V oldValue = map.get(key);
+ * V newValue = remappingFunction.apply(key, oldValue);
+ * if (newValue != null)
+ * map.put(key, newValue);
+ * else
+ * map.remove(key);
+ * }
+ * }</pre>
+ *
+ * <p>The default implementation makes no guarantees about detecting if the
+ * remapping function modifies this map during computation and, if
+ * appropriate, reporting an error. Non-concurrent implementations should
+ * override this method and, on a best-effort basis, throw a
+ * {@code ConcurrentModificationException} if it is detected that the
+ * remapping function modifies this map during computation. Concurrent
+ * implementations should override this method and, on a best-effort basis,
+ * throw an {@code IllegalStateException} if it is detected that the
+ * remapping function modifies this map during computation and as a result
+ * computation would never complete.
+ *
+ * <p>The default implementation makes no guarantees about synchronization
+ * or atomicity properties of this method. Any implementation providing
+ * atomicity guarantees must override this method and document its
+ * concurrency properties. In particular, all implementations of
+ * subinterface {@link java.util.concurrent.ConcurrentMap} must document
+ * whether the remapping function is applied once atomically only if the
+ * value is not present.
+ *
+ * @param key key with which the specified value is to be associated
+ * @param remappingFunction the remapping function to compute a value
+ * @return the new value associated with the specified key, or null if none
+ * @throws NullPointerException if the specified key is null and
+ * this map does not support null keys, or the
+ * remappingFunction is null
+ * @throws UnsupportedOperationException if the {@code put} operation
+ * is not supported by this map
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @throws ClassCastException if the class of the specified key or value
+ * prevents it from being stored in this map
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @throws IllegalArgumentException if some property of the specified key
+ * or value prevents it from being stored in this map
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @since 1.8
+ */
+ default V computeIfPresent(K key,
+ BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ Objects.requireNonNull(remappingFunction);
+ V oldValue;
+ if ((oldValue = get(key)) != null) {
+ V newValue = remappingFunction.apply(key, oldValue);
+ if (newValue != null) {
+ put(key, newValue);
+ return newValue;
+ } else {
+ remove(key);
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Attempts to compute a mapping for the specified key and its current
+ * mapped value (or {@code null} if there is no current mapping). For
+ * example, to either create or append a {@code String} msg to a value
+ * mapping:
+ *
+ * <pre> {@code
+ * map.compute(key, (k, v) -> (v == null) ? msg : v.concat(msg))}</pre>
+ * (Method {@link #merge merge()} is often simpler to use for such purposes.)
+ *
+ * <p>If the remapping function returns {@code null}, the mapping is removed
+ * (or remains absent if initially absent). If the remapping function
+ * itself throws an (unchecked) exception, the exception is rethrown, and
+ * the current mapping is left unchanged.
+ *
+ * <p>The remapping function should not modify this map during computation.
+ *
+ * @implSpec
+ * The default implementation is equivalent to performing the following
+ * steps for this {@code map}, then returning the current value or
+ * {@code null} if absent:
+ *
+ * <pre> {@code
+ * V oldValue = map.get(key);
+ * V newValue = remappingFunction.apply(key, oldValue);
+ * if (oldValue != null) {
+ * if (newValue != null)
+ * map.put(key, newValue);
+ * else
+ * map.remove(key);
+ * } else {
+ * if (newValue != null)
+ * map.put(key, newValue);
+ * else
+ * return null;
+ * }
+ * }</pre>
+ *
+ * <p>The default implementation makes no guarantees about detecting if the
+ * remapping function modifies this map during computation and, if
+ * appropriate, reporting an error. Non-concurrent implementations should
+ * override this method and, on a best-effort basis, throw a
+ * {@code ConcurrentModificationException} if it is detected that the
+ * remapping function modifies this map during computation. Concurrent
+ * implementations should override this method and, on a best-effort basis,
+ * throw an {@code IllegalStateException} if it is detected that the
+ * remapping function modifies this map during computation and as a result
+ * computation would never complete.
+ *
+ * <p>The default implementation makes no guarantees about synchronization
+ * or atomicity properties of this method. Any implementation providing
+ * atomicity guarantees must override this method and document its
+ * concurrency properties. In particular, all implementations of
+ * subinterface {@link java.util.concurrent.ConcurrentMap} must document
+ * whether the remapping function is applied once atomically only if the
+ * value is not present.
+ *
+ * @param key key with which the specified value is to be associated
+ * @param remappingFunction the remapping function to compute a value
+ * @return the new value associated with the specified key, or null if none
+ * @throws NullPointerException if the specified key is null and
+ * this map does not support null keys, or the
+ * remappingFunction is null
+ * @throws UnsupportedOperationException if the {@code put} operation
+ * is not supported by this map
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @throws ClassCastException if the class of the specified key or value
+ * prevents it from being stored in this map
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @throws IllegalArgumentException if some property of the specified key
+ * or value prevents it from being stored in this map
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @since 1.8
+ */
+ default V compute(K key,
+ BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ Objects.requireNonNull(remappingFunction);
+ V oldValue = get(key);
+
+ V newValue = remappingFunction.apply(key, oldValue);
+ if (newValue == null) {
+ // delete mapping
+ if (oldValue != null || containsKey(key)) {
+ // something to remove
+ remove(key);
+ return null;
+ } else {
+ // nothing to do. Leave things as they were.
+ return null;
+ }
+ } else {
+ // add or replace old mapping
+ put(key, newValue);
+ return newValue;
+ }
+ }
+
+ /**
+ * If the specified key is not already associated with a value or is
+ * associated with null, associates it with the given non-null value.
+ * Otherwise, replaces the associated value with the results of the given
+ * remapping function, or removes if the result is {@code null}. This
+ * method may be of use when combining multiple mapped values for a key.
+ * For example, to either create or append a {@code String msg} to a
+ * value mapping:
+ *
+ * <pre> {@code
+ * map.merge(key, msg, String::concat)
+ * }</pre>
+ *
+ * <p>If the remapping function returns {@code null}, the mapping is removed.
+ * If the remapping function itself throws an (unchecked) exception, the
+ * exception is rethrown, and the current mapping is left unchanged.
+ *
+ * <p>The remapping function should not modify this map during computation.
+ *
+ * @implSpec
+ * The default implementation is equivalent to performing the following
+ * steps for this {@code map}, then returning the current value or
+ * {@code null} if absent:
+ *
+ * <pre> {@code
+ * V oldValue = map.get(key);
+ * V newValue = (oldValue == null) ? value :
+ * remappingFunction.apply(oldValue, value);
+ * if (newValue == null)
+ * map.remove(key);
+ * else
+ * map.put(key, newValue);
+ * }</pre>
+ *
+ * <p>The default implementation makes no guarantees about detecting if the
+ * remapping function modifies this map during computation and, if
+ * appropriate, reporting an error. Non-concurrent implementations should
+ * override this method and, on a best-effort basis, throw a
+ * {@code ConcurrentModificationException} if it is detected that the
+ * remapping function modifies this map during computation. Concurrent
+ * implementations should override this method and, on a best-effort basis,
+ * throw an {@code IllegalStateException} if it is detected that the
+ * remapping function modifies this map during computation and as a result
+ * computation would never complete.
+ *
+ * <p>The default implementation makes no guarantees about synchronization
+ * or atomicity properties of this method. Any implementation providing
+ * atomicity guarantees must override this method and document its
+ * concurrency properties. In particular, all implementations of
+ * subinterface {@link java.util.concurrent.ConcurrentMap} must document
+ * whether the remapping function is applied once atomically only if the
+ * value is not present.
+ *
+ * @param key key with which the resulting value is to be associated
+ * @param value the non-null value to be merged with the existing value
+ * associated with the key or, if no existing value or a null value
+ * is associated with the key, to be associated with the key
+ * @param remappingFunction the remapping function to recompute a value if
+ * present
+ * @return the new value associated with the specified key, or null if no
+ * value is associated with the key
+ * @throws UnsupportedOperationException if the {@code put} operation
+ * is not supported by this map
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @throws ClassCastException if the class of the specified key or value
+ * prevents it from being stored in this map
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @throws IllegalArgumentException if some property of the specified key
+ * or value prevents it from being stored in this map
+ * (<a href="Collection.html#optional-restrictions">optional</a>)
+ * @throws NullPointerException if the specified key is null and this map
+ * does not support null keys or the value or remappingFunction is
+ * null
+ * @since 1.8
+ */
+ default V merge(K key, V value,
+ BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
+ Objects.requireNonNull(remappingFunction);
+ Objects.requireNonNull(value);
+ V oldValue = get(key);
+ V newValue = (oldValue == null) ? value :
+ remappingFunction.apply(oldValue, value);
+ if (newValue == null) {
+ remove(key);
+ } else {
+ put(key, newValue);
+ }
+ return newValue;
+ }
}
diff --git a/ojluni/src/main/java/java/util/NavigableMap.java b/ojluni/src/main/java/java/util/NavigableMap.java
index b0d9453..fdbc0b5 100644
--- a/ojluni/src/main/java/java/util/NavigableMap.java
+++ b/ojluni/src/main/java/java/util/NavigableMap.java
@@ -41,30 +41,32 @@
/**
* A {@link SortedMap} extended with navigation methods returning the
* closest matches for given search targets. Methods
- * {@code lowerEntry}, {@code floorEntry}, {@code ceilingEntry},
- * and {@code higherEntry} return {@code Map.Entry} objects
+ * {@link #lowerEntry}, {@link #floorEntry}, {@link #ceilingEntry},
+ * and {@link #higherEntry} return {@code Map.Entry} objects
* associated with keys respectively less than, less than or equal,
* greater than or equal, and greater than a given key, returning
* {@code null} if there is no such key. Similarly, methods
- * {@code lowerKey}, {@code floorKey}, {@code ceilingKey}, and
- * {@code higherKey} return only the associated keys. All of these
+ * {@link #lowerKey}, {@link #floorKey}, {@link #ceilingKey}, and
+ * {@link #higherKey} return only the associated keys. All of these
* methods are designed for locating, not traversing entries.
*
* <p>A {@code NavigableMap} may be accessed and traversed in either
- * ascending or descending key order. The {@code descendingMap}
+ * ascending or descending key order. The {@link #descendingMap}
* method returns a view of the map with the senses of all relational
* and directional methods inverted. The performance of ascending
* operations and views is likely to be faster than that of descending
- * ones. Methods {@code subMap}, {@code headMap},
- * and {@code tailMap} differ from the like-named {@code
- * SortedMap} methods in accepting additional arguments describing
- * whether lower and upper bounds are inclusive versus exclusive.
- * Submaps of any {@code NavigableMap} must implement the {@code
- * NavigableMap} interface.
+ * ones. Methods
+ * {@link #subMap(Object, boolean, Object, boolean) subMap(K, boolean, K, boolean)},
+ * {@link #headMap(Object, boolean) headMap(K, boolean)}, and
+ * {@link #tailMap(Object, boolean) tailMap(K, boolean)}
+ * differ from the like-named {@code SortedMap} methods in accepting
+ * additional arguments describing whether lower and upper bounds are
+ * inclusive versus exclusive. Submaps of any {@code NavigableMap}
+ * must implement the {@code NavigableMap} interface.
*
- * <p>This interface additionally defines methods {@code firstEntry},
- * {@code pollFirstEntry}, {@code lastEntry}, and
- * {@code pollLastEntry} that return and/or remove the least and
+ * <p>This interface additionally defines methods {@link #firstEntry},
+ * {@link #pollFirstEntry}, {@link #lastEntry}, and
+ * {@link #pollLastEntry} that return and/or remove the least and
* greatest mappings, if any exist, else returning {@code null}.
*
* <p>Implementations of entry-returning methods are expected to
@@ -83,7 +85,7 @@
* implement {@code NavigableMap}, but extensions and implementations
* of this interface are encouraged to override these methods to return
* {@code NavigableMap}. Similarly,
- * {@link #keySet()} can be overridden to return {@code NavigableSet}.
+ * {@link #keySet()} can be overridden to return {@link NavigableSet}.
*
* @author Doug Lea
* @author Josh Bloch
@@ -297,7 +299,7 @@
* Returns a view of the portion of this map whose keys range from
* {@code fromKey} to {@code toKey}. If {@code fromKey} and
* {@code toKey} are equal, the returned map is empty unless
- * {@code fromExclusive} and {@code toExclusive} are both true. The
+ * {@code fromInclusive} and {@code toInclusive} are both true. The
* returned map is backed by this map, so changes in the returned map are
* reflected in this map, and vice-versa. The returned map supports all
* optional map operations that this map supports.
diff --git a/ojluni/src/main/java/java/util/NavigableSet.java b/ojluni/src/main/java/java/util/NavigableSet.java
index 11a9bdb..412a07d 100644
--- a/ojluni/src/main/java/java/util/NavigableSet.java
+++ b/ojluni/src/main/java/java/util/NavigableSet.java
@@ -32,32 +32,36 @@
* http://creativecommons.org/publicdomain/zero/1.0/
*/
-package java.util;
-
// BEGIN android-note
// removed link to collections framework docs
// END android-note
+package java.util;
+
/**
* A {@link SortedSet} extended with navigation methods reporting
- * closest matches for given search targets. Methods {@code lower},
- * {@code floor}, {@code ceiling}, and {@code higher} return elements
+ * closest matches for given search targets. Methods {@link #lower},
+ * {@link #floor}, {@link #ceiling}, and {@link #higher} return elements
* respectively less than, less than or equal, greater than or equal,
* and greater than a given element, returning {@code null} if there
- * is no such element. A {@code NavigableSet} may be accessed and
- * traversed in either ascending or descending order. The {@code
- * descendingSet} method returns a view of the set with the senses of
- * all relational and directional methods inverted. The performance of
- * ascending operations and views is likely to be faster than that of
- * descending ones. This interface additionally defines methods
- * {@code pollFirst} and {@code pollLast} that return and remove the
- * lowest and highest element, if one exists, else returning {@code
- * null}. Methods {@code subSet}, {@code headSet},
- * and {@code tailSet} differ from the like-named {@code
- * SortedSet} methods in accepting additional arguments describing
- * whether lower and upper bounds are inclusive versus exclusive.
- * Subsets of any {@code NavigableSet} must implement the {@code
- * NavigableSet} interface.
+ * is no such element.
+ *
+ * <p>A {@code NavigableSet} may be accessed and traversed in either
+ * ascending or descending order. The {@link #descendingSet} method
+ * returns a view of the set with the senses of all relational and
+ * directional methods inverted. The performance of ascending
+ * operations and views is likely to be faster than that of descending
+ * ones. This interface additionally defines methods {@link
+ * #pollFirst} and {@link #pollLast} that return and remove the lowest
+ * and highest element, if one exists, else returning {@code null}.
+ * Methods
+ * {@link #subSet(Object, boolean, Object, boolean) subSet(E, boolean, E, boolean)},
+ * {@link #headSet(Object, boolean) headSet(E, boolean)}, and
+ * {@link #tailSet(Object, boolean) tailSet(E, boolean)}
+ * differ from the like-named {@code SortedSet} methods in accepting
+ * additional arguments describing whether lower and upper bounds are
+ * inclusive versus exclusive. Subsets of any {@code NavigableSet}
+ * must implement the {@code NavigableSet} interface.
*
* <p>The return values of navigation methods may be ambiguous in
* implementations that permit {@code null} elements. However, even
@@ -191,7 +195,7 @@
* Returns a view of the portion of this set whose elements range from
* {@code fromElement} to {@code toElement}. If {@code fromElement} and
* {@code toElement} are equal, the returned set is empty unless {@code
- * fromExclusive} and {@code toExclusive} are both true. The returned set
+ * fromInclusive} and {@code toInclusive} are both true. The returned set
* is backed by this set, so changes in the returned set are reflected in
* this set, and vice-versa. The returned set supports all optional set
* operations that this set supports.
diff --git a/ojluni/src/main/java/java/util/PriorityQueue.java b/ojluni/src/main/java/java/util/PriorityQueue.java
old mode 100755
new mode 100644
index b42647e..7c929bd
--- a/ojluni/src/main/java/java/util/PriorityQueue.java
+++ b/ojluni/src/main/java/java/util/PriorityQueue.java
@@ -77,7 +77,7 @@
*
* @since 1.5
* @author Josh Bloch, Doug Lea
- * @param <E> the type of elements held in this collection
+ * @param <E> the type of elements held in this queue
*/
public class PriorityQueue<E> extends AbstractQueue<E>
implements java.io.Serializable {
@@ -99,7 +99,7 @@
/**
* The number of elements in the priority queue.
*/
- private int size = 0;
+ int size;
/**
* The comparator, or null if priority queue uses elements'
@@ -111,7 +111,7 @@
* The number of times this priority queue has been
* <i>structurally modified</i>. See AbstractList for gory details.
*/
- transient int modCount = 0; // non-private to simplify nested class access
+ transient int modCount; // non-private to simplify nested class access
/**
* Creates a {@code PriorityQueue} with the default initial
@@ -258,8 +258,8 @@
a = Arrays.copyOf(a, a.length, Object[].class);
int len = a.length;
if (len == 1 || this.comparator != null)
- for (int i = 0; i < len; i++)
- if (a[i] == null)
+ for (Object e : a)
+ if (e == null)
throw new NullPointerException();
this.queue = a;
this.size = a.length;
@@ -406,7 +406,7 @@
* @return {@code true} if this queue contains the specified element
*/
public boolean contains(Object o) {
- return indexOf(o) != -1;
+ return indexOf(o) >= 0;
}
/**
@@ -448,7 +448,7 @@
* The following code can be used to dump the queue into a newly
* allocated array of {@code String}:
*
- * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
+ * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
*
* Note that {@code toArray(new Object[0])} is identical in function to
* {@code toArray()}.
@@ -489,7 +489,7 @@
* Index (into queue array) of element to be returned by
* subsequent call to next.
*/
- private int cursor = 0;
+ private int cursor;
/**
* Index of element returned by most recent call to next,
@@ -509,13 +509,13 @@
* We expect that most iterations, even those involving removals,
* will not need to store elements in this field.
*/
- private ArrayDeque<E> forgetMeNot = null;
+ private ArrayDeque<E> forgetMeNot;
/**
* Element returned by the most recent call to next iff that
* element was drawn from the forgetMeNot list.
*/
- private E lastRetElt = null;
+ private E lastRetElt;
/**
* The modCount value that the iterator believes that the backing
@@ -609,8 +609,8 @@
* avoid missing traversing elements.
*/
@SuppressWarnings("unchecked")
- private E removeAt(int i) {
- assert i >= 0 && i < size;
+ E removeAt(int i) {
+ // assert i >= 0 && i < size;
modCount++;
int s = --size;
if (s == i) // removed last element
@@ -756,6 +756,7 @@
* emitted (int), followed by all of its elements
* (each an {@code Object}) in the proper order.
* @param s the stream
+ * @throws java.io.IOException if an I/O error occurs
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
@@ -775,6 +776,9 @@
* (that is, deserializes it).
*
* @param s the stream
+ * @throws ClassNotFoundException if the class of a serialized object
+ * could not be found
+ * @throws java.io.IOException if an I/O error occurs
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
@@ -809,7 +813,7 @@
* @since 1.8
*/
public final Spliterator<E> spliterator() {
- return new PriorityQueueSpliterator<E>(this, 0, -1, 0);
+ return new PriorityQueueSpliterator<>(this, 0, -1, 0);
}
static final class PriorityQueueSpliterator<E> implements Spliterator<E> {
@@ -822,9 +826,9 @@
private int fence; // -1 until first use
private int expectedModCount; // initialized when fence set
- /** Creates new spliterator covering the given range */
+ /** Creates new spliterator covering the given range. */
PriorityQueueSpliterator(PriorityQueue<E> pq, int origin, int fence,
- int expectedModCount) {
+ int expectedModCount) {
this.pq = pq;
this.index = origin;
this.fence = fence;
@@ -843,8 +847,8 @@
public PriorityQueueSpliterator<E> trySplit() {
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
return (lo >= mid) ? null :
- new PriorityQueueSpliterator<E>(pq, lo, index = mid,
- expectedModCount);
+ new PriorityQueueSpliterator<>(pq, lo, index = mid,
+ expectedModCount);
}
@SuppressWarnings("unchecked")
diff --git a/ojluni/src/main/java/java/util/Queue.java b/ojluni/src/main/java/java/util/Queue.java
index b43c6c1..6520337 100644
--- a/ojluni/src/main/java/java/util/Queue.java
+++ b/ojluni/src/main/java/java/util/Queue.java
@@ -50,7 +50,6 @@
* implementations; in most implementations, insert operations cannot
* fail.
*
- * <p>
* <table BORDER CELLPADDING=3 CELLSPACING=1>
* <caption>Summary of Queue methods</caption>
* <tr>
@@ -128,17 +127,9 @@
* always well-defined for queues with the same elements but different
* ordering properties.
*
- * @see java.util.Collection
- * @see LinkedList
- * @see PriorityQueue
- * @see java.util.concurrent.LinkedBlockingQueue
- * @see java.util.concurrent.BlockingQueue
- * @see java.util.concurrent.ArrayBlockingQueue
- * @see java.util.concurrent.LinkedBlockingQueue
- * @see java.util.concurrent.PriorityBlockingQueue
* @since 1.5
* @author Doug Lea
- * @param <E> the type of elements held in this collection
+ * @param <E> the type of elements held in this queue
*/
public interface Queue<E> extends Collection<E> {
/**
diff --git a/ojluni/src/main/java/java/util/SplittableRandom.java b/ojluni/src/main/java/java/util/SplittableRandom.java
new file mode 100644
index 0000000..b2a07ec
--- /dev/null
+++ b/ojluni/src/main/java/java/util/SplittableRandom.java
@@ -0,0 +1,998 @@
+/*
+ * 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. 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.
+ */
+
+package java.util;
+
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+import java.util.stream.DoubleStream;
+import java.util.stream.IntStream;
+import java.util.stream.LongStream;
+import java.util.stream.StreamSupport;
+
+
+// TODO(streams): Include in openjdk_java_files.mk
+/**
+ * A generator of uniform pseudorandom values applicable for use in
+ * (among other contexts) isolated parallel computations that may
+ * generate subtasks. Class {@code SplittableRandom} supports methods for
+ * producing pseudorandom numbers of type {@code int}, {@code long},
+ * and {@code double} with similar usages as for class
+ * {@link java.util.Random} but differs in the following ways:
+ *
+ * <ul>
+ *
+ * <li>Series of generated values pass the DieHarder suite testing
+ * independence and uniformity properties of random number generators.
+ * (Most recently validated with <a
+ * href="http://www.phy.duke.edu/~rgb/General/dieharder.php"> version
+ * 3.31.1</a>.) These tests validate only the methods for certain
+ * types and ranges, but similar properties are expected to hold, at
+ * least approximately, for others as well. The <em>period</em>
+ * (length of any series of generated values before it repeats) is at
+ * least 2<sup>64</sup>.
+ *
+ * <li>Method {@link #split} constructs and returns a new
+ * SplittableRandom instance that shares no mutable state with the
+ * current instance. However, with very high probability, the
+ * values collectively generated by the two objects have the same
+ * statistical properties as if the same quantity of values were
+ * generated by a single thread using a single {@code
+ * SplittableRandom} object.
+ *
+ * <li>Instances of SplittableRandom are <em>not</em> thread-safe.
+ * They are designed to be split, not shared, across threads. For
+ * example, a {@link java.util.concurrent.ForkJoinTask
+ * fork/join-style} computation using random numbers might include a
+ * construction of the form {@code new
+ * Subtask(aSplittableRandom.split()).fork()}.
+ *
+ * <li>This class provides additional methods for generating random
+ * streams, that employ the above techniques when used in {@code
+ * stream.parallel()} mode.
+ *
+ * </ul>
+ *
+ * <p>Instances of {@code SplittableRandom} are not cryptographically
+ * secure. Consider instead using {@link java.security.SecureRandom}
+ * in security-sensitive applications. Additionally,
+ * default-constructed instances do not use a cryptographically random
+ * seed unless the {@linkplain System#getProperty system property}
+ * {@code java.util.secureRandomSeed} is set to {@code true}.
+ *
+ * @author Guy Steele
+ * @author Doug Lea
+ * @since 1.8
+ */
+public final class SplittableRandom {
+
+ /*
+ * Implementation Overview.
+ *
+ * This algorithm was inspired by the "DotMix" algorithm by
+ * Leiserson, Schardl, and Sukha "Deterministic Parallel
+ * Random-Number Generation for Dynamic-Multithreading Platforms",
+ * PPoPP 2012, as well as those in "Parallel random numbers: as
+ * easy as 1, 2, 3" by Salmon, Morae, Dror, and Shaw, SC 2011. It
+ * differs mainly in simplifying and cheapening operations.
+ *
+ * The primary update step (method nextSeed()) is to add a
+ * constant ("gamma") to the current (64 bit) seed, forming a
+ * simple sequence. The seed and the gamma values for any two
+ * SplittableRandom instances are highly likely to be different.
+ *
+ * Methods nextLong, nextInt, and derivatives do not return the
+ * sequence (seed) values, but instead a hash-like bit-mix of
+ * their bits, producing more independently distributed sequences.
+ * For nextLong, the mix64 function is based on David Stafford's
+ * (http://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html)
+ * "Mix13" variant of the "64-bit finalizer" function in Austin
+ * Appleby's MurmurHash3 algorithm (see
+ * http://code.google.com/p/smhasher/wiki/MurmurHash3). The mix32
+ * function is based on Stafford's Mix04 mix function, but returns
+ * the upper 32 bits cast as int.
+ *
+ * The split operation uses the current generator to form the seed
+ * and gamma for another SplittableRandom. To conservatively
+ * avoid potential correlations between seed and value generation,
+ * gamma selection (method mixGamma) uses different
+ * (Murmurhash3's) mix constants. To avoid potential weaknesses
+ * in bit-mixing transformations, we restrict gammas to odd values
+ * with at least 24 0-1 or 1-0 bit transitions. Rather than
+ * rejecting candidates with too few or too many bits set, method
+ * mixGamma flips some bits (which has the effect of mapping at
+ * most 4 to any given gamma value). This reduces the effective
+ * set of 64bit odd gamma values by about 2%, and serves as an
+ * automated screening for sequence constant selection that is
+ * left as an empirical decision in some other hashing and crypto
+ * algorithms.
+ *
+ * The resulting generator thus transforms a sequence in which
+ * (typically) many bits change on each step, with an inexpensive
+ * mixer with good (but less than cryptographically secure)
+ * avalanching.
+ *
+ * The default (no-argument) constructor, in essence, invokes
+ * split() for a common "defaultGen" SplittableRandom. Unlike
+ * other cases, this split must be performed in a thread-safe
+ * manner, so we use an AtomicLong to represent the seed rather
+ * than use an explicit SplittableRandom. To bootstrap the
+ * defaultGen, we start off using a seed based on current time
+ * unless the java.util.secureRandomSeed property is set. This
+ * serves as a slimmed-down (and insecure) variant of SecureRandom
+ * that also avoids stalls that may occur when using /dev/random.
+ *
+ * It is a relatively simple matter to apply the basic design here
+ * to use 128 bit seeds. However, emulating 128bit arithmetic and
+ * carrying around twice the state add more overhead than appears
+ * warranted for current usages.
+ *
+ * File organization: First the non-public methods that constitute
+ * the main algorithm, then the main public methods, followed by
+ * some custom spliterator classes needed for stream methods.
+ */
+
+ /**
+ * The golden ratio scaled to 64bits, used as the initial gamma
+ * value for (unsplit) SplittableRandoms.
+ */
+ private static final long GOLDEN_GAMMA = 0x9e3779b97f4a7c15L;
+
+ /**
+ * The least non-zero value returned by nextDouble(). This value
+ * is scaled by a random value of 53 bits to produce a result.
+ */
+ private static final double DOUBLE_UNIT = 0x1.0p-53; // 1.0 / (1L << 53);
+
+ /**
+ * The seed. Updated only via method nextSeed.
+ */
+ private long seed;
+
+ /**
+ * The step value.
+ */
+ private final long gamma;
+
+ /**
+ * Internal constructor used by all others except default constructor.
+ */
+ private SplittableRandom(long seed, long gamma) {
+ this.seed = seed;
+ this.gamma = gamma;
+ }
+
+ /**
+ * Computes Stafford variant 13 of 64bit mix function.
+ */
+ private static long mix64(long z) {
+ z = (z ^ (z >>> 30)) * 0xbf58476d1ce4e5b9L;
+ z = (z ^ (z >>> 27)) * 0x94d049bb133111ebL;
+ return z ^ (z >>> 31);
+ }
+
+ /**
+ * Returns the 32 high bits of Stafford variant 4 mix64 function as int.
+ */
+ private static int mix32(long z) {
+ z = (z ^ (z >>> 33)) * 0x62a9d9ed799705f5L;
+ return (int)(((z ^ (z >>> 28)) * 0xcb24d0a5c88c35b3L) >>> 32);
+ }
+
+ /**
+ * Returns the gamma value to use for a new split instance.
+ */
+ private static long mixGamma(long z) {
+ z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL; // MurmurHash3 mix constants
+ z = (z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L;
+ z = (z ^ (z >>> 33)) | 1L; // force to be odd
+ int n = Long.bitCount(z ^ (z >>> 1)); // ensure enough transitions
+ return (n < 24) ? z ^ 0xaaaaaaaaaaaaaaaaL : z;
+ }
+
+ /**
+ * Adds gamma to seed.
+ */
+ private long nextSeed() {
+ return seed += gamma;
+ }
+
+ // IllegalArgumentException messages
+ static final String BAD_BOUND = "bound must be positive";
+ static final String BAD_RANGE = "bound must be greater than origin";
+ static final String BAD_SIZE = "size must be non-negative";
+
+ /**
+ * The seed generator for default constructors.
+ */
+ private static final AtomicLong defaultGen
+ = new AtomicLong(mix64(System.currentTimeMillis()) ^
+ mix64(System.nanoTime()));
+
+ // at end of <clinit> to survive static initialization circularity
+ static {
+ if (java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<Boolean>() {
+ public Boolean run() {
+ return Boolean.getBoolean("java.util.secureRandomSeed");
+ }})) {
+ byte[] seedBytes = java.security.SecureRandom.getSeed(8);
+ long s = (long)seedBytes[0] & 0xffL;
+ for (int i = 1; i < 8; ++i)
+ s = (s << 8) | ((long)seedBytes[i] & 0xffL);
+ defaultGen.set(s);
+ }
+ }
+
+ /*
+ * Internal versions of nextX methods used by streams, as well as
+ * the public nextX(origin, bound) methods. These exist mainly to
+ * avoid the need for multiple versions of stream spliterators
+ * across the different exported forms of streams.
+ */
+
+ /**
+ * The form of nextLong used by LongStream Spliterators. If
+ * origin is greater than bound, acts as unbounded form of
+ * nextLong, else as bounded form.
+ *
+ * @param origin the least value, unless greater than bound
+ * @param bound the upper bound (exclusive), must not equal origin
+ * @return a pseudorandom value
+ */
+ final long internalNextLong(long origin, long bound) {
+ /*
+ * Four Cases:
+ *
+ * 1. If the arguments indicate unbounded form, act as
+ * nextLong().
+ *
+ * 2. If the range is an exact power of two, apply the
+ * associated bit mask.
+ *
+ * 3. If the range is positive, loop to avoid potential bias
+ * when the implicit nextLong() bound (2<sup>64</sup>) is not
+ * evenly divisible by the range. The loop rejects candidates
+ * computed from otherwise over-represented values. The
+ * expected number of iterations under an ideal generator
+ * varies from 1 to 2, depending on the bound. The loop itself
+ * takes an unlovable form. Because the first candidate is
+ * already available, we need a break-in-the-middle
+ * construction, which is concisely but cryptically performed
+ * within the while-condition of a body-less for loop.
+ *
+ * 4. Otherwise, the range cannot be represented as a positive
+ * long. The loop repeatedly generates unbounded longs until
+ * obtaining a candidate meeting constraints (with an expected
+ * number of iterations of less than two).
+ */
+
+ long r = mix64(nextSeed());
+ if (origin < bound) {
+ long n = bound - origin, m = n - 1;
+ if ((n & m) == 0L) // power of two
+ r = (r & m) + origin;
+ else if (n > 0L) { // reject over-represented candidates
+ for (long u = r >>> 1; // ensure nonnegative
+ u + m - (r = u % n) < 0L; // rejection check
+ u = mix64(nextSeed()) >>> 1) // retry
+ ;
+ r += origin;
+ }
+ else { // range not representable as long
+ while (r < origin || r >= bound)
+ r = mix64(nextSeed());
+ }
+ }
+ return r;
+ }
+
+ /**
+ * The form of nextInt used by IntStream Spliterators.
+ * Exactly the same as long version, except for types.
+ *
+ * @param origin the least value, unless greater than bound
+ * @param bound the upper bound (exclusive), must not equal origin
+ * @return a pseudorandom value
+ */
+ final int internalNextInt(int origin, int bound) {
+ int r = mix32(nextSeed());
+ if (origin < bound) {
+ int n = bound - origin, m = n - 1;
+ if ((n & m) == 0)
+ r = (r & m) + origin;
+ else if (n > 0) {
+ for (int u = r >>> 1;
+ u + m - (r = u % n) < 0;
+ u = mix32(nextSeed()) >>> 1)
+ ;
+ r += origin;
+ }
+ else {
+ while (r < origin || r >= bound)
+ r = mix32(nextSeed());
+ }
+ }
+ return r;
+ }
+
+ /**
+ * The form of nextDouble used by DoubleStream Spliterators.
+ *
+ * @param origin the least value, unless greater than bound
+ * @param bound the upper bound (exclusive), must not equal origin
+ * @return a pseudorandom value
+ */
+ final double internalNextDouble(double origin, double bound) {
+ double r = (nextLong() >>> 11) * DOUBLE_UNIT;
+ if (origin < bound) {
+ r = r * (bound - origin) + origin;
+ if (r >= bound) // correct for rounding
+ r = Double.longBitsToDouble(Double.doubleToLongBits(bound) - 1);
+ }
+ return r;
+ }
+
+ /* ---------------- public methods ---------------- */
+
+ /**
+ * Creates a new SplittableRandom instance using the specified
+ * initial seed. SplittableRandom instances created with the same
+ * seed in the same program generate identical sequences of values.
+ *
+ * @param seed the initial seed
+ */
+ public SplittableRandom(long seed) {
+ this(seed, GOLDEN_GAMMA);
+ }
+
+ /**
+ * Creates a new SplittableRandom instance that is likely to
+ * generate sequences of values that are statistically independent
+ * of those of any other instances in the current program; and
+ * may, and typically does, vary across program invocations.
+ */
+ public SplittableRandom() { // emulate defaultGen.split()
+ long s = defaultGen.getAndAdd(2 * GOLDEN_GAMMA);
+ this.seed = mix64(s);
+ this.gamma = mixGamma(s + GOLDEN_GAMMA);
+ }
+
+ /**
+ * Constructs and returns a new SplittableRandom instance that
+ * shares no mutable state with this instance. However, with very
+ * high probability, the set of values collectively generated by
+ * the two objects has the same statistical properties as if the
+ * same quantity of values were generated by a single thread using
+ * a single SplittableRandom object. Either or both of the two
+ * objects may be further split using the {@code split()} method,
+ * and the same expected statistical properties apply to the
+ * entire set of generators constructed by such recursive
+ * splitting.
+ *
+ * @return the new SplittableRandom instance
+ */
+ public SplittableRandom split() {
+ return new SplittableRandom(nextLong(), mixGamma(nextSeed()));
+ }
+
+ /**
+ * Returns a pseudorandom {@code int} value.
+ *
+ * @return a pseudorandom {@code int} value
+ */
+ public int nextInt() {
+ return mix32(nextSeed());
+ }
+
+ /**
+ * Returns a pseudorandom {@code int} value between zero (inclusive)
+ * and the specified bound (exclusive).
+ *
+ * @param bound the upper bound (exclusive). Must be positive.
+ * @return a pseudorandom {@code int} value between zero
+ * (inclusive) and the bound (exclusive)
+ * @throws IllegalArgumentException if {@code bound} is not positive
+ */
+ public int nextInt(int bound) {
+ if (bound <= 0)
+ throw new IllegalArgumentException(BAD_BOUND);
+ // Specialize internalNextInt for origin 0
+ int r = mix32(nextSeed());
+ int m = bound - 1;
+ if ((bound & m) == 0) // power of two
+ r &= m;
+ else { // reject over-represented candidates
+ for (int u = r >>> 1;
+ u + m - (r = u % bound) < 0;
+ u = mix32(nextSeed()) >>> 1)
+ ;
+ }
+ return r;
+ }
+
+ /**
+ * Returns a pseudorandom {@code int} value between the specified
+ * origin (inclusive) and the specified bound (exclusive).
+ *
+ * @param origin the least value returned
+ * @param bound the upper bound (exclusive)
+ * @return a pseudorandom {@code int} value between the origin
+ * (inclusive) and the bound (exclusive)
+ * @throws IllegalArgumentException if {@code origin} is greater than
+ * or equal to {@code bound}
+ */
+ public int nextInt(int origin, int bound) {
+ if (origin >= bound)
+ throw new IllegalArgumentException(BAD_RANGE);
+ return internalNextInt(origin, bound);
+ }
+
+ /**
+ * Returns a pseudorandom {@code long} value.
+ *
+ * @return a pseudorandom {@code long} value
+ */
+ public long nextLong() {
+ return mix64(nextSeed());
+ }
+
+ /**
+ * Returns a pseudorandom {@code long} value between zero (inclusive)
+ * and the specified bound (exclusive).
+ *
+ * @param bound the upper bound (exclusive). Must be positive.
+ * @return a pseudorandom {@code long} value between zero
+ * (inclusive) and the bound (exclusive)
+ * @throws IllegalArgumentException if {@code bound} is not positive
+ */
+ public long nextLong(long bound) {
+ if (bound <= 0)
+ throw new IllegalArgumentException(BAD_BOUND);
+ // Specialize internalNextLong for origin 0
+ long r = mix64(nextSeed());
+ long m = bound - 1;
+ if ((bound & m) == 0L) // power of two
+ r &= m;
+ else { // reject over-represented candidates
+ for (long u = r >>> 1;
+ u + m - (r = u % bound) < 0L;
+ u = mix64(nextSeed()) >>> 1)
+ ;
+ }
+ return r;
+ }
+
+ /**
+ * Returns a pseudorandom {@code long} value between the specified
+ * origin (inclusive) and the specified bound (exclusive).
+ *
+ * @param origin the least value returned
+ * @param bound the upper bound (exclusive)
+ * @return a pseudorandom {@code long} value between the origin
+ * (inclusive) and the bound (exclusive)
+ * @throws IllegalArgumentException if {@code origin} is greater than
+ * or equal to {@code bound}
+ */
+ public long nextLong(long origin, long bound) {
+ if (origin >= bound)
+ throw new IllegalArgumentException(BAD_RANGE);
+ return internalNextLong(origin, bound);
+ }
+
+ /**
+ * Returns a pseudorandom {@code double} value between zero
+ * (inclusive) and one (exclusive).
+ *
+ * @return a pseudorandom {@code double} value between zero
+ * (inclusive) and one (exclusive)
+ */
+ public double nextDouble() {
+ return (mix64(nextSeed()) >>> 11) * DOUBLE_UNIT;
+ }
+
+ /**
+ * Returns a pseudorandom {@code double} value between 0.0
+ * (inclusive) and the specified bound (exclusive).
+ *
+ * @param bound the upper bound (exclusive). Must be positive.
+ * @return a pseudorandom {@code double} value between zero
+ * (inclusive) and the bound (exclusive)
+ * @throws IllegalArgumentException if {@code bound} is not positive
+ */
+ public double nextDouble(double bound) {
+ if (!(bound > 0.0))
+ throw new IllegalArgumentException(BAD_BOUND);
+ double result = (mix64(nextSeed()) >>> 11) * DOUBLE_UNIT * bound;
+ return (result < bound) ? result : // correct for rounding
+ Double.longBitsToDouble(Double.doubleToLongBits(bound) - 1);
+ }
+
+ /**
+ * Returns a pseudorandom {@code double} value between the specified
+ * origin (inclusive) and bound (exclusive).
+ *
+ * @param origin the least value returned
+ * @param bound the upper bound (exclusive)
+ * @return a pseudorandom {@code double} value between the origin
+ * (inclusive) and the bound (exclusive)
+ * @throws IllegalArgumentException if {@code origin} is greater than
+ * or equal to {@code bound}
+ */
+ public double nextDouble(double origin, double bound) {
+ if (!(origin < bound))
+ throw new IllegalArgumentException(BAD_RANGE);
+ return internalNextDouble(origin, bound);
+ }
+
+ /**
+ * Returns a pseudorandom {@code boolean} value.
+ *
+ * @return a pseudorandom {@code boolean} value
+ */
+ public boolean nextBoolean() {
+ return mix32(nextSeed()) < 0;
+ }
+
+ // stream methods, coded in a way intended to better isolate for
+ // maintenance purposes the small differences across forms.
+
+ /**
+ * Returns a stream producing the given {@code streamSize} number
+ * of pseudorandom {@code int} values from this generator and/or
+ * one split from it.
+ *
+ * @param streamSize the number of values to generate
+ * @return a stream of pseudorandom {@code int} values
+ * @throws IllegalArgumentException if {@code streamSize} is
+ * less than zero
+ */
+ public IntStream ints(long streamSize) {
+ if (streamSize < 0L)
+ throw new IllegalArgumentException(BAD_SIZE);
+ return StreamSupport.intStream
+ (new RandomIntsSpliterator
+ (this, 0L, streamSize, Integer.MAX_VALUE, 0),
+ false);
+ }
+
+ /**
+ * Returns an effectively unlimited stream of pseudorandom {@code int}
+ * values from this generator and/or one split from it.
+ *
+ * @implNote This method is implemented to be equivalent to {@code
+ * ints(Long.MAX_VALUE)}.
+ *
+ * @return a stream of pseudorandom {@code int} values
+ */
+ public IntStream ints() {
+ return StreamSupport.intStream
+ (new RandomIntsSpliterator
+ (this, 0L, Long.MAX_VALUE, Integer.MAX_VALUE, 0),
+ false);
+ }
+
+ /**
+ * Returns a stream producing the given {@code streamSize} number
+ * of pseudorandom {@code int} values from this generator and/or one split
+ * from it; each value conforms to the given origin (inclusive) and bound
+ * (exclusive).
+ *
+ * @param streamSize the number of values to generate
+ * @param randomNumberOrigin the origin (inclusive) of each random value
+ * @param randomNumberBound the bound (exclusive) of each random value
+ * @return a stream of pseudorandom {@code int} values,
+ * each with the given origin (inclusive) and bound (exclusive)
+ * @throws IllegalArgumentException if {@code streamSize} is
+ * less than zero, or {@code randomNumberOrigin}
+ * is greater than or equal to {@code randomNumberBound}
+ */
+ public IntStream ints(long streamSize, int randomNumberOrigin,
+ int randomNumberBound) {
+ if (streamSize < 0L)
+ throw new IllegalArgumentException(BAD_SIZE);
+ if (randomNumberOrigin >= randomNumberBound)
+ throw new IllegalArgumentException(BAD_RANGE);
+ return StreamSupport.intStream
+ (new RandomIntsSpliterator
+ (this, 0L, streamSize, randomNumberOrigin, randomNumberBound),
+ false);
+ }
+
+ /**
+ * Returns an effectively unlimited stream of pseudorandom {@code
+ * int} values from this generator and/or one split from it; each value
+ * conforms to the given origin (inclusive) and bound (exclusive).
+ *
+ * @implNote This method is implemented to be equivalent to {@code
+ * ints(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}.
+ *
+ * @param randomNumberOrigin the origin (inclusive) of each random value
+ * @param randomNumberBound the bound (exclusive) of each random value
+ * @return a stream of pseudorandom {@code int} values,
+ * each with the given origin (inclusive) and bound (exclusive)
+ * @throws IllegalArgumentException if {@code randomNumberOrigin}
+ * is greater than or equal to {@code randomNumberBound}
+ */
+ public IntStream ints(int randomNumberOrigin, int randomNumberBound) {
+ if (randomNumberOrigin >= randomNumberBound)
+ throw new IllegalArgumentException(BAD_RANGE);
+ return StreamSupport.intStream
+ (new RandomIntsSpliterator
+ (this, 0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound),
+ false);
+ }
+
+ /**
+ * Returns a stream producing the given {@code streamSize} number
+ * of pseudorandom {@code long} values from this generator and/or
+ * one split from it.
+ *
+ * @param streamSize the number of values to generate
+ * @return a stream of pseudorandom {@code long} values
+ * @throws IllegalArgumentException if {@code streamSize} is
+ * less than zero
+ */
+ public LongStream longs(long streamSize) {
+ if (streamSize < 0L)
+ throw new IllegalArgumentException(BAD_SIZE);
+ return StreamSupport.longStream
+ (new RandomLongsSpliterator
+ (this, 0L, streamSize, Long.MAX_VALUE, 0L),
+ false);
+ }
+
+ /**
+ * Returns an effectively unlimited stream of pseudorandom {@code
+ * long} values from this generator and/or one split from it.
+ *
+ * @implNote This method is implemented to be equivalent to {@code
+ * longs(Long.MAX_VALUE)}.
+ *
+ * @return a stream of pseudorandom {@code long} values
+ */
+ public LongStream longs() {
+ return StreamSupport.longStream
+ (new RandomLongsSpliterator
+ (this, 0L, Long.MAX_VALUE, Long.MAX_VALUE, 0L),
+ false);
+ }
+
+ /**
+ * Returns a stream producing the given {@code streamSize} number of
+ * pseudorandom {@code long} values from this generator and/or one split
+ * from it; each value conforms to the given origin (inclusive) and bound
+ * (exclusive).
+ *
+ * @param streamSize the number of values to generate
+ * @param randomNumberOrigin the origin (inclusive) of each random value
+ * @param randomNumberBound the bound (exclusive) of each random value
+ * @return a stream of pseudorandom {@code long} values,
+ * each with the given origin (inclusive) and bound (exclusive)
+ * @throws IllegalArgumentException if {@code streamSize} is
+ * less than zero, or {@code randomNumberOrigin}
+ * is greater than or equal to {@code randomNumberBound}
+ */
+ public LongStream longs(long streamSize, long randomNumberOrigin,
+ long randomNumberBound) {
+ if (streamSize < 0L)
+ throw new IllegalArgumentException(BAD_SIZE);
+ if (randomNumberOrigin >= randomNumberBound)
+ throw new IllegalArgumentException(BAD_RANGE);
+ return StreamSupport.longStream
+ (new RandomLongsSpliterator
+ (this, 0L, streamSize, randomNumberOrigin, randomNumberBound),
+ false);
+ }
+
+ /**
+ * Returns an effectively unlimited stream of pseudorandom {@code
+ * long} values from this generator and/or one split from it; each value
+ * conforms to the given origin (inclusive) and bound (exclusive).
+ *
+ * @implNote This method is implemented to be equivalent to {@code
+ * longs(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}.
+ *
+ * @param randomNumberOrigin the origin (inclusive) of each random value
+ * @param randomNumberBound the bound (exclusive) of each random value
+ * @return a stream of pseudorandom {@code long} values,
+ * each with the given origin (inclusive) and bound (exclusive)
+ * @throws IllegalArgumentException if {@code randomNumberOrigin}
+ * is greater than or equal to {@code randomNumberBound}
+ */
+ public LongStream longs(long randomNumberOrigin, long randomNumberBound) {
+ if (randomNumberOrigin >= randomNumberBound)
+ throw new IllegalArgumentException(BAD_RANGE);
+ return StreamSupport.longStream
+ (new RandomLongsSpliterator
+ (this, 0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound),
+ false);
+ }
+
+ /**
+ * Returns a stream producing the given {@code streamSize} number of
+ * pseudorandom {@code double} values from this generator and/or one split
+ * from it; each value is between zero (inclusive) and one (exclusive).
+ *
+ * @param streamSize the number of values to generate
+ * @return a stream of {@code double} values
+ * @throws IllegalArgumentException if {@code streamSize} is
+ * less than zero
+ */
+ public DoubleStream doubles(long streamSize) {
+ if (streamSize < 0L)
+ throw new IllegalArgumentException(BAD_SIZE);
+ return StreamSupport.doubleStream
+ (new RandomDoublesSpliterator
+ (this, 0L, streamSize, Double.MAX_VALUE, 0.0),
+ false);
+ }
+
+ /**
+ * Returns an effectively unlimited stream of pseudorandom {@code
+ * double} values from this generator and/or one split from it; each value
+ * is between zero (inclusive) and one (exclusive).
+ *
+ * @implNote This method is implemented to be equivalent to {@code
+ * doubles(Long.MAX_VALUE)}.
+ *
+ * @return a stream of pseudorandom {@code double} values
+ */
+ public DoubleStream doubles() {
+ return StreamSupport.doubleStream
+ (new RandomDoublesSpliterator
+ (this, 0L, Long.MAX_VALUE, Double.MAX_VALUE, 0.0),
+ false);
+ }
+
+ /**
+ * Returns a stream producing the given {@code streamSize} number of
+ * pseudorandom {@code double} values from this generator and/or one split
+ * from it; each value conforms to the given origin (inclusive) and bound
+ * (exclusive).
+ *
+ * @param streamSize the number of values to generate
+ * @param randomNumberOrigin the origin (inclusive) of each random value
+ * @param randomNumberBound the bound (exclusive) of each random value
+ * @return a stream of pseudorandom {@code double} values,
+ * each with the given origin (inclusive) and bound (exclusive)
+ * @throws IllegalArgumentException if {@code streamSize} is
+ * less than zero
+ * @throws IllegalArgumentException if {@code randomNumberOrigin}
+ * is greater than or equal to {@code randomNumberBound}
+ */
+ public DoubleStream doubles(long streamSize, double randomNumberOrigin,
+ double randomNumberBound) {
+ if (streamSize < 0L)
+ throw new IllegalArgumentException(BAD_SIZE);
+ if (!(randomNumberOrigin < randomNumberBound))
+ throw new IllegalArgumentException(BAD_RANGE);
+ return StreamSupport.doubleStream
+ (new RandomDoublesSpliterator
+ (this, 0L, streamSize, randomNumberOrigin, randomNumberBound),
+ false);
+ }
+
+ /**
+ * Returns an effectively unlimited stream of pseudorandom {@code
+ * double} values from this generator and/or one split from it; each value
+ * conforms to the given origin (inclusive) and bound (exclusive).
+ *
+ * @implNote This method is implemented to be equivalent to {@code
+ * doubles(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}.
+ *
+ * @param randomNumberOrigin the origin (inclusive) of each random value
+ * @param randomNumberBound the bound (exclusive) of each random value
+ * @return a stream of pseudorandom {@code double} values,
+ * each with the given origin (inclusive) and bound (exclusive)
+ * @throws IllegalArgumentException if {@code randomNumberOrigin}
+ * is greater than or equal to {@code randomNumberBound}
+ */
+ public DoubleStream doubles(double randomNumberOrigin, double randomNumberBound) {
+ if (!(randomNumberOrigin < randomNumberBound))
+ throw new IllegalArgumentException(BAD_RANGE);
+ return StreamSupport.doubleStream
+ (new RandomDoublesSpliterator
+ (this, 0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound),
+ false);
+ }
+
+ /**
+ * Spliterator for int streams. We multiplex the four int
+ * versions into one class by treating a bound less than origin as
+ * unbounded, and also by treating "infinite" as equivalent to
+ * Long.MAX_VALUE. For splits, it uses the standard divide-by-two
+ * approach. The long and double versions of this class are
+ * identical except for types.
+ */
+ private static final class RandomIntsSpliterator
+ implements Spliterator.OfInt {
+ final SplittableRandom rng;
+ long index;
+ final long fence;
+ final int origin;
+ final int bound;
+ RandomIntsSpliterator(SplittableRandom rng, long index, long fence,
+ int origin, int bound) {
+ this.rng = rng; this.index = index; this.fence = fence;
+ this.origin = origin; this.bound = bound;
+ }
+
+ public RandomIntsSpliterator trySplit() {
+ long i = index, m = (i + fence) >>> 1;
+ return (m <= i) ? null :
+ new RandomIntsSpliterator(rng.split(), i, index = m, origin, bound);
+ }
+
+ public long estimateSize() {
+ return fence - index;
+ }
+
+ public int characteristics() {
+ return (Spliterator.SIZED | Spliterator.SUBSIZED |
+ Spliterator.NONNULL | Spliterator.IMMUTABLE);
+ }
+
+ public boolean tryAdvance(IntConsumer consumer) {
+ if (consumer == null) throw new NullPointerException();
+ long i = index, f = fence;
+ if (i < f) {
+ consumer.accept(rng.internalNextInt(origin, bound));
+ index = i + 1;
+ return true;
+ }
+ return false;
+ }
+
+ public void forEachRemaining(IntConsumer consumer) {
+ if (consumer == null) throw new NullPointerException();
+ long i = index, f = fence;
+ if (i < f) {
+ index = f;
+ SplittableRandom r = rng;
+ int o = origin, b = bound;
+ do {
+ consumer.accept(r.internalNextInt(o, b));
+ } while (++i < f);
+ }
+ }
+ }
+
+ /**
+ * Spliterator for long streams.
+ */
+ private static final class RandomLongsSpliterator
+ implements Spliterator.OfLong {
+ final SplittableRandom rng;
+ long index;
+ final long fence;
+ final long origin;
+ final long bound;
+ RandomLongsSpliterator(SplittableRandom rng, long index, long fence,
+ long origin, long bound) {
+ this.rng = rng; this.index = index; this.fence = fence;
+ this.origin = origin; this.bound = bound;
+ }
+
+ public RandomLongsSpliterator trySplit() {
+ long i = index, m = (i + fence) >>> 1;
+ return (m <= i) ? null :
+ new RandomLongsSpliterator(rng.split(), i, index = m, origin, bound);
+ }
+
+ public long estimateSize() {
+ return fence - index;
+ }
+
+ public int characteristics() {
+ return (Spliterator.SIZED | Spliterator.SUBSIZED |
+ Spliterator.NONNULL | Spliterator.IMMUTABLE);
+ }
+
+ public boolean tryAdvance(LongConsumer consumer) {
+ if (consumer == null) throw new NullPointerException();
+ long i = index, f = fence;
+ if (i < f) {
+ consumer.accept(rng.internalNextLong(origin, bound));
+ index = i + 1;
+ return true;
+ }
+ return false;
+ }
+
+ public void forEachRemaining(LongConsumer consumer) {
+ if (consumer == null) throw new NullPointerException();
+ long i = index, f = fence;
+ if (i < f) {
+ index = f;
+ SplittableRandom r = rng;
+ long o = origin, b = bound;
+ do {
+ consumer.accept(r.internalNextLong(o, b));
+ } while (++i < f);
+ }
+ }
+
+ }
+
+ /**
+ * Spliterator for double streams.
+ */
+ private static final class RandomDoublesSpliterator
+ implements Spliterator.OfDouble {
+ final SplittableRandom rng;
+ long index;
+ final long fence;
+ final double origin;
+ final double bound;
+ RandomDoublesSpliterator(SplittableRandom rng, long index, long fence,
+ double origin, double bound) {
+ this.rng = rng; this.index = index; this.fence = fence;
+ this.origin = origin; this.bound = bound;
+ }
+
+ public RandomDoublesSpliterator trySplit() {
+ long i = index, m = (i + fence) >>> 1;
+ return (m <= i) ? null :
+ new RandomDoublesSpliterator(rng.split(), i, index = m, origin, bound);
+ }
+
+ public long estimateSize() {
+ return fence - index;
+ }
+
+ public int characteristics() {
+ return (Spliterator.SIZED | Spliterator.SUBSIZED |
+ Spliterator.NONNULL | Spliterator.IMMUTABLE);
+ }
+
+ public boolean tryAdvance(DoubleConsumer consumer) {
+ if (consumer == null) throw new NullPointerException();
+ long i = index, f = fence;
+ if (i < f) {
+ consumer.accept(rng.internalNextDouble(origin, bound));
+ index = i + 1;
+ return true;
+ }
+ return false;
+ }
+
+ public void forEachRemaining(DoubleConsumer consumer) {
+ if (consumer == null) throw new NullPointerException();
+ long i = index, f = fence;
+ if (i < f) {
+ index = f;
+ SplittableRandom r = rng;
+ double o = origin, b = bound;
+ do {
+ consumer.accept(r.internalNextDouble(o, b));
+ } while (++i < f);
+ }
+ }
+ }
+
+}