Skip to content

Commit 53650c4

Browse files
committed
Remove reflection for virtual threads.
Motivation: Vert.x relies on reflection to support virtual thread with a Java 11 build. Recently multi-release jar support has been added to support Jackson V3. We can reuse this structure to handle virtual thread support as well. Changes: Replace reflective code by two implementations of the same class with different behaviors in JDK 11/21.
1 parent 7be2d4a commit 53650c4

4 files changed

Lines changed: 77 additions & 57 deletions

File tree

vertx-core/src/main/java/io/vertx/core/impl/VertxImpl.java

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -127,22 +127,6 @@ private static boolean disposedCAS(InternalTimerHandler handler) {
127127
return INTERNAL_TIMER_HANDLER_DISPOSED.compareAndSet(handler, false, true);
128128
}
129129

130-
// Not cached for graalvm
131-
private static ThreadFactory virtualThreadFactory() {
132-
try {
133-
Class<?> builderClass = ClassLoader.getSystemClassLoader().loadClass("java.lang.Thread$Builder");
134-
Class<?> ofVirtualClass = ClassLoader.getSystemClassLoader().loadClass("java.lang.Thread$Builder$OfVirtual");
135-
Method ofVirtualMethod = Thread.class.getDeclaredMethod("ofVirtual");
136-
Object builder = ofVirtualMethod.invoke(null);
137-
Method nameMethod = ofVirtualClass.getDeclaredMethod("name", String.class, long.class);
138-
Method factoryMethod = builderClass.getDeclaredMethod("factory");
139-
builder = nameMethod.invoke(builder, "vert.x-virtual-thread-", 0L);
140-
return (ThreadFactory) factoryMethod.invoke(builder);
141-
} catch (Exception e) {
142-
return null;
143-
}
144-
}
145-
146130
static {
147131
// Disable Netty's resource leak detection to reduce the performance overhead if not set by user
148132
// Supports both the default netty leak detection system property and the deprecated one
@@ -222,7 +206,7 @@ private static ThreadFactory virtualThreadFactory() {
222206
ExecutorService internalWorkerExec = executorServiceFactory.createExecutor(internalWorkerThreadFactory, internalBlockingPoolSize, internalBlockingPoolSize);
223207
PoolMetrics internalBlockingPoolMetrics = metrics != null ? metrics.createPoolMetrics("worker", "vert.x-internal-blocking", internalBlockingPoolSize) : null;
224208

225-
ThreadFactory virtualThreadFactory = virtualThreadFactory();
209+
ThreadFactory virtualThreadFactory = VirtualThreadSupport.VIRTUAL_THREAD_FACTORY;
226210
PoolMetrics virtualThreadWorkerPoolMetrics = metrics != null && virtualThreadFactory != null ? metrics.createPoolMetrics("worker", "vert.x-virtual-thread", -1) : null;
227211

228212
int numberOfEventLoops = options.getEventLoopPoolSize();
@@ -1227,7 +1211,7 @@ public void removeCloseHook(Closeable hook) {
12271211

12281212
@Override
12291213
public boolean isVirtualThreadAvailable() {
1230-
return virtualThreadExecutor != null;
1214+
return VirtualThreadSupport.VIRTUAL_THREAD_AVAILABLE;
12311215
}
12321216

12331217
private CloseFuture resolveCloseFuture() {
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright (c) 2011-2026 Contributors to the Eclipse Foundation
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7+
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
8+
*
9+
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10+
*/
11+
package io.vertx.core.impl;
12+
13+
import java.util.concurrent.ThreadFactory;
14+
15+
/**
16+
* Pre Java 21 implementation : no virtual threads.
17+
*
18+
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
19+
*/
20+
public class VirtualThreadSupport {
21+
22+
public static final ThreadFactory VIRTUAL_THREAD_FACTORY;
23+
public static final boolean VIRTUAL_THREAD_AVAILABLE;
24+
25+
static {
26+
VIRTUAL_THREAD_FACTORY = null;
27+
VIRTUAL_THREAD_AVAILABLE = false;
28+
}
29+
30+
/**
31+
* @return whether the {@code thread} is virtual
32+
*/
33+
public static boolean isVirtual(Thread thread) {
34+
return false;
35+
}
36+
}

vertx-core/src/main/java/io/vertx/core/json/jackson/HybridJacksonPool.java

Lines changed: 3 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import com.fasterxml.jackson.core.util.BufferRecycler;
1111
import com.fasterxml.jackson.core.util.JsonRecyclerPools;
1212
import com.fasterxml.jackson.core.util.RecyclerPool;
13+
import io.vertx.core.impl.VirtualThreadSupport;
1314

1415
/**
1516
* This is a custom implementation of the Jackson's {@link RecyclerPool} intended to work equally well with both
@@ -34,8 +35,6 @@ public class HybridJacksonPool implements RecyclerPool<BufferRecycler> {
3435

3536
private static final HybridJacksonPool INSTANCE = new HybridJacksonPool();
3637

37-
private static final Predicate<Thread> isVirtual = VirtualPredicate.findIsVirtualPredicate();
38-
3938
private final RecyclerPool<BufferRecycler> nativePool = JsonRecyclerPools.threadLocalPool();
4039

4140
private static class VirtualPoolHolder {
@@ -53,15 +52,15 @@ public static HybridJacksonPool getInstance() {
5352

5453
@Override
5554
public BufferRecycler acquirePooled() {
56-
return isVirtual.test(Thread.currentThread()) ?
55+
return VirtualThreadSupport.isVirtual(Thread.currentThread()) ?
5756
VirtualPoolHolder.virtualPool.acquirePooled() :
5857
nativePool.acquirePooled();
5958
}
6059

6160
@Override
6261
public BufferRecycler acquireAndLinkPooled() {
6362
// when using the ThreadLocal based pool it is not necessary to register the BufferRecycler on the pool
64-
return isVirtual.test(Thread.currentThread()) ?
63+
return VirtualThreadSupport.isVirtual(Thread.currentThread()) ?
6564
VirtualPoolHolder.virtualPool.acquireAndLinkPooled() :
6665
nativePool.acquirePooled();
6766
}
@@ -170,41 +169,6 @@ private static class VThreadBufferRecycler extends BufferRecycler {
170169
}
171170
}
172171

173-
private static class VirtualPredicate {
174-
private static final MethodHandle virtualMh = findVirtualMH();
175-
176-
private static MethodHandle findVirtualMH() {
177-
try {
178-
return MethodHandles.publicLookup().findVirtual(Thread.class, "isVirtual",
179-
MethodType.methodType(boolean.class));
180-
} catch (Exception e) {
181-
return null;
182-
}
183-
}
184-
185-
private static Predicate<Thread> findIsVirtualPredicate() {
186-
if (virtualMh != null) {
187-
return new Predicate<Thread>() {
188-
@Override
189-
public boolean test(Thread thread) {
190-
try {
191-
return (boolean) virtualMh.invokeExact(thread);
192-
} catch (Throwable e) {
193-
throw new RuntimeException(e);
194-
}
195-
}
196-
};
197-
}
198-
199-
return new Predicate<Thread>() {
200-
@Override
201-
public boolean test(Thread thread) {
202-
return false;
203-
}
204-
};
205-
}
206-
}
207-
208172
/**
209173
* This class is used to hash the thread requiring a pooled resource using a multiplicative
210174
* Fibonacci hashing implementation. The resulting hash is then used to calculate the
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright (c) 2011-2026 Contributors to the Eclipse Foundation
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7+
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
8+
*
9+
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10+
*/
11+
package io.vertx.core.impl;
12+
13+
import java.util.concurrent.ThreadFactory;
14+
15+
/**
16+
* Virtual thread support.
17+
*
18+
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
19+
*/
20+
public class VirtualThreadSupport {
21+
22+
public static final ThreadFactory VIRTUAL_THREAD_FACTORY;
23+
public static final boolean VIRTUAL_THREAD_AVAILABLE;
24+
25+
static {
26+
VIRTUAL_THREAD_FACTORY = Thread.ofVirtual().name("vert.x-virtual-thread-").factory();
27+
VIRTUAL_THREAD_AVAILABLE = true;
28+
}
29+
30+
/**
31+
* @return whether the {@code thread} is virtual
32+
*/
33+
public static boolean isVirtual(Thread thread) {
34+
return thread.isVirtual();
35+
}
36+
}

0 commit comments

Comments
 (0)