diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/ContinuationsFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/ContinuationsFeature.java index c25fd86f1bf7..690a57c2646a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/ContinuationsFeature.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/ContinuationsFeature.java @@ -36,7 +36,9 @@ import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.heap.StoredContinuation; import com.oracle.svm.core.heap.StoredContinuationAccess; +import com.oracle.svm.core.option.SubstrateOptionsParser; import com.oracle.svm.core.util.UserError; +import com.oracle.svm.core.util.VMError; import com.oracle.svm.util.ReflectionUtil; @AutomaticallyRegisteredFeature @@ -45,43 +47,52 @@ public class ContinuationsFeature implements InternalFeature { @Override public void afterRegistration(AfterRegistrationAccess access) { + final int firstLoomPreviewVersion = 19; + final int lastLoomPreviewVersion = 20; + boolean supportLoom = false; - if (JavaVersionUtil.JAVA_SPEC >= 19) { - boolean haveLoom = false; - try { - haveLoom = (Boolean) Class.forName("jdk.internal.misc.PreviewFeatures") - .getDeclaredMethod("isEnabled").invoke(null); - } catch (ReflectiveOperationException ignored) { - } - if (!haveLoom) { - // Can still get initialized and fail the image build despite substitution, so defer - RuntimeClassInitialization.initializeAtRunTime("jdk.internal.vm.Continuation"); + if (JavaVersionUtil.JAVA_SPEC >= firstLoomPreviewVersion) { + boolean haveLoom; + if (JavaVersionUtil.JAVA_SPEC > lastLoomPreviewVersion) { + haveLoom = true; + } else { + try { + haveLoom = (Boolean) Class.forName("jdk.internal.misc.PreviewFeatures") + .getDeclaredMethod("isEnabled").invoke(null); + } catch (ReflectiveOperationException e) { + throw VMError.shouldNotReachHere(e); + } + if (!haveLoom) { + // Defer: can get initialized and fail the image build despite substitution + RuntimeClassInitialization.initializeAtRunTime("jdk.internal.vm.Continuation"); + } } - // Fail if virtual threads are used and runtime compilation is enabled supportLoom = haveLoom && !DeoptimizationSupport.enabled() && !SubstrateOptions.useLLVMBackend(); } + /* + * Note: missing support for Loom due to preview features being off, runtime compilation, or + * the LLVM backend is reported at runtime to allow probing without failing the image build. + */ if (supportLoom) { LoomVirtualThreads vt = new LoomVirtualThreads(); ImageSingletons.add(VirtualThreads.class, vt); ImageSingletons.add(LoomVirtualThreads.class, vt); // for simpler check in LoomSupport } else if (SubstrateOptions.SupportContinuations.getValue()) { - if (SubstrateOptions.useLLVMBackend()) { - throw UserError.abort("Virtual threads are not supported together with the LLVM backend."); + if (DeoptimizationSupport.enabled()) { + throw UserError.abort("Option %s is in use, but is not supported together with Truffle JIT compilation.", + SubstrateOptionsParser.commandArgument(SubstrateOptions.SupportContinuations, "+")); + } else if (SubstrateOptions.useLLVMBackend()) { + throw UserError.abort("Option %s is in use, but is not supported together with the LLVM backend.", + SubstrateOptionsParser.commandArgument(SubstrateOptions.SupportContinuations, "+")); } else if (JavaVersionUtil.JAVA_SPEC == 17) { - if (DeoptimizationSupport.enabled()) { - throw UserError.abort("Virtual threads are enabled, but are currently not supported together with Truffle JIT compilation."); - } ImageSingletons.add(VirtualThreads.class, new SubstrateVirtualThreads()); + } else if (JavaVersionUtil.JAVA_SPEC >= firstLoomPreviewVersion && JavaVersionUtil.JAVA_SPEC <= lastLoomPreviewVersion) { + throw UserError.abort("Virtual threads on JDK %d are supported only with preview features enabled (--enable-preview). Using option %s is unnecessary.", + JavaVersionUtil.JAVA_SPEC, SubstrateOptionsParser.commandArgument(SubstrateOptions.SupportContinuations, "+")); } else { - /* - * GR-37518: on 11, ForkJoinPool syncs on a String that doesn't have its own monitor - * field, and unparking a virtual thread in additionalMonitorsLock.unlock causes a - * deadlock between carrier thread and virtual thread. 17 uses a ReentrantLock. - * - * We intentionally do not advertise non-Loom continuation support on 17. - */ - throw UserError.abort("Virtual threads are supported only on JDK 19 with preview features enabled (--enable-preview)."); + throw UserError.abort("Option %s is in use, but is not supported on JDK %d.", + SubstrateOptionsParser.commandArgument(SubstrateOptions.SupportContinuations, "+"), JavaVersionUtil.JAVA_SPEC); } } finishedRegistration = true; @@ -105,11 +116,6 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { ReflectionUtil.lookupMethod(StoredContinuationAccess.class, "allocate", int.class)); } else { access.registerReachabilityHandler(a -> abortIfUnsupported(), StoredContinuationAccess.class); - if (JavaVersionUtil.JAVA_SPEC >= 19) { - access.registerReachabilityHandler(a -> abortIfUnsupported(), - ReflectionUtil.lookupMethod(Thread.class, "ofVirtual"), - ReflectionUtil.lookupMethod(Thread.class, "startVirtualThread", Runnable.class)); - } } } @@ -123,11 +129,6 @@ public void beforeCompilation(BeforeCompilationAccess access) { } static void abortIfUnsupported() { - if (!Continuation.isSupported()) { - if (DeoptimizationSupport.enabled()) { - throw UserError.abort("Virtual threads are used in code, but are currently not supported together with Truffle JIT compilation."); - } - throw UserError.abort("Virtual threads are used in code, but are not currently available or active. Use JDK 19 with preview features enabled (--enable-preview)."); - } + VMError.guarantee(Continuation.isSupported(), "Virtual thread internals are reachable but support is not available or active."); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_java_lang_Thread.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_java_lang_Thread.java index bfea8fbfbb7a..c3a197717534 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_java_lang_Thread.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_java_lang_Thread.java @@ -33,7 +33,6 @@ import java.util.concurrent.ThreadFactory; import java.util.function.BooleanSupplier; -import com.oracle.svm.util.ReflectionUtil; import org.graalvm.compiler.api.directives.GraalDirectives; import org.graalvm.compiler.replacements.ReplacementsUtil; import org.graalvm.compiler.serviceprovider.JavaVersionUtil; @@ -52,6 +51,7 @@ import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.annotate.TargetElement; +import com.oracle.svm.core.deopt.DeoptimizationSupport; import com.oracle.svm.core.jdk.ContinuationsNotSupported; import com.oracle.svm.core.jdk.ContinuationsSupported; import com.oracle.svm.core.jdk.JDK11OrEarlier; @@ -63,6 +63,7 @@ import com.oracle.svm.core.jdk.NotLoomJDK; import com.oracle.svm.core.monitor.MonitorSupport; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.util.ReflectionUtil; @TargetClass(Thread.class) @SuppressWarnings({"unused"}) @@ -635,17 +636,27 @@ private static void clearInterruptEvent() { @TargetElement(onlyWith = LoomJDK.class) public static native Target_java_lang_Thread_Builder ofVirtual(); - /** This method being reachable fails the image build, see {@link ContinuationsFeature}. */ @Substitute @TargetElement(name = "ofVirtual", onlyWith = {JDK19OrLater.class, NotLoomJDK.class}) public static Target_java_lang_Thread_Builder ofVirtualWithoutLoom() { + if (Target_jdk_internal_misc_PreviewFeatures.isEnabled()) { + if (DeoptimizationSupport.enabled()) { + throw new UnsupportedOperationException("Virtual threads are not supported together with Truffle JIT compilation."); + } + if (SubstrateOptions.useLLVMBackend()) { + throw new UnsupportedOperationException("Virtual threads are not supported together with the LLVM backend."); + } + } else { + Target_jdk_internal_misc_PreviewFeatures.ensureEnabled(); // throws + } throw VMError.shouldNotReachHere(); } - /** This method being reachable fails the image build, see {@link ContinuationsFeature}. */ @Substitute - @TargetElement(onlyWith = {JDK19OrLater.class, NotLoomJDK.class}) - static Thread startVirtualThread(Runnable task) { + @TargetElement(name = "startVirtualThread", onlyWith = {JDK19OrLater.class, NotLoomJDK.class}) + static Thread startVirtualThreadWithoutLoom(Runnable task) { + Objects.requireNonNull(task); + ofVirtualWithoutLoom(); // throws throw VMError.shouldNotReachHere(); } @@ -783,3 +794,12 @@ interface Target_sun_nio_ch_Interruptible { @Alias void interrupt(Thread t); } + +@TargetClass(className = "jdk.internal.misc.PreviewFeatures", onlyWith = JDK19OrLater.class) +final class Target_jdk_internal_misc_PreviewFeatures { + @Alias + static native boolean isEnabled(); + + @Alias + static native void ensureEnabled(); +}