Repository: quarkusio/quarkus
Issue: dev-mode Classloader leak via Vert.x internal worker pool

### Describe the bug

This is similar to #18911, but it turns out Vert.x has an additional internal pool which is keeping hold of previous classloaders: these threads are lazily started (so easy to miss in a simple app) and follow the naming scheme `vertx-internal-blocking-` N

### Expected behavior

We should reset these as well.


Changed files:
extensions/vertx-core/runtime/src/main/java/io/quarkus/vertx/core/runtime/VertxCoreRecorder.java

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 c7c0357c730..8823f29604f 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
@@ -7,6 +7,7 @@
 import static io.quarkus.vertx.core.runtime.SSLConfigHelper.configurePfxTrustOptions;
 
 import java.io.File;
+import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
@@ -133,12 +134,8 @@ private void tryCleanTccl(Vertx devModeVertx) {
         //this is a best effort attempt to clean out the old TCCL from
         ClassLoader cl = Thread.currentThread().getContextClassLoader();
 
-        EnhancedQueueExecutor executor = extractExecutor(devModeVertx);
-
-        final Thread[] runningThreads = executor.getRunningThreads();
-        for (Thread t : runningThreads) {
-            t.setContextClassLoader(cl);
-        }
+        resetExecutorsClassloaderContext(extractWorkerPool(devModeVertx), cl);
+        resetExecutorsClassloaderContext(extractInternalWorkerPool(devModeVertx), cl);
 
         EventLoopGroup group = ((VertxImpl) devModeVertx).getEventLoopGroup();
         for (EventExecutor i : group) {
@@ -154,13 +151,37 @@ public void run() {
 
     }
 
+    private WorkerPool extractInternalWorkerPool(Vertx devModeVertx) {
+        VertxImpl vertxImpl = (VertxImpl) devModeVertx;
+        final Object internalWorkerPool;
+        final Field field;
+        try {
+            field = VertxImpl.class.getDeclaredField("internalWorkerPool");
+            field.setAccessible(true);
+        } catch (NoSuchFieldException e) {
+            throw new RuntimeException(e);
+        }
+        try {
+            internalWorkerPool = field.get(vertxImpl);
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+
+        return (WorkerPool) internalWorkerPool;
+    }
+
+    private WorkerPool extractWorkerPool(Vertx devModeVertx) {
+        final ContextInternal ctx = (ContextInternal) devModeVertx.getOrCreateContext();
+        return ctx.workerPool();
+    }
+
     /**
      * Extract the JBoss Threads EnhancedQueueExecutor from the Vertx instance
-     * this is messy as it needs to use reflection until Vertx can expose it.
+     * and reset all threads to use the given ClassLoader.
+     * This is messy as it needs to use reflection until Vertx can expose it:
+     * - https://github.com/eclipse-vertx/vert.x/pull/4029
      */
-    private EnhancedQueueExecutor extractExecutor(Vertx devModeVertx) {
-        final ContextInternal ctx = (ContextInternal) devModeVertx.getOrCreateContext();
-        final WorkerPool workerPool = ctx.workerPool();
+    private void resetExecutorsClassloaderContext(WorkerPool workerPool, ClassLoader cl) {
         final Method executorMethod;
         try {
             executorMethod = WorkerPool.class.getDeclaredMethod("executor");
@@ -175,7 +196,10 @@ private EnhancedQueueExecutor extractExecutor(Vertx devModeVertx) {
             throw new RuntimeException(e);
         }
         EnhancedQueueExecutor executor = (EnhancedQueueExecutor) result;
-        return executor;
+        final Thread[] runningThreads = executor.getRunningThreads();
+        for (Thread t : runningThreads) {
+            t.setContextClassLoader(cl);
+        }
     }
 
     public IOThreadDetector detector() {