Skip to content
Draft
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
Empty file removed build-release-17
Empty file.
69 changes: 49 additions & 20 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,6 @@
<artifactId>nativeimage</artifactId>
<version>24.1.2</version>
<scope>provided</scope>
<!-- We only want the included annotations -->
<exclusions>
<exclusion>
<artifactId>*</artifactId>
<groupId>*</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
Expand Down Expand Up @@ -182,19 +175,55 @@
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<annotationProcessorPath>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging-processor</artifactId>
<version>${version.jboss.logging.tools}</version>
</annotationProcessorPath>
</annotationProcessorPaths>
<compilerArgs>
<!-- This is for SVM dependency -->
<compilerArg>--add-reads=org.jboss.threads=ALL-UNNAMED</compilerArg>
</compilerArgs>
</configuration>
<executions>
<execution>
<id>default-compile</id>
<configuration>
<annotationProcessorPaths>
<annotationProcessorPath>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging-processor</artifactId>
<version>${version.jboss.logging.tools}</version>
</annotationProcessorPath>
</annotationProcessorPaths>
<compilerArgs>
<compilerArg>--add-exports=java.base/jdk.internal.vm=ALL-UNNAMED,org.jboss.threads</compilerArg>
</compilerArgs>
<source>21</source>
<target>21</target>
</configuration>
</execution>
<execution>
<id>compile-virtual-threads</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<annotationProcessorPaths>
<annotationProcessorPath>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging-processor</artifactId>
<version>${version.jboss.logging.tools}</version>
</annotationProcessorPath>
</annotationProcessorPaths>
<excludes>
<exclude>**/org/jboss/threads/virtual/*.java</exclude>
</excludes>
<release>17</release>
</configuration>
</execution>
<execution>
<id>default-testCompile</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
<configuration>
<release>17</release>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-source-plugin</artifactId>
Expand Down
1 change: 1 addition & 0 deletions src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
requires jdk.unsupported;
requires org.jboss.logging;
requires static org.jboss.logging.annotations;
requires static org.graalvm.nativeimage;
requires org.wildfly.common;
requires io.smallrye.common.annotation;
requires io.smallrye.common.constraint;
Expand Down
113 changes: 113 additions & 0 deletions src/main/java/org/jboss/threads/virtual/Access.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package org.jboss.threads.virtual;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;

import jdk.internal.vm.ThreadContainer;

/**
* Access methods for virtual thread internals.
*/
final class Access {
private static final MethodHandle currentCarrierThread;
private static final MethodHandle virtualThreadFactory;
private static final MethodHandle threadStartWithContainer;
private static final MethodHandle schedulerGetter;
private static final MethodHandle continuationGetter;

static {
MethodHandle ct;
MethodHandle vtf;
MethodHandle tswc;
MethodHandle sg;
MethodHandle cg;
try {
MethodHandles.Lookup thr = MethodHandles.privateLookupIn(Thread.class, MethodHandles.lookup());
ct = thr.findStatic(Thread.class, "currentCarrierThread", MethodType.methodType(Thread.class));
Class<?> vtbClass = Class.forName("java.lang.ThreadBuilders$VirtualThreadBuilder", false, null);
try {
vtf = thr.findConstructor(vtbClass, MethodType.methodType(void.class, ScheduledExecutorService.class));
} catch (NoSuchMethodException | NoSuchMethodError ignored) {
// unpatched JDK
vtf = thr.findConstructor(vtbClass, MethodType.methodType(void.class, Executor.class));
}
// create efficient transformer
vtf = vtf.asType(MethodType.methodType(Thread.Builder.OfVirtual.class, ThreadScheduler.class));
// todo: maybe instead, we can directly call `java.lang.ThreadBuilders.newVirtualThread`
//void start(jdk.internal.vm.ThreadContainer container)
tswc = thr.findVirtual(Thread.class, "start", MethodType.methodType(void.class, ThreadContainer.class));
Class<?> vtc = thr.findClass("java.lang.VirtualThread");
MethodHandles.Lookup vthr = MethodHandles.privateLookupIn(vtc, MethodHandles.lookup());
try {
sg = vthr.findGetter(vtc, "scheduler", ScheduledExecutorService.class);
} catch (NoSuchFieldException | NoSuchFieldError ignored) {
// unpatched JDK
sg = vthr.findGetter(vtc, "scheduler", Executor.class);
}
sg = sg.asType(MethodType.methodType(Executor.class, Thread.class));
cg = vthr.findGetter(vtc, "runContinuation", Runnable.class).asType(MethodType.methodType(Runnable.class, Thread.class));
} catch (Throwable e) {
// no good
throw new InternalError("Cannot initialize virtual threads", e);
}
currentCarrierThread = ct;
virtualThreadFactory = vtf;
threadStartWithContainer = tswc;
schedulerGetter = sg;
continuationGetter = cg;
}

static Thread currentCarrier() {
try {
return (Thread) currentCarrierThread.invokeExact();
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}

static Thread.Builder.OfVirtual threadBuilder(ThreadScheduler threadScheduler) {
try {
return (Thread.Builder.OfVirtual) virtualThreadFactory.invokeExact(threadScheduler);
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}

static void startThread(Thread thread, ThreadContainer threadContainer) {
try {
threadStartWithContainer.invokeExact(thread, threadContainer);
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}

static Executor schedulerOf(Thread thread) {
try {
return (Executor) schedulerGetter.invokeExact(thread);
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}

static Runnable continuationOf(Thread thread) {
try {
return (Runnable) continuationGetter.invokeExact(thread);
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}
9 changes: 9 additions & 0 deletions src/main/java/org/jboss/threads/virtual/Dispatcher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.jboss.threads.virtual;

import java.util.concurrent.ScheduledFuture;

abstract class Dispatcher {
abstract void execute(UserThreadScheduler continuation);

abstract ScheduledFuture<?> schedule(Runnable task, long nanos);
}
51 changes: 51 additions & 0 deletions src/main/java/org/jboss/threads/virtual/EventLoop.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.jboss.threads.virtual;

import java.util.concurrent.locks.LockSupport;

import io.smallrye.common.annotation.Experimental;

/**
* An event loop for a virtual thread scheduler.
* There will be one instance per I/O thread within an event loop group.
*/
@Experimental("Experimental virtual thread support")
public abstract class EventLoop {
/**
* Construct a new instance.
*/
protected EventLoop() {}

/**
* Unpark all ready threads and return,
* possibly waiting for some amount of time if no threads are ready.
* The wait time may be {@code 0}, in which case this method should return immediately if no threads are ready,
* or {@code -1}, in which case the method should wait indefinitely for threads to become ready.
* Otherwise, the wait time is the maximum number of nanoseconds to wait for threads to become ready before returning.
* <p>
* Regardless of the wait time, the method should park or return immediately if the {@link #wakeup()} method is invoked
* from any thread.
* <p>
* This method will be called in a loop (the event loop, in fact).
* After each invocation of this method, up to one other waiting thread will be continued.
* Since this generally would lead to busy-looping,
* the implementation of this method <em>should</em>
* {@linkplain VirtualThreads#yieldNanos(long) yield for some amount of time} before returning to allow other threads to run.
* <p>
* Note that {@linkplain Thread#sleep(long) sleeping} or {@linkplain LockSupport#park() parking} may cause latency spikes,
* so it is not recommended.
* <p>
* This method should only be called from the event loop virtual thread.
*
* @param waitTime {@code 0} to return immediately after unparking any ready threads (even if there are none),
* {@code -1} unpark any ready threads or to wait indefinitely for a thread to become ready,
* or any positive integer to unpark any ready threads or to wait for no more than that number of nanoseconds
* @throws InterruptedException if some interruptible operation was interrupted
*/
protected abstract void unparkAny(long waitTime) throws InterruptedException;

/**
* Forcibly awaken the event loop, if it is currently blocked in {@link #unparkAny(long)}.
* This method may be called from any thread.
*/
protected abstract void wakeup();
}
Loading