1 /* 2 * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 #include "classfile/vmSymbols.hpp" 26 #include "gc/shared/barrierSetNMethod.hpp" 27 #include "oops/method.inline.hpp" 28 #include "oops/oop.inline.hpp" 29 #include "prims/jvmtiThreadState.inline.hpp" 30 #include "runtime/continuation.hpp" 31 #include "runtime/continuationEntry.inline.hpp" 32 #include "runtime/continuationHelper.inline.hpp" 33 #include "runtime/continuationJavaClasses.inline.hpp" 34 #include "runtime/continuationWrapper.inline.hpp" 35 #include "runtime/interfaceSupport.inline.hpp" 36 #include "runtime/javaThread.inline.hpp" 37 #include "runtime/jniHandles.inline.hpp" 38 #include "runtime/osThread.hpp" 39 #include "runtime/vframe.inline.hpp" 40 #include "runtime/vframe_hp.hpp" 41 42 // defined in continuationFreezeThaw.cpp 43 extern "C" jint JNICALL CONT_isPinned0(JNIEnv* env, jobject cont_scope); 44 45 JVM_ENTRY(void, CONT_pin(JNIEnv* env, jclass cls)) { 46 if (!Continuation::pin(JavaThread::thread_from_jni_environment(env))) { 47 THROW_MSG(vmSymbols::java_lang_IllegalStateException(), "pin overflow"); 48 } 49 } 50 JVM_END 51 52 JVM_ENTRY(void, CONT_unpin(JNIEnv* env, jclass cls)) { 53 if (!Continuation::unpin(JavaThread::thread_from_jni_environment(env))) { 54 THROW_MSG(vmSymbols::java_lang_IllegalStateException(), "pin underflow"); 55 } 56 } 57 JVM_END 58 59 #if INCLUDE_JVMTI 60 class JvmtiUnmountBeginMark : public StackObj { 61 Handle _vthread; 62 JavaThread* _current; 63 freeze_result _result; 64 bool _failed; 65 66 public: 67 JvmtiUnmountBeginMark(JavaThread* t) : 68 _vthread(t, t->vthread()), _current(t), _result(freeze_pinned_native), _failed(false) { 69 assert(!_current->is_in_VTMS_transition(), "must be"); 70 71 if (JvmtiVTMSTransitionDisabler::VTMS_notify_jvmti_events()) { 72 JvmtiVTMSTransitionDisabler::VTMS_vthread_unmount((jthread)_vthread.raw_value(), true); 73 74 // Don't preempt if there is a pending popframe or earlyret operation. This can 75 // be installed in start_VTMS_transition() so we need to check it here. 76 if (JvmtiExport::can_pop_frame() || JvmtiExport::can_force_early_return()) { 77 JvmtiThreadState* state = _current->jvmti_thread_state(); 78 if (_current->has_pending_popframe() || (state != nullptr && state->is_earlyret_pending())) { 79 _failed = true; 80 } 81 } 82 83 // Don't preempt in case there is an async exception installed since 84 // we would incorrectly throw it during the unmount logic in the carrier. 85 if (_current->has_async_exception_condition()) { 86 _failed = true; 87 } 88 } else { 89 _current->set_is_in_VTMS_transition(true); 90 java_lang_Thread::set_is_in_VTMS_transition(_vthread(), true); 91 } 92 } 93 ~JvmtiUnmountBeginMark() { 94 assert(!_current->is_suspended(), "must be"); 95 96 assert(_current->is_in_VTMS_transition(), "must be"); 97 assert(java_lang_Thread::is_in_VTMS_transition(_vthread()), "must be"); 98 99 // Read it again since for late binding agents the flag could have 100 // been set while blocked in the allocation path during freeze. 101 bool jvmti_present = JvmtiVTMSTransitionDisabler::VTMS_notify_jvmti_events(); 102 103 if (_result != freeze_ok) { 104 // Undo transition 105 if (jvmti_present) { 106 JvmtiVTMSTransitionDisabler::VTMS_vthread_mount((jthread)_vthread.raw_value(), false); 107 } else { 108 _current->set_is_in_VTMS_transition(false); 109 java_lang_Thread::set_is_in_VTMS_transition(_vthread(), false); 110 } 111 } 112 } 113 void set_result(freeze_result res) { _result = res; } 114 bool failed() { return _failed; } 115 }; 116 117 static bool is_vthread_safe_to_preempt_for_jvmti(JavaThread* current) { 118 if (current->is_in_VTMS_transition()) { 119 // We are at the end of a mount transition. 120 return false; 121 } 122 return true; 123 } 124 #endif // INCLUDE_JVMTI 125 126 static bool is_vthread_safe_to_preempt(JavaThread* current, oop vthread) { 127 assert(java_lang_VirtualThread::is_instance(vthread), ""); 128 if (java_lang_VirtualThread::state(vthread) != java_lang_VirtualThread::RUNNING) { // inside transition 129 return false; 130 } 131 return JVMTI_ONLY(is_vthread_safe_to_preempt_for_jvmti(current)) NOT_JVMTI(true); 132 } 133 134 typedef freeze_result (*FreezeContFnT)(JavaThread*, intptr_t*); 135 136 static void verify_preempt_preconditions(JavaThread* current, oop continuation) { 137 assert(current == JavaThread::current(), "no support for external preemption"); 138 assert(current->has_last_Java_frame(), ""); 139 assert(!current->preempting(), ""); 140 assert(current->last_continuation() != nullptr, ""); 141 assert(current->last_continuation()->cont_oop(current) == continuation, ""); 142 assert(Continuation::continuation_scope(continuation) == java_lang_VirtualThread::vthread_scope(), ""); 143 assert(!current->has_pending_exception(), ""); 144 } 145 146 freeze_result Continuation::try_preempt(JavaThread* current, oop continuation) { 147 verify_preempt_preconditions(current, continuation); 148 149 if (LockingMode == LM_LEGACY) { 150 return freeze_unsupported; 151 } 152 153 if (!is_vthread_safe_to_preempt(current, current->vthread())) { 154 return freeze_pinned_native; 155 } 156 157 JVMTI_ONLY(JvmtiUnmountBeginMark jubm(current);) 158 JVMTI_ONLY(if (jubm.failed()) return freeze_pinned_native;) 159 freeze_result res = CAST_TO_FN_PTR(FreezeContFnT, freeze_preempt_entry())(current, current->last_Java_sp()); 160 log_trace(continuations, preempt)("try_preempt: %d", res); 161 JVMTI_ONLY(jubm.set_result(res);) 162 163 if (current->has_pending_exception()) { 164 assert(res == freeze_exception, "expecting an exception result from freeze"); 165 // We don't want to throw exceptions, especially when returning 166 // from monitorenter since the compiler does not expect one. We 167 // just ignore the exception and pin the vthread to the carrier. 168 current->clear_pending_exception(); 169 } 170 return res; 171 } 172 173 #ifndef PRODUCT 174 static jlong java_tid(JavaThread* thread) { 175 return java_lang_Thread::thread_id(thread->threadObj()); 176 } 177 #endif 178 179 ContinuationEntry* Continuation::get_continuation_entry_for_continuation(JavaThread* thread, oop continuation) { 180 if (thread == nullptr || continuation == nullptr) { 181 return nullptr; 182 } 183 184 for (ContinuationEntry* entry = thread->last_continuation(); entry != nullptr; entry = entry->parent()) { 185 if (continuation == entry->cont_oop(thread)) { 186 return entry; 187 } 188 } 189 return nullptr; 190 } 191 192 static bool is_on_stack(JavaThread* thread, const ContinuationEntry* entry) { 193 if (entry == nullptr) { 194 return false; 195 } 196 197 assert(thread->is_in_full_stack((address)entry), ""); 198 return true; 199 // return false if called when transitioning to Java on return from freeze 200 // return !thread->has_last_Java_frame() || thread->last_Java_sp() < cont->entry_sp(); 201 } 202 203 bool Continuation::is_continuation_mounted(JavaThread* thread, oop continuation) { 204 return is_on_stack(thread, get_continuation_entry_for_continuation(thread, continuation)); 205 } 206 207 // When walking the virtual stack, this method returns true 208 // iff the frame is a thawed continuation frame whose 209 // caller is still frozen on the h-stack. 210 // The continuation object can be extracted from the thread. 211 bool Continuation::is_cont_barrier_frame(const frame& f) { 212 assert(f.is_interpreted_frame() || f.cb() != nullptr, ""); 213 if (!Continuations::enabled()) return false; 214 return is_return_barrier_entry(f.is_interpreted_frame() ? ContinuationHelper::InterpretedFrame::return_pc(f) 215 : ContinuationHelper::CompiledFrame::return_pc(f)); 216 } 217 218 bool Continuation::is_return_barrier_entry(const address pc) { 219 if (!Continuations::enabled()) return false; 220 return pc == StubRoutines::cont_returnBarrier(); 221 } 222 223 bool Continuation::is_continuation_enterSpecial(const frame& f) { 224 if (f.cb() == nullptr || !f.cb()->is_nmethod()) { 225 return false; 226 } 227 Method* m = f.cb()->as_nmethod()->method(); 228 return (m != nullptr && m->is_continuation_enter_intrinsic()); 229 } 230 231 bool Continuation::is_continuation_entry_frame(const frame& f, const RegisterMap *map) { 232 // we can do this because the entry frame is never inlined 233 Method* m = (map != nullptr && map->in_cont() && f.is_interpreted_frame()) 234 ? map->stack_chunk()->interpreter_frame_method(f) 235 : ContinuationHelper::Frame::frame_method(f); 236 return m != nullptr && m->intrinsic_id() == vmIntrinsics::_Continuation_enter; 237 } 238 239 // The parameter `sp` should be the actual sp and not the unextended sp because at 240 // least on PPC64 unextended_sp < sp is possible as interpreted frames are trimmed 241 // to the actual size of the expression stack before calls. The problem there is 242 // that even unextended_sp < entry_sp < sp is possible for an interpreted frame. 243 static inline bool is_sp_in_continuation(const ContinuationEntry* entry, intptr_t* const sp) { 244 // entry_sp() returns the unextended_sp which is always greater or equal to the actual sp 245 return entry->entry_sp() > sp; 246 } 247 248 bool Continuation::is_frame_in_continuation(const ContinuationEntry* entry, const frame& f) { 249 return is_sp_in_continuation(entry, f.sp()); 250 } 251 252 ContinuationEntry* Continuation::get_continuation_entry_for_sp(JavaThread* thread, intptr_t* const sp) { 253 assert(thread != nullptr, ""); 254 ContinuationEntry* entry = thread->last_continuation(); 255 while (entry != nullptr && !is_sp_in_continuation(entry, sp)) { 256 entry = entry->parent(); 257 } 258 return entry; 259 } 260 261 ContinuationEntry* Continuation::get_continuation_entry_for_entry_frame(JavaThread* thread, const frame& f) { 262 assert(is_continuation_enterSpecial(f), ""); 263 ContinuationEntry* entry = (ContinuationEntry*)f.unextended_sp(); 264 assert(entry == get_continuation_entry_for_sp(thread, f.sp()-2), "mismatched entry"); 265 return entry; 266 } 267 268 bool Continuation::is_frame_in_continuation(JavaThread* thread, const frame& f) { 269 return f.is_heap_frame() || (get_continuation_entry_for_sp(thread, f.sp()) != nullptr); 270 } 271 272 static frame continuation_top_frame(const ContinuationWrapper& cont, RegisterMap* map) { 273 stackChunkOop chunk = cont.last_nonempty_chunk(); 274 map->set_stack_chunk(chunk); 275 return chunk != nullptr ? chunk->top_frame(map) : frame(); 276 } 277 278 bool Continuation::has_last_Java_frame(oop continuation, frame* frame, RegisterMap* map) { 279 ContinuationWrapper cont(continuation); 280 if (!cont.is_empty()) { 281 *frame = continuation_top_frame(cont, map); 282 return true; 283 } else { 284 return false; 285 } 286 } 287 288 frame Continuation::last_frame(oop continuation, RegisterMap *map) { 289 assert(map != nullptr, "a map must be given"); 290 return continuation_top_frame(ContinuationWrapper(continuation), map); 291 } 292 293 frame Continuation::top_frame(const frame& callee, RegisterMap* map) { 294 assert(map != nullptr, ""); 295 ContinuationEntry* ce = get_continuation_entry_for_sp(map->thread(), callee.sp()); 296 assert(ce != nullptr, ""); 297 oop continuation = ce->cont_oop(map->thread()); 298 ContinuationWrapper cont(continuation); 299 return continuation_top_frame(cont, map); 300 } 301 302 javaVFrame* Continuation::last_java_vframe(Handle continuation, RegisterMap *map) { 303 assert(map != nullptr, "a map must be given"); 304 if (!ContinuationWrapper(continuation()).is_empty()) { 305 frame f = last_frame(continuation(), map); 306 for (vframe* vf = vframe::new_vframe(&f, map, nullptr); vf; vf = vf->sender()) { 307 if (vf->is_java_frame()) { 308 return javaVFrame::cast(vf); 309 } 310 } 311 } 312 return nullptr; 313 } 314 315 frame Continuation::continuation_parent_frame(RegisterMap* map) { 316 assert(map->in_cont(), ""); 317 ContinuationWrapper cont(map); 318 assert(map->thread() != nullptr || !cont.is_mounted(), ""); 319 320 log_develop_trace(continuations)("continuation_parent_frame"); 321 if (map->update_map()) { 322 // we need to register the link address for the entry frame 323 if (cont.entry() != nullptr) { 324 cont.entry()->update_register_map(map); 325 } else { 326 map->clear(); 327 } 328 } 329 330 if (!cont.is_mounted()) { // When we're walking an unmounted continuation and reached the end 331 oop parent = jdk_internal_vm_Continuation::parent(cont.continuation()); 332 stackChunkOop chunk = parent != nullptr ? ContinuationWrapper(parent).last_nonempty_chunk() : nullptr; 333 if (chunk != nullptr) { 334 return chunk->top_frame(map); 335 } 336 337 map->set_stack_chunk(nullptr); 338 return frame(); 339 } 340 341 map->set_stack_chunk(nullptr); 342 343 #if (defined(X86) || defined(AARCH64) || defined(RISCV64) || defined(PPC64)) && !defined(ZERO) 344 frame sender(cont.entrySP(), cont.entryFP(), cont.entryPC()); 345 #else 346 frame sender = frame(); 347 Unimplemented(); 348 #endif 349 350 return sender; 351 } 352 353 oop Continuation::continuation_scope(oop continuation) { 354 return continuation != nullptr ? jdk_internal_vm_Continuation::scope(continuation) : nullptr; 355 } 356 357 bool Continuation::is_scope_bottom(oop cont_scope, const frame& f, const RegisterMap* map) { 358 if (cont_scope == nullptr || !is_continuation_entry_frame(f, map)) { 359 return false; 360 } 361 362 oop continuation; 363 if (map->in_cont()) { 364 continuation = map->cont(); 365 } else { 366 ContinuationEntry* ce = get_continuation_entry_for_sp(map->thread(), f.sp()); 367 if (ce == nullptr) { 368 return false; 369 } 370 continuation = ce->cont_oop(map->thread()); 371 } 372 if (continuation == nullptr) { 373 return false; 374 } 375 376 oop sc = continuation_scope(continuation); 377 assert(sc != nullptr, ""); 378 return sc == cont_scope; 379 } 380 381 bool Continuation::is_in_usable_stack(address addr, const RegisterMap* map) { 382 ContinuationWrapper cont(map); 383 stackChunkOop chunk = cont.find_chunk_by_address(addr); 384 return chunk != nullptr ? chunk->is_usable_in_chunk(addr) : false; 385 } 386 387 bool Continuation::pin(JavaThread* current) { 388 ContinuationEntry* ce = current->last_continuation(); 389 if (ce == nullptr) { 390 return true; // no continuation mounted 391 } 392 return ce->pin(); 393 } 394 395 bool Continuation::unpin(JavaThread* current) { 396 ContinuationEntry* ce = current->last_continuation(); 397 if (ce == nullptr) { 398 return true; // no continuation mounted 399 } 400 return ce->unpin(); 401 } 402 403 frame Continuation::continuation_bottom_sender(JavaThread* thread, const frame& callee, intptr_t* sender_sp) { 404 assert (thread != nullptr, ""); 405 ContinuationEntry* ce = get_continuation_entry_for_sp(thread, callee.sp()); 406 assert(ce != nullptr, "callee.sp(): " INTPTR_FORMAT, p2i(callee.sp())); 407 408 log_develop_debug(continuations)("continuation_bottom_sender: [" JLONG_FORMAT "] [%d] callee: " INTPTR_FORMAT 409 " sender_sp: " INTPTR_FORMAT, 410 java_tid(thread), thread->osthread()->thread_id(), p2i(callee.sp()), p2i(sender_sp)); 411 412 frame entry = ce->to_frame(); 413 if (callee.is_interpreted_frame()) { 414 entry.set_sp(sender_sp); // sp != unextended_sp 415 } 416 return entry; 417 } 418 419 address Continuation::get_top_return_pc_post_barrier(JavaThread* thread, address pc) { 420 ContinuationEntry* ce; 421 if (thread != nullptr && is_return_barrier_entry(pc) && (ce = thread->last_continuation()) != nullptr) { 422 return ce->entry_pc(); 423 } 424 return pc; 425 } 426 427 void Continuation::set_cont_fastpath_thread_state(JavaThread* thread) { 428 assert(thread != nullptr, ""); 429 bool fast = !thread->is_interp_only_mode(); 430 thread->set_cont_fastpath_thread_state(fast); 431 } 432 433 void Continuation::notify_deopt(JavaThread* thread, intptr_t* sp) { 434 ContinuationEntry* entry = thread->last_continuation(); 435 436 if (entry == nullptr) { 437 return; 438 } 439 440 if (is_sp_in_continuation(entry, sp)) { 441 thread->push_cont_fastpath(sp); 442 return; 443 } 444 445 ContinuationEntry* prev; 446 do { 447 prev = entry; 448 entry = entry->parent(); 449 } while (entry != nullptr && !is_sp_in_continuation(entry, sp)); 450 451 if (entry == nullptr) { 452 return; 453 } 454 assert(is_sp_in_continuation(entry, sp), ""); 455 if (sp > prev->parent_cont_fastpath()) { 456 prev->set_parent_cont_fastpath(sp); 457 } 458 } 459 460 #ifndef PRODUCT 461 void Continuation::describe(FrameValues &values) { 462 JavaThread* thread = JavaThread::active(); 463 if (thread != nullptr) { 464 for (ContinuationEntry* ce = thread->last_continuation(); ce != nullptr; ce = ce->parent()) { 465 intptr_t* bottom = ce->entry_sp(); 466 if (bottom != nullptr) { 467 values.describe(-1, bottom, "continuation entry"); 468 } 469 } 470 } 471 } 472 #endif 473 474 #ifdef ASSERT 475 void Continuation::debug_verify_continuation(oop contOop) { 476 if (!VerifyContinuations) { 477 return; 478 } 479 assert(contOop != nullptr, ""); 480 assert(oopDesc::is_oop(contOop), ""); 481 ContinuationWrapper cont(contOop); 482 483 assert(oopDesc::is_oop_or_null(cont.tail()), ""); 484 assert(cont.chunk_invariant(), ""); 485 486 bool nonempty_chunk = false; 487 size_t max_size = 0; 488 int num_chunks = 0; 489 int num_frames = 0; 490 int num_interpreted_frames = 0; 491 int num_oops = 0; 492 493 for (stackChunkOop chunk = cont.tail(); chunk != nullptr; chunk = chunk->parent()) { 494 log_develop_trace(continuations)("debug_verify_continuation chunk %d", num_chunks); 495 chunk->verify(&max_size, &num_oops, &num_frames, &num_interpreted_frames); 496 if (!chunk->is_empty()) { 497 nonempty_chunk = true; 498 } 499 num_chunks++; 500 } 501 502 const bool is_empty = cont.is_empty(); 503 assert(!nonempty_chunk || !is_empty, ""); 504 assert(is_empty == (!nonempty_chunk && cont.last_frame().is_empty()), ""); 505 } 506 507 void Continuation::print(oop continuation) { print_on(tty, continuation); } 508 509 void Continuation::print_on(outputStream* st, oop continuation) { 510 ContinuationWrapper cont(continuation); 511 512 st->print_cr("CONTINUATION: " PTR_FORMAT " done: %d", 513 continuation->identity_hash(), jdk_internal_vm_Continuation::done(continuation)); 514 st->print_cr("CHUNKS:"); 515 for (stackChunkOop chunk = cont.tail(); chunk != nullptr; chunk = chunk->parent()) { 516 st->print("* "); 517 chunk->print_on(true, st); 518 } 519 } 520 #endif // ASSERT 521 522 523 void continuations_init() { Continuations::init(); } 524 525 void Continuations::init() { 526 Continuation::init(); 527 } 528 529 bool Continuations::enabled() { 530 return VMContinuations; 531 } 532 533 #define CC (char*) /*cast a literal from (const char*)*/ 534 #define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f) 535 536 static JNINativeMethod CONT_methods[] = { 537 {CC"pin", CC"()V", FN_PTR(CONT_pin)}, 538 {CC"unpin", CC"()V", FN_PTR(CONT_unpin)}, 539 {CC"isPinned0", CC"(Ljdk/internal/vm/ContinuationScope;)I", FN_PTR(CONT_isPinned0)}, 540 }; 541 542 void CONT_RegisterNativeMethods(JNIEnv *env, jclass cls) { 543 JavaThread* thread = JavaThread::current(); 544 ThreadToNativeFromVM trans(thread); 545 int status = env->RegisterNatives(cls, CONT_methods, sizeof(CONT_methods)/sizeof(JNINativeMethod)); 546 guarantee(status == JNI_OK, "register jdk.internal.vm.Continuation natives"); 547 guarantee(!env->ExceptionCheck(), "register jdk.internal.vm.Continuation natives"); 548 }