8347039: ThreadPerTaskExecutor terminates if cancelled tasks still running

Reviewed-by: vklang
This commit is contained in:
Alan Bateman 2025-01-15 10:54:03 +00:00
parent 91197b384f
commit af3f5d852e
2 changed files with 64 additions and 12 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2025, 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
@ -318,8 +318,12 @@ class ThreadPerTaskExecutor extends ThreadContainer implements ExecutorService {
}
@Override
protected void done() {
executor.taskComplete(thread);
public void run() {
try {
super.run();
} finally {
executor.taskComplete(thread);
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2025, 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
@ -23,6 +23,7 @@
/*
* @test id=platform
* @bug 8284161 8347039
* @summary Basic tests for new thread-per-task executors
* @run junit/othervm -DthreadFactory=platform ThreadPerTaskExecutorTest
*/
@ -157,6 +158,7 @@ class ThreadPerTaskExecutorTest {
assertTrue(executor.isShutdown());
assertFalse(executor.isTerminated());
assertFalse(executor.awaitTermination(500, TimeUnit.MILLISECONDS));
assertFalse(future.isDone());
} finally {
future.cancel(true); // interrupt task
}
@ -280,13 +282,13 @@ class ThreadPerTaskExecutorTest {
}
/**
* Test awaitTermination when not shutdown.
* Test awaitTermination with no tasks running.
*/
@ParameterizedTest
@MethodSource("executors")
void testAwaitTermination1(ExecutorService executor) throws Exception {
assertFalse(executor.awaitTermination(100, TimeUnit.MILLISECONDS));
executor.close();
executor.shutdown();
assertTrue(executor.awaitTermination(100, TimeUnit.MILLISECONDS));
}
@ -296,16 +298,62 @@ class ThreadPerTaskExecutorTest {
@ParameterizedTest
@MethodSource("executors")
void testAwaitTermination2(ExecutorService executor) throws Exception {
Phaser barrier = new Phaser(2);
Future<?> future = executor.submit(barrier::arriveAndAwaitAdvance);
var started = new CountDownLatch(1);
var stop = new CountDownLatch(1);
Future<?> future = executor.submit(() -> {
started.countDown();
stop.await();
return null;
});
started.await();
try {
executor.shutdown();
assertFalse(executor.awaitTermination(100, TimeUnit.MILLISECONDS));
barrier.arriveAndAwaitAdvance();
assertTrue(executor.awaitTermination(10, TimeUnit.SECONDS));
assertFalse(executor.awaitTermination(1, TimeUnit.SECONDS));
assertFalse(future.isDone());
} finally {
future.cancel(true);
stop.countDown();
}
assertTrue(executor.awaitTermination(1, TimeUnit.MINUTES));
assertTrue(future.isDone());
}
/**
* Test awaitTermination with cancelled task still running.
*/
@ParameterizedTest
@MethodSource("executors")
void testAwaitTermination3(ExecutorService executor) throws Exception {
var started = new CountDownLatch(1);
var stop = new CountDownLatch(1);
Future<?> future = executor.submit(() -> {
started.countDown();
stop.await();
return null;
});
started.await();
try {
future.cancel(false);
executor.shutdown();
assertFalse(executor.awaitTermination(1, TimeUnit.SECONDS));
} finally {
stop.countDown();
}
assertTrue(executor.awaitTermination(1, TimeUnit.MINUTES));
}
/**
* Test awaitTermination with cancelled task that may not have started execution.
*/
@ParameterizedTest
@MethodSource("executors")
void testAwaitTermination4(ExecutorService executor) throws Exception {
Future<?> future = executor.submit(() -> {
Thread.sleep(Duration.ofMillis(50));
return null;
});
future.cancel(false);
executor.shutdown();
assertTrue(executor.awaitTermination(1, TimeUnit.MINUTES));
}
/**