From 8e9ad775712b53c689242be3ff65cc5e90664410 Mon Sep 17 00:00:00 2001 From: Lari Hotari Date: Thu, 2 Nov 2023 09:40:20 +0200 Subject: [PATCH] [improve][ci] Improve thread leak detection by ignoring more Testcontainers threads (#21499) --- .../tests/ThreadLeakDetectorListener.java | 40 +++++++++++++++++++ 1 file changed, 40 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..ef6296a5f6cc9 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,18 @@ 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) { + // 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; + } @Override protected void onBetweenTestClasses(Class endedTestClass, Class startedTestClass) { @@ -201,10 +214,37 @@ private static boolean shouldSkipThread(Thread thread) { if (threadName.equals("Grizzly-HttpSession-Expirer")) { return true; } + // Testcontainers AbstractWaitStrategy.EXECUTOR + if (threadName.startsWith("testcontainers-wait-")) { + 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; } + // 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; + } + 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