|  | /* | 
|  | * 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.SECONDS; | 
|  |  | 
|  | import java.util.Arrays; | 
|  | import java.util.HashSet; | 
|  | import java.util.concurrent.CancellationException; | 
|  | 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.SynchronousQueue; | 
|  | import java.util.concurrent.ThreadLocalRandom; | 
|  | import java.util.concurrent.TimeoutException; | 
|  |  | 
|  | import junit.framework.Test; | 
|  | import junit.framework.TestSuite; | 
|  |  | 
|  | public class RecursiveActionTest 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(RecursiveActionTest.class); | 
|  | // } | 
|  |  | 
|  | private static ForkJoinPool mainPool() { | 
|  | return new ForkJoinPool(); | 
|  | } | 
|  |  | 
|  | private static ForkJoinPool singletonPool() { | 
|  | return new ForkJoinPool(1); | 
|  | } | 
|  |  | 
|  | private static ForkJoinPool asyncSingletonPool() { | 
|  | return new ForkJoinPool(1, | 
|  | ForkJoinPool.defaultForkJoinWorkerThreadFactory, | 
|  | null, true); | 
|  | } | 
|  |  | 
|  | private void testInvokeOnPool(ForkJoinPool pool, RecursiveAction a) { | 
|  | try (PoolCleaner cleaner = cleaner(pool)) { | 
|  | checkNotDone(a); | 
|  |  | 
|  | assertNull(pool.invoke(a)); | 
|  |  | 
|  | checkCompletedNormally(a); | 
|  | } | 
|  | } | 
|  |  | 
|  | void checkNotDone(RecursiveAction 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(RecursiveAction 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(RecursiveAction 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(RecursiveAction 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); | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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); | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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); | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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()); | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), a); | 
|  | a.reinitialize(); | 
|  | testInvokeOnPool(singletonPool(), a); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * join/quietlyJoin of a forked task when not in ForkJoinPool | 
|  | * succeeds in the presence of interrupts | 
|  | */ | 
|  | public void testJoinIgnoresInterruptsOutsideForkJoinPool() { | 
|  | final SynchronousQueue<FibAction[]> sq = | 
|  | new SynchronousQueue<FibAction[]>(); | 
|  | RecursiveAction a = new CheckedRecursiveAction() { | 
|  | protected void realCompute() throws InterruptedException { | 
|  | FibAction[] fibActions = new FibAction[6]; | 
|  | for (int i = 0; i < fibActions.length; i++) | 
|  | fibActions[i] = new FibAction(8); | 
|  |  | 
|  | fibActions[1].cancel(false); | 
|  | fibActions[2].completeExceptionally(new FJException()); | 
|  | fibActions[4].cancel(true); | 
|  | fibActions[5].completeExceptionally(new FJException()); | 
|  |  | 
|  | for (int i = 0; i < fibActions.length; i++) | 
|  | fibActions[i].fork(); | 
|  |  | 
|  | sq.put(fibActions); | 
|  |  | 
|  | helpQuiesce(); | 
|  | }}; | 
|  |  | 
|  | Runnable r = new CheckedRunnable() { | 
|  | public void realRun() throws InterruptedException { | 
|  | FibAction[] fibActions = sq.take(); | 
|  | FibAction f; | 
|  | final Thread myself = Thread.currentThread(); | 
|  |  | 
|  | // test join() ------------ | 
|  |  | 
|  | f = fibActions[0]; | 
|  | assertFalse(ForkJoinTask.inForkJoinPool()); | 
|  | myself.interrupt(); | 
|  | assertTrue(myself.isInterrupted()); | 
|  | assertNull(f.join()); | 
|  | assertTrue(Thread.interrupted()); | 
|  | assertEquals(21, f.result); | 
|  | checkCompletedNormally(f); | 
|  |  | 
|  | f = fibActions[1]; | 
|  | myself.interrupt(); | 
|  | assertTrue(myself.isInterrupted()); | 
|  | try { | 
|  | f.join(); | 
|  | shouldThrow(); | 
|  | } catch (CancellationException success) { | 
|  | assertTrue(Thread.interrupted()); | 
|  | checkCancelled(f); | 
|  | } | 
|  |  | 
|  | f = fibActions[2]; | 
|  | myself.interrupt(); | 
|  | assertTrue(myself.isInterrupted()); | 
|  | try { | 
|  | f.join(); | 
|  | shouldThrow(); | 
|  | } catch (FJException success) { | 
|  | assertTrue(Thread.interrupted()); | 
|  | checkCompletedAbnormally(f, success); | 
|  | } | 
|  |  | 
|  | // test quietlyJoin() --------- | 
|  |  | 
|  | f = fibActions[3]; | 
|  | myself.interrupt(); | 
|  | assertTrue(myself.isInterrupted()); | 
|  | f.quietlyJoin(); | 
|  | assertTrue(Thread.interrupted()); | 
|  | assertEquals(21, f.result); | 
|  | checkCompletedNormally(f); | 
|  |  | 
|  | f = fibActions[4]; | 
|  | myself.interrupt(); | 
|  | assertTrue(myself.isInterrupted()); | 
|  | f.quietlyJoin(); | 
|  | assertTrue(Thread.interrupted()); | 
|  | checkCancelled(f); | 
|  |  | 
|  | f = fibActions[5]; | 
|  | myself.interrupt(); | 
|  | assertTrue(myself.isInterrupted()); | 
|  | f.quietlyJoin(); | 
|  | assertTrue(Thread.interrupted()); | 
|  | assertTrue(f.getException() instanceof FJException); | 
|  | checkCompletedAbnormally(f, f.getException()); | 
|  | }}; | 
|  |  | 
|  | Thread t; | 
|  |  | 
|  | t = newStartedThread(r); | 
|  | testInvokeOnPool(mainPool(), a); | 
|  | awaitTermination(t); | 
|  |  | 
|  | a.reinitialize(); | 
|  | t = newStartedThread(r); | 
|  | testInvokeOnPool(singletonPool(), a); | 
|  | awaitTermination(t); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * 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); | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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); | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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) {} | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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); | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), a); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * helpQuiesce returns when tasks are complete. | 
|  | * getQueuedTaskCount returns 0 when quiescent | 
|  | */ | 
|  | public void testForkHelpQuiesce() { | 
|  | RecursiveAction a = new CheckedRecursiveAction() { | 
|  | protected void realCompute() { | 
|  | FibAction f = new FibAction(8); | 
|  | assertSame(f, f.fork()); | 
|  | helpQuiesce(); | 
|  | while (!f.isDone()) // wait out race | 
|  | ; | 
|  | assertEquals(21, f.result); | 
|  | assertEquals(0, getQueuedTaskCount()); | 
|  | checkCompletedNormally(f); | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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); | 
|  | } | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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()); | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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); | 
|  | } | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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); | 
|  | } | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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); | 
|  | } | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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()); | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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); | 
|  | } | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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); | 
|  | } | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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); | 
|  | } | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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); | 
|  | } | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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); | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), a); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * getPool of executing task returns its pool | 
|  | */ | 
|  | public void testGetPool() { | 
|  | final ForkJoinPool mainPool = mainPool(); | 
|  | RecursiveAction a = new CheckedRecursiveAction() { | 
|  | protected void realCompute() { | 
|  | assertSame(mainPool, getPool()); | 
|  | }}; | 
|  | testInvokeOnPool(mainPool, 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() { | 
|  | RecursiveAction a = new CheckedRecursiveAction() { | 
|  | protected void realCompute() { | 
|  | assertTrue(inForkJoinPool()); | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), a); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * inForkJoinPool of non-FJ task returns false | 
|  | */ | 
|  | public void testInForkJoinPool2() { | 
|  | RecursiveAction a = new CheckedRecursiveAction() { | 
|  | protected void realCompute() { | 
|  | assertFalse(inForkJoinPool()); | 
|  | }}; | 
|  | assertNull(a.invoke()); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * getPool of current thread in pool returns its pool | 
|  | */ | 
|  | public void testWorkerGetPool() { | 
|  | final ForkJoinPool mainPool = mainPool(); | 
|  | RecursiveAction a = new CheckedRecursiveAction() { | 
|  | protected void realCompute() { | 
|  | ForkJoinWorkerThread w = | 
|  | (ForkJoinWorkerThread) Thread.currentThread(); | 
|  | assertSame(mainPool, w.getPool()); | 
|  | }}; | 
|  | testInvokeOnPool(mainPool, a); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * getPoolIndex of current thread in pool returns 0 <= value < poolSize | 
|  | */ | 
|  | public void testWorkerGetPoolIndex() { | 
|  | final ForkJoinPool mainPool = mainPool(); | 
|  | RecursiveAction a = new CheckedRecursiveAction() { | 
|  | protected void realCompute() { | 
|  | ForkJoinWorkerThread w = | 
|  | (ForkJoinWorkerThread) Thread.currentThread(); | 
|  | assertTrue(w.getPoolIndex() >= 0); | 
|  | // pool size can shrink after assigning index, so cannot check | 
|  | // assertTrue(w.getPoolIndex() < mainPool.getPoolSize()); | 
|  | }}; | 
|  | testInvokeOnPool(mainPool, a); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * setRawResult(null) succeeds | 
|  | */ | 
|  | public void testSetRawResult() { | 
|  | RecursiveAction a = new CheckedRecursiveAction() { | 
|  | protected void realCompute() { | 
|  | setRawResult(null); | 
|  | assertNull(getRawResult()); | 
|  | }}; | 
|  | 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); | 
|  | } | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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); | 
|  | } | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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); | 
|  | } | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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); | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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); | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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); | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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); | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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); | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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) {} | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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); | 
|  | } | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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); | 
|  | } | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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); | 
|  | } | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), 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); | 
|  | } | 
|  | }}; | 
|  | testInvokeOnPool(mainPool(), a); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * tryUnfork returns true for most recent unexecuted task, | 
|  | * and suppresses execution | 
|  | */ | 
|  | public void testTryUnfork() { | 
|  | RecursiveAction a = new CheckedRecursiveAction() { | 
|  | protected void realCompute() { | 
|  | FibAction g = new FibAction(9); | 
|  | assertSame(g, g.fork()); | 
|  | FibAction f = new FibAction(8); | 
|  | assertSame(f, f.fork()); | 
|  | assertTrue(f.tryUnfork()); | 
|  | helpQuiesce(); | 
|  | checkNotDone(f); | 
|  | checkCompletedNormally(g); | 
|  | }}; | 
|  | testInvokeOnPool(singletonPool(), a); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * getSurplusQueuedTaskCount returns > 0 when | 
|  | * there are more tasks than threads | 
|  | */ | 
|  | public void testGetSurplusQueuedTaskCount() { | 
|  | RecursiveAction a = new CheckedRecursiveAction() { | 
|  | protected void realCompute() { | 
|  | FibAction h = new FibAction(7); | 
|  | assertSame(h, h.fork()); | 
|  | FibAction g = new FibAction(9); | 
|  | assertSame(g, g.fork()); | 
|  | FibAction f = new FibAction(8); | 
|  | assertSame(f, f.fork()); | 
|  | assertTrue(getSurplusQueuedTaskCount() > 0); | 
|  | helpQuiesce(); | 
|  | assertEquals(0, getSurplusQueuedTaskCount()); | 
|  | checkCompletedNormally(f); | 
|  | checkCompletedNormally(g); | 
|  | checkCompletedNormally(h); | 
|  | }}; | 
|  | testInvokeOnPool(singletonPool(), a); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * peekNextLocalTask returns most recent unexecuted task. | 
|  | */ | 
|  | public void testPeekNextLocalTask() { | 
|  | RecursiveAction a = new CheckedRecursiveAction() { | 
|  | protected void realCompute() { | 
|  | FibAction g = new FibAction(9); | 
|  | assertSame(g, g.fork()); | 
|  | FibAction f = new FibAction(8); | 
|  | assertSame(f, f.fork()); | 
|  | assertSame(f, peekNextLocalTask()); | 
|  | assertNull(f.join()); | 
|  | checkCompletedNormally(f); | 
|  | helpQuiesce(); | 
|  | checkCompletedNormally(f); | 
|  | checkCompletedNormally(g); | 
|  | }}; | 
|  | testInvokeOnPool(singletonPool(), a); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * pollNextLocalTask returns most recent unexecuted task | 
|  | * without executing it | 
|  | */ | 
|  | public void testPollNextLocalTask() { | 
|  | RecursiveAction a = new CheckedRecursiveAction() { | 
|  | protected void realCompute() { | 
|  | FibAction g = new FibAction(9); | 
|  | assertSame(g, g.fork()); | 
|  | FibAction f = new FibAction(8); | 
|  | assertSame(f, f.fork()); | 
|  | assertSame(f, pollNextLocalTask()); | 
|  | helpQuiesce(); | 
|  | checkNotDone(f); | 
|  | checkCompletedNormally(g); | 
|  | }}; | 
|  | testInvokeOnPool(singletonPool(), a); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * pollTask returns an unexecuted task without executing it | 
|  | */ | 
|  | public void testPollTask() { | 
|  | RecursiveAction a = new CheckedRecursiveAction() { | 
|  | protected void realCompute() { | 
|  | FibAction g = new FibAction(9); | 
|  | assertSame(g, g.fork()); | 
|  | FibAction f = new FibAction(8); | 
|  | assertSame(f, f.fork()); | 
|  | assertSame(f, pollTask()); | 
|  | helpQuiesce(); | 
|  | checkNotDone(f); | 
|  | checkCompletedNormally(g); | 
|  | }}; | 
|  | testInvokeOnPool(singletonPool(), a); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * peekNextLocalTask returns least recent unexecuted task in async mode | 
|  | */ | 
|  | public void testPeekNextLocalTaskAsync() { | 
|  | RecursiveAction a = new CheckedRecursiveAction() { | 
|  | protected void realCompute() { | 
|  | FibAction g = new FibAction(9); | 
|  | assertSame(g, g.fork()); | 
|  | FibAction f = new FibAction(8); | 
|  | assertSame(f, f.fork()); | 
|  | assertSame(g, peekNextLocalTask()); | 
|  | assertNull(f.join()); | 
|  | helpQuiesce(); | 
|  | checkCompletedNormally(f); | 
|  | checkCompletedNormally(g); | 
|  | }}; | 
|  | 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() { | 
|  | FibAction g = new FibAction(9); | 
|  | assertSame(g, g.fork()); | 
|  | FibAction f = new FibAction(8); | 
|  | assertSame(f, f.fork()); | 
|  | assertSame(g, pollNextLocalTask()); | 
|  | helpQuiesce(); | 
|  | checkCompletedNormally(f); | 
|  | 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() { | 
|  | FibAction g = new FibAction(9); | 
|  | assertSame(g, g.fork()); | 
|  | FibAction f = new FibAction(8); | 
|  | assertSame(f, f.fork()); | 
|  | assertSame(g, pollTask()); | 
|  | helpQuiesce(); | 
|  | checkCompletedNormally(f); | 
|  | checkNotDone(g); | 
|  | }}; | 
|  | testInvokeOnPool(asyncSingletonPool(), a); | 
|  | } | 
|  |  | 
|  | /** Demo from RecursiveAction javadoc */ | 
|  | static class SortTask extends RecursiveAction { | 
|  | final long[] array; final int lo, hi; | 
|  | SortTask(long[] array, int lo, int hi) { | 
|  | this.array = array; this.lo = lo; this.hi = hi; | 
|  | } | 
|  | SortTask(long[] array) { this(array, 0, array.length); } | 
|  | protected void compute() { | 
|  | if (hi - lo < THRESHOLD) | 
|  | sortSequentially(lo, hi); | 
|  | else { | 
|  | int mid = (lo + hi) >>> 1; | 
|  | invokeAll(new SortTask(array, lo, mid), | 
|  | new SortTask(array, mid, hi)); | 
|  | merge(lo, mid, hi); | 
|  | } | 
|  | } | 
|  | // implementation details follow: | 
|  | static final int THRESHOLD = 100; | 
|  | void sortSequentially(int lo, int hi) { | 
|  | Arrays.sort(array, lo, hi); | 
|  | } | 
|  | void merge(int lo, int mid, int hi) { | 
|  | long[] buf = Arrays.copyOfRange(array, lo, mid); | 
|  | for (int i = 0, j = lo, k = mid; i < buf.length; j++) | 
|  | array[j] = (k == hi || buf[i] < array[k]) ? | 
|  | buf[i++] : array[k++]; | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * SortTask demo works as advertised | 
|  | */ | 
|  | public void testSortTaskDemo() { | 
|  | ThreadLocalRandom rnd = ThreadLocalRandom.current(); | 
|  | long[] array = new long[1007]; | 
|  | for (int i = 0; i < array.length; i++) | 
|  | array[i] = rnd.nextLong(); | 
|  | long[] arrayClone = array.clone(); | 
|  | testInvokeOnPool(mainPool(), new SortTask(array)); | 
|  | Arrays.sort(arrayClone); | 
|  | assertTrue(Arrays.equals(array, arrayClone)); | 
|  | } | 
|  | } |