8287496: Alternative virtual thread implementation that maps to OS thread

Reviewed-by: rehn, mchung
This commit is contained in:
Alan Bateman 2022-06-02 09:02:37 +00:00
parent 199832a710
commit 6ff2d89ea1
72 changed files with 694 additions and 173 deletions

View File

@ -155,6 +155,7 @@ JVM_IsFinalizationEnabled
JVM_IsHiddenClass JVM_IsHiddenClass
JVM_IsInterface JVM_IsInterface
JVM_IsPreviewEnabled JVM_IsPreviewEnabled
JVM_IsContinuationsSupported
JVM_IsPrimitiveClass JVM_IsPrimitiveClass
JVM_IsRecord JVM_IsRecord
JVM_IsSameClassPackage JVM_IsSameClassPackage

View File

@ -53,6 +53,8 @@ define_pd_global(intx, OptoLoopAlignment, 16);
#define MIN_STACK_SHADOW_PAGES DEFAULT_STACK_SHADOW_PAGES #define MIN_STACK_SHADOW_PAGES DEFAULT_STACK_SHADOW_PAGES
#define MIN_STACK_RESERVED_PAGES (0) #define MIN_STACK_RESERVED_PAGES (0)
define_pd_global(bool, VMContinuations, true);
define_pd_global(intx, StackYellowPages, DEFAULT_STACK_YELLOW_PAGES); define_pd_global(intx, StackYellowPages, DEFAULT_STACK_YELLOW_PAGES);
define_pd_global(intx, StackRedPages, DEFAULT_STACK_RED_PAGES); define_pd_global(intx, StackRedPages, DEFAULT_STACK_RED_PAGES);
define_pd_global(intx, StackShadowPages, DEFAULT_STACK_SHADOW_PAGES); define_pd_global(intx, StackShadowPages, DEFAULT_STACK_SHADOW_PAGES);

View File

@ -53,6 +53,8 @@ define_pd_global(intx, StackRedPages, DEFAULT_STACK_RED_PAGES);
define_pd_global(intx, StackShadowPages, DEFAULT_STACK_SHADOW_PAGES); define_pd_global(intx, StackShadowPages, DEFAULT_STACK_SHADOW_PAGES);
define_pd_global(intx, StackReservedPages, DEFAULT_STACK_RESERVED_PAGES); define_pd_global(intx, StackReservedPages, DEFAULT_STACK_RESERVED_PAGES);
define_pd_global(bool, VMContinuations, false);
#if defined(COMPILER1) || defined(COMPILER2) #if defined(COMPILER1) || defined(COMPILER2)
define_pd_global(intx, InlineSmallCode, 1500); define_pd_global(intx, InlineSmallCode, 1500);
#endif #endif

View File

@ -54,6 +54,8 @@ define_pd_global(intx, StackRedPages, DEFAULT_STACK_RED_PAGES);
define_pd_global(intx, StackShadowPages, DEFAULT_STACK_SHADOW_PAGES); define_pd_global(intx, StackShadowPages, DEFAULT_STACK_SHADOW_PAGES);
define_pd_global(intx, StackReservedPages, DEFAULT_STACK_RESERVED_PAGES); define_pd_global(intx, StackReservedPages, DEFAULT_STACK_RESERVED_PAGES);
define_pd_global(bool, VMContinuations, false);
// Use large code-entry alignment. // Use large code-entry alignment.
define_pd_global(uintx, CodeCacheSegmentSize, 128); define_pd_global(uintx, CodeCacheSegmentSize, 128);
define_pd_global(intx, CodeEntryAlignment, 128); define_pd_global(intx, CodeEntryAlignment, 128);

View File

@ -58,6 +58,8 @@ define_pd_global(intx, StackRedPages, DEFAULT_STACK_RED_PAGES);
define_pd_global(intx, StackShadowPages, DEFAULT_STACK_SHADOW_PAGES); define_pd_global(intx, StackShadowPages, DEFAULT_STACK_SHADOW_PAGES);
define_pd_global(intx, StackReservedPages, DEFAULT_STACK_RESERVED_PAGES); define_pd_global(intx, StackReservedPages, DEFAULT_STACK_RESERVED_PAGES);
define_pd_global(bool, VMContinuations, false);
define_pd_global(bool, RewriteBytecodes, true); define_pd_global(bool, RewriteBytecodes, true);
define_pd_global(bool, RewriteFrequentPairs, true); define_pd_global(bool, RewriteFrequentPairs, true);

View File

@ -62,6 +62,8 @@ define_pd_global(intx, StackRedPages, DEFAULT_STACK_RED_PAGES);
define_pd_global(intx, StackShadowPages, DEFAULT_STACK_SHADOW_PAGES); define_pd_global(intx, StackShadowPages, DEFAULT_STACK_SHADOW_PAGES);
define_pd_global(intx, StackReservedPages, DEFAULT_STACK_RESERVED_PAGES); define_pd_global(intx, StackReservedPages, DEFAULT_STACK_RESERVED_PAGES);
define_pd_global(bool, VMContinuations, false);
define_pd_global(bool, RewriteBytecodes, true); define_pd_global(bool, RewriteBytecodes, true);
define_pd_global(bool, RewriteFrequentPairs, true); define_pd_global(bool, RewriteFrequentPairs, true);

View File

@ -77,6 +77,12 @@ define_pd_global(intx, StackRedPages, DEFAULT_STACK_RED_PAGES);
define_pd_global(intx, StackShadowPages, DEFAULT_STACK_SHADOW_PAGES); define_pd_global(intx, StackShadowPages, DEFAULT_STACK_SHADOW_PAGES);
define_pd_global(intx, StackReservedPages, DEFAULT_STACK_RESERVED_PAGES); define_pd_global(intx, StackReservedPages, DEFAULT_STACK_RESERVED_PAGES);
#ifdef _LP64
define_pd_global(bool, VMContinuations, true);
#else
define_pd_global(bool, VMContinuations, false);
#endif
define_pd_global(bool, RewriteBytecodes, true); define_pd_global(bool, RewriteBytecodes, true);
define_pd_global(bool, RewriteFrequentPairs, true); define_pd_global(bool, RewriteFrequentPairs, true);

View File

@ -62,6 +62,8 @@ define_pd_global(intx, StackRedPages, DEFAULT_STACK_RED_PAGES);
define_pd_global(intx, StackShadowPages, DEFAULT_STACK_SHADOW_PAGES); define_pd_global(intx, StackShadowPages, DEFAULT_STACK_SHADOW_PAGES);
define_pd_global(intx, StackReservedPages, DEFAULT_STACK_RESERVED_PAGES); define_pd_global(intx, StackReservedPages, DEFAULT_STACK_RESERVED_PAGES);
define_pd_global(bool, VMContinuations, false);
define_pd_global(bool, RewriteBytecodes, true); define_pd_global(bool, RewriteBytecodes, true);
define_pd_global(bool, RewriteFrequentPairs, true); define_pd_global(bool, RewriteFrequentPairs, true);

View File

@ -90,6 +90,7 @@
do_klass(Thread_FieldHolder_klass, java_lang_Thread_FieldHolder ) \ do_klass(Thread_FieldHolder_klass, java_lang_Thread_FieldHolder ) \
do_klass(Thread_Constants_klass, java_lang_Thread_Constants ) \ do_klass(Thread_Constants_klass, java_lang_Thread_Constants ) \
do_klass(ThreadGroup_klass, java_lang_ThreadGroup ) \ do_klass(ThreadGroup_klass, java_lang_ThreadGroup ) \
do_klass(BasicVirtualThread_klass, java_lang_BaseVirtualThread ) \
do_klass(VirtualThread_klass, java_lang_VirtualThread ) \ do_klass(VirtualThread_klass, java_lang_VirtualThread ) \
do_klass(Properties_klass, java_util_Properties ) \ do_klass(Properties_klass, java_util_Properties ) \
do_klass(Module_klass, java_lang_Module ) \ do_klass(Module_klass, java_lang_Module ) \

View File

@ -65,6 +65,7 @@
template(java_lang_Thread_FieldHolder, "java/lang/Thread$FieldHolder") \ template(java_lang_Thread_FieldHolder, "java/lang/Thread$FieldHolder") \
template(java_lang_Thread_Constants, "java/lang/Thread$Constants") \ template(java_lang_Thread_Constants, "java/lang/Thread$Constants") \
template(java_lang_ThreadGroup, "java/lang/ThreadGroup") \ template(java_lang_ThreadGroup, "java/lang/ThreadGroup") \
template(java_lang_BaseVirtualThread, "java/lang/BaseVirtualThread") \
template(java_lang_VirtualThread, "java/lang/VirtualThread") \ template(java_lang_VirtualThread, "java/lang/VirtualThread") \
template(java_lang_Cloneable, "java/lang/Cloneable") \ template(java_lang_Cloneable, "java/lang/Cloneable") \
template(java_lang_Throwable, "java/lang/Throwable") \ template(java_lang_Throwable, "java/lang/Throwable") \

View File

@ -169,7 +169,10 @@ JNIEXPORT jobjectArray JNICALL
JVM_GetVmArguments(JNIEnv *env); JVM_GetVmArguments(JNIEnv *env);
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
JVM_IsPreviewEnabled(JNIEnv* env); JVM_IsPreviewEnabled(void);
JNIEXPORT jboolean JNICALL
JVM_IsContinuationsSupported(void);
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
JVM_InitializeFromArchive(JNIEnv* env, jclass cls); JVM_InitializeFromArchive(JNIEnv* env, jclass cls);

View File

@ -151,7 +151,11 @@ AbstractInterpreter::MethodKind AbstractInterpreter::method_kind(const methodHan
case vmIntrinsics::_dsqrt: return java_lang_math_sqrt; case vmIntrinsics::_dsqrt: return java_lang_math_sqrt;
case vmIntrinsics::_dsqrt_strict: return native; case vmIntrinsics::_dsqrt_strict: return native;
case vmIntrinsics::_Reference_get: return java_lang_ref_reference_get; case vmIntrinsics::_Reference_get: return java_lang_ref_reference_get;
case vmIntrinsics::_Continuation_doYield: return java_lang_continuation_doYield; case vmIntrinsics::_Continuation_doYield:
if (VMContinuations) {
return java_lang_continuation_doYield;
}
break;
case vmIntrinsics::_Object_init: case vmIntrinsics::_Object_init:
if (RegisterFinalizersAtInit && m->code_size() == 1) { if (RegisterFinalizersAtInit && m->code_size() == 1) {
// We need to execute the special return bytecode to check for // We need to execute the special return bytecode to check for

View File

@ -73,6 +73,7 @@
#include "prims/jvm_misc.hpp" #include "prims/jvm_misc.hpp"
#include "prims/jvmtiExport.hpp" #include "prims/jvmtiExport.hpp"
#include "prims/jvmtiThreadState.hpp" #include "prims/jvmtiThreadState.hpp"
#include "runtime/arguments.hpp"
#include "runtime/atomic.hpp" #include "runtime/atomic.hpp"
#include "runtime/fieldDescriptor.inline.hpp" #include "runtime/fieldDescriptor.inline.hpp"
#include "runtime/handles.inline.hpp" #include "runtime/handles.inline.hpp"
@ -3143,7 +3144,11 @@ JNI_END
JNI_ENTRY(jboolean, jni_IsVirtualThread(JNIEnv* env, jobject obj)) JNI_ENTRY(jboolean, jni_IsVirtualThread(JNIEnv* env, jobject obj))
oop thread_obj = JNIHandles::resolve_external_guard(obj); oop thread_obj = JNIHandles::resolve_external_guard(obj);
return java_lang_VirtualThread::is_instance(thread_obj) ? JNI_TRUE : JNI_FALSE; if (thread_obj != NULL && thread_obj->is_a(vmClasses::BasicVirtualThread_klass())) {
return JNI_TRUE;
} else {
return JNI_FALSE;
}
JNI_END JNI_END
@ -3987,6 +3992,13 @@ jint JNICALL jni_GetEnv(JavaVM *vm, void **penv, jint version) {
return ret; return ret;
} }
// No JVM TI with --enable-preview and no continuations support.
if (!VMContinuations && Arguments::enable_preview() && JvmtiExport::is_jvmti_version(version)) {
*penv = NULL;
ret = JNI_EVERSION;
return ret;
}
if (JniExportedInterface::GetExportedInterface(vm, penv, version, &ret)) { if (JniExportedInterface::GetExportedInterface(vm, penv, version, &ret)) {
return ret; return ret;
} }

View File

@ -3487,10 +3487,13 @@ JVM_LEAF(jboolean, JVM_IsSupportedJNIVersion(jint version))
JVM_END JVM_END
JVM_LEAF(jboolean, JVM_IsPreviewEnabled(JNIEnv *env)) JVM_LEAF(jboolean, JVM_IsPreviewEnabled(void))
return Arguments::enable_preview() ? JNI_TRUE : JNI_FALSE; return Arguments::enable_preview() ? JNI_TRUE : JNI_FALSE;
JVM_END JVM_END
JVM_LEAF(jboolean, JVM_IsContinuationsSupported(void))
return VMContinuations ? JNI_TRUE : JNI_FALSE;
JVM_END
// String support /////////////////////////////////////////////////////////////////////////// // String support ///////////////////////////////////////////////////////////////////////////

View File

@ -412,11 +412,7 @@ void Continuations::init() {
// While virtual threads are in Preview, there are some VM mechanisms we disable if continuations aren't used // While virtual threads are in Preview, there are some VM mechanisms we disable if continuations aren't used
// See NMethodSweeper::do_stack_scanning and nmethod::is_not_on_continuation_stack // See NMethodSweeper::do_stack_scanning and nmethod::is_not_on_continuation_stack
bool Continuations::enabled() { bool Continuations::enabled() {
#if defined(AMD64) || defined(AARCH64) return VMContinuations && Arguments::enable_preview();
return Arguments::enable_preview();
#else
return false;
#endif
} }
// We initialize the _gc_epoch to 2, because previous_completed_gc_marking_cycle // We initialize the _gc_epoch to 2, because previous_completed_gc_marking_cycle

View File

@ -2000,6 +2000,9 @@ const intx ObjectAlignmentInBytes = 8;
"Path to the directory where a temporary file will be created " \ "Path to the directory where a temporary file will be created " \
"to use as the backing store for Java Heap.") \ "to use as the backing store for Java Heap.") \
\ \
product_pd(bool, VMContinuations, EXPERIMENTAL, \
"Enable VM continuations support") \
\
develop(bool, LoomDeoptAfterThaw, false, \ develop(bool, LoomDeoptAfterThaw, false, \
"Deopt stack after thaw") \ "Deopt stack after thaw") \
\ \

View File

@ -368,6 +368,19 @@ void ThreadService::reset_contention_time_stat(JavaThread* thread) {
} }
} }
bool ThreadService::is_virtual_or_carrier_thread(JavaThread* jt) {
oop threadObj = jt->threadObj();
if (threadObj != NULL && threadObj->is_a(vmClasses::BasicVirtualThread_klass())) {
// a virtual thread backed by JavaThread
return true;
}
if (jt->is_vthread_mounted()) {
// carrier thread
return true;
}
return false;
}
// Find deadlocks involving raw monitors, object monitors and concurrent locks // Find deadlocks involving raw monitors, object monitors and concurrent locks
// if concurrent_locks is true. // if concurrent_locks is true.
// We skip virtual thread carriers under the assumption that the current scheduler, ForkJoinPool, // We skip virtual thread carriers under the assumption that the current scheduler, ForkJoinPool,
@ -388,15 +401,19 @@ DeadlockCycle* ThreadService::find_deadlocks_at_safepoint(ThreadsList * t_list,
// Initialize the depth-first-number for each JavaThread. // Initialize the depth-first-number for each JavaThread.
JavaThreadIterator jti(t_list); JavaThreadIterator jti(t_list);
for (JavaThread* jt = jti.first(); jt != NULL; jt = jti.next()) { for (JavaThread* jt = jti.first(); jt != NULL; jt = jti.next()) {
if (jt->is_vthread_mounted()) continue; if (!is_virtual_or_carrier_thread(jt)) {
jt->set_depth_first_number(-1); jt->set_depth_first_number(-1);
}
} }
DeadlockCycle* deadlocks = NULL; DeadlockCycle* deadlocks = NULL;
DeadlockCycle* last = NULL; DeadlockCycle* last = NULL;
DeadlockCycle* cycle = new DeadlockCycle(); DeadlockCycle* cycle = new DeadlockCycle();
for (JavaThread* jt = jti.first(); jt != NULL; jt = jti.next()) { for (JavaThread* jt = jti.first(); jt != NULL; jt = jti.next()) {
if (jt->is_vthread_mounted()) continue; if (is_virtual_or_carrier_thread(jt)) {
// skip virtual and carrier threads
continue;
}
if (jt->depth_first_number() >= 0) { if (jt->depth_first_number() >= 0) {
// this thread was already visited // this thread was already visited
continue; continue;
@ -471,7 +488,7 @@ DeadlockCycle* ThreadService::find_deadlocks_at_safepoint(ThreadsList * t_list,
} }
} }
if (currentThread == NULL || currentThread->is_vthread_mounted()) { if (currentThread == NULL || is_virtual_or_carrier_thread(currentThread)) {
// No dependency on another thread // No dependency on another thread
break; break;
} }

View File

@ -78,6 +78,9 @@ private:
static void decrement_thread_counts(JavaThread* jt, bool daemon); static void decrement_thread_counts(JavaThread* jt, bool daemon);
// test if the JavaThread is a virtual thread or has a mounted virtual thread
static bool is_virtual_or_carrier_thread(JavaThread* jt);
public: public:
static void init(); static void init();
static void add_thread(JavaThread* thread, bool daemon); static void add_thread(JavaThread* thread, bool daemon);

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang;
/**
* Base class for virtual thread implementations.
*/
sealed abstract class BaseVirtualThread extends Thread
permits VirtualThread, ThreadBuilders.BoundVirtualThread {
/**
* Initializes a virtual Thread.
*
* @param name thread name, can be null
* @param characteristics thread characteristics
* @param bound true when bound to an OS thread
*/
BaseVirtualThread(String name, int characteristics, boolean bound) {
super(name, characteristics, bound);
}
/**
* Parks the current virtual thread until the parking permit is available or
* the thread is interrupted.
*
* The behavior of this method when the current thread is not this thread
* is not defined.
*/
abstract void park();
/**
* Parks current virtual thread up to the given waiting time until the parking
* permit is available or the thread is interrupted.
*
* The behavior of this method when the current thread is not this thread
* is not defined.
*/
abstract void parkNanos(long nanos);
/**
* Makes available the parking permit to the given this virtual thread.
*/
abstract void unpark();
}

View File

@ -2591,18 +2591,28 @@ public final class System {
} }
public void parkVirtualThread() { public void parkVirtualThread() {
VirtualThread.park(); Thread thread = Thread.currentThread();
if (thread instanceof BaseVirtualThread vthread) {
vthread.park();
} else {
throw new WrongThreadException();
}
} }
public void parkVirtualThread(long nanos) { public void parkVirtualThread(long nanos) {
VirtualThread.parkNanos(nanos); Thread thread = Thread.currentThread();
if (thread instanceof BaseVirtualThread vthread) {
vthread.parkNanos(nanos);
} else {
throw new WrongThreadException();
}
} }
public void unparkVirtualThread(Thread thread) { public void unparkVirtualThread(Thread thread) {
if (thread instanceof VirtualThread vthread) { if (thread instanceof BaseVirtualThread vthread) {
vthread.unpark(); vthread.unpark();
} else { } else {
throw new IllegalArgumentException("Not a virtual thread"); throw new WrongThreadException();
} }
} }

View File

@ -33,8 +33,11 @@ import java.security.ProtectionDomain;
import java.time.Duration; import java.time.Duration;
import java.util.Map; import java.util.Map;
import java.util.HashMap; import java.util.HashMap;
import java.util.Objects;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;
import java.util.concurrent.locks.LockSupport; import java.util.concurrent.locks.LockSupport;
import java.util.function.Predicate;
import java.util.stream.Stream;
import jdk.internal.event.ThreadSleepEvent; import jdk.internal.event.ThreadSleepEvent;
import jdk.internal.javac.PreviewFeature; import jdk.internal.javac.PreviewFeature;
import jdk.internal.misc.PreviewFeatures; import jdk.internal.misc.PreviewFeatures;
@ -736,8 +739,9 @@ public class Thread implements Runnable {
* *
* @param name thread name, can be null * @param name thread name, can be null
* @param characteristics thread characteristics * @param characteristics thread characteristics
* @param bound true when bound to an OS thread
*/ */
Thread(String name, int characteristics) { Thread(String name, int characteristics, boolean bound) {
this.tid = ThreadIdentifiers.next(); this.tid = ThreadIdentifiers.next();
this.name = (name != null) ? name : ""; this.name = (name != null) ? name : "";
this.inheritedAccessControlContext = Constants.NO_PERMISSIONS_ACC; this.inheritedAccessControlContext = Constants.NO_PERMISSIONS_ACC;
@ -767,8 +771,14 @@ public class Thread implements Runnable {
this.contextClassLoader = ClassLoader.getSystemClassLoader(); this.contextClassLoader = ClassLoader.getSystemClassLoader();
} }
// no additional fields // create a FieldHolder object, needed when bound to an OS thread
this.holder = null; if (bound) {
ThreadGroup g = Constants.VTHREAD_GROUP;
int pri = NORM_PRIORITY;
this.holder = new FieldHolder(g, null, -1, pri, true);
} else {
this.holder = null;
}
} }
/** /**
@ -1495,8 +1505,9 @@ public class Thread implements Runnable {
*/ */
@PreviewFeature(feature = PreviewFeature.Feature.VIRTUAL_THREADS) @PreviewFeature(feature = PreviewFeature.Feature.VIRTUAL_THREADS)
public static Thread startVirtualThread(Runnable task) { public static Thread startVirtualThread(Runnable task) {
Objects.requireNonNull(task);
PreviewFeatures.ensureEnabled(); PreviewFeatures.ensureEnabled();
var thread = new VirtualThread(null, null, 0, task); var thread = ThreadBuilders.newVirtualThread(null, null, 0, task);
thread.start(); thread.start();
return thread; return thread;
} }
@ -1511,7 +1522,7 @@ public class Thread implements Runnable {
*/ */
@PreviewFeature(feature = PreviewFeature.Feature.VIRTUAL_THREADS) @PreviewFeature(feature = PreviewFeature.Feature.VIRTUAL_THREADS)
public final boolean isVirtual() { public final boolean isVirtual() {
return (this instanceof VirtualThread); return (this instanceof BaseVirtualThread);
} }
/** /**
@ -2617,8 +2628,10 @@ public class Thread implements Runnable {
StackTraceElement[][] traces = dumpThreads(threads); StackTraceElement[][] traces = dumpThreads(threads);
Map<Thread, StackTraceElement[]> m = HashMap.newHashMap(threads.length); Map<Thread, StackTraceElement[]> m = HashMap.newHashMap(threads.length);
for (int i = 0; i < threads.length; i++) { for (int i = 0; i < threads.length; i++) {
Thread thread = threads[i];
StackTraceElement[] stackTrace = traces[i]; StackTraceElement[] stackTrace = traces[i];
if (stackTrace != null) { // BoundVirtualThread objects may be in list returned by the VM
if (!thread.isVirtual() && stackTrace != null) {
m.put(threads[i], stackTrace); m.put(threads[i], stackTrace);
} }
// else terminated so we don't put it in the map // else terminated so we don't put it in the map
@ -2688,7 +2701,11 @@ public class Thread implements Runnable {
* Return an array of all live threads. * Return an array of all live threads.
*/ */
static Thread[] getAllThreads() { static Thread[] getAllThreads() {
return getThreads(); Thread[] threads = getThreads();
return Stream.of(threads)
// BoundVirtualThread objects may be in list returned by the VM
.filter(Predicate.not(Thread::isVirtual))
.toArray(Thread[]::new);
} }
private static native StackTraceElement[][] dumpThreads(Thread[] threads); private static native StackTraceElement[][] dumpThreads(Thread[] threads);

View File

@ -30,9 +30,12 @@ import java.lang.Thread.Builder.OfVirtual;
import java.lang.Thread.UncaughtExceptionHandler; import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle; import java.lang.invoke.VarHandle;
import java.util.Locale;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;
import jdk.internal.misc.Unsafe;
import jdk.internal.vm.ContinuationSupport;
/** /**
* Defines static methods to create platform and virtual thread builders. * Defines static methods to create platform and virtual thread builders.
@ -133,6 +136,9 @@ class ThreadBuilders {
private int priority; private int priority;
private long stackSize; private long stackSize;
PlatformThreadBuilder() {
}
@Override @Override
String nextThreadName() { String nextThreadName() {
String name = super.nextThreadName(); String name = super.nextThreadName();
@ -203,12 +209,22 @@ class ThreadBuilders {
*/ */
static final class VirtualThreadBuilder static final class VirtualThreadBuilder
extends BaseThreadBuilder<OfVirtual> implements OfVirtual { extends BaseThreadBuilder<OfVirtual> implements OfVirtual {
private Executor scheduler; // set by tests private Executor scheduler;
VirtualThreadBuilder() {
}
// invoked by tests
VirtualThreadBuilder(Executor scheduler) {
if (!ContinuationSupport.isSupported())
throw new UnsupportedOperationException();
this.scheduler = Objects.requireNonNull(scheduler);
}
@Override @Override
public Thread unstarted(Runnable task) { public Thread unstarted(Runnable task) {
Objects.requireNonNull(task); Objects.requireNonNull(task);
var thread = new VirtualThread(scheduler, nextThreadName(), characteristics(), task); var thread = newVirtualThread(scheduler, nextThreadName(), characteristics(), task);
UncaughtExceptionHandler uhe = uncaughtExceptionHandler(); UncaughtExceptionHandler uhe = uncaughtExceptionHandler();
if (uhe != null) if (uhe != null)
thread.uncaughtExceptionHandler(uhe); thread.uncaughtExceptionHandler(uhe);
@ -349,11 +365,82 @@ class ThreadBuilders {
public Thread newThread(Runnable task) { public Thread newThread(Runnable task) {
Objects.requireNonNull(task); Objects.requireNonNull(task);
String name = nextThreadName(); String name = nextThreadName();
Thread thread = new VirtualThread(scheduler, name, characteristics(), task); Thread thread = newVirtualThread(scheduler, name, characteristics(), task);
UncaughtExceptionHandler uhe = uncaughtExceptionHandler(); UncaughtExceptionHandler uhe = uncaughtExceptionHandler();
if (uhe != null) if (uhe != null)
thread.uncaughtExceptionHandler(uhe); thread.uncaughtExceptionHandler(uhe);
return thread; return thread;
} }
} }
/**
* Creates a new virtual thread to run the given task.
*/
static Thread newVirtualThread(Executor scheduler,
String name,
int characteristics,
Runnable task) {
if (ContinuationSupport.isSupported()) {
return new VirtualThread(scheduler, name, characteristics, task);
} else {
if (scheduler != null)
throw new UnsupportedOperationException();
return new BoundVirtualThread(name, characteristics, task);
}
}
/**
* A "virtual thread" that is backed by a platform thread. This implementation
* is intended for platforms that don't have the underlying VM support for
* continuations. It can also be used for testing.
*/
static final class BoundVirtualThread extends BaseVirtualThread {
private static final Unsafe U = Unsafe.getUnsafe();
private final Runnable task;
private boolean runInvoked;
BoundVirtualThread(String name, int characteristics, Runnable task) {
super(name, characteristics, true);
this.task = task;
}
@Override
public void run() {
// run is specified to do nothing when Thread is a virtual thread
if (Thread.currentThread() == this && !runInvoked) {
runInvoked = true;
task.run();
}
}
@Override
void park() {
U.park(false, 0L);
}
@Override
void parkNanos(long nanos) {
U.park(false, nanos);
}
@Override
void unpark() {
U.unpark(this);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("VirtualThread[#");
sb.append(threadId());
String name = getName();
if (!name.isEmpty()) {
sb.append(",");
sb.append(name);
}
sb.append("]/");
String stateAsString = threadState().toString();
sb.append(stateAsString.toLowerCase(Locale.ROOT));
return sb.toString();
}
}
} }

View File

@ -62,7 +62,7 @@ import static java.util.concurrent.TimeUnit.*;
* A thread that is scheduled by the Java virtual machine rather than the operating * A thread that is scheduled by the Java virtual machine rather than the operating
* system. * system.
*/ */
final class VirtualThread extends Thread { final class VirtualThread extends BaseVirtualThread {
private static final Unsafe U = Unsafe.getUnsafe(); private static final Unsafe U = Unsafe.getUnsafe();
private static final ContinuationScope VTHREAD_SCOPE = new ContinuationScope("VirtualThreads"); private static final ContinuationScope VTHREAD_SCOPE = new ContinuationScope("VirtualThreads");
private static final ForkJoinPool DEFAULT_SCHEDULER = createDefaultScheduler(); private static final ForkJoinPool DEFAULT_SCHEDULER = createDefaultScheduler();
@ -148,7 +148,7 @@ final class VirtualThread extends Thread {
* @param task the task to execute * @param task the task to execute
*/ */
VirtualThread(Executor scheduler, String name, int characteristics, Runnable task) { VirtualThread(Executor scheduler, String name, int characteristics, Runnable task) {
super(name, characteristics); super(name, characteristics, /*bound*/ false);
Objects.requireNonNull(task); Objects.requireNonNull(task);
// choose scheduler if not specified // choose scheduler if not specified
@ -480,23 +480,15 @@ final class VirtualThread extends Thread {
// do nothing // do nothing
} }
/**
* Parks the current virtual thread until unparked or interrupted.
*/
static void park() {
if (currentThread() instanceof VirtualThread vthread) {
vthread.doPark();
} else {
throw new WrongThreadException();
}
}
/** /**
* Parks until unparked or interrupted. If already unparked then the parking * Parks until unparked or interrupted. If already unparked then the parking
* permit is consumed and this method completes immediately (meaning it doesn't * permit is consumed and this method completes immediately (meaning it doesn't
* yield). It also completes immediately if the interrupt status is set. * yield). It also completes immediately if the interrupt status is set.
*/ */
private void doPark() { @Override
void park() {
assert Thread.currentThread() == this;
// complete immediately if parking permit available or interrupted // complete immediately if parking permit available or interrupted
if (getAndSetParkPermit(false) || interrupted) if (getAndSetParkPermit(false) || interrupted)
return; return;
@ -513,20 +505,6 @@ final class VirtualThread extends Thread {
} }
} }
/**
* Parks the current virtual thread up to the given waiting time or until
* unparked or interrupted.
*
* @param nanos the maximum number of nanoseconds to wait
*/
static void parkNanos(long nanos) {
if (currentThread() instanceof VirtualThread vthread) {
vthread.doParkNanos(nanos);
} else {
throw new WrongThreadException();
}
}
/** /**
* Parks up to the given waiting time or until unparked or interrupted. * Parks up to the given waiting time or until unparked or interrupted.
* If already unparked then the parking permit is consumed and this method * If already unparked then the parking permit is consumed and this method
@ -535,7 +513,8 @@ final class VirtualThread extends Thread {
* *
* @param nanos the maximum number of nanoseconds to wait. * @param nanos the maximum number of nanoseconds to wait.
*/ */
private void doParkNanos(long nanos) { @Override
void parkNanos(long nanos) {
assert Thread.currentThread() == this; assert Thread.currentThread() == this;
// complete immediately if parking permit available or interrupted // complete immediately if parking permit available or interrupted
@ -638,6 +617,7 @@ final class VirtualThread extends Thread {
* not to block. * not to block.
* @throws RejectedExecutionException if the scheduler cannot accept a task * @throws RejectedExecutionException if the scheduler cannot accept a task
*/ */
@Override
@ChangesCurrentThread @ChangesCurrentThread
void unpark() { void unpark() {
Thread currentThread = Thread.currentThread(); Thread currentThread = Thread.currentThread();

View File

@ -49,6 +49,7 @@ public class Continuation {
private static final boolean PRESERVE_EXTENT_LOCAL_CACHE; private static final boolean PRESERVE_EXTENT_LOCAL_CACHE;
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
static { static {
ContinuationSupport.ensureSupported();
PreviewFeatures.ensureEnabled(); PreviewFeatures.ensureEnabled();
StackChunk.init(); // ensure StackChunk class is initialized StackChunk.init(); // ensure StackChunk class is initialized

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.vm;
/**
* Defines a static method to test if the VM has continuations support.
*/
public class ContinuationSupport {
private static final boolean SUPPORTED = isSupported0();
private ContinuationSupport() {
}
/**
* Return true if the VM has continuations support.
*/
public static boolean isSupported() {
return SUPPORTED;
}
/**
* Ensures that VM has continuations support.
* @throws UnsupportedOperationException if not supported
*/
public static void ensureSupported() {
if (!isSupported()) {
throw new UnsupportedOperationException("VM does not support continuations");
}
}
private static native boolean isSupported0();
}

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "jni.h"
#include "jvm.h"
#include "jdk_internal_misc_PreviewFeatures.h"
JNIEXPORT jboolean JNICALL
Java_jdk_internal_vm_ContinuationSupport_isSupported0(JNIEnv *env, jclass cls) {
return JVM_IsContinuationsSupported();
}

View File

@ -23,12 +23,12 @@
* questions. * questions.
*/ */
#include "jni.h" #include "jni.h"
#include "jvm.h" #include "jvm.h"
#include "jdk_internal_misc_PreviewFeatures.h" #include "jdk_internal_misc_PreviewFeatures.h"
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
Java_jdk_internal_misc_PreviewFeatures_isPreviewEnabled(JNIEnv *env, jclass cls) { Java_jdk_internal_misc_PreviewFeatures_isPreviewEnabled(JNIEnv *env, jclass cls) {
return JVM_IsPreviewEnabled(env); return JVM_IsPreviewEnabled();
} }

View File

@ -29,6 +29,7 @@ import javax.management.openmbean.ArrayType;
import javax.management.openmbean.CompositeData; import javax.management.openmbean.CompositeData;
import sun.management.ManagementFactoryHelper; import sun.management.ManagementFactoryHelper;
import sun.management.ThreadInfoCompositeData; import sun.management.ThreadInfoCompositeData;
import sun.management.Util;
import static java.lang.Thread.State.*; import static java.lang.Thread.State.*;
/** /**
@ -93,6 +94,7 @@ import static java.lang.Thread.State.*;
*/ */
public class ThreadInfo { public class ThreadInfo {
private boolean virtual; // accessed by ThreadImpl
private String threadName; private String threadName;
private long threadId; private long threadId;
private long blockedTime; private long blockedTime;
@ -224,6 +226,7 @@ public class ThreadInfo {
StackTraceElement[] stackTrace, StackTraceElement[] stackTrace,
MonitorInfo[] lockedMonitors, MonitorInfo[] lockedMonitors,
LockInfo[] lockedSynchronizers) { LockInfo[] lockedSynchronizers) {
this.virtual = Util.isVirtual(t);
this.threadId = t.threadId(); this.threadId = t.threadId();
this.threadName = t.getName(); this.threadName = t.getName();
this.threadState = ManagementFactoryHelper.toThreadState(state); this.threadState = ManagementFactoryHelper.toThreadState(state);

View File

@ -28,14 +28,10 @@ package sun.management;
import java.lang.management.ManagementFactory; import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo; import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean; import java.lang.management.ThreadMXBean;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.stream.Stream; import java.util.stream.Stream;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import javax.management.ObjectName; import javax.management.ObjectName;
import java.util.Objects; import java.util.Objects;
import sun.management.Util;
/** /**
* Implementation for java.lang.management.ThreadMXBean as well as providing the * Implementation for java.lang.management.ThreadMXBean as well as providing the
@ -134,31 +130,24 @@ public class ThreadImpl implements ThreadMXBean {
@Override @Override
public long[] getAllThreadIds() { public long[] getAllThreadIds() {
Util.checkMonitorAccess(); Util.checkMonitorAccess();
Thread[] threads = getThreads(); Thread[] threads = getThreads();
int length = threads.length; return platformThreadIds(threads);
long[] ids = new long[length];
for (int i = 0; i < length; i++) {
Thread t = threads[i];
ids[i] = t.threadId();
}
return ids;
} }
@Override @Override
public ThreadInfo getThreadInfo(long id) { public ThreadInfo getThreadInfo(long id) {
long[] ids = new long[1]; return getThreadInfo(id, 0);
ids[0] = id;
final ThreadInfo[] infos = getThreadInfo(ids, 0);
return infos[0];
} }
@Override @Override
public ThreadInfo getThreadInfo(long id, int maxDepth) { public ThreadInfo getThreadInfo(long id, int maxDepth) {
long[] ids = new long[1]; long[] ids = new long[] { id };
ids[0] = id; ThreadInfo ti = getThreadInfo(ids, maxDepth)[0];
final ThreadInfo[] infos = getThreadInfo(ids, maxDepth); if (ti == null || Util.isVirtual(ti)) {
return infos[0]; return null;
} else {
return ti;
}
} }
@Override @Override
@ -202,6 +191,7 @@ public class ThreadImpl implements ThreadMXBean {
} else { } else {
getThreadInfo1(ids, maxDepth, infos); getThreadInfo1(ids, maxDepth, infos);
} }
nullVirtualThreads(infos);
return infos; return infos;
} }
@ -232,7 +222,7 @@ public class ThreadImpl implements ThreadMXBean {
private boolean verifyCurrentThreadCpuTime() { private boolean verifyCurrentThreadCpuTime() {
// check if Thread CPU time measurement is supported. // check if Thread CPU time measurement is supported.
if (isVirtual(Thread.currentThread())) { if (Util.isVirtual(Thread.currentThread())) {
throw new UnsupportedOperationException("Not supported by virtual threads"); throw new UnsupportedOperationException("Not supported by virtual threads");
} }
if (!isCurrentThreadCpuTimeSupported()) { if (!isCurrentThreadCpuTimeSupported()) {
@ -294,7 +284,7 @@ public class ThreadImpl implements ThreadMXBean {
long id = ids[0]; long id = ids[0];
Thread thread = Thread.currentThread(); Thread thread = Thread.currentThread();
if (id == thread.threadId()) { if (id == thread.threadId()) {
if (isVirtual(thread)) { if (Util.isVirtual(thread)) {
times[0] = -1; times[0] = -1;
} else { } else {
times[0] = getThreadTotalCpuTime0(0); times[0] = getThreadTotalCpuTime0(0);
@ -337,7 +327,7 @@ public class ThreadImpl implements ThreadMXBean {
long id = ids[0]; long id = ids[0];
Thread thread = Thread.currentThread(); Thread thread = Thread.currentThread();
if (id == thread.threadId()) { if (id == thread.threadId()) {
if (isVirtual(thread)) { if (Util.isVirtual(thread)) {
times[0] = -1; times[0] = -1;
} else { } else {
times[0] = getThreadUserCpuTime0(0); times[0] = getThreadUserCpuTime0(0);
@ -371,7 +361,7 @@ public class ThreadImpl implements ThreadMXBean {
} }
protected long getCurrentThreadAllocatedBytes() { protected long getCurrentThreadAllocatedBytes() {
if (isThreadAllocatedMemoryEnabled() && !isVirtual(Thread.currentThread())) { if (isThreadAllocatedMemoryEnabled() && !Util.isVirtual(Thread.currentThread())) {
return getThreadAllocatedMemory0(0); return getThreadAllocatedMemory0(0);
} }
return -1; return -1;
@ -387,7 +377,7 @@ public class ThreadImpl implements ThreadMXBean {
if (verified) { if (verified) {
Thread thread = Thread.currentThread(); Thread thread = Thread.currentThread();
if (id == thread.threadId()) { if (id == thread.threadId()) {
if (isVirtual(thread)) { if (Util.isVirtual(thread)) {
return -1L; return -1L;
} else { } else {
return getThreadAllocatedMemory0(0); return getThreadAllocatedMemory0(0);
@ -443,9 +433,7 @@ public class ThreadImpl implements ThreadMXBean {
*/ */
private long[] threadsToIds(Thread[] threads) { private long[] threadsToIds(Thread[] threads) {
if (threads != null) { if (threads != null) {
long[] tids = Stream.of(threads) long[] tids = platformThreadIds(threads);
.mapToLong(Thread::threadId)
.toArray();
if (tids.length > 0) { if (tids.length > 0) {
return tids; return tids;
} }
@ -509,8 +497,10 @@ public class ThreadImpl implements ThreadMXBean {
public ThreadInfo[] getThreadInfo(long[] ids, public ThreadInfo[] getThreadInfo(long[] ids,
boolean lockedMonitors, boolean lockedMonitors,
boolean lockedSynchronizers) { boolean lockedSynchronizers) {
return dumpThreads0(ids, lockedMonitors, lockedSynchronizers, ThreadInfo[] infos = dumpThreads0(ids, lockedMonitors, lockedSynchronizers,
Integer.MAX_VALUE); Integer.MAX_VALUE);
nullVirtualThreads(infos);
return infos;
} }
public ThreadInfo[] getThreadInfo(long[] ids, public ThreadInfo[] getThreadInfo(long[] ids,
@ -527,14 +517,17 @@ public class ThreadImpl implements ThreadMXBean {
if (ids.length == 0) return new ThreadInfo[0]; if (ids.length == 0) return new ThreadInfo[0];
verifyDumpThreads(lockedMonitors, lockedSynchronizers); verifyDumpThreads(lockedMonitors, lockedSynchronizers);
return dumpThreads0(ids, lockedMonitors, lockedSynchronizers, maxDepth); ThreadInfo[] infos = dumpThreads0(ids, lockedMonitors, lockedSynchronizers, maxDepth);
nullVirtualThreads(infos);
return infos;
} }
@Override @Override
public ThreadInfo[] dumpAllThreads(boolean lockedMonitors, public ThreadInfo[] dumpAllThreads(boolean lockedMonitors,
boolean lockedSynchronizers) { boolean lockedSynchronizers) {
return dumpAllThreads(lockedMonitors, lockedSynchronizers, ThreadInfo[] infos = dumpAllThreads(lockedMonitors, lockedSynchronizers,
Integer.MAX_VALUE); Integer.MAX_VALUE);
return platformThreads(infos);
} }
public ThreadInfo[] dumpAllThreads(boolean lockedMonitors, public ThreadInfo[] dumpAllThreads(boolean lockedMonitors,
@ -545,7 +538,8 @@ public class ThreadImpl implements ThreadMXBean {
"Invalid maxDepth parameter: " + maxDepth); "Invalid maxDepth parameter: " + maxDepth);
} }
verifyDumpThreads(lockedMonitors, lockedSynchronizers); verifyDumpThreads(lockedMonitors, lockedSynchronizers);
return dumpThreads0(null, lockedMonitors, lockedSynchronizers, maxDepth); ThreadInfo[] infos = dumpThreads0(null, lockedMonitors, lockedSynchronizers, maxDepth);
return platformThreads(infos);
} }
// VM support where maxDepth == -1 to request entire stack dump // VM support where maxDepth == -1 to request entire stack dump
@ -579,29 +573,34 @@ public class ThreadImpl implements ThreadMXBean {
} }
/** /**
* Returns true if the given Thread is a virutal thread. * Returns the thread identifiers of the platform threads in the given array.
*
* @implNote This method uses reflection because Thread::isVirtual is a preview API
* and the java.management cannot be compiled with --enable-preview.
*/ */
private static boolean isVirtual(Thread thread) { private static long[] platformThreadIds(Thread[] threads) {
try { return Stream.of(threads)
return (boolean) IS_VIRTUAL.invoke(thread); .filter(t -> !Util.isVirtual(t))
} catch (IllegalAccessException | InvocationTargetException e) { .mapToLong(Thread::threadId)
throw new InternalError(e); .toArray();
}
} }
static final Method IS_VIRTUAL; /**
static { * Returns the ThreadInfo objects from the given array that correspond to platform
try { * threads.
PrivilegedExceptionAction<Method> pa = () -> Thread.class.getMethod("isVirtual"); */
@SuppressWarnings("removal") private ThreadInfo[] platformThreads(ThreadInfo[] infos) {
Method m = AccessController.doPrivileged(pa); return Stream.of(infos)
IS_VIRTUAL = m; .filter(ti -> !Util.isVirtual(ti))
} catch (PrivilegedActionException e) { .toArray(ThreadInfo[]::new);
throw new InternalError(e); }
}
/**
* Set the elements of the given array to null if they correspond to a virtual thread.
*/
private static void nullVirtualThreads(ThreadInfo[] infos) {
for (int i = 0; i < infos.length; i++) {
ThreadInfo ti = infos[i];
if (ti != null && Util.isVirtual(ti)) {
infos[i] = null;
}
}
} }
} }

View File

@ -25,7 +25,13 @@
package sun.management; package sun.management;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.lang.management.ManagementPermission; import java.lang.management.ManagementPermission;
import java.lang.management.ThreadInfo;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.List; import java.util.List;
import javax.management.ObjectName; import javax.management.ObjectName;
import javax.management.MalformedObjectNameException; import javax.management.MalformedObjectNameException;
@ -81,4 +87,56 @@ public class Util {
public static void checkControlAccess() throws SecurityException { public static void checkControlAccess() throws SecurityException {
checkAccess(controlPermission); checkAccess(controlPermission);
} }
/**
* Returns true if the given Thread is a virtual thread.
*
* @implNote This method uses reflection because Thread::isVirtual is a preview API
* and the java.management module cannot be compiled with --enable-preview.
*/
public static boolean isVirtual(Thread thread) {
try {
return (boolean) THREAD_IS_VIRTUAL.invoke(thread);
} catch (Exception e) {
throw new InternalError(e);
}
}
/**
* Returns true if the given ThreadInfo is for a virtual thread.
*/
public static boolean isVirtual(ThreadInfo threadInfo) {
try {
return (boolean) THREADINFO_VIRTUAL.get(threadInfo);
} catch (Exception e) {
throw new InternalError(e);
}
}
@SuppressWarnings("removal")
private static Method threadIsVirtual() {
PrivilegedExceptionAction<Method> pa = () -> Thread.class.getMethod("isVirtual");
try {
return AccessController.doPrivileged(pa);
} catch (PrivilegedActionException e) {
throw new InternalError(e);
}
}
@SuppressWarnings("removal")
private static Field threadInfoVirtual() {
PrivilegedExceptionAction<Field> pa = () -> {
Field f = ThreadInfo.class.getDeclaredField("virtual");
f.setAccessible(true);
return f;
};
try {
return AccessController.doPrivileged(pa);
} catch (PrivilegedActionException e) {
throw new InternalError(e);
}
}
private static final Method THREAD_IS_VIRTUAL = threadIsVirtual();
private static final Field THREADINFO_VIRTUAL = threadInfoVirtual();
} }

View File

@ -69,6 +69,7 @@ requires.properties= \
vm.cds \ vm.cds \
vm.cds.custom.loaders \ vm.cds.custom.loaders \
vm.cds.write.archived.java.heap \ vm.cds.write.archived.java.heap \
vm.continuations \
vm.jvmti \ vm.jvmti \
vm.graal.enabled \ vm.graal.enabled \
vm.compiler1.enabled \ vm.compiler1.enabled \

View File

@ -27,6 +27,7 @@
* @summary Run /serviceability/jvmti/RedefineClasses/RedefineRunningMethods in AppCDS mode to * @summary Run /serviceability/jvmti/RedefineClasses/RedefineRunningMethods in AppCDS mode to
* make sure class redefinition works with CDS. * make sure class redefinition works with CDS.
* @requires vm.cds * @requires vm.cds
* @requires vm.continuations
* @requires vm.jvmti * @requires vm.jvmti
* @library /test/lib /test/hotspot/jtreg/serviceability/jvmti/RedefineClasses /test/hotspot/jtreg/runtime/cds/appcds * @library /test/lib /test/hotspot/jtreg/serviceability/jvmti/RedefineClasses /test/hotspot/jtreg/runtime/cds/appcds
* @run driver RedefineClassHelper * @run driver RedefineClassHelper

View File

@ -26,7 +26,7 @@ import java.util.concurrent.atomic.AtomicReference;
/** /**
* @test JNIMonitor * @test JNIMonitor
* @summary Tests that JNI monitors works correctly for native threads * @summary Tests that JNI monitors work correctly with virtual threads
* @library /test/lib * @library /test/lib
* @compile --enable-preview -source ${jdk.version} JNIMonitor.java * @compile --enable-preview -source ${jdk.version} JNIMonitor.java
* @run main/native/othervm --enable-preview JNIMonitor * @run main/native/othervm --enable-preview JNIMonitor

View File

@ -83,7 +83,7 @@ public class framecnt01 {
Thread.sleep(100); Thread.sleep(100);
// this is too fragile, implementation can change at any time. // this is too fragile, implementation can change at any time.
checkFrames(vThread1, false, 15); checkFrames(vThread1, false, 14);
LockSupport.unpark(vThread1); LockSupport.unpark(vThread1);
vThread1.join(); vThread1.join();

View File

@ -60,6 +60,7 @@ requires.properties= \
vm.compiler1.enabled \ vm.compiler1.enabled \
vm.compiler2.enabled \ vm.compiler2.enabled \
vm.cds \ vm.cds \
vm.continuations \
vm.musl \ vm.musl \
vm.debug \ vm.debug \
vm.hasSA \ vm.hasSA \

View File

@ -25,6 +25,7 @@
* @test * @test
* @bug 8234808 * @bug 8234808
* *
* @requires vm.continuations
* @library /test/lib * @library /test/lib
* @run main/othervm JdbOptions * @run main/othervm JdbOptions
*/ */

View File

@ -24,6 +24,7 @@
/** /**
* @test * @test
* @summary Test virtual threads using a custom scheduler * @summary Test virtual threads using a custom scheduler
* @requires vm.continuations
* @modules java.base/java.lang:+open * @modules java.base/java.lang:+open
* @compile --enable-preview -source ${jdk.version} CustomScheduler.java * @compile --enable-preview -source ${jdk.version} CustomScheduler.java
* @run testng/othervm --enable-preview CustomScheduler * @run testng/othervm --enable-preview CustomScheduler

View File

@ -24,6 +24,7 @@
/** /**
* @test * @test
* @summary Test Thread::getStackTrace on a virtual thread that is runnable-unmounted * @summary Test Thread::getStackTrace on a virtual thread that is runnable-unmounted
* @requires vm.continuations
* @compile --enable-preview -source ${jdk.version} GetStackTraceWhenRunnable.java * @compile --enable-preview -source ${jdk.version} GetStackTraceWhenRunnable.java
* @run main/othervm --enable-preview -Djdk.virtualThreadScheduler.maxPoolSize=1 GetStackTraceWhenRunnable * @run main/othervm --enable-preview -Djdk.virtualThreadScheduler.maxPoolSize=1 GetStackTraceWhenRunnable
*/ */

View File

@ -24,6 +24,7 @@
/** /**
* @test * @test
* @summary Test Thread.holdsLock when lock held by carrier thread * @summary Test Thread.holdsLock when lock held by carrier thread
* @requires vm.continuations
* @modules java.base/java.lang:+open * @modules java.base/java.lang:+open
* @compile --enable-preview -source ${jdk.version} HoldsLock.java * @compile --enable-preview -source ${jdk.version} HoldsLock.java
* @run testng/othervm --enable-preview HoldsLock * @run testng/othervm --enable-preview HoldsLock

View File

@ -23,7 +23,8 @@
/** /**
* @test * @test
* @summary Basic test for JFR jdk.VirtualThreadXXX events. * @summary Basic test for JFR jdk.VirtualThreadXXX events
* @requires vm.continuations
* @modules jdk.jfr java.base/java.lang:+open * @modules jdk.jfr java.base/java.lang:+open
* @compile --enable-preview -source ${jdk.version} JfrEvents.java * @compile --enable-preview -source ${jdk.version} JfrEvents.java
* @run testng/othervm --enable-preview JfrEvents * @run testng/othervm --enable-preview JfrEvents

View File

@ -24,6 +24,7 @@
/* /*
* @test * @test
* @summary Test virtual thread park when scheduler is a fixed thread pool * @summary Test virtual thread park when scheduler is a fixed thread pool
* @requires vm.continuations
* @modules java.base/java.lang:+open * @modules java.base/java.lang:+open
* @compile --enable-preview -source ${jdk.version} ParkWithFixedThreadPool.java * @compile --enable-preview -source ${jdk.version} ParkWithFixedThreadPool.java
* @run testng/othervm --enable-preview ParkWithFixedThreadPool * @run testng/othervm --enable-preview ParkWithFixedThreadPool

View File

@ -39,6 +39,7 @@ import java.util.concurrent.ThreadFactory;
import java.util.concurrent.locks.LockSupport; import java.util.concurrent.locks.LockSupport;
import jdk.test.lib.thread.VThreadRunner; import jdk.test.lib.thread.VThreadRunner;
import org.testng.SkipException;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import static org.testng.Assert.*; import static org.testng.Assert.*;
@ -141,10 +142,12 @@ public class Reflection {
*/ */
@Test @Test
public void testInvokeStatic6() throws Exception { public void testInvokeStatic6() throws Exception {
if (!ThreadBuilders.supportsCustomScheduler())
throw new SkipException("No support for custom schedulers");
Method parkMethod = Parker.class.getDeclaredMethod("park"); Method parkMethod = Parker.class.getDeclaredMethod("park");
try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) {
ThreadFactory factory = ThreadBuilders.virtualThreadBuilder(scheduler).factory(); Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler);
ThreadFactory factory = builder.factory();
Thread vthread = factory.newThread(() -> { Thread vthread = factory.newThread(() -> {
try { try {
parkMethod.invoke(null); // blocks parkMethod.invoke(null); // blocks
@ -315,10 +318,12 @@ public class Reflection {
*/ */
@Test @Test
public void testNewInstance6() throws Exception { public void testNewInstance6() throws Exception {
if (!ThreadBuilders.supportsCustomScheduler())
throw new SkipException("No support for custom schedulers");
Constructor<?> ctor = Parker.class.getDeclaredConstructor(); Constructor<?> ctor = Parker.class.getDeclaredConstructor();
try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) {
ThreadFactory factory = ThreadBuilders.virtualThreadBuilder(scheduler).factory(); Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler);
ThreadFactory factory = builder.factory();
Thread vthread = factory.newThread(() -> { Thread vthread = factory.newThread(() -> {
try { try {
ctor.newInstance(); ctor.newInstance();

View File

@ -25,6 +25,7 @@
* @test * @test
* @summary Test stack traces in exceptions and stack frames waslked by the StackWalker * @summary Test stack traces in exceptions and stack frames waslked by the StackWalker
* API do not include the carrier stack frames * API do not include the carrier stack frames
* @requires vm.continuations
* @modules java.management * @modules java.management
* @library /test/lib * @library /test/lib
* @compile --enable-preview -source ${jdk.version} StackTraces.java * @compile --enable-preview -source ${jdk.version} StackTraces.java

View File

@ -31,6 +31,15 @@
* @run testng/othervm/timeout=300 ThreadAPI * @run testng/othervm/timeout=300 ThreadAPI
*/ */
/**
* @test
* @requires vm.continuations
* @enablePreview
* @modules java.base/java.lang:+open
* @library /test/lib
* @run testng/othervm/timeout=300 -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations ThreadAPI
*/
import java.time.Duration; import java.time.Duration;
import java.util.Arrays; import java.util.Arrays;
import java.util.ArrayList; import java.util.ArrayList;
@ -53,6 +62,7 @@ import java.util.stream.Stream;
import java.nio.channels.Selector; import java.nio.channels.Selector;
import jdk.test.lib.thread.VThreadRunner; import jdk.test.lib.thread.VThreadRunner;
import org.testng.SkipException;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import static org.testng.Assert.*; import static org.testng.Assert.*;
@ -1085,6 +1095,8 @@ public class ThreadAPI {
*/ */
@Test @Test
public void testYield1() throws Exception { public void testYield1() throws Exception {
if (!ThreadBuilders.supportsCustomScheduler())
throw new SkipException("No support for custom schedulers");
var list = new CopyOnWriteArrayList<String>(); var list = new CopyOnWriteArrayList<String>();
try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) {
Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler); Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler);
@ -1112,6 +1124,8 @@ public class ThreadAPI {
*/ */
@Test @Test
public void testYield2() throws Exception { public void testYield2() throws Exception {
if (!ThreadBuilders.supportsCustomScheduler())
throw new SkipException("No support for custom schedulers");
var list = new CopyOnWriteArrayList<String>(); var list = new CopyOnWriteArrayList<String>();
try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) {
Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler); Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler);
@ -1687,6 +1701,8 @@ public class ThreadAPI {
*/ */
@Test @Test
public void testGetState3() throws Exception { public void testGetState3() throws Exception {
if (!ThreadBuilders.supportsCustomScheduler())
throw new SkipException("No support for custom schedulers");
AtomicBoolean completed = new AtomicBoolean(); AtomicBoolean completed = new AtomicBoolean();
try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) {
Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler); Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler);
@ -1853,6 +1869,8 @@ public class ThreadAPI {
*/ */
@Test @Test
public void testGetStackTrace2() throws Exception { public void testGetStackTrace2() throws Exception {
if (!ThreadBuilders.supportsCustomScheduler())
throw new SkipException("Requires continuations support");
List<Thread> threads = new ArrayList<>(); List<Thread> threads = new ArrayList<>();
AtomicBoolean done = new AtomicBoolean(); AtomicBoolean done = new AtomicBoolean();
try { try {
@ -1914,6 +1932,8 @@ public class ThreadAPI {
*/ */
@Test @Test
public void testGetStackTrace4() throws Exception { public void testGetStackTrace4() throws Exception {
if (!ThreadBuilders.supportsCustomScheduler())
throw new SkipException("No support for custom schedulers");
try (ForkJoinPool pool = new ForkJoinPool(1)) { try (ForkJoinPool pool = new ForkJoinPool(1)) {
AtomicReference<Thread> ref = new AtomicReference<>(); AtomicReference<Thread> ref = new AtomicReference<>();
Executor scheduler = task -> { Executor scheduler = task -> {
@ -2010,6 +2030,8 @@ public class ThreadAPI {
*/ */
@Test @Test
public void testGetAllStackTraces2() throws Exception { public void testGetAllStackTraces2() throws Exception {
if (!ThreadBuilders.supportsCustomScheduler())
throw new SkipException("No support for custom schedulers");
try (ForkJoinPool pool = new ForkJoinPool(1)) { try (ForkJoinPool pool = new ForkJoinPool(1)) {
AtomicReference<Thread> ref = new AtomicReference<>(); AtomicReference<Thread> ref = new AtomicReference<>();
Executor scheduler = task -> { Executor scheduler = task -> {
@ -2271,7 +2293,6 @@ public class ThreadAPI {
assertTrue(thread.toString().contains("fred")); assertTrue(thread.toString().contains("fred"));
} }
/** /**
* Schedule a thread to be interrupted after a delay. * Schedule a thread to be interrupted after a delay.
*/ */

View File

@ -21,32 +21,61 @@
* questions. * questions.
*/ */
import java.lang.reflect.Field; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/** /**
* Helper class for creating Thread buidlers. * Helper class for creating Thread buidlers.
*
* Tests using this class need to open java.base/java.lang.
*/ */
class ThreadBuilders { class ThreadBuilders {
private ThreadBuilders() { } private ThreadBuilders() { }
private static final Constructor<?> VTBUILDER_CTOR;
static {
try {
Class<?> clazz = Class.forName("java.lang.ThreadBuilders$VirtualThreadBuilder");
Constructor<?> ctor = clazz.getDeclaredConstructor(Executor.class);
ctor.setAccessible(true);
VTBUILDER_CTOR = ctor;
} catch (Exception e) {
throw new InternalError(e);
}
}
/** /**
* Returns a builder to create virtual threads that use the given scheduler. * Returns a builder to create virtual threads that use the given scheduler.
* * @throws UnsupportedOperationException if custom schedulers are not supported
* Tests using this method need to open java.base/java.lang.
*/ */
static Thread.Builder.OfVirtual virtualThreadBuilder(Executor scheduler) { static Thread.Builder.OfVirtual virtualThreadBuilder(Executor scheduler) {
Thread.Builder.OfVirtual builder = Thread.ofVirtual(); Thread.Builder.OfVirtual builder = Thread.ofVirtual();
try { try {
Class<?> clazz = Class.forName("java.lang.ThreadBuilders$VirtualThreadBuilder"); return (Thread.Builder.OfVirtual) VTBUILDER_CTOR.newInstance(scheduler);
Field field = clazz.getDeclaredField("scheduler"); } catch (InvocationTargetException e) {
field.setAccessible(true); Throwable cause = e.getCause();
field.set(builder, scheduler); if (cause instanceof RuntimeException re) {
} catch (RuntimeException | Error e) { throw re;
throw e; }
throw new RuntimeException(e);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
return builder; }
/**
* Return true if custom schedulers are supported.
*/
static boolean supportsCustomScheduler() {
try (var pool = Executors.newCachedThreadPool()) {
try {
virtualThreadBuilder(pool);
return true;
} catch (UnsupportedOperationException e) {
return false;
}
}
} }
} }

View File

@ -24,6 +24,7 @@
/** /**
* @test * @test
* @summary Basic test of debugging option to trace pinned threads * @summary Basic test of debugging option to trace pinned threads
* @requires vm.continuations
* @compile --enable-preview -source ${jdk.version} TracePinnedThreads.java * @compile --enable-preview -source ${jdk.version} TracePinnedThreads.java
* @run main/othervm --enable-preview -Djdk.tracePinnedThreads=full TracePinnedThreads * @run main/othervm --enable-preview -Djdk.tracePinnedThreads=full TracePinnedThreads
* @run main/othervm --enable-preview -Djdk.tracePinnedThreads=short TracePinnedThreads * @run main/othervm --enable-preview -Djdk.tracePinnedThreads=short TracePinnedThreads

View File

@ -24,7 +24,7 @@
/** /**
* @test * @test
* @summary Stress test asynchronous Thread.getStackTrace * @summary Stress test asynchronous Thread.getStackTrace
* @requires vm.debug != true * @requires vm.debug != true & vm.continuations
* @modules java.base/java.lang:+open * @modules java.base/java.lang:+open
* @compile --enable-preview -source ${jdk.version} GetStackTraceALot.java ../ThreadBuilders.java * @compile --enable-preview -source ${jdk.version} GetStackTraceALot.java ../ThreadBuilders.java
* @run main/othervm --enable-preview GetStackTraceALot * @run main/othervm --enable-preview GetStackTraceALot
@ -33,7 +33,7 @@
/** /**
* @test * @test
* @requires vm.debug == true * @requires vm.debug == true & vm.continuations
* @modules java.base/java.lang:+open * @modules java.base/java.lang:+open
* @compile --enable-preview -source ${jdk.version} GetStackTraceALot.java ../ThreadBuilders.java * @compile --enable-preview -source ${jdk.version} GetStackTraceALot.java ../ThreadBuilders.java
* @run main/othervm/timeout=300 --enable-preview GetStackTraceALot 1000 * @run main/othervm/timeout=300 --enable-preview GetStackTraceALot 1000

View File

@ -24,13 +24,14 @@
/** /**
* @test * @test
* @summary Stress test virtual threads with a variation of the Skynet 1M benchmark * @summary Stress test virtual threads with a variation of the Skynet 1M benchmark
* @requires vm.continuations
* @compile --enable-preview -source ${jdk.version} Skynet.java * @compile --enable-preview -source ${jdk.version} Skynet.java
* @run main/othervm/timeout=300 --enable-preview Skynet * @run main/othervm/timeout=300 --enable-preview Skynet
*/ */
/** /**
* @test * @test
* @requires vm.debug == true * @requires vm.debug == true & vm.continuations
* @requires vm.gc.Z * @requires vm.gc.Z
* @compile --enable-preview -source ${jdk.version} Skynet.java * @compile --enable-preview -source ${jdk.version} Skynet.java
* @run main/othervm/timeout=300 --enable-preview -XX:+UnlockDiagnosticVMOptions * @run main/othervm/timeout=300 --enable-preview -XX:+UnlockDiagnosticVMOptions

View File

@ -24,14 +24,14 @@
/** /**
* @test * @test
* @summary Stress test Thread.sleep * @summary Stress test Thread.sleep
* @requires vm.debug != true * @requires vm.debug != true & vm.continuations
* @compile --enable-preview -source ${jdk.version} SleepALot.java * @compile --enable-preview -source ${jdk.version} SleepALot.java
* @run main/othervm --enable-preview SleepALot * @run main/othervm --enable-preview SleepALot
*/ */
/** /**
* @test * @test
* @requires vm.debug == true * @requires vm.debug == true & vm.continuations
* @compile --enable-preview -source ${jdk.version} SleepALot.java * @compile --enable-preview -source ${jdk.version} SleepALot.java
* @run main/othervm/timeout=300 --enable-preview SleepALot 200000 * @run main/othervm/timeout=300 --enable-preview SleepALot 200000
*/ */

View File

@ -24,7 +24,7 @@
/** /**
* @test * @test
* @summary Stress parking with CompletableFuture timed get * @summary Stress parking with CompletableFuture timed get
* @requires vm.debug != true * @requires vm.debug != true & vm.continuations
* @compile --enable-preview -source ${jdk.version} TimedGet.java * @compile --enable-preview -source ${jdk.version} TimedGet.java
* @run main/othervm -Xmx1g --enable-preview TimedGet * @run main/othervm -Xmx1g --enable-preview TimedGet
*/ */

View File

@ -31,6 +31,7 @@
/** /**
* @test * @test
* @summary * @summary
* @requires vm.continuations
* @library /test/lib * @library /test/lib
* @run build TestClass1 TestClass2 TestClass3 * @run build TestClass1 TestClass2 TestClass3
* @compile --enable-preview -source ${jdk.version} ParallelTransformerLoaderTest.java * @compile --enable-preview -source ${jdk.version} ParallelTransformerLoaderTest.java

View File

@ -26,10 +26,21 @@
* @bug 8284161 8287103 * @bug 8284161 8287103
* @summary Test ThredMXBean.findMonitorDeadlockedThreads with cycles of * @summary Test ThredMXBean.findMonitorDeadlockedThreads with cycles of
* platform and virtual threads in deadlock * platform and virtual threads in deadlock
* @compile --enable-preview -source ${jdk.version} VirtualThreadDeadlocks.java * @enablePreview
* @run main/othervm --enable-preview VirtualThreadDeadlocks PP * @modules java.management
* @run main/othervm --enable-preview VirtualThreadDeadlocks PV * @run main/othervm VirtualThreadDeadlocks PP
* @run main/othervm --enable-preview VirtualThreadDeadlocks VV * @run main/othervm VirtualThreadDeadlocks PV
* @run main/othervm VirtualThreadDeadlocks VV
*/
/**
* @test
* @requires vm.continuations
* @enablePreview
* @modules java.management
* @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations VirtualThreadDeadlocks PP
* @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations VirtualThreadDeadlocks PV
* @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations VirtualThreadDeadlocks VV
*/ */
import java.lang.management.ManagementFactory; import java.lang.management.ManagementFactory;

View File

@ -23,16 +23,26 @@
/** /**
* @test * @test
* @bug 8284161
* @summary Test java.lang.management.ThreadMXBean with virtual threads * @summary Test java.lang.management.ThreadMXBean with virtual threads
* @modules java.base/java.lang:+open * @enablePreview
* @compile --enable-preview -source ${jdk.version} VirtualThreads.java * @modules java.base/java.lang:+open java.management
* @run testng/othervm --enable-preview VirtualThreads * @run testng/othervm VirtualThreads
*/
/**
* @test
* @requires vm.continuations
* @enablePreview
* @modules java.base/java.lang:+open java.management
* @run testng/othervm -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations VirtualThreads
*/ */
import java.lang.management.ManagementFactory; import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo; import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean; import java.lang.management.ThreadMXBean;
import java.lang.reflect.Field; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.nio.channels.Selector; import java.nio.channels.Selector;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@ -41,6 +51,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport; import java.util.concurrent.locks.LockSupport;
import org.testng.SkipException;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import static org.testng.Assert.*; import static org.testng.Assert.*;
@ -78,6 +89,8 @@ public class VirtualThreads {
*/ */
@Test @Test
public void testGetThreadInfo2() throws Exception { public void testGetThreadInfo2() throws Exception {
if (!supportsCustomScheduler())
throw new SkipException("No support for custom schedulers");
try (ExecutorService pool = Executors.newFixedThreadPool(1)) { try (ExecutorService pool = Executors.newFixedThreadPool(1)) {
var carrierRef = new AtomicReference<Thread>(); var carrierRef = new AtomicReference<Thread>();
Executor scheduler = (task) -> { Executor scheduler = (task) -> {
@ -203,18 +216,39 @@ public class VirtualThreads {
.anyMatch(className::equals); .anyMatch(className::equals);
} }
/**
* Returns a builder to create virtual threads that use the given scheduler.
* @throws UnsupportedOperationException if there is no support for custom schedulers
*/
private static Thread.Builder.OfVirtual virtualThreadBuilder(Executor scheduler) { private static Thread.Builder.OfVirtual virtualThreadBuilder(Executor scheduler) {
Thread.Builder.OfVirtual builder = Thread.ofVirtual(); Thread.Builder.OfVirtual builder = Thread.ofVirtual();
try { try {
Class<?> clazz = Class.forName("java.lang.ThreadBuilders$VirtualThreadBuilder"); Class<?> clazz = Class.forName("java.lang.ThreadBuilders$VirtualThreadBuilder");
Field field = clazz.getDeclaredField("scheduler"); Constructor<?> ctor = clazz.getDeclaredConstructor(Executor.class);
field.setAccessible(true); ctor.setAccessible(true);
field.set(builder, scheduler); return (Thread.Builder.OfVirtual) ctor.newInstance(scheduler);
} catch (RuntimeException | Error e) { } catch (InvocationTargetException e) {
throw e; Throwable cause = e.getCause();
if (cause instanceof RuntimeException re) {
throw re;
}
throw new RuntimeException(e);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
return builder; }
/**
* Return true if custom schedulers are supported.
*/
private static boolean supportsCustomScheduler() {
try (var pool = Executors.newCachedThreadPool()) {
try {
virtualThreadBuilder(pool);
return true;
} catch (UnsupportedOperationException e) {
return false;
}
}
} }
} }

View File

@ -23,11 +23,20 @@
/** /**
* @test * @test
* @bug 8284161
* @summary Basic tests of virtual threads doing blocking I/O with java.net sockets * @summary Basic tests of virtual threads doing blocking I/O with java.net sockets
* @enablePreview
* @library /test/lib * @library /test/lib
* @compile --enable-preview -source ${jdk.version} BlockingSocketOps.java * @run testng/othervm/timeout=300 BlockingSocketOps
* @run testng/othervm/timeout=300 --enable-preview BlockingSocketOps * @run testng/othervm/timeout=300 -Djdk.useDirectRegister BlockingSocketOps
* @run testng/othervm/timeout=300 --enable-preview -Djdk.useDirectRegister BlockingSocketOps */
/**
* @test
* @requires vm.continuations
* @enablePreview
* @library /test/lib
* @run testng/othervm/timeout=300 -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations BlockingSocketOps
*/ */
import java.io.Closeable; import java.io.Closeable;

View File

@ -23,11 +23,20 @@
/** /**
* @test * @test
* @bug 8284161
* @summary Basic tests of virtual threads doing blocking I/O with NIO channels * @summary Basic tests of virtual threads doing blocking I/O with NIO channels
* @enablePreview
* @library /test/lib * @library /test/lib
* @compile --enable-preview -source ${jdk.version} BlockingChannelOps.java * @run testng/othervm/timeout=300 BlockingChannelOps
* @run testng/othervm/timeout=300 --enable-preview BlockingChannelOps * @run testng/othervm/timeout=300 -Djdk.useDirectRegister BlockingChannelOps
* @run testng/othervm/timeout=300 --enable-preview -Djdk.useDirectRegister BlockingChannelOps */
/**
* @test
* @requires vm.continuations
* @enablePreview
* @library /test/lib
* @run testng/othervm/timeout=300 -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations BlockingChannelOps
*/ */
import java.io.Closeable; import java.io.Closeable;

View File

@ -26,6 +26,7 @@
/** /**
* @test * @test
* @summary Basic tests for jdk.internal.vm.Continuation * @summary Basic tests for jdk.internal.vm.Continuation
* @requires vm.continuations
* @modules java.base/jdk.internal.vm * @modules java.base/jdk.internal.vm
* @build java.base/java.lang.StackWalkerHelper * @build java.base/java.lang.StackWalkerHelper
* *

View File

@ -25,6 +25,7 @@
* @test * @test
* @summary Tests class unloading on virtual threads * @summary Tests class unloading on virtual threads
* *
* @requires vm.continuations
* @compile --enable-preview -source ${jdk.version} ClassUnloading.java * @compile --enable-preview -source ${jdk.version} ClassUnloading.java
* @run main/othervm --enable-preview -XX:-UseCompressedOops ClassUnloading * @run main/othervm --enable-preview -XX:-UseCompressedOops ClassUnloading
* @run main/othervm --enable-preview -XX:+UseCompressedOops ClassUnloading * @run main/othervm --enable-preview -XX:+UseCompressedOops ClassUnloading

View File

@ -25,6 +25,7 @@
* @test * @test
* @key randomness * @key randomness
* @summary Fuzz tests for jdk.internal.vm.Continuation * @summary Fuzz tests for jdk.internal.vm.Continuation
* @requires vm.continuations
* @modules java.base/jdk.internal.vm * @modules java.base/jdk.internal.vm
* *
* @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4)

View File

@ -24,6 +24,7 @@
/** /**
* @test * @test
* @summary Tests humongous stack-chunk handling * @summary Tests humongous stack-chunk handling
* @requires vm.continuations
* @modules java.base/jdk.internal.vm * @modules java.base/jdk.internal.vm
* *
* @requires vm.gc.G1 * @requires vm.gc.G1

View File

@ -24,6 +24,7 @@
/** /**
* @test * @test
* @summary Functional test for continuations walked with StackWalker's LiveStackFrames * @summary Functional test for continuations walked with StackWalker's LiveStackFrames
* @requires vm.continuations
* @build java.base/java.lang.LiveFrames * @build java.base/java.lang.LiveFrames
* @modules java.base/jdk.internal.vm * @modules java.base/jdk.internal.vm
* *

View File

@ -24,6 +24,7 @@
/** /**
* @test * @test
* @summary Nested continuations test * @summary Nested continuations test
* @requires vm.continuations
* @modules java.base/jdk.internal.vm * @modules java.base/jdk.internal.vm
* @build java.base/java.lang.StackWalkerHelper * @build java.base/java.lang.StackWalkerHelper
* *

View File

@ -35,7 +35,7 @@ import jdk.test.lib.jfr.Events;
/** /**
* @test * @test
* @key jfr * @key jfr
* @requires vm.hasJFR * @requires vm.hasJFR & vm.continuations
* @library /test/lib * @library /test/lib
* @compile --enable-preview -source ${jdk.version} TestThreadEndEvent.java LatchedThread.java * @compile --enable-preview -source ${jdk.version} TestThreadEndEvent.java LatchedThread.java
* @run main/othervm --enable-preview jdk.jfr.event.runtime.TestThreadEndEvent * @run main/othervm --enable-preview jdk.jfr.event.runtime.TestThreadEndEvent

View File

@ -38,7 +38,7 @@ import jdk.test.lib.jfr.Events;
/** /**
* @test * @test
* @key jfr * @key jfr
* @requires vm.hasJFR * @requires vm.hasJFR & vm.continuations
* @library /test/lib * @library /test/lib
* @compile --enable-preview -source ${jdk.version} TestThreadSleepEvent.java * @compile --enable-preview -source ${jdk.version} TestThreadSleepEvent.java
* @run main/othervm --enable-preview jdk.jfr.event.runtime.TestThreadSleepEvent * @run main/othervm --enable-preview jdk.jfr.event.runtime.TestThreadSleepEvent

View File

@ -35,7 +35,7 @@ import jdk.test.lib.jfr.Events;
/** /**
* @test * @test
* @key jfr * @key jfr
* @requires vm.hasJFR * @requires vm.hasJFR & vm.continuations
* @library /test/lib * @library /test/lib
* @compile --enable-preview -source ${jdk.version} TestThreadStartEvent.java LatchedThread.java * @compile --enable-preview -source ${jdk.version} TestThreadStartEvent.java LatchedThread.java
* @run main/othervm --enable-preview jdk.jfr.event.runtime.TestThreadStartEvent * @run main/othervm --enable-preview jdk.jfr.event.runtime.TestThreadStartEvent

View File

@ -35,7 +35,7 @@ import jdk.test.lib.jfr.Events;
/** /**
* @test * @test
* @key jfr * @key jfr
* @requires vm.hasJFR * @requires vm.hasJFR & vm.continuations
* @library /test/lib * @library /test/lib
* @compile --enable-preview -source ${jdk.version} TestVirtualThreadEndEvent.java LatchedThread.java * @compile --enable-preview -source ${jdk.version} TestVirtualThreadEndEvent.java LatchedThread.java
* @run main/othervm --enable-preview jdk.jfr.event.runtime.TestVirtualThreadEndEvent * @run main/othervm --enable-preview jdk.jfr.event.runtime.TestVirtualThreadEndEvent

View File

@ -35,7 +35,7 @@ import jdk.test.lib.jfr.Events;
/** /**
* @test * @test
* @key jfr * @key jfr
* @requires vm.hasJFR * @requires vm.hasJFR & vm.continuations
* @library /test/lib * @library /test/lib
* @compile --enable-preview -source ${jdk.version} TestVirtualThreadStartEvent.java LatchedThread.java * @compile --enable-preview -source ${jdk.version} TestVirtualThreadStartEvent.java LatchedThread.java
* @run main/othervm --enable-preview jdk.jfr.event.runtime.TestVirtualThreadStartEvent * @run main/othervm --enable-preview jdk.jfr.event.runtime.TestVirtualThreadStartEvent

View File

@ -39,7 +39,7 @@ import static jdk.test.lib.Asserts.assertTrue;
/** /**
* @test * @test
* @key jfr * @key jfr
* @requires vm.hasJFR * @requires vm.hasJFR & vm.continuations
* @library /test/lib * @library /test/lib
* @modules jdk.jfr/jdk.jfr.internal * @modules jdk.jfr/jdk.jfr.internal
* @compile --enable-preview -source ${jdk.version} TestThreadExclusion.java LatchedThread.java * @compile --enable-preview -source ${jdk.version} TestThreadExclusion.java LatchedThread.java

View File

@ -39,7 +39,7 @@ import static jdk.test.lib.Asserts.assertTrue;
/** /**
* @test * @test
* @key jfr * @key jfr
* @requires vm.hasJFR * @requires vm.hasJFR & vm.continuations
* @library /test/lib * @library /test/lib
* @modules jdk.jfr/jdk.jfr.internal * @modules jdk.jfr/jdk.jfr.internal
* @compile --enable-preview -source ${jdk.version} TestVirtualThreadExclusion.java LatchedThread.java * @compile --enable-preview -source ${jdk.version} TestVirtualThreadExclusion.java LatchedThread.java

View File

@ -39,7 +39,7 @@ import jdk.test.lib.jfr.Events;
* @summary Tests emitting an event, both in Java and native, in a virtual * @summary Tests emitting an event, both in Java and native, in a virtual
* thread with the maximum number of allowed stack frames for JFR * thread with the maximum number of allowed stack frames for JFR
* @key jfr * @key jfr
* @requires vm.hasJFR * @requires vm.hasJFR & vm.continuations
* @library /test/lib /test/jdk * @library /test/lib /test/jdk
* @modules jdk.jfr/jdk.jfr.internal * @modules jdk.jfr/jdk.jfr.internal
* @compile --enable-preview -source ${jdk.version} TestDeepVirtualStackTrace.java * @compile --enable-preview -source ${jdk.version} TestDeepVirtualStackTrace.java

View File

@ -40,7 +40,7 @@ import jdk.test.lib.Asserts;
* @test * @test
* @summary Tests starting virtual threads from a set of ordinary threads * @summary Tests starting virtual threads from a set of ordinary threads
* @key jfr * @key jfr
* @requires vm.hasJFR * @requires vm.hasJFR & vm.continuations
* @library /test/lib /test/jdk * @library /test/lib /test/jdk
* @modules jdk.jfr/jdk.jfr.internal * @modules jdk.jfr/jdk.jfr.internal
* @compile --enable-preview -source ${jdk.version} TestManyVirtualThreads.java * @compile --enable-preview -source ${jdk.version} TestManyVirtualThreads.java

View File

@ -38,7 +38,7 @@ import jdk.test.lib.jfr.Events;
* @summary Tests committing an event in a virtual thread created by a virtual * @summary Tests committing an event in a virtual thread created by a virtual
* thread * thread
* @key jfr * @key jfr
* @requires vm.hasJFR * @requires vm.hasJFR & vm.continuations
* @library /test/lib /test/jdk * @library /test/lib /test/jdk
* @modules jdk.jfr/jdk.jfr.internal * @modules jdk.jfr/jdk.jfr.internal
* @compile --enable-preview -source ${jdk.version} TestNestedVirtualThreads.java * @compile --enable-preview -source ${jdk.version} TestNestedVirtualThreads.java

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -112,6 +112,7 @@ public class VMProps implements Callable<Map<String, String>> {
map.put("vm.cds", this::vmCDS); map.put("vm.cds", this::vmCDS);
map.put("vm.cds.custom.loaders", this::vmCDSForCustomLoaders); map.put("vm.cds.custom.loaders", this::vmCDSForCustomLoaders);
map.put("vm.cds.write.archived.java.heap", this::vmCDSCanWriteArchivedJavaHeap); map.put("vm.cds.write.archived.java.heap", this::vmCDSCanWriteArchivedJavaHeap);
map.put("vm.continuations", this::vmContinuations);
// vm.graal.enabled is true if Graal is used as JIT // vm.graal.enabled is true if Graal is used as JIT
map.put("vm.graal.enabled", this::isGraalEnabled); map.put("vm.graal.enabled", this::isGraalEnabled);
map.put("vm.compiler1.enabled", this::isCompiler1Enabled); map.put("vm.compiler1.enabled", this::isCompiler1Enabled);
@ -421,6 +422,17 @@ public class VMProps implements Callable<Map<String, String>> {
return "" + ("true".equals(vmCDS()) && WB.canWriteJavaHeapArchive()); return "" + ("true".equals(vmCDS()) && WB.canWriteJavaHeapArchive());
} }
/**
* @return "true" if this VM supports continuations.
*/
protected String vmContinuations() {
if (WB.getBooleanVMFlag("VMContinuations")) {
return "true";
} else {
return "false";
}
}
/** /**
* @return System page size in bytes. * @return System page size in bytes.
*/ */