Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 67 additions & 50 deletions test/jdk/java/util/concurrent/forkjoin/AsyncShutdownNow.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2022, 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
Expand Down Expand Up @@ -28,8 +28,7 @@
* invokeAll, and invokeAny
*/

// TODO: this test is far too slow

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
Expand All @@ -38,12 +37,9 @@
import java.util.concurrent.Executors;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static java.lang.Thread.State.*;

import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
Expand All @@ -56,29 +52,6 @@ public class AsyncShutdownNow {
return null;
};

private ScheduledExecutorService scheduledExecutor;

@BeforeClass
public void setup() {
scheduledExecutor = Executors.newScheduledThreadPool(1);
}

@AfterClass
public void teardown() {
scheduledExecutor.shutdown();
}

/**
* Schedule the given executor service to be shutdown abruptly after the given
* delay, in seconds.
*/
private void scheduleShutdownNow(ExecutorService executor, int delayInSeconds) {
scheduledExecutor.schedule(() -> {
executor.shutdownNow();
return null;
}, delayInSeconds, TimeUnit.SECONDS);
}

/**
* The executors to test.
*/
Expand All @@ -96,18 +69,19 @@ public Object[][] executors() {
@Test(dataProvider = "executors")
public void testFutureGet(ExecutorService executor) throws Exception {
System.out.format("testFutureGet: %s%n", executor);
scheduleShutdownNow(executor, 5);
try {
// submit long running task, the task should be cancelled
Future<?> future = executor.submit(SLEEP_FOR_A_DAY);

// shutdownNow when main thread waits in ForkJoinTask.get
onWait("java.util.concurrent.ForkJoinTask.get", executor::shutdownNow);
try {
future.get();
assertTrue(false);
} catch (ExecutionException | RejectedExecutionException e) {
fail();
} catch (ExecutionException | CancellationException e) {
// expected
}
} finally {
executor.shutdown();
executor.shutdownNow();
}
}

Expand All @@ -117,18 +91,19 @@ public void testFutureGet(ExecutorService executor) throws Exception {
@Test(dataProvider = "executors")
public void testTimedFutureGet(ExecutorService executor) throws Exception {
System.out.format("testTimedFutureGet: %s%n", executor);
scheduleShutdownNow(executor, 5);
try {
// submit long running task, the task should be cancelled
Future<?> future = executor.submit(SLEEP_FOR_A_DAY);

// shutdownNow when main thread waits in ForkJoinTask.get
onWait("java.util.concurrent.ForkJoinTask.get", executor::shutdownNow);
try {
future.get(1, TimeUnit.HOURS);
assertTrue(false);
} catch (ExecutionException | RejectedExecutionException e) {
fail();
} catch (ExecutionException | CancellationException e) {
// expected
}
} finally {
executor.shutdown();
executor.shutdownNow();
}
}

Expand All @@ -138,41 +113,83 @@ public void testTimedFutureGet(ExecutorService executor) throws Exception {
@Test(dataProvider = "executors")
public void testInvokeAll(ExecutorService executor) throws Exception {
System.out.format("testInvokeAll: %s%n", executor);
scheduleShutdownNow(executor, 5);
try {
// execute long running tasks
// shutdownNow when main thread waits in ForkJoinTask.awaitPoolInvoke
onWait("java.util.concurrent.ForkJoinTask.awaitPoolInvoke", executor::shutdownNow);
List<Future<Void>> futures = executor.invokeAll(List.of(SLEEP_FOR_A_DAY, SLEEP_FOR_A_DAY));
for (Future<Void> f : futures) {
assertTrue(f.isDone());
try {
Object result = f.get();
assertTrue(false);
fail();
} catch (ExecutionException | CancellationException e) {
// expected
}
}
} finally {
executor.shutdown();
executor.shutdownNow();
}
}

/**
* Test shutdownNow with thread blocked in invokeAny.
*/
@Test(dataProvider = "executors")
@Test(dataProvider = "executors", enabled = false)
public void testInvokeAny(ExecutorService executor) throws Exception {
System.out.format("testInvokeAny: %s%n", executor);
scheduleShutdownNow(executor, 5);
try {
// shutdownNow when main thread waits in ForkJoinTask.get
onWait("java.util.concurrent.ForkJoinTask.get", executor::shutdownNow);
try {
// execute long running tasks
executor.invokeAny(List.of(SLEEP_FOR_A_DAY, SLEEP_FOR_A_DAY));
assertTrue(false);
} catch (ExecutionException | RejectedExecutionException e) {
fail();
} catch (ExecutionException e) {
// expected
}
} finally {
executor.shutdown();
executor.shutdownNow();
}
}

/**
* Runs the given action when the current thread is sampled as waiting (timed or
* untimed) at the given location. The location takes the form "{@code c.m}" where
* {@code c} is the fully qualified class name and {@code m} is the method name.
*/
private void onWait(String location, Runnable action) {
int index = location.lastIndexOf('.');
String className = location.substring(0, index);
String methodName = location.substring(index + 1);
Thread target = Thread.currentThread();
var thread = new Thread(() -> {
try {
boolean found = false;
while (!found) {
Thread.State state = target.getState();
assertTrue(state != TERMINATED);
if ((state == WAITING || state == TIMED_WAITING)
&& contains(target.getStackTrace(), className, methodName)) {
found = true;
} else {
Thread.sleep(20);
}
}
action.run();
} catch (Exception e) {
e.printStackTrace();
}
});
thread.setDaemon(true);
thread.start();
}

/**
* Returns true if the given stack trace contains an element for the given class
* and method name.
*/
private boolean contains(StackTraceElement[] stack, String className, String methodName) {
return Arrays.stream(stack)
.anyMatch(e -> className.equals(e.getClassName())
&& methodName.equals(e.getMethodName()));
}
}