Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP - Show the worst possible workaround for QCL leak in tests #15151

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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<Duration> optionalInterval = threadPoolConfig.shutdownCheckInterval;
long remaining = shutdownTimeout.toNanos();
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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) {

}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down