Skip to content

Commit

Permalink
Fix some ClassLoader leaks running tests
Browse files Browse the repository at this point in the history
  • Loading branch information
stuartwdouglas committed Jul 26, 2021
1 parent bc2fdfa commit f2fd752
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,10 @@ public static void init(QuarkusConsole console, DevModeType devModeType) {
return;
}
initialized = true;
console.setInputHandler(INSTANCE.consumer);
INSTANCE.installBuiltins(devModeType);
if (console.isInputSupported()) {
console.setInputHandler(INSTANCE.consumer);
INSTANCE.installBuiltins(devModeType);
}
}

void installBuiltins(DevModeType devModeType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,12 @@ public void accept(LogRecord logRecord, Consumer<LogRecord> logRecordConsumer) {
}
}
};
((QuarkusClassLoader) getClass().getClassLoader()).addCloseTask(new Runnable() {
@Override
public void run() {
CurrentAppExceptionHighlighter.THROWABLE_FORMATTER = null;
}
});
}

@BuildStep
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,9 @@ public void write(byte[] buf, int off, int len) {
write(new String(buf, off, len, StandardCharsets.UTF_8));
}

@Override
public boolean isInputSupported() {
return inputSupport;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,7 @@ public void setOutputFilter(Predicate<String> logHandler) {
this.outputFilter = logHandler;
}

public boolean isInputSupported() {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public static Consumer<State> getStateListener() {

public static void setStateListener(Consumer<State> stateListener) {
ContinuousTestingWebsocketListener.stateListener = stateListener;
if (lastState != null) {
if (lastState != null && stateListener != null) {
stateListener.accept(lastState);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
import io.quarkus.deployment.dev.testing.TestClassResult;
import io.quarkus.deployment.dev.testing.TestListenerBuildItem;
import io.quarkus.deployment.dev.testing.TestRunResults;
Expand Down Expand Up @@ -45,6 +46,7 @@ public void setupTestRoutes(
DevConsoleRecorder recorder,
NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem,
LaunchModeBuildItem launchModeBuildItem,
ShutdownContextBuildItem shutdownContextBuildItem,
BuildProducer<RouteBuildItem> routeBuildItemBuildProducer,
BuildProducer<TestListenerBuildItem> testListenerBuildItemBuildProducer) throws IOException {
DevModeType devModeType = launchModeBuildItem.getDevModeType().orElse(null);
Expand All @@ -56,7 +58,7 @@ public void setupTestRoutes(
// Add continuous testing
routeBuildItemBuildProducer.produce(nonApplicationRootPathBuildItem.routeBuilder()
.route("dev/test")
.handler(recorder.continousTestHandler())
.handler(recorder.continousTestHandler(shutdownContextBuildItem))
.build());
testListenerBuildItemBuildProducer.produce(new TestListenerBuildItem(new ContinuousTestingWebSocketListener()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,30 @@
import io.vertx.core.http.ServerWebSocket;
import io.vertx.ext.web.RoutingContext;

public class ContinuousTestWebSocketHandler implements Handler<RoutingContext> {
public class ContinuousTestWebSocketHandler
implements Handler<RoutingContext>, Consumer<ContinuousTestingWebsocketListener.State> {

private static final Logger log = Logger.getLogger(ContinuousTestingWebsocketListener.class);
private static final Set<ServerWebSocket> sockets = Collections.newSetFromMap(new ConcurrentHashMap<>());
private static volatile String lastMessage;

static {
ContinuousTestingWebsocketListener.setStateListener(new Consumer<ContinuousTestingWebsocketListener.State>() {
@Override
public void accept(ContinuousTestingWebsocketListener.State state) {
Json.JsonObjectBuilder response = Json.object();
response.put("running", state.running);
response.put("inProgress", state.inProgress);
response.put("run", state.run);
response.put("passed", state.passed);
response.put("failed", state.failed);
response.put("skipped", state.skipped);
response.put("isBrokenOnly", state.isBrokenOnly);
response.put("isTestOutput", state.isTestOutput);
response.put("isInstrumentationBasedReload", state.isInstrumentationBasedReload);
@Override
public void accept(ContinuousTestingWebsocketListener.State state) {
Json.JsonObjectBuilder response = Json.object();
response.put("running", state.running);
response.put("inProgress", state.inProgress);
response.put("run", state.run);
response.put("passed", state.passed);
response.put("failed", state.failed);
response.put("skipped", state.skipped);
response.put("isBrokenOnly", state.isBrokenOnly);
response.put("isTestOutput", state.isTestOutput);
response.put("isInstrumentationBasedReload", state.isInstrumentationBasedReload);

lastMessage = response.build();
for (ServerWebSocket i : sockets) {
i.writeTextMessage(lastMessage);
}
}
});
lastMessage = response.build();
for (ServerWebSocket i : sockets) {
i.writeTextMessage(lastMessage);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.jboss.logging.Logger;

import io.quarkus.dev.console.DevConsoleManager;
import io.quarkus.dev.testing.ContinuousTestingWebsocketListener;
import io.quarkus.runtime.ShutdownContext;
import io.quarkus.runtime.annotations.Recorder;
import io.vertx.core.Handler;
Expand Down Expand Up @@ -44,8 +45,18 @@ public Handler<RoutingContext> devConsoleHandler(String devConsoleFinalDestinati
return new DevConsoleStaticHandler(devConsoleFinalDestination);
}

public Handler<RoutingContext> continousTestHandler() {
return new ContinuousTestWebSocketHandler();
public Handler<RoutingContext> continousTestHandler(ShutdownContext context) {

ContinuousTestWebSocketHandler handler = new ContinuousTestWebSocketHandler();
ContinuousTestingWebsocketListener.setStateListener(handler);
context.addShutdownTask(new Runnable() {
@Override
public void run() {
ContinuousTestingWebsocketListener.setStateListener(null);

}
});
return handler;
}

private static final class CleanupDevConsoleTempDirectory implements Runnable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
Expand Down Expand Up @@ -148,9 +149,9 @@ public class QuarkusTestExtension
private static DeepClone deepClone;
//needed for @Nested
private static final Deque<Class<?>> currentTestClassStack = new ArrayDeque<>();
private static ScheduledExecutorService hangDetectionExecutor;
private static Duration hangTimeout;
private static ScheduledFuture<?> hangTaskKey;
private static volatile ScheduledExecutorService hangDetectionExecutor;
private static volatile Duration hangTimeout;
private static volatile ScheduledFuture<?> hangTaskKey;
private static final Runnable hangDetectionTask = new Runnable() {

final AtomicBoolean runOnce = new AtomicBoolean();
Expand Down Expand Up @@ -188,9 +189,28 @@ public void run() {
}
};

static {
ClassLoader classLoader = QuarkusTestExtension.class.getClassLoader();
if (classLoader instanceof QuarkusClassLoader) {
((QuarkusClassLoader) classLoader).addCloseTask(new Runnable() {
@Override
public void run() {
if (hangDetectionExecutor != null) {
hangDetectionExecutor.shutdownNow();
}
}
});
}
}

private ExtensionState doJavaStart(ExtensionContext context, Class<? extends QuarkusTestProfile> profile) throws Throwable {
TracingHandler.quarkusStarting();
hangDetectionExecutor = Executors.newSingleThreadScheduledExecutor();
hangDetectionExecutor = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "Quarkus hang detection timer thread");
}
});
String time = "10m";
//config is not established yet
//we can only read from system properties
Expand Down

0 comments on commit f2fd752

Please sign in to comment.