Skip to content

Commit 34fbf99

Browse files
committed
Consolidate virtual thread factory creation into VertxThread
Replace three duplicate reflection blocks with a single VertxThread.virtualThreadFactory(prefix) method. Remove phantom platform thread hacks for naming in VirtualThreadNioTransport and VertxTestBase. Update reflect-config.json to point at VertxThread.
1 parent 3a922b5 commit 34fbf99

5 files changed

Lines changed: 38 additions & 75 deletions

File tree

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

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@
7575
import java.io.InputStream;
7676
import java.lang.ref.Cleaner;
7777
import java.lang.ref.WeakReference;
78-
import java.lang.reflect.Method;
7978
import java.net.InetSocketAddress;
8079
import java.nio.charset.StandardCharsets;
8180
import java.util.*;
@@ -113,20 +112,8 @@ public class VertxImpl implements VertxInternal, MetricsProvider {
113112
private static final String NETTY_IO_RATIO_PROPERTY_NAME = "vertx.nettyIORatio";
114113
private static final int NETTY_IO_RATIO = Integer.getInteger(NETTY_IO_RATIO_PROPERTY_NAME, 50);
115114

116-
// Not cached for graalvm
117115
private static ThreadFactory virtualThreadFactory() {
118-
try {
119-
Class<?> builderClass = ClassLoader.getSystemClassLoader().loadClass("java.lang.Thread$Builder");
120-
Class<?> ofVirtualClass = ClassLoader.getSystemClassLoader().loadClass("java.lang.Thread$Builder$OfVirtual");
121-
Method ofVirtualMethod = Thread.class.getDeclaredMethod("ofVirtual");
122-
Object builder = ofVirtualMethod.invoke(null);
123-
Method nameMethod = ofVirtualClass.getDeclaredMethod("name", String.class, long.class);
124-
Method factoryMethod = builderClass.getDeclaredMethod("factory");
125-
builder = nameMethod.invoke(builder, "vert.x-virtual-thread-", 0L);
126-
return (ThreadFactory) factoryMethod.invoke(builder);
127-
} catch (Exception e) {
128-
return null;
129-
}
116+
return VertxThread.virtualThreadFactory("vert.x-virtual-thread-");
130117
}
131118

132119
static {

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
import io.vertx.core.internal.threadchecker.ThreadInfo;
1616
import io.vertx.core.internal.ContextInternal;
1717

18+
import java.lang.reflect.Method;
1819
import java.util.concurrent.ConcurrentHashMap;
20+
import java.util.concurrent.ThreadFactory;
1921
import java.util.concurrent.TimeUnit;
2022

2123
/**
@@ -29,6 +31,27 @@ public class VertxThread extends FastThreadLocalThread {
2931
// One day ScopedValues will be a better choice.
3032
private static final ConcurrentHashMap<Thread, VtInfo> VIRTUAL_THREADS = new ConcurrentHashMap<>();
3133

34+
/**
35+
* Create a {@link ThreadFactory} that produces virtual threads with the given name prefix
36+
* and a monotonically increasing counter. Returns {@code null} if virtual threads are not available.
37+
* <p>
38+
* Not cached for GraalVM native-image compatibility.
39+
*/
40+
public static ThreadFactory virtualThreadFactory(String prefix) {
41+
try {
42+
Class<?> builderClass = ClassLoader.getSystemClassLoader().loadClass("java.lang.Thread$Builder");
43+
Class<?> ofVirtualClass = ClassLoader.getSystemClassLoader().loadClass("java.lang.Thread$Builder$OfVirtual");
44+
Method ofVirtualMethod = Thread.class.getDeclaredMethod("ofVirtual");
45+
Object builder = ofVirtualMethod.invoke(null);
46+
Method nameMethod = ofVirtualClass.getDeclaredMethod("name", String.class, long.class);
47+
Method factoryMethod = builderClass.getDeclaredMethod("factory");
48+
builder = nameMethod.invoke(builder, prefix, 0L);
49+
return (ThreadFactory) factoryMethod.invoke(builder);
50+
} catch (Exception e) {
51+
return null;
52+
}
53+
}
54+
3255
static final class VtInfo {
3356
final boolean worker;
3457
final VertxImpl owner;

vertx-core/src/main/java/io/vertx/core/impl/transports/VirtualThreadNioTransport.java

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,9 @@
1414
import io.netty.channel.nio.NioIoHandler;
1515
import io.netty.channel.socket.DatagramChannel;
1616
import io.netty.channel.socket.InternetProtocolFamily;
17-
import io.vertx.core.impl.VertxImpl;
1817
import io.vertx.core.impl.VertxThread;
1918
import io.vertx.core.spi.transport.Transport;
2019

21-
import java.lang.reflect.Method;
2220
import java.util.concurrent.Executor;
2321
import java.util.concurrent.ThreadFactory;
2422
import java.util.concurrent.TimeUnit;
@@ -33,24 +31,9 @@ public class VirtualThreadNioTransport implements Transport {
3331
private static final long RUNNING_YIELD_NS = TimeUnit.MICROSECONDS
3432
.toNanos(Integer.getInteger("io.vertx.virtualthread.running.yield.us", 1));
3533

36-
private static final ThreadFactory VIRTUAL_THREAD_FACTORY;
37-
private static final Throwable UNAVAILABILITY_CAUSE;
38-
39-
static {
40-
ThreadFactory factory = null;
41-
Throwable cause = null;
42-
try {
43-
Class<?> builderClass = ClassLoader.getSystemClassLoader().loadClass("java.lang.Thread$Builder");
44-
Method ofVirtualMethod = Thread.class.getDeclaredMethod("ofVirtual");
45-
Object builder = ofVirtualMethod.invoke(null);
46-
Method factoryMethod = builderClass.getDeclaredMethod("factory");
47-
factory = (ThreadFactory) factoryMethod.invoke(builder);
48-
} catch (Exception e) {
49-
cause = e;
50-
}
51-
VIRTUAL_THREAD_FACTORY = factory;
52-
UNAVAILABILITY_CAUSE = cause;
53-
}
34+
private static final ThreadFactory VIRTUAL_THREAD_FACTORY = VertxThread.virtualThreadFactory("vert.x-eventloop-vt-");
35+
private static final Throwable UNAVAILABILITY_CAUSE = VIRTUAL_THREAD_FACTORY == null
36+
? new UnsupportedOperationException("Virtual threads are not available") : null;
5437

5538
public static final Transport INSTANCE = new VirtualThreadNioTransport();
5639

@@ -109,17 +92,12 @@ public ChannelFactory<? extends ServerChannel> serverChannelFactory(boolean doma
10992
@Override
11093
public EventLoopGroup eventLoopGroup(int type, int nThreads, ThreadFactory threadFactory, int ioRatio) {
11194
if (VIRTUAL_THREAD_FACTORY == null) {
112-
throw new IllegalStateException("Virtual threads are not available", UNAVAILABILITY_CAUSE);
95+
throw new IllegalStateException("Virtual threads are not available");
11396
}
11497
return new MultiThreadIoEventLoopGroup(nThreads, (Executor) null, ioHandlerFactory()) {
11598
@Override
11699
protected IoEventLoop newChild(Executor executor, IoHandlerFactory ioHandlerFactory, Object... args) {
117100
ManualIoEventLoop eventLoop = new ManualIoEventLoop(this, null, ioHandlerFactory);
118-
// Create a platform thread via the Vert.x thread factory to obtain the correct name
119-
// and register it with the blocked thread checker. Then name the virtual thread the same.
120-
Thread platformThread = threadFactory.newThread(() -> {});
121-
VertxImpl owner = (platformThread instanceof VertxThread)
122-
? ((VertxThread) platformThread).owner() : null;
123101
Runnable pumpTask = () -> {
124102
while (!eventLoop.isShuttingDown()) {
125103
eventLoop.run(0, RUNNING_YIELD_NS);
@@ -132,8 +110,7 @@ protected IoEventLoop newChild(Executor executor, IoHandlerFactory ioHandlerFact
132110
Thread.yield();
133111
}
134112
};
135-
Thread vt = VIRTUAL_THREAD_FACTORY.newThread(VertxThread.apply(pumpTask, false, owner));
136-
vt.setName(platformThread.getName());
113+
Thread vt = VIRTUAL_THREAD_FACTORY.newThread(VertxThread.apply(pumpTask, false, null));
137114
eventLoop.setOwningThread(vt);
138115
vt.start();
139116
return eventLoop;

vertx-core/src/main/resources/META-INF/native-image/io.vertx/vertx-core/reflect-config.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
{
33
"name": "java.lang.Thread$Builder",
44
"condition": {
5-
"typeReachable": "io.vertx.core.impl.VertxImpl"
5+
"typeReachable": "io.vertx.core.impl.VertxThread"
66
},
77
"methods": [
88
{
@@ -14,7 +14,7 @@
1414
{
1515
"name": "java.lang.Thread$Builder$OfVirtual",
1616
"condition": {
17-
"typeReachable": "io.vertx.core.impl.VertxImpl"
17+
"typeReachable": "io.vertx.core.impl.VertxThread"
1818
},
1919
"methods": [
2020
{
@@ -26,7 +26,7 @@
2626
{
2727
"name": "java.lang.Thread",
2828
"condition": {
29-
"typeReachable": "io.vertx.core.impl.VertxImpl"
29+
"typeReachable": "io.vertx.core.impl.VertxThread"
3030
},
3131
"methods": [
3232
{

vertx-core/src/test/java/io/vertx/test/core/VertxTestBase.java

Lines changed: 6 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import io.vertx.core.*;
1515
import io.vertx.core.impl.ThreadPerTaskExecutorService;
16+
import io.vertx.core.impl.VertxThread;
1617
import io.vertx.core.internal.VertxInternal;
1718
import io.vertx.core.internal.logging.Logger;
1819
import io.vertx.core.internal.logging.LoggerFactory;
@@ -229,37 +230,12 @@ protected Vertx createVertx(VertxOptions options) {
229230
return vertx;
230231
}
231232

232-
private static final java.util.concurrent.ThreadFactory VIRTUAL_THREAD_FACTORY;
233-
234-
static {
235-
java.util.concurrent.ThreadFactory f = null;
236-
if (USE_VIRTUAL_THREAD_WORKER) {
237-
try {
238-
Class<?> builderClass = ClassLoader.getSystemClassLoader().loadClass("java.lang.Thread$Builder");
239-
java.lang.reflect.Method ofVirtualMethod = Thread.class.getDeclaredMethod("ofVirtual");
240-
Object builder = ofVirtualMethod.invoke(null);
241-
java.lang.reflect.Method factoryMethod = builderClass.getDeclaredMethod("factory");
242-
f = (java.util.concurrent.ThreadFactory) factoryMethod.invoke(builder);
243-
} catch (Exception e) {
244-
throw new ExceptionInInitializerError(e);
245-
}
246-
}
247-
VIRTUAL_THREAD_FACTORY = f;
248-
}
249-
250233
private io.vertx.core.spi.ExecutorServiceFactory createVirtualThreadExecutorServiceFactory() {
251-
// Replace worker pool with virtual-thread-per-task executor.
252-
// The provided threadFactory is used to obtain thread names for the virtual threads.
253-
return (threadFactory, concurrency, maxConcurrency) -> {
254-
java.util.concurrent.ThreadFactory namingVtFactory = runnable -> {
255-
Thread platformThread = threadFactory.newThread(() -> {});
256-
String name = platformThread.getName();
257-
Thread vt = VIRTUAL_THREAD_FACTORY.newThread(runnable);
258-
vt.setName(name);
259-
return vt;
260-
};
261-
return new ThreadPerTaskExecutorService(namingVtFactory);
262-
};
234+
java.util.concurrent.ThreadFactory vtFactory = VertxThread.virtualThreadFactory("vert.x-worker-vt-");
235+
if (vtFactory == null) {
236+
throw new IllegalStateException("Virtual threads are not available");
237+
}
238+
return (threadFactory, concurrency, maxConcurrency) -> new ThreadPerTaskExecutorService(vtFactory);
263239
}
264240

265241
/**

0 commit comments

Comments
 (0)