From 2daace550983e73147b2bfa1245201405aef6d11 Mon Sep 17 00:00:00 2001 From: Lari Hotari Date: Wed, 1 Nov 2023 18:48:24 +0200 Subject: [PATCH 1/4] Ignore testcontainers-wait- threads --- .../org/apache/pulsar/tests/ThreadLeakDetectorListener.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/buildtools/src/main/java/org/apache/pulsar/tests/ThreadLeakDetectorListener.java b/buildtools/src/main/java/org/apache/pulsar/tests/ThreadLeakDetectorListener.java index 02103f259760a..0f9bc228b3efb 100644 --- a/buildtools/src/main/java/org/apache/pulsar/tests/ThreadLeakDetectorListener.java +++ b/buildtools/src/main/java/org/apache/pulsar/tests/ThreadLeakDetectorListener.java @@ -201,6 +201,10 @@ private static boolean shouldSkipThread(Thread thread) { if (threadName.equals("Grizzly-HttpSession-Expirer")) { return true; } + // Testcontainers AbstractWaitStrategy.EXECUTOR + if (threadName.startsWith("testcontainers-wait-")) { + return true; + } } return false; } From 076fd55ad2e8592b16bc3763a3a21bc01df5b3c8 Mon Sep 17 00:00:00 2001 From: Lari Hotari Date: Wed, 1 Nov 2023 23:45:02 +0200 Subject: [PATCH 2/4] Ignore threads created by Testcontainers --- .../tests/ThreadLeakDetectorListener.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/buildtools/src/main/java/org/apache/pulsar/tests/ThreadLeakDetectorListener.java b/buildtools/src/main/java/org/apache/pulsar/tests/ThreadLeakDetectorListener.java index 0f9bc228b3efb..6433e7fbf3f8b 100644 --- a/buildtools/src/main/java/org/apache/pulsar/tests/ThreadLeakDetectorListener.java +++ b/buildtools/src/main/java/org/apache/pulsar/tests/ThreadLeakDetectorListener.java @@ -24,6 +24,7 @@ import java.io.File; import java.io.IOException; import java.io.PrintWriter; +import java.lang.reflect.Field; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Collections; @@ -56,6 +57,17 @@ public class ThreadLeakDetectorListener extends BetweenTestClassesListenerAdapte private Set capturedThreadKeys; + private static final Field THREAD_TARGET_FIELD; + static { + Field targetField = null; + try { + targetField = Thread.class.getDeclaredField("target"); + targetField.setAccessible(true); + } catch (NoSuchFieldException e) { + LOG.warn("Cannot find target field in Thread.class", e); + } + THREAD_TARGET_FIELD = targetField; + } @Override protected void onBetweenTestClasses(Class endedTestClass, Class startedTestClass) { @@ -206,9 +218,27 @@ private static boolean shouldSkipThread(Thread thread) { return true; } } + Runnable target = extractRunnableTarget(thread); + if (target != null) { + String targetClassName = target.getClass().getName(); + // ignore threads that contain a Runnable class under org.testcontainers package + if (targetClassName.startsWith("org.testcontainers.")) { + return true; + } + } return false; } + private static Runnable extractRunnableTarget(Thread thread) { + Runnable target = null; + try { + target = (Runnable) THREAD_TARGET_FIELD.get(thread); + } catch (IllegalAccessException e) { + LOG.warn("Cannot access target field in Thread.class", e); + } + return target; + } + /** * Unique key for a thread * Based on thread id and it's identity hash code From d409bdbc49d4abb5dc04153becc456b492301264 Mon Sep 17 00:00:00 2001 From: Lari Hotari Date: Thu, 2 Nov 2023 01:15:24 +0200 Subject: [PATCH 3/4] Don't crash on Java 21 --- .../org/apache/pulsar/tests/ThreadLeakDetectorListener.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/buildtools/src/main/java/org/apache/pulsar/tests/ThreadLeakDetectorListener.java b/buildtools/src/main/java/org/apache/pulsar/tests/ThreadLeakDetectorListener.java index 6433e7fbf3f8b..8da389736d11f 100644 --- a/buildtools/src/main/java/org/apache/pulsar/tests/ThreadLeakDetectorListener.java +++ b/buildtools/src/main/java/org/apache/pulsar/tests/ThreadLeakDetectorListener.java @@ -64,7 +64,8 @@ public class ThreadLeakDetectorListener extends BetweenTestClassesListenerAdapte targetField = Thread.class.getDeclaredField("target"); targetField.setAccessible(true); } catch (NoSuchFieldException e) { - LOG.warn("Cannot find target field in Thread.class", e); + // ignore this error. on Java 21, the field is not present + // TODO: add support for extracting the Runnable target on Java 21 } THREAD_TARGET_FIELD = targetField; } @@ -230,6 +231,9 @@ private static boolean shouldSkipThread(Thread thread) { } private static Runnable extractRunnableTarget(Thread thread) { + if (THREAD_TARGET_FIELD == null) { + return null; + } Runnable target = null; try { target = (Runnable) THREAD_TARGET_FIELD.get(thread); From c7546b9b77963693f4825a4831910f81dd412ddf Mon Sep 17 00:00:00 2001 From: Lari Hotari Date: Thu, 2 Nov 2023 01:19:21 +0200 Subject: [PATCH 4/4] add comment about reflection --- .../org/apache/pulsar/tests/ThreadLeakDetectorListener.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/buildtools/src/main/java/org/apache/pulsar/tests/ThreadLeakDetectorListener.java b/buildtools/src/main/java/org/apache/pulsar/tests/ThreadLeakDetectorListener.java index 8da389736d11f..ef6296a5f6cc9 100644 --- a/buildtools/src/main/java/org/apache/pulsar/tests/ThreadLeakDetectorListener.java +++ b/buildtools/src/main/java/org/apache/pulsar/tests/ThreadLeakDetectorListener.java @@ -230,6 +230,8 @@ private static boolean shouldSkipThread(Thread thread) { return false; } + // use reflection to extract the Runnable target from a thread so that we can detect threads created by + // Testcontainers based on the Runnable's class name. private static Runnable extractRunnableTarget(Thread thread) { if (THREAD_TARGET_FIELD == null) { return null;