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
24 changes: 20 additions & 4 deletions guava-tests/test/com/google/common/util/concurrent/TestThread.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,27 @@ public TestThread(L lockLikeObject, String threadName) {
*/
@Override
public void tearDown() throws Exception {
// First, try to interrupt the thread. This allows interruptible threads (like those in
// InterruptibleMonitorTest) to clean up gracefully. For uninterruptible threads, this
// will have no effect, but we try stop() next as a fallback for older JDKs.
interrupt();
try {
Thread.class.getMethod("stop").invoke(this);
join();
} catch (ReflectiveOperationException e) {
// stop() threw or did not exist. Don't join() the thread, which might hang forever.
// Give the thread a chance to respond to the interrupt before trying stop().
join(DUE_DILIGENCE_MILLIS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}

if (isAlive()) {
// Thread didn't respond to interrupt. Try stop() for older JDKs (pre-Java 20).
// On Java 20+, stop() throws UnsupportedOperationException, so the thread will
// remain alive. This is unavoidable for uninterruptible threads.
try {
Thread.class.getMethod("stop").invoke(this);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Do not use Thread.stop. A recommended way to stop one thread from another is to have the first thread poll a boolean field that is initially false but can be set to true by the second
thread to indicate that the first thread is to stop itself. Because reading and
writing a boolean field is atomic, some programmers dispense with synchronization when accessing the field:" - Item 78 of Effective Java programming (3rd edition).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review! A couple of important points about context:

  1. This PR doesn't introduce Thread.stop() — the existing code already calls it. This PR actually reduces reliance on it by trying interrupt() first.

  2. The boolean polling pattern from Effective Java Item 78 can't apply here. TestThread is used in concurrency tests where threads are deliberately blocked inside synchronization primitives (Monitor.enter(), Condition.await(), etc.). A thread suspended in a native blocking call cannot poll a boolean field — it's not in a loop.

  3. The Guava maintainers already addressed this in the existing TODO comment, noting that some threads are in uninterruptible operations intentionally and there is no other way to clean them up. The long-term plan is to drop stop() once Java 20+ is the minimum.

This PR implements the approach suggested by @cpovirk in #7319: try interrupt() first, keep stop() as a fallback only for older JDKs.

join();
} catch (ReflectiveOperationException e) {
// stop() threw or did not exist. Don't join() the thread, which might hang forever.
}
}

if (uncaughtThrowable != null) {
Expand Down