From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Martijn Muijsers Date: Mon, 13 Feb 2023 21:40:20 +0100 Subject: [PATCH] Virtual threads utility License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) Gale - https://galemc.org diff --git a/src/main/java/org/galemc/gale/util/VirtualThreads.java b/src/main/java/org/galemc/gale/util/VirtualThreads.java new file mode 100644 index 0000000000000000000000000000000000000000..0ae0cde2ed65939f84d0f843e2c2b89beb76ed3f --- /dev/null +++ b/src/main/java/org/galemc/gale/util/VirtualThreads.java @@ -0,0 +1,144 @@ +// Gale - virtual threads utility + +package org.galemc.gale.util; + +import org.galemc.gale.executor.annotation.YieldFree; +import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Method; +import java.util.concurrent.ThreadFactory; + +/** + * A utility class to detect and provide the ability to use virtual threads. + * + * @author Martijn Muijsers under AGPL-3.0 + */ +@SuppressWarnings({"JavadocReference", "Since15"}) +@AnyThreadSafe +@YieldFree +public final class VirtualThreads { + + private VirtualThreads() {} + + /** + * The minimum major version of Java that is known to support using virtual threads. + */ + public static final int minimumJavaMajorVersion = 19; + + /** + * The {@link Thread#ofVirtual()} method, obtained via Reflection, + * or null if it could not be obtained. + */ + public static final @Nullable Method Thread_ofVirtual_method; + + /** + * The {@link Thread.Builder} class, obtained via Reflection, + * or null if it could not be obtained. + */ + public static final @Nullable Class Thread_Builder_class; + + /** + * The {@link Thread.Builder#factory()} method, obtained via Reflection, + * or null if it could not be obtained. + */ + public static final @Nullable Method Thread_Builder_factory_method; + + /** + * The {@link Thread.Builder#start(Runnable)} method, obtained via Reflection, + * or null if it could not be obtained. + */ + public static final @Nullable Method Thread_Builder_start_method; + + /** + * Whether virtual threads are enabled. + */ + public static final boolean areVirtualThreadsEnabled; + + /** + * @return A {@link ThreadFactory} that produces {@link java.lang.VirtualThread}s, + * or null if {@link #areVirtualThreadsEnabled} is false. + */ + public static @Nullable ThreadFactory createVirtualThreadFactory() { + if (!areVirtualThreadsEnabled) { + return null; + } + try { + //noinspection DataFlowIssue + return (ThreadFactory) Thread_Builder_factory_method.invoke(Thread_ofVirtual_method.invoke(null)); + } catch (Exception e) { + // We assume this does not happen when areVirtualThreadsEnabled has been set to true + throw new RuntimeException(e); + } + } + + /** + * @param task The runnable for the thread to execute. + * @return A {@link java.lang.VirtualThread} that has been started with the given task, + * or null if {@link #areVirtualThreadsEnabled} is false. + */ + public static @Nullable Thread startVirtualThread(Runnable task) { + if (!areVirtualThreadsEnabled) { + return null; + } + try { + //noinspection DataFlowIssue + return (Thread) Thread_Builder_start_method.invoke(Thread_ofVirtual_method.invoke(null), task); + } catch (Exception e) { + // We assume this does not happen when areVirtualThreadsEnabled has been set to true + throw new RuntimeException(e); + } + } + + static { + { + Method method; + try { + method = Thread.class.getMethod("ofVirtual"); + } catch (Throwable ignored) { + method = null; + } + Thread_ofVirtual_method = method; + if (Thread_ofVirtual_method != null) { + Thread_ofVirtual_method.setAccessible(true); + } + } + { + Class clazz; + try { + //noinspection DataFlowIssue + clazz = Thread_ofVirtual_method.invoke(null).getClass(); + } catch (Throwable ignored) { + clazz = null; + } + Thread_Builder_class = clazz; + } + { + Method method; + try { + //noinspection DataFlowIssue + method = Thread_Builder_class.getMethod("factory"); + } catch (Throwable ignored) { + method = null; + } + Thread_Builder_factory_method = method; + if (Thread_Builder_factory_method != null) { + Thread_Builder_factory_method.setAccessible(true); + } + } + { + Method method; + try { + method = Thread_Builder_class.getMethod("start"); + } catch (Throwable ignored) { + method = null; + } + Thread_Builder_start_method = method; + if (Thread_Builder_start_method != null) { + Thread_Builder_start_method.setAccessible(true); + } + } + areVirtualThreadsEnabled = Thread_ofVirtual_method != null && Thread_Builder_class != null && Thread_Builder_factory_method != null && Thread_Builder_start_method != null; + } + +}