|
31 | 31 | * http://creativecommons.org/publicdomain/zero/1.0/ |
32 | 32 | */ |
33 | 33 |
|
34 | | -import static java.util.concurrent.TimeUnit.MILLISECONDS; |
35 | | -import static java.util.concurrent.TimeUnit.NANOSECONDS; |
36 | | - |
37 | 34 | import java.security.PrivilegedAction; |
38 | 35 | import java.security.PrivilegedExceptionAction; |
39 | 36 | import java.util.ArrayList; |
40 | 37 | import java.util.Collection; |
41 | 38 | import java.util.Collections; |
42 | 39 | import java.util.List; |
43 | 40 | import java.util.concurrent.Callable; |
| 41 | +import java.util.concurrent.CancellationException; |
44 | 42 | import java.util.concurrent.CountDownLatch; |
45 | 43 | import java.util.concurrent.ExecutionException; |
46 | 44 | import java.util.concurrent.Executors; |
|
51 | 49 | import java.util.concurrent.Future; |
52 | 50 | import java.util.concurrent.RecursiveTask; |
53 | 51 | import java.util.concurrent.RejectedExecutionException; |
| 52 | +import java.util.concurrent.TimeoutException; |
| 53 | +import java.util.concurrent.TimeUnit; |
| 54 | +import java.util.function.Function; |
54 | 55 | import java.util.concurrent.atomic.AtomicBoolean; |
55 | 56 | import java.util.concurrent.atomic.AtomicInteger; |
| 57 | +import java.util.concurrent.atomic.AtomicReference; |
56 | 58 | import java.util.concurrent.locks.ReentrantLock; |
57 | 59 |
|
58 | 60 | import junit.framework.Test; |
59 | 61 | import junit.framework.TestSuite; |
60 | 62 |
|
| 63 | +import static java.util.concurrent.TimeUnit.*; |
| 64 | + |
61 | 65 | public class ForkJoinPoolTest extends JSR166TestCase { |
62 | 66 | public static void main(String[] args) { |
63 | 67 | main(suite(), args); |
@@ -482,6 +486,57 @@ public void testSubmitRunnable() throws Throwable { |
482 | 486 | } |
483 | 487 | } |
484 | 488 |
|
| 489 | + public void testCancellationExceptionInGet() throws Exception { |
| 490 | + final ExecutorService e = new ForkJoinPool(1); |
| 491 | + try (var cleaner = cleaner(e)) { |
| 492 | + assertCancellationExceptionFrom( |
| 493 | + e::submit, |
| 494 | + f -> () -> f.get(1000, TimeUnit.SECONDS) |
| 495 | + ); |
| 496 | + assertCancellationExceptionFrom( |
| 497 | + e::submit, |
| 498 | + f -> f::get |
| 499 | + ); |
| 500 | + assertCancellationExceptionFrom( |
| 501 | + c -> e.submit(() -> { try { c.call(); } catch (Exception ex) { throw new RuntimeException(ex); } }), |
| 502 | + f -> () -> f.get(1000, TimeUnit.SECONDS) |
| 503 | + ); |
| 504 | + assertCancellationExceptionFrom( |
| 505 | + c -> e.submit(() -> { try { c.call(); } catch (Exception ex) { throw new RuntimeException(ex); } }), |
| 506 | + f -> f::get |
| 507 | + ); |
| 508 | + } |
| 509 | + } |
| 510 | + |
| 511 | + private void assertCancellationExceptionFrom( |
| 512 | + Function<Callable<Void>, Future<?>> createTask, |
| 513 | + Function<Future<?>, Callable<?>> getResult) throws Exception { |
| 514 | + final var t = new AtomicReference<Thread>(); |
| 515 | + final var c = new CountDownLatch(1); // Only used to induce WAITING state (never counted down) |
| 516 | + final var task = createTask.apply(() -> { |
| 517 | + try { |
| 518 | + t.set(Thread.currentThread()); |
| 519 | + c.await(); |
| 520 | + } catch (InterruptedException ie) { |
| 521 | + Thread.currentThread().interrupt();; |
| 522 | + } |
| 523 | + return null; |
| 524 | + }); |
| 525 | + Thread taskThread; |
| 526 | + while((taskThread = t.get()) == null || taskThread.getState() != Thread.State.WAITING) { |
| 527 | + if (Thread.interrupted()) |
| 528 | + throw new InterruptedException(); |
| 529 | + Thread.onSpinWait(); |
| 530 | + } |
| 531 | + task.cancel(true); |
| 532 | + try { |
| 533 | + getResult.apply(task).call(); |
| 534 | + } catch (CancellationException ce) { |
| 535 | + return; // Success |
| 536 | + } |
| 537 | + shouldThrow(); |
| 538 | + } |
| 539 | + |
485 | 540 | /** |
486 | 541 | * Completed submit(runnable, result) returns result |
487 | 542 | */ |
|
0 commit comments