mirror of
https://github.com/Dreeam-qwq/Gale.git
synced 2025-12-19 14:59:29 +00:00
283 lines
12 KiB
Diff
283 lines
12 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Martijn Muijsers <martijnmuijsers@live.nl>
|
|
Date: Wed, 9 Aug 2023 15:04:56 +0200
|
|
Subject: [PATCH] Virtual thread support
|
|
|
|
License: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
|
|
Gale - https://galemc.org
|
|
|
|
diff --git a/src/main/java/com/destroystokyo/paper/Metrics.java b/src/main/java/com/destroystokyo/paper/Metrics.java
|
|
index 13a51c939b8bd17584a474217823150688af809f..ac23c85f17e0a5cd9702888a873cc470428b3e34 100644
|
|
--- a/src/main/java/com/destroystokyo/paper/Metrics.java
|
|
+++ b/src/main/java/com/destroystokyo/paper/Metrics.java
|
|
@@ -973,6 +973,22 @@ public class Metrics {
|
|
}));
|
|
// Gale end - SIMD support - include in metrics
|
|
|
|
+ // Gale start - virtual thread support - include in metrics
|
|
+ Map<String, Map<String, Integer>> virtualThreadSupportMap = new HashMap<>(2);
|
|
+ {
|
|
+ Map<String, Integer> entry = new HashMap<>(2);
|
|
+ boolean isSupported = org.galemc.gale.virtualthread.VirtualThreadService.isSupported();
|
|
+ try {
|
|
+ int javaMajorVersion = org.galemc.gale.virtualthread.VirtualThreadService.getJavaMajorVersion();
|
|
+ entry.put(isSupported + " (Java " + javaMajorVersion + ")", 1);
|
|
+ } catch (Exception ignored) {
|
|
+ entry.put(String.valueOf(isSupported), 1);
|
|
+ }
|
|
+ virtualThreadSupportMap.put(String.valueOf(isSupported), entry);
|
|
+ }
|
|
+ metrics.addCustomChart(new Metrics.DrilldownPie("virtual_thread_support", () -> virtualThreadSupportMap));
|
|
+ // Gale end - virtual thread support - include in metrics
|
|
+
|
|
}
|
|
|
|
}
|
|
diff --git a/src/main/java/org/galemc/gale/virtualthread/DirectVirtualThreadService.java b/src/main/java/org/galemc/gale/virtualthread/DirectVirtualThreadService.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..38d3b77f3f8d582ce6732afab7e6787d6d0a0ead
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/galemc/gale/virtualthread/DirectVirtualThreadService.java
|
|
@@ -0,0 +1,50 @@
|
|
+// Gale - virtual thread support
|
|
+
|
|
+package org.galemc.gale.virtualthread;
|
|
+
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+
|
|
+import java.util.Objects;
|
|
+import java.util.concurrent.ThreadFactory;
|
|
+
|
|
+/**
|
|
+ * An implementation of {@link VirtualThreadService} that can create virtual threads directly.
|
|
+ *
|
|
+ * @author Martijn Muijsers
|
|
+ */
|
|
+final class DirectVirtualThreadService extends VirtualThreadService {
|
|
+
|
|
+ private DirectVirtualThreadService() {
|
|
+ super();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @NotNull ThreadFactory createFactory() {
|
|
+ // Disabled until Minecraft requires servers to have a Java version that can read class files compiled with functionality from Java 19+ on preview / Java 21+ on stable
|
|
+ //throw new UnsupportedOperationException();
|
|
+ return Thread.ofVirtual().factory();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @NotNull Thread start(@NotNull Runnable task) {
|
|
+ // Disabled until Minecraft requires servers to have a Java version that can read class files compiled with functionality from Java 19+ on preview / Java 21+ on stable
|
|
+ //throw new UnsupportedOperationException();
|
|
+ Objects.requireNonNull(task, "The task to start a virtual thread cannot be null");
|
|
+ return Thread.ofVirtual().start(task);
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * @return A functional {@link DirectVirtualThreadService}.
|
|
+ * @throws Throwable If creating virtual threads directly is not supported by the current runtime.
|
|
+ * This could be any {@link Throwable}, including an {@link Exception} or an {@link Error}.
|
|
+ */
|
|
+ static @NotNull DirectVirtualThreadService create() throws Throwable {
|
|
+ // Disabled until Minecraft requires servers to have a Java version that can read class files compiled with functionality from Java 19+ on preview / Java 21+ on stable
|
|
+ //throw new UnsupportedOperationException();
|
|
+ var service = new DirectVirtualThreadService();
|
|
+ // Run some tests to verify
|
|
+ service.runTest();
|
|
+ // If we end up here, it works
|
|
+ return service;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/galemc/gale/virtualthread/ReflectionVirtualThreadService.java b/src/main/java/org/galemc/gale/virtualthread/ReflectionVirtualThreadService.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..19b83cf45034be57d11413361b1b0c8756b12a29
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/galemc/gale/virtualthread/ReflectionVirtualThreadService.java
|
|
@@ -0,0 +1,75 @@
|
|
+// Gale - virtual thread support
|
|
+
|
|
+package org.galemc.gale.virtualthread;
|
|
+
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+
|
|
+import java.lang.reflect.Method;
|
|
+import java.util.Objects;
|
|
+import java.util.concurrent.ThreadFactory;
|
|
+
|
|
+/**
|
|
+ * An implementation of {@link VirtualThreadService} that can create virtual threads using Java reflection.
|
|
+ *
|
|
+ * @author Martijn Muijsers
|
|
+ */
|
|
+final class ReflectionVirtualThreadService extends VirtualThreadService {
|
|
+
|
|
+ /**
|
|
+ * The {@link Thread}<code>#ofVirtual()</code> method.
|
|
+ */
|
|
+ private final @NotNull Method Thread_ofVirtual_method;
|
|
+
|
|
+ /**
|
|
+ * The {@link Thread}<code>.Builder#factory()</code> method.
|
|
+ */
|
|
+ private final @NotNull Method Thread_Builder_factory_method;
|
|
+
|
|
+ /**
|
|
+ * The {@link Thread}<code>.Builder#start(Runnable)</code> method.
|
|
+ */
|
|
+ private final @NotNull Method Thread_Builder_start_method;
|
|
+
|
|
+ private ReflectionVirtualThreadService() throws Throwable {
|
|
+ this.Thread_ofVirtual_method = Objects.requireNonNull(Thread.class.getMethod("ofVirtual"));
|
|
+ // The Thread.Builder class
|
|
+ var Thread_Builder_class = Objects.requireNonNull(Class.forName("java.lang.Thread$Builder"));
|
|
+ this.Thread_Builder_factory_method = Objects.requireNonNull(Thread_Builder_class.getMethod("factory"));
|
|
+ this.Thread_Builder_start_method = Objects.requireNonNull(Thread_Builder_class.getMethod("start", Runnable.class));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @NotNull ThreadFactory createFactory() {
|
|
+ try {
|
|
+ return (ThreadFactory) this.Thread_Builder_factory_method.invoke(this.Thread_ofVirtual_method.invoke(null));
|
|
+ } catch (Exception e) {
|
|
+ // This should not be possible because it was tested in create()
|
|
+ throw new RuntimeException(e);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @NotNull Thread start(@NotNull Runnable task) {
|
|
+ Objects.requireNonNull(task, "The task to start a virtual thread cannot be null");
|
|
+ try {
|
|
+ return (Thread) this.Thread_Builder_start_method.invoke(this.Thread_ofVirtual_method.invoke(null), task);
|
|
+ } catch (Exception e) {
|
|
+ // This should not be possible because it was tested in create()
|
|
+ throw new RuntimeException(e);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * @return A functional {@link ReflectionVirtualThreadService}.
|
|
+ * @throws Throwable If creating virtual threads via reflection is not supported by the current runtime.
|
|
+ * This could be any {@link Throwable}, including an {@link Exception} or an {@link Error}.
|
|
+ */
|
|
+ static @NotNull ReflectionVirtualThreadService create() throws Throwable {
|
|
+ // This will already throw something if the reflection fails
|
|
+ var service = new ReflectionVirtualThreadService();
|
|
+ // Run some tests to verify
|
|
+ service.runTest();
|
|
+ // If we end up here, it works
|
|
+ return service;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/org/galemc/gale/virtualthread/VirtualThreadService.java b/src/main/java/org/galemc/gale/virtualthread/VirtualThreadService.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..e9757e8ade7198accb7cf34390ed70628a0f7e33
|
|
--- /dev/null
|
|
+++ b/src/main/java/org/galemc/gale/virtualthread/VirtualThreadService.java
|
|
@@ -0,0 +1,104 @@
|
|
+// Gale - virtual thread support
|
|
+
|
|
+package org.galemc.gale.virtualthread;
|
|
+
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.jetbrains.annotations.Nullable;
|
|
+
|
|
+import java.util.concurrent.ThreadFactory;
|
|
+
|
|
+/**
|
|
+ * An abstract service to create virtual threads.
|
|
+ *
|
|
+ * @author Martijn Muijsers
|
|
+ */
|
|
+public sealed abstract class VirtualThreadService permits ReflectionVirtualThreadService, DirectVirtualThreadService {
|
|
+
|
|
+ /**
|
|
+ * @return A {@link ThreadFactory} that produces virtual threads.
|
|
+ */
|
|
+ public abstract @NotNull ThreadFactory createFactory();
|
|
+
|
|
+ /**
|
|
+ * @param task The runnable for the thread to execute.
|
|
+ * @return A virtual thread that has been started with the given task.
|
|
+ */
|
|
+ public abstract @NotNull Thread start(Runnable task);
|
|
+
|
|
+ /**
|
|
+ * Runs a test on the {@link #createFactory} and {@link #start} methods,
|
|
+ * which certainly throws some {@link Throwable} if something goes wrong.
|
|
+ */
|
|
+ protected void runTest() throws Throwable {
|
|
+ // This will definitely throw something if it doesn't work
|
|
+ try {
|
|
+ this.start(() -> {}).join();
|
|
+ } catch (InterruptedException ignored) {} // Except InterruptedException, we don't care about that one
|
|
+ try {
|
|
+ var thread = this.createFactory().newThread(() -> {});
|
|
+ thread.start();
|
|
+ thread.join();
|
|
+ } catch (InterruptedException ignored) {} // Except InterruptedException, we don't care about that one
|
|
+ // If we end up here, it works
|
|
+ }
|
|
+
|
|
+ private static boolean initialized = false;
|
|
+
|
|
+ /**
|
|
+ * The {@link VirtualThreadService} for the current runtime,
|
|
+ * or null if virtual threads are not supported, or if not {@link #initialized} yet.
|
|
+ */
|
|
+ private static @Nullable VirtualThreadService implementation;
|
|
+
|
|
+ /**
|
|
+ * @return Whether virtual threads are supported on the current runtime.
|
|
+ */
|
|
+ public static boolean isSupported() {
|
|
+ return get() != null;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * @return The {@link VirtualThreadService} for the current runtime,
|
|
+ * or null if virtual threads are not {@linkplain #isSupported() supported}.
|
|
+ *
|
|
+ * This method is thread-safe only after the first time it has been fully run.
|
|
+ */
|
|
+ public static @Nullable VirtualThreadService get() {
|
|
+ if (!initialized) {
|
|
+ initialized = true;
|
|
+ try {
|
|
+ implementation = DirectVirtualThreadService.create();
|
|
+ } catch (Throwable ignored) {
|
|
+ try {
|
|
+ implementation = ReflectionVirtualThreadService.create();
|
|
+ } catch (Throwable ignored2) {}
|
|
+ }
|
|
+ }
|
|
+ return implementation;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * The minimum major version of Java that is known to support using virtual threads
|
|
+ * (although possibly behind a feature preview flag).
|
|
+ */
|
|
+ public static final int minimumJavaMajorVersionWithFeaturePreview = 19;
|
|
+
|
|
+ /**
|
|
+ * The minimum major version of Java that is known to support using virtual threads
|
|
+ * even without any feature preview flags.
|
|
+ */
|
|
+ public static final int minimumJavaMajorVersionWithoutFeaturePreview = 21;
|
|
+
|
|
+ public static int getJavaMajorVersion() {
|
|
+ var version = System.getProperty("java.version");
|
|
+ if (version.startsWith("1.")) {
|
|
+ return version.charAt(2) - '0';
|
|
+ }
|
|
+ if (version.contains("-")) {
|
|
+ version = version.substring(0, version.indexOf("-"));
|
|
+ }
|
|
+
|
|
+ int dotIndex = version.indexOf(".");
|
|
+ return Integer.parseInt(dotIndex == -1 ? version : version.substring(0, dotIndex));
|
|
+ }
|
|
+}
|