From 91efc6ab4c96878dab7a52be4246aeaa73bab6aa Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Wed, 17 Feb 2021 18:46:42 +0200 Subject: [PATCH] WIP - Show the worst possible workaround for QCL leak in tests Relates to: #12498 --- .../io/quarkus/runtime/ExecutorRecorder.java | 26 ++++++++++++++++++- .../vertx/core/runtime/VertxCoreRecorder.java | 26 +++++++++++++++++++ .../runtime/config/VertxConfiguration.java | 5 +++- 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/core/runtime/src/main/java/io/quarkus/runtime/ExecutorRecorder.java b/core/runtime/src/main/java/io/quarkus/runtime/ExecutorRecorder.java index 372b4a0adbc93..99070c9a47a92 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/ExecutorRecorder.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/ExecutorRecorder.java @@ -1,5 +1,7 @@ package io.quarkus.runtime; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.time.Duration; import java.util.List; import java.util.Optional; @@ -33,6 +35,7 @@ public ExecutorRecorder() { static volatile CleanableExecutor devModeExecutor; private static volatile Executor current; + private static volatile JBossThreadFactory threadFactory; public ExecutorService setupRunTime(ShutdownContext shutdownContext, ThreadPoolConfig threadPoolConfig, LaunchMode launchMode) { @@ -84,6 +87,27 @@ private static Runnable createShutdownTask(ThreadPoolConfig threadPoolConfig, En @Override public void run() { executor.shutdown(); + try { + //we need to get rid of AccessControlContext from the Executor and the Factory + + Field modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + + Field executorAccField = EnhancedQueueExecutor.class.getDeclaredField("acc"); + executorAccField.setAccessible(true); + Field factoryAccField = JBossThreadFactory.class.getDeclaredField("creatingContext"); + factoryAccField.setAccessible(true); + + modifiersField.setInt(executorAccField, executorAccField.getModifiers() & ~Modifier.FINAL); + modifiersField.setInt(factoryAccField, factoryAccField.getModifiers() & ~Modifier.FINAL); + executorAccField.set(executor, null); + factoryAccField.set(threadFactory, null); + } catch (NoSuchFieldException | IllegalAccessException ignored) { + + } + current = null; + threadFactory = null; + final Duration shutdownTimeout = threadPoolConfig.shutdownTimeout; final Optional optionalInterval = threadPoolConfig.shutdownCheckInterval; long remaining = shutdownTimeout.toNanos(); @@ -155,7 +179,7 @@ public void run() { } private static EnhancedQueueExecutor createExecutor(ThreadPoolConfig threadPoolConfig) { - final JBossThreadFactory threadFactory = new JBossThreadFactory(new ThreadGroup("executor"), Boolean.TRUE, null, + threadFactory = new JBossThreadFactory(new ThreadGroup("executor"), Boolean.TRUE, null, "executor-thread-%t", JBossExecutors.loggingExceptionHandler("org.jboss.executor.uncaught"), null); final EnhancedQueueExecutor.Builder builder = new EnhancedQueueExecutor.Builder() .setRegisterMBean(false) diff --git a/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/VertxCoreRecorder.java b/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/VertxCoreRecorder.java index d682a9612b87f..f4b37f3745a17 100644 --- a/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/VertxCoreRecorder.java +++ b/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/VertxCoreRecorder.java @@ -8,6 +8,8 @@ import static io.vertx.core.file.impl.FileResolver.CACHE_DIR_BASE_PROP_NAME; import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -24,8 +26,10 @@ import org.wildfly.common.cpu.ProcessorInfo; import io.netty.channel.EventLoopGroup; +import io.netty.util.concurrent.DefaultPromise; import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.FastThreadLocal; +import io.netty.util.concurrent.GlobalEventExecutor; import io.quarkus.runtime.IOThreadDetector; import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.ShutdownContext; @@ -293,6 +297,28 @@ void destroy() { throw new IllegalStateException("Exception when closing Vert.x instance", e); } vertx = null; + + try { + // we need to get rid of all Netty fields that use Exceptions to control the flow + // because these fields can leak the QCL due to the backtrace field of Throwable + + Field modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + + Field cancellationCauseHolderField = DefaultPromise.class.getDeclaredField("CANCELLATION_CAUSE_HOLDER"); + cancellationCauseHolderField.setAccessible(true); + Field terminationFutureField = GlobalEventExecutor.class.getDeclaredField("terminationFuture"); + terminationFutureField.setAccessible(true); + + modifiersField.setInt(cancellationCauseHolderField, + cancellationCauseHolderField.getModifiers() & ~Modifier.FINAL); + modifiersField.setInt(terminationFutureField, + terminationFutureField.getModifiers() & ~Modifier.FINAL); + cancellationCauseHolderField.set(null, null); + terminationFutureField.set(GlobalEventExecutor.INSTANCE, null); + } catch (NoSuchFieldException | IllegalAccessException ignored) { + + } } } diff --git a/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/config/VertxConfiguration.java b/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/config/VertxConfiguration.java index 040c37cd325a3..a234f8c6abeda 100644 --- a/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/config/VertxConfiguration.java +++ b/extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/config/VertxConfiguration.java @@ -18,8 +18,11 @@ public class VertxConfiguration { /** * Enables or disabled the Vert.x classpath resource resolver. + * + * This is set to false because it spawns a thread which then holds on to the QCL + * and leads to leaks in tests */ - @ConfigItem(defaultValue = "true") + @ConfigItem(defaultValue = "false") public boolean classpathResolving; /**