From 42689325ccb086224c17f0fe593e6e508b4b7e51 Mon Sep 17 00:00:00 2001 From: Nickita Khylkouski <90287684+nickita-khylkouski@users.noreply.github.com> Date: Sun, 25 Jan 2026 23:10:02 -0800 Subject: [PATCH 1/2] TestThread: Try interrupt() before stop() in tearDown() This allows interruptible threads (like those in InterruptibleMonitorTest) to clean up gracefully instead of relying solely on the deprecated Thread.stop() method. The implementation: 1. Calls interrupt() first 2. Waits DUE_DILIGENCE_MILLIS for the thread to respond 3. Falls back to reflective stop() only if the thread is still alive This addresses Issue #7319 as suggested by cpovirk. --- .../common/util/concurrent/TestThread.java | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/guava-tests/test/com/google/common/util/concurrent/TestThread.java b/guava-tests/test/com/google/common/util/concurrent/TestThread.java index c37b0dd19558..0d4ae90783a6 100644 --- a/guava-tests/test/com/google/common/util/concurrent/TestThread.java +++ b/guava-tests/test/com/google/common/util/concurrent/TestThread.java @@ -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); + join(); + } catch (ReflectiveOperationException e) { + // stop() threw or did not exist. Don't join() the thread, which might hang forever. + } } if (uncaughtThrowable != null) { From 722988acb44690fc0e272b463bfcd50bc6e028d3 Mon Sep 17 00:00:00 2001 From: Nickita Khylkouski <90287684+nickita-khylkouski@users.noreply.github.com> Date: Wed, 28 Jan 2026 09:35:23 -0800 Subject: [PATCH 2/2] chore: trigger CI