9
0
mirror of https://github.com/Dreeam-qwq/Gale.git synced 2025-12-19 14:59:29 +00:00
Files
Gale/patches/server/0129-Virtual-thread-support.patch
2024-06-30 00:04:31 +08:00

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));
+ }
+}