/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * This file is available under and governed by the GNU General Public * License version 2 only, as published by the Free Software Foundation. * However, the following notice accompanied the original version of this * file: * * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at * http://creativecommons.org/publicdomain/zero/1.0/ */ import static java.util.concurrent.TimeUnit.MILLISECONDS; 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 java.util.concurrent.Callable; import junit.framework.Test; import junit.framework.TestSuite; /** * Tests for ForkJoinPool and corresponding ForkJoinTask additions. */ public class ForkJoinPool19Test extends JSR166TestCase { public static void main(String[] args) { main(suite(), args); } public static Test suite() { return new TestSuite(ForkJoinPool19Test.class); } /** * SetParallelism sets reported parallellism and returns previous value */ public void testSetParallelism() { final ForkJoinPool p = new ForkJoinPool(2); assertEquals(2, p.getParallelism()); assertEquals(2, p.setParallelism(3)); assertEquals(3, p.setParallelism(2)); p.shutdown(); } /** * SetParallelism throws exception if argument out of bounds */ public void testSetParallelismBadArgs() { final ForkJoinPool p = new ForkJoinPool(2); try { p.setParallelism(0); shouldThrow(); } catch (Exception success) { } try { p.setParallelism(Integer.MAX_VALUE); shouldThrow(); } catch (Exception success) { } assertEquals(2, p.getParallelism()); p.shutdown(); } /* * Some test methods adapted from RecursiveAction */ private static ForkJoinPool mainPool() { return new ForkJoinPool(); } private void testInvokeOnPool(ForkJoinPool pool, RecursiveAction a) { try (PoolCleaner cleaner = cleaner(pool)) { checkNotDone(a); assertNull(pool.invoke(a)); checkCompletedNormally(a); } } 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(randomTimeout(), randomTimeUnit()); shouldThrow(); } catch (InterruptedException success) { } catch (Throwable fail) { threadUnexpectedException(fail); } } try { a.get(randomExpiredTimeout(), randomTimeUnit()); 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)); Object v1 = null, v2 = null; try { v1 = a.get(); v2 = a.get(randomTimeout(), randomTimeUnit()); } catch (Throwable fail) { threadUnexpectedException(fail); } assertNull(v1); assertNull(v2); } 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(randomTimeout(), randomTimeUnit()); 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(randomTimeout(), randomTimeUnit()); 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) { try { FailingFibAction f1 = new FailingFibAction(n - 1); FailingFibAction f2 = new FailingFibAction(n - 2); invokeAll(f1, f2); result = f1.result + f2.result; return; } catch (CancellationException fallthrough) { } } throw new FJException(); } } /** * lazySubmit submits a task that is not executed until new * workers are created or it is explicitly joined by a worker. */ @SuppressWarnings("removal") public void testLazySubmit() { ForkJoinPool p; try { p = new ForkJoinPool(); } catch (java.security.AccessControlException e) { return; } FibAction f = new FibAction(8); RecursiveAction j = new RecursiveAction() { protected void compute() { f.join(); }}; RecursiveAction a = new CheckedRecursiveAction() { protected void realCompute() { p.invoke(new FibAction(8)); p.lazySubmit(f); p.invoke(new FibAction(8)); p.invoke(j); assertEquals(21, f.result); checkCompletedNormally(f); }}; testInvokeOnPool(p, 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); } /** * timed quietlyJoinUninterruptibly of a forked task succeeds in * the presence of interrupts */ public void testTimedQuietlyJoinUninterruptiblyInterrupts() { RecursiveAction a = new CheckedRecursiveAction() { protected void realCompute() { FibAction f; final Thread currentThread = Thread.currentThread(); // test quietlyJoin() f = new FibAction(8); assertSame(f, f.fork()); currentThread.interrupt(); f.quietlyJoinUninterruptibly(LONG_DELAY_MS, MILLISECONDS); Thread.interrupted(); assertEquals(21, f.result); checkCompletedNormally(f); f = new FibAction(8); f.cancel(true); assertSame(f, f.fork()); currentThread.interrupt(); f.quietlyJoinUninterruptibly(LONG_DELAY_MS, MILLISECONDS); Thread.interrupted(); checkCancelled(f); f = new FibAction(8); f.completeExceptionally(new FJException()); assertSame(f, f.fork()); currentThread.interrupt(); f.quietlyJoinUninterruptibly(LONG_DELAY_MS, MILLISECONDS); Thread.interrupted(); checkCompletedAbnormally(f, f.getException()); }}; checkInvoke(a); a.reinitialize(); checkInvoke(a); } /** * timed quietlyJoin throws IE in the presence of interrupts */ public void testTimedQuietlyJoinInterrupts() { RecursiveAction a = new CheckedRecursiveAction() { protected void realCompute() { FibAction f; final Thread currentThread = Thread.currentThread(); f = new FibAction(8); assertSame(f, f.fork()); currentThread.interrupt(); try { f.quietlyJoin(LONG_DELAY_MS, MILLISECONDS); } catch (InterruptedException success) { } Thread.interrupted(); f.quietlyJoin(); f = new FibAction(8); f.cancel(true); assertSame(f, f.fork()); currentThread.interrupt(); try { f.quietlyJoin(LONG_DELAY_MS, MILLISECONDS); } catch (InterruptedException success) { } f.quietlyJoin(); checkCancelled(f); Thread.interrupted(); }}; checkInvoke(a); a.reinitialize(); checkInvoke(a); } /** * timed quietlyJoin of a forked task returns when task completes */ public void testForkTimedQuietlyJoin() { RecursiveAction a = new CheckedRecursiveAction() { protected void realCompute() throws Exception { FibAction f = new FibAction(8); assertSame(f, f.fork()); assertTrue(f.quietlyJoin(LONG_DELAY_MS, MILLISECONDS)); assertEquals(21, f.result); checkCompletedNormally(f); }}; checkInvoke(a); } /** * timed quietlyJoin with null time unit throws NPE */ public void testForkTimedQuietlyJoinNPE() { RecursiveAction a = new CheckedRecursiveAction() { protected void realCompute() throws Exception { FibAction f = new FibAction(8); assertSame(f, f.fork()); try { f.quietlyJoin(randomTimeout(), null); shouldThrow(); } catch (NullPointerException success) {} }}; checkInvoke(a); } /** * quietlyInvoke task returns when task completes abnormally */ public void testAbnormalTimedQuietlyJoin() { RecursiveAction a = new CheckedRecursiveAction() { protected void realCompute() throws Exception { FailingFibAction f = new FailingFibAction(8); assertSame(f, f.fork()); assertTrue(f.quietlyJoin(LONG_DELAY_MS, MILLISECONDS)); assertTrue(f.getException() instanceof FJException); checkCompletedAbnormally(f, f.getException()); }}; checkInvoke(a); } /** * timed quietlyJoinUninterruptibly of a forked task returns when task completes */ public void testForkTimedQuietlyJoinUninterruptibly() { RecursiveAction a = new CheckedRecursiveAction() { protected void realCompute() throws Exception { FibAction f = new FibAction(8); assertSame(f, f.fork()); assertTrue(f.quietlyJoinUninterruptibly(LONG_DELAY_MS, MILLISECONDS)); assertEquals(21, f.result); checkCompletedNormally(f); }}; checkInvoke(a); } /** * timed quietlyJoinUninterruptibly with null time unit throws NPE */ public void testForkTimedQuietlyJoinUninterruptiblyNPE() { RecursiveAction a = new CheckedRecursiveAction() { protected void realCompute() throws Exception { FibAction f = new FibAction(8); assertSame(f, f.fork()); try { f.quietlyJoinUninterruptibly(randomTimeout(), null); shouldThrow(); } catch (NullPointerException success) {} }}; checkInvoke(a); } /** * quietlyInvoke task returns when task completes abnormally */ public void testAbnormalTimedQuietlyJoinUninterruptibly() { RecursiveAction a = new CheckedRecursiveAction() { protected void realCompute() { FailingFibAction f = new FailingFibAction(8); assertSame(f, f.fork()); assertTrue(f.quietlyJoinUninterruptibly(LONG_DELAY_MS, MILLISECONDS)); assertTrue(f.getException() instanceof FJException); checkCompletedAbnormally(f, f.getException()); }}; checkInvoke(a); } /** * adaptInterruptible(callable).toString() contains toString of wrapped task */ public void testAdaptInterruptible_Callable_toString() { if (testImplementationDetails) { Callable c = () -> ""; ForkJoinTask task = ForkJoinTask.adaptInterruptible(c); assertEquals( identityString(task) + "[Wrapped task = " + c.toString() + "]", task.toString()); } } /** * Implicitly closing a new pool using try-with-resources terminates it */ public void testClose() { Thread t = newStartedThread(new CheckedRunnable() { public void realRun() throws InterruptedException { FibAction f = new FibAction(1); ForkJoinPool pool = null; try (ForkJoinPool p = new ForkJoinPool()) { pool = p; p.execute(f); } assertTrue(pool != null && pool.isTerminated()); f.join(); assertEquals(1, f.result); }}); awaitTermination(t); } /** * Explicitly closing a new pool terminates it */ public void testClose2() { Thread t = newStartedThread(new CheckedRunnable() { public void realRun() throws InterruptedException { ForkJoinPool pool = new ForkJoinPool(); FibAction f = new FibAction(1); pool.execute(f); pool.close(); assertTrue(pool.isTerminated()); f.join(); assertEquals(1, f.result); }}); awaitTermination(t); } /** * Explicitly closing a shutdown pool awaits termination */ public void testClose3() { Thread t = newStartedThread(new CheckedRunnable() { public void realRun() throws InterruptedException { ForkJoinPool pool = new ForkJoinPool(); FibAction f = new FibAction(1); pool.execute(f); pool.shutdown(); pool.close(); assertTrue(pool.isTerminated()); f.join(); assertEquals(1, f.result); }}); awaitTermination(t); } /** * Implicitly closing common pool using try-with-resources has no effect. */ public void testCloseCommonPool() { String prop = System.getProperty( "java.util.concurrent.ForkJoinPool.common.parallelism"); boolean nothreads = "0".equals(prop); Thread t = newStartedThread(new CheckedRunnable() { public void realRun() throws InterruptedException { ForkJoinTask f = new FibAction(8); ForkJoinPool pool; try (ForkJoinPool p = pool = ForkJoinPool.commonPool()) { p.execute(f); } assertFalse(pool.isShutdown()); assertFalse(pool.isTerminating()); assertFalse(pool.isTerminated()); if (!nothreads) { f.join(); checkCompletedNormally(f); } }}); awaitTermination(t); } }