From da93a6feec8c81cba6f457981d43bbc11946cc50 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Thu, 10 Sep 2026 18:06:40 -0400 Subject: [PATCH 1/2] Prevent smoke output teardown race --- .../java/datadog/smoketest/OutputThreads.java | 16 +++++++++--- .../datadog/smoketest/OutputThreadsTest.java | 26 +++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 dd-smoke-tests/src/test/java/datadog/smoketest/OutputThreadsTest.java diff --git a/dd-smoke-tests/src/main/java/datadog/smoketest/OutputThreads.java b/dd-smoke-tests/src/main/java/datadog/smoketest/OutputThreads.java index 0166663bc26..e1d669f925e 100644 --- a/dd-smoke-tests/src/main/java/datadog/smoketest/OutputThreads.java +++ b/dd-smoke-tests/src/main/java/datadog/smoketest/OutputThreads.java @@ -23,17 +23,25 @@ public class OutputThreads implements Closeable { private static final int MAX_LINE_SIZE = 1024 * 1024; private static final int DEFAULT_TIMEOUT_MILLIS = 10_000; - final ThreadGroup tg = new ThreadGroup("smoke-output"); + final ThreadGroup tg; final List testLogMessages = new ArrayList<>(); + public OutputThreads() { + this(new ThreadGroup("smoke-output")); + } + + OutputThreads(ThreadGroup tg) { + this.tg = tg; + } + public void close() { tg.interrupt(); Thread[] threads = new Thread[tg.activeCount()]; - tg.enumerate(threads); + int threadCount = tg.enumerate(threads); - for (Thread thread : threads) { + for (int i = 0; i < threadCount; i++) { try { - thread.join(THREAD_JOIN_TIMEOUT_MILLIS); + threads[i].join(THREAD_JOIN_TIMEOUT_MILLIS); } catch (InterruptedException e) { // ignore } diff --git a/dd-smoke-tests/src/test/java/datadog/smoketest/OutputThreadsTest.java b/dd-smoke-tests/src/test/java/datadog/smoketest/OutputThreadsTest.java new file mode 100644 index 00000000000..d41d549f830 --- /dev/null +++ b/dd-smoke-tests/src/test/java/datadog/smoketest/OutputThreadsTest.java @@ -0,0 +1,26 @@ +package datadog.smoketest; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.Test; + +class OutputThreadsTest { + + @Test + void closeOnlyJoinsEnumeratedThreads() { + ThreadGroup shrinkingThreadGroup = + new ThreadGroup("shrinking-smoke-output") { + @Override + public int activeCount() { + return 1; + } + + @Override + public int enumerate(Thread[] threads) { + return 0; + } + }; + + assertDoesNotThrow(() -> new OutputThreads(shrinkingThreadGroup).close()); + } +} From 4d5c789199098f5ed223f7cf6e7ae88ff84f9d04 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Thu, 10 Sep 2026 18:15:18 -0400 Subject: [PATCH 2/2] Mark test-only OutputThreads constructor --- .../src/main/java/datadog/smoketest/OutputThreads.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dd-smoke-tests/src/main/java/datadog/smoketest/OutputThreads.java b/dd-smoke-tests/src/main/java/datadog/smoketest/OutputThreads.java index e1d669f925e..515a38567ea 100644 --- a/dd-smoke-tests/src/main/java/datadog/smoketest/OutputThreads.java +++ b/dd-smoke-tests/src/main/java/datadog/smoketest/OutputThreads.java @@ -1,5 +1,6 @@ package datadog.smoketest; +import datadog.trace.api.internal.VisibleForTesting; import java.io.Closeable; import java.io.File; import java.io.FileNotFoundException; @@ -30,6 +31,7 @@ public OutputThreads() { this(new ThreadGroup("smoke-output")); } + @VisibleForTesting OutputThreads(ThreadGroup tg) { this.tg = tg; }