mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-19 02:24:40 +02:00
8284161: Implementation of Virtual Threads (Preview)
Co-authored-by: Ron Pressler <rpressler@openjdk.org> Co-authored-by: Alan Bateman <alanb@openjdk.org> Co-authored-by: Erik Österlund <eosterlund@openjdk.org> Co-authored-by: Andrew Haley <aph@openjdk.org> Co-authored-by: Rickard Bäckman <rbackman@openjdk.org> Co-authored-by: Markus Grönlund <mgronlun@openjdk.org> Co-authored-by: Leonid Mesnik <lmesnik@openjdk.org> Co-authored-by: Serguei Spitsyn <sspitsyn@openjdk.org> Co-authored-by: Chris Plummer <cjplummer@openjdk.org> Co-authored-by: Coleen Phillimore <coleenp@openjdk.org> Co-authored-by: Robbin Ehn <rehn@openjdk.org> Co-authored-by: Stefan Karlsson <stefank@openjdk.org> Co-authored-by: Thomas Schatzl <tschatzl@openjdk.org> Co-authored-by: Sergey Kuksenko <skuksenko@openjdk.org> Reviewed-by: lancea, eosterlund, rehn, sspitsyn, stefank, tschatzl, dfuchs, lmesnik, dcubed, kevinw, amenkov, dlong, mchung, psandoz, bpb, coleenp, smarks, egahlin, mseledtsov, coffeys, darcy
This commit is contained in:
parent
5212535a27
commit
9583e3657e
1133 changed files with 95935 additions and 8335 deletions
|
@ -65,10 +65,11 @@
|
|||
#include "oops/oop.inline.hpp"
|
||||
#include "prims/jvm_misc.hpp"
|
||||
#include "prims/jvmtiExport.hpp"
|
||||
#include "prims/jvmtiThreadState.hpp"
|
||||
#include "prims/jvmtiThreadState.inline.hpp"
|
||||
#include "prims/stackwalk.hpp"
|
||||
#include "runtime/arguments.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "runtime/continuation.hpp"
|
||||
#include "runtime/globals_extension.hpp"
|
||||
#include "runtime/handles.inline.hpp"
|
||||
#include "runtime/init.hpp"
|
||||
|
@ -85,6 +86,7 @@
|
|||
#include "runtime/reflection.hpp"
|
||||
#include "runtime/synchronizer.hpp"
|
||||
#include "runtime/thread.inline.hpp"
|
||||
#include "runtime/threadIdentifier.hpp"
|
||||
#include "runtime/threadSMR.hpp"
|
||||
#include "runtime/vframe.inline.hpp"
|
||||
#include "runtime/vmOperations.hpp"
|
||||
|
@ -532,12 +534,12 @@ JVM_END
|
|||
// java.lang.StackTraceElement //////////////////////////////////////////////
|
||||
|
||||
|
||||
JVM_ENTRY(void, JVM_InitStackTraceElementArray(JNIEnv *env, jobjectArray elements, jobject throwable))
|
||||
Handle exception(THREAD, JNIHandles::resolve(throwable));
|
||||
JVM_ENTRY(void, JVM_InitStackTraceElementArray(JNIEnv *env, jobjectArray elements, jobject backtrace, jint depth))
|
||||
Handle backtraceh(THREAD, JNIHandles::resolve(backtrace));
|
||||
objArrayOop st = objArrayOop(JNIHandles::resolve(elements));
|
||||
objArrayHandle stack_trace(THREAD, st);
|
||||
// Fill in the allocated stack trace
|
||||
java_lang_Throwable::get_stack_trace_elements(exception, stack_trace, CHECK);
|
||||
java_lang_Throwable::get_stack_trace_elements(depth, backtraceh, stack_trace, CHECK);
|
||||
JVM_END
|
||||
|
||||
|
||||
|
@ -552,14 +554,15 @@ JVM_END
|
|||
|
||||
|
||||
JVM_ENTRY(jobject, JVM_CallStackWalk(JNIEnv *env, jobject stackStream, jlong mode,
|
||||
jint skip_frames, jint frame_count, jint start_index,
|
||||
jobjectArray frames))
|
||||
jint skip_frames, jobject contScope, jobject cont,
|
||||
jint frame_count, jint start_index, jobjectArray frames))
|
||||
if (!thread->has_last_Java_frame()) {
|
||||
THROW_MSG_(vmSymbols::java_lang_InternalError(), "doStackWalk: no stack trace", NULL);
|
||||
}
|
||||
|
||||
Handle stackStream_h(THREAD, JNIHandles::resolve_non_null(stackStream));
|
||||
|
||||
Handle contScope_h(THREAD, JNIHandles::resolve(contScope));
|
||||
Handle cont_h(THREAD, JNIHandles::resolve(cont));
|
||||
// frames array is a Class<?>[] array when only getting caller reference,
|
||||
// and a StackFrameInfo[] array (or derivative) otherwise. It should never
|
||||
// be null.
|
||||
|
@ -571,8 +574,8 @@ JVM_ENTRY(jobject, JVM_CallStackWalk(JNIEnv *env, jobject stackStream, jlong mod
|
|||
THROW_MSG_(vmSymbols::java_lang_IllegalArgumentException(), "not enough space in buffers", NULL);
|
||||
}
|
||||
|
||||
oop result = StackWalk::walk(stackStream_h, mode, skip_frames, frame_count,
|
||||
start_index, frames_array_h, CHECK_NULL);
|
||||
oop result = StackWalk::walk(stackStream_h, mode, skip_frames, contScope_h, cont_h,
|
||||
frame_count, start_index, frames_array_h, CHECK_NULL);
|
||||
return JNIHandles::make_local(THREAD, result);
|
||||
JVM_END
|
||||
|
||||
|
@ -593,7 +596,16 @@ JVM_ENTRY(jint, JVM_MoreStackWalk(JNIEnv *env, jobject stackStream, jlong mode,
|
|||
|
||||
Handle stackStream_h(THREAD, JNIHandles::resolve_non_null(stackStream));
|
||||
return StackWalk::fetchNextBatch(stackStream_h, mode, anchor, frame_count,
|
||||
start_index, frames_array_h, THREAD);
|
||||
start_index, frames_array_h, THREAD);
|
||||
JVM_END
|
||||
|
||||
JVM_ENTRY(void, JVM_SetStackWalkContinuation(JNIEnv *env, jobject stackStream, jlong anchor, jobjectArray frames, jobject cont))
|
||||
objArrayOop fa = objArrayOop(JNIHandles::resolve_non_null(frames));
|
||||
objArrayHandle frames_array_h(THREAD, fa);
|
||||
Handle stackStream_h(THREAD, JNIHandles::resolve_non_null(stackStream));
|
||||
Handle cont_h(THREAD, JNIHandles::resolve_non_null(cont));
|
||||
|
||||
StackWalk::setContinuation(stackStream_h, anchor, frames_array_h, cont_h, THREAD);
|
||||
JVM_END
|
||||
|
||||
// java.lang.Object ///////////////////////////////////////////////
|
||||
|
@ -694,6 +706,12 @@ JVM_LEAF(jboolean, JVM_IsFinalizationEnabled(JNIEnv * env))
|
|||
return InstanceKlass::is_finalization_enabled();
|
||||
JVM_END
|
||||
|
||||
// jdk.internal.vm.Continuation /////////////////////////////////////////////////////
|
||||
|
||||
JVM_ENTRY(void, JVM_RegisterContinuationMethods(JNIEnv *env, jclass cls))
|
||||
CONT_RegisterNativeMethods(env, cls);
|
||||
JVM_END
|
||||
|
||||
// java.io.File ///////////////////////////////////////////////////////////////
|
||||
|
||||
JVM_LEAF(char*, JVM_NativePath(char* path))
|
||||
|
@ -2956,14 +2974,7 @@ JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
|
|||
os::native_thread_creation_failed_msg());
|
||||
}
|
||||
|
||||
#if INCLUDE_JFR
|
||||
if (Jfr::is_recording() && EventThreadStart::is_enabled() &&
|
||||
EventThreadStart::is_stacktrace_enabled()) {
|
||||
JfrThreadLocal* tl = native_thread->jfr_thread_local();
|
||||
// skip Thread.start() and Thread.start0()
|
||||
tl->set_cached_stack_trace_id(JfrStackTraceRepository::record(thread, 2));
|
||||
}
|
||||
#endif
|
||||
JFR_ONLY(Jfr::on_java_thread_start(thread, native_thread);)
|
||||
|
||||
Thread::start(native_thread);
|
||||
|
||||
|
@ -3061,13 +3072,6 @@ JVM_LEAF(void, JVM_Yield(JNIEnv *env, jclass threadClass))
|
|||
os::naked_yield();
|
||||
JVM_END
|
||||
|
||||
static void post_thread_sleep_event(EventThreadSleep* event, jlong millis) {
|
||||
assert(event != NULL, "invariant");
|
||||
assert(event->should_commit(), "invariant");
|
||||
event->set_time(millis);
|
||||
event->commit();
|
||||
}
|
||||
|
||||
JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis))
|
||||
if (millis < 0) {
|
||||
THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
|
||||
|
@ -3082,7 +3086,6 @@ JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis))
|
|||
JavaThreadSleepState jtss(thread);
|
||||
|
||||
HOTSPOT_THREAD_SLEEP_BEGIN(millis);
|
||||
EventThreadSleep event;
|
||||
|
||||
if (millis == 0) {
|
||||
os::naked_yield();
|
||||
|
@ -3093,9 +3096,6 @@ JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis))
|
|||
// An asynchronous exception (e.g., ThreadDeathException) could have been thrown on
|
||||
// us while we were sleeping. We do not overwrite those.
|
||||
if (!HAS_PENDING_EXCEPTION) {
|
||||
if (event.should_commit()) {
|
||||
post_thread_sleep_event(&event, millis);
|
||||
}
|
||||
HOTSPOT_THREAD_SLEEP_END(1);
|
||||
|
||||
// TODO-FIXME: THROW_MSG returns which means we will not call set_state()
|
||||
|
@ -3105,18 +3105,32 @@ JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis))
|
|||
}
|
||||
thread->osthread()->set_state(old_state);
|
||||
}
|
||||
if (event.should_commit()) {
|
||||
post_thread_sleep_event(&event, millis);
|
||||
}
|
||||
HOTSPOT_THREAD_SLEEP_END(0);
|
||||
JVM_END
|
||||
|
||||
JVM_ENTRY(jobject, JVM_CurrentThread(JNIEnv* env, jclass threadClass))
|
||||
JVM_ENTRY(jobject, JVM_CurrentCarrierThread(JNIEnv* env, jclass threadClass))
|
||||
oop jthread = thread->threadObj();
|
||||
assert(jthread != NULL, "no current thread!");
|
||||
assert(jthread != NULL, "no current carrier thread!");
|
||||
return JNIHandles::make_local(THREAD, jthread);
|
||||
JVM_END
|
||||
|
||||
JVM_ENTRY(jobject, JVM_CurrentThread(JNIEnv* env, jclass threadClass))
|
||||
oop theThread = thread->vthread();
|
||||
assert(theThread != (oop)NULL, "no current thread!");
|
||||
return JNIHandles::make_local(THREAD, theThread);
|
||||
JVM_END
|
||||
|
||||
JVM_ENTRY(void, JVM_SetCurrentThread(JNIEnv* env, jobject thisThread,
|
||||
jobject theThread))
|
||||
oop threadObj = JNIHandles::resolve(theThread);
|
||||
thread->set_vthread(threadObj);
|
||||
JFR_ONLY(Jfr::on_set_current_thread(thread, threadObj);)
|
||||
JVM_END
|
||||
|
||||
JVM_ENTRY(jlong, JVM_GetNextThreadIdOffset(JNIEnv* env, jclass threadClass))
|
||||
return ThreadIdentifier::unsafe_offset();
|
||||
JVM_END
|
||||
|
||||
JVM_ENTRY(void, JVM_Interrupt(JNIEnv* env, jobject jthread))
|
||||
ThreadsListHandle tlh(thread);
|
||||
JavaThread* receiver = NULL;
|
||||
|
@ -3127,7 +3141,6 @@ JVM_ENTRY(void, JVM_Interrupt(JNIEnv* env, jobject jthread))
|
|||
}
|
||||
JVM_END
|
||||
|
||||
|
||||
// Return true iff the current thread has locked the object passed in
|
||||
|
||||
JVM_ENTRY(jboolean, JVM_HoldsLock(JNIEnv* env, jclass threadClass, jobject obj))
|
||||
|
@ -3138,6 +3151,10 @@ JVM_ENTRY(jboolean, JVM_HoldsLock(JNIEnv* env, jclass threadClass, jobject obj))
|
|||
return ObjectSynchronizer::current_thread_holds_lock(thread, h_obj);
|
||||
JVM_END
|
||||
|
||||
JVM_ENTRY(jobject, JVM_GetStackTrace(JNIEnv *env, jobject jthread))
|
||||
oop trace = java_lang_Thread::async_get_stack_trace(JNIHandles::resolve(jthread), THREAD);
|
||||
return JNIHandles::make_local(THREAD, trace);
|
||||
JVM_END
|
||||
|
||||
JVM_ENTRY(void, JVM_DumpAllStacks(JNIEnv* env, jclass))
|
||||
VM_PrintThreads op;
|
||||
|
@ -3162,6 +3179,24 @@ JVM_ENTRY(void, JVM_SetNativeThreadName(JNIEnv* env, jobject jthread, jstring na
|
|||
}
|
||||
JVM_END
|
||||
|
||||
JVM_ENTRY(jobject, JVM_ExtentLocalCache(JNIEnv* env, jclass threadClass))
|
||||
oop theCache = thread->extentLocalCache();
|
||||
if (theCache) {
|
||||
arrayOop objs = arrayOop(theCache);
|
||||
assert(objs->length() == ExtentLocalCacheSize * 2, "wrong length");
|
||||
}
|
||||
return JNIHandles::make_local(THREAD, theCache);
|
||||
JVM_END
|
||||
|
||||
JVM_ENTRY(void, JVM_SetExtentLocalCache(JNIEnv* env, jclass threadClass,
|
||||
jobject theCache))
|
||||
arrayOop objs = arrayOop(JNIHandles::resolve(theCache));
|
||||
if (objs != NULL) {
|
||||
assert(objs->length() == ExtentLocalCacheSize * 2, "wrong length");
|
||||
}
|
||||
thread->set_extentLocalCache(objs);
|
||||
JVM_END
|
||||
|
||||
// java.lang.SecurityManager ///////////////////////////////////////////////////////////////////////
|
||||
|
||||
JVM_ENTRY(jobjectArray, JVM_GetClassContext(JNIEnv *env))
|
||||
|
@ -3452,6 +3487,11 @@ JVM_LEAF(jboolean, JVM_IsSupportedJNIVersion(jint version))
|
|||
JVM_END
|
||||
|
||||
|
||||
JVM_LEAF(jboolean, JVM_IsPreviewEnabled(JNIEnv *env))
|
||||
return Arguments::enable_preview() ? JNI_TRUE : JNI_FALSE;
|
||||
JVM_END
|
||||
|
||||
|
||||
// String support ///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JVM_ENTRY(jstring, JVM_InternString(JNIEnv *env, jstring str))
|
||||
|
@ -3895,3 +3935,109 @@ JVM_LEAF(jint, JVM_FindSignal(const char *name))
|
|||
return os::get_signal_number(name);
|
||||
JVM_END
|
||||
|
||||
JVM_ENTRY(void, JVM_VirtualThreadMountBegin(JNIEnv* env, jobject vthread, jboolean first_mount))
|
||||
#if INCLUDE_JVMTI
|
||||
if (!DoJVMTIVirtualThreadTransitions) {
|
||||
assert(!JvmtiExport::can_support_virtual_threads(), "sanity check");
|
||||
return;
|
||||
}
|
||||
JvmtiVTMSTransitionDisabler::start_VTMS_transition(vthread, /* is_mount */ true);
|
||||
#else
|
||||
fatal("Should only be called with JVMTI enabled");
|
||||
#endif
|
||||
JVM_END
|
||||
|
||||
JVM_ENTRY(void, JVM_VirtualThreadMountEnd(JNIEnv* env, jobject vthread, jboolean first_mount))
|
||||
#if INCLUDE_JVMTI
|
||||
if (!DoJVMTIVirtualThreadTransitions) {
|
||||
assert(!JvmtiExport::can_support_virtual_threads(), "sanity check");
|
||||
return;
|
||||
}
|
||||
oop vt = JNIHandles::resolve(vthread);
|
||||
|
||||
thread->rebind_to_jvmti_thread_state_of(vt);
|
||||
|
||||
{
|
||||
MutexLocker mu(JvmtiThreadState_lock);
|
||||
JvmtiThreadState* state = thread->jvmti_thread_state();
|
||||
if (state != NULL && state->is_pending_interp_only_mode()) {
|
||||
JvmtiEventController::enter_interp_only_mode();
|
||||
}
|
||||
}
|
||||
assert(thread->is_in_VTMS_transition(), "sanity check");
|
||||
JvmtiVTMSTransitionDisabler::finish_VTMS_transition(vthread, /* is_mount */ true);
|
||||
if (first_mount) {
|
||||
// thread start
|
||||
if (JvmtiExport::can_support_virtual_threads()) {
|
||||
JvmtiEventController::thread_started(thread);
|
||||
if (JvmtiExport::should_post_vthread_start()) {
|
||||
JvmtiExport::post_vthread_start(vthread);
|
||||
}
|
||||
} else { // compatibility for vthread unaware agents: legacy thread_start
|
||||
if (PostVirtualThreadCompatibleLifecycleEvents &&
|
||||
JvmtiExport::should_post_thread_life()) {
|
||||
// JvmtiEventController::thread_started is called here
|
||||
JvmtiExport::post_thread_start(thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (JvmtiExport::should_post_vthread_mount()) {
|
||||
JvmtiExport::post_vthread_mount(vthread);
|
||||
}
|
||||
#else
|
||||
fatal("Should only be called with JVMTI enabled");
|
||||
#endif
|
||||
JVM_END
|
||||
|
||||
JVM_ENTRY(void, JVM_VirtualThreadUnmountBegin(JNIEnv* env, jobject vthread, jboolean last_unmount))
|
||||
#if INCLUDE_JVMTI
|
||||
if (!DoJVMTIVirtualThreadTransitions) {
|
||||
assert(!JvmtiExport::can_support_virtual_threads(), "sanity check");
|
||||
return;
|
||||
}
|
||||
HandleMark hm(thread);
|
||||
Handle ct(thread, thread->threadObj());
|
||||
|
||||
if (JvmtiExport::should_post_vthread_unmount()) {
|
||||
JvmtiExport::post_vthread_unmount(vthread);
|
||||
}
|
||||
if (last_unmount) {
|
||||
if (JvmtiExport::can_support_virtual_threads()) {
|
||||
if (JvmtiExport::should_post_vthread_end()) {
|
||||
JvmtiExport::post_vthread_end(vthread);
|
||||
}
|
||||
} else { // compatibility for vthread unaware agents: legacy thread_end
|
||||
if (PostVirtualThreadCompatibleLifecycleEvents &&
|
||||
JvmtiExport::should_post_thread_life()) {
|
||||
JvmtiExport::post_thread_end(thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(!thread->is_in_VTMS_transition(), "sanity check");
|
||||
JvmtiVTMSTransitionDisabler::start_VTMS_transition(vthread, /* is_mount */ false);
|
||||
|
||||
if (last_unmount && thread->jvmti_thread_state() != NULL) {
|
||||
JvmtiExport::cleanup_thread(thread);
|
||||
thread->set_jvmti_thread_state(NULL);
|
||||
oop vt = JNIHandles::resolve(vthread);
|
||||
java_lang_Thread::set_jvmti_thread_state(vt, NULL);
|
||||
}
|
||||
thread->rebind_to_jvmti_thread_state_of(ct());
|
||||
#else
|
||||
fatal("Should only be called with JVMTI enabled");
|
||||
#endif
|
||||
JVM_END
|
||||
|
||||
JVM_ENTRY(void, JVM_VirtualThreadUnmountEnd(JNIEnv* env, jobject vthread, jboolean last_unmount))
|
||||
#if INCLUDE_JVMTI
|
||||
if (!DoJVMTIVirtualThreadTransitions) {
|
||||
assert(!JvmtiExport::can_support_virtual_threads(), "sanity check");
|
||||
return;
|
||||
}
|
||||
assert(thread->is_in_VTMS_transition(), "sanity check");
|
||||
JvmtiVTMSTransitionDisabler::finish_VTMS_transition(vthread, /* is_mount */ false);
|
||||
#else
|
||||
fatal("Should only be called with JVMTI enabled");
|
||||
#endif
|
||||
JVM_END
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue