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 ContinuationEntry* Continuation::get_continuation_entry_for_continuation(JavaThread* thread, oop continuation) { 174 if (thread == nullptr || continuation == nullptr) { 175 return nullptr; 176 } 177 178 for (ContinuationEntry* entry = thread->last_continuation(); entry != nullptr; entry = entry->parent()) { 179 if (continuation == entry->cont_oop(thread)) { 180 return entry; 181 } 182 } 183 return nullptr; 184 } 185 186 static bool is_on_stack(JavaThread* thread, const ContinuationEntry* entry) { 187 if (entry == nullptr) { 188 return false; 189 } 190 191 assert(thread->is_in_full_stack((address)entry), ""); 192 return true; 193 // return false if called when transitioning to Java on return from freeze 194 // return !thread->has_last_Java_frame() || thread->last_Java_sp() < cont->entry_sp(); 195 } 196 197 bool Continuation::is_continuation_mounted(JavaThread* thread, oop continuation) { 198 return is_on_stack(thread, get_continuation_entry_for_continuation(thread, continuation)); 199 } 200 201 // When walking the virtual stack, this method returns true 202 // iff the frame is a thawed continuation frame whose 203 // caller is still frozen on the h-stack. 204 // The continuation object can be extracted from the thread. 205 bool Continuation::is_cont_barrier_frame(const frame& f) { 206 assert(f.is_interpreted_frame() || f.cb() != nullptr, ""); 207 if (!Continuations::enabled()) return false; 208 return is_return_barrier_entry(f.is_interpreted_frame() ? ContinuationHelper::InterpretedFrame::return_pc(f) 209 : ContinuationHelper::CompiledFrame::return_pc(f)); 210 } 211 212 bool Continuation::is_return_barrier_entry(const address pc) { 213 if (!Continuations::enabled()) return false; 214 return pc == StubRoutines::cont_returnBarrier(); 215 } 216 217 bool Continuation::is_continuation_enterSpecial(const frame& f) { 218 if (f.cb() == nullptr || !f.cb()->is_nmethod()) { 219 return false; 220 } 221 Method* m = f.cb()->as_nmethod()->method(); 222 return (m != nullptr && m->is_continuation_enter_intrinsic()); 223 } 224 225 bool Continuation::is_continuation_entry_frame(const frame& f, const RegisterMap *map) { 226 // we can do this because the entry frame is never inlined 227 Method* m = (map != nullptr && map->in_cont() && f.is_interpreted_frame()) 228 ? map->stack_chunk()->interpreter_frame_method(f) 229 : ContinuationHelper::Frame::frame_method(f); 230 return m != nullptr && m->intrinsic_id() == vmIntrinsics::_Continuation_enter; 231 } 232 233 // The parameter `sp` should be the actual sp and not the unextended sp because at 234 // least on PPC64 unextended_sp < sp is possible as interpreted frames are trimmed 235 // to the actual size of the expression stack before calls. The problem there is 236 // that even unextended_sp < entry_sp < sp is possible for an interpreted frame. 237 static inline bool is_sp_in_continuation(const ContinuationEntry* entry, intptr_t* const sp) { 238 // entry_sp() returns the unextended_sp which is always greater or equal to the actual sp 239 return entry->entry_sp() > sp; 240 } 241 242 bool Continuation::is_frame_in_continuation(const ContinuationEntry* entry, const frame& f) { 243 return is_sp_in_continuation(entry, f.sp()); 244 } 245 246 ContinuationEntry* Continuation::get_continuation_entry_for_sp(JavaThread* thread, intptr_t* const sp) { 247 assert(thread != nullptr, ""); 248 ContinuationEntry* entry = thread->last_continuation(); 249 while (entry != nullptr && !is_sp_in_continuation(entry, sp)) { 250 entry = entry->parent(); 251 } 252 return entry; 253 } 254 255 ContinuationEntry* Continuation::get_continuation_entry_for_entry_frame(JavaThread* thread, const frame& f) { 256 assert(is_continuation_enterSpecial(f), ""); 257 ContinuationEntry* entry = (ContinuationEntry*)f.unextended_sp(); 258 assert(entry == get_continuation_entry_for_sp(thread, f.sp()-2), "mismatched entry"); 259 return entry; 260 } 261 262 bool Continuation::is_frame_in_continuation(JavaThread* thread, const frame& f) { 263 return f.is_heap_frame() || (get_continuation_entry_for_sp(thread, f.sp()) != nullptr); 264 } 265 266 static frame continuation_top_frame(const ContinuationWrapper& cont, RegisterMap* map) { 267 stackChunkOop chunk = cont.last_nonempty_chunk(); 268 map->set_stack_chunk(chunk); 269 return chunk != nullptr ? chunk->top_frame(map) : frame(); 270 } 271 272 bool Continuation::has_last_Java_frame(oop continuation, frame* frame, RegisterMap* map) { 273 ContinuationWrapper cont(continuation); 274 if (!cont.is_empty()) { 275 *frame = continuation_top_frame(cont, map); 276 return true; 277 } else { 278 return false; 279 } 280 } 281 282 frame Continuation::last_frame(oop continuation, RegisterMap *map) { 283 assert(map != nullptr, "a map must be given"); 284 return continuation_top_frame(ContinuationWrapper(continuation), map); 285 } 286 287 frame Continuation::top_frame(const frame& callee, RegisterMap* map) { 288 assert(map != nullptr, ""); 289 ContinuationEntry* ce = get_continuation_entry_for_sp(map->thread(), callee.sp()); 290 assert(ce != nullptr, ""); 291 oop continuation = ce->cont_oop(map->thread()); 292 ContinuationWrapper cont(continuation); 293 return continuation_top_frame(cont, map); 294 } 295 296 javaVFrame* Continuation::last_java_vframe(Handle continuation, RegisterMap *map) { 297 assert(map != nullptr, "a map must be given"); 298 if (!ContinuationWrapper(continuation()).is_empty()) { 299 frame f = last_frame(continuation(), map); 300 for (vframe* vf = vframe::new_vframe(&f, map, nullptr); vf; vf = vf->sender()) { 301 if (vf->is_java_frame()) { 302 return javaVFrame::cast(vf); 303 } 304 } 305 } 306 return nullptr; 307 } 308 309 frame Continuation::continuation_parent_frame(RegisterMap* map) { 310 assert(map->in_cont(), ""); 311 ContinuationWrapper cont(map); 312 assert(map->thread() != nullptr || !cont.is_mounted(), ""); 313 314 log_develop_trace(continuations)("continuation_parent_frame"); 315 if (map->update_map()) { 316 // we need to register the link address for the entry frame 317 if (cont.entry() != nullptr) { 318 cont.entry()->update_register_map(map); 319 } else { 320 map->clear(); 321 } 322 } 323 324 if (!cont.is_mounted()) { // When we're walking an unmounted continuation and reached the end 325 oop parent = jdk_internal_vm_Continuation::parent(cont.continuation()); 326 stackChunkOop chunk = parent != nullptr ? ContinuationWrapper(parent).last_nonempty_chunk() : nullptr; 327 if (chunk != nullptr) { 328 return chunk->top_frame(map); 329 } 330 331 map->set_stack_chunk(nullptr); 332 return frame(); 333 } 334 335 map->set_stack_chunk(nullptr); 336 337 #if (defined(X86) || defined(AARCH64) || defined(RISCV64) || defined(PPC64)) && !defined(ZERO) 338 frame sender(cont.entrySP(), cont.entryFP(), cont.entryPC()); 339 #else 340 frame sender = frame(); 341 Unimplemented(); 342 #endif 343 344 return sender; 345 } 346 347 oop Continuation::continuation_scope(oop continuation) { 348 return continuation != nullptr ? jdk_internal_vm_Continuation::scope(continuation) : nullptr; 349 } 350 351 bool Continuation::is_scope_bottom(oop cont_scope, const frame& f, const RegisterMap* map) { 352 if (cont_scope == nullptr || !is_continuation_entry_frame(f, map)) { 353 return false; 354 } 355 356 oop continuation; 357 if (map->in_cont()) { 358 continuation = map->cont(); 359 } else { 360 ContinuationEntry* ce = get_continuation_entry_for_sp(map->thread(), f.sp()); 361 if (ce == nullptr) { 362 return false; 363 } 364 continuation = ce->cont_oop(map->thread()); 365 } 366 if (continuation == nullptr) { 367 return false; 368 } 369 370 oop sc = continuation_scope(continuation); 371 assert(sc != nullptr, ""); 372 return sc == cont_scope; 373 } 374 375 bool Continuation::is_in_usable_stack(address addr, const RegisterMap* map) { 376 ContinuationWrapper cont(map); 377 stackChunkOop chunk = cont.find_chunk_by_address(addr); 378 return chunk != nullptr ? chunk->is_usable_in_chunk(addr) : false; 379 } 380 381 bool Continuation::pin(JavaThread* current) { 382 ContinuationEntry* ce = current->last_continuation(); 383 if (ce == nullptr) { 384 return true; // no continuation mounted 385 } 386 return ce->pin(); 387 } 388 389 bool Continuation::unpin(JavaThread* current) { 390 ContinuationEntry* ce = current->last_continuation(); 391 if (ce == nullptr) { 392 return true; // no continuation mounted 393 } 394 return ce->unpin(); 395 } 396 397 frame Continuation::continuation_bottom_sender(JavaThread* thread, const frame& callee, intptr_t* sender_sp) { 398 assert (thread != nullptr, ""); 399 ContinuationEntry* ce = get_continuation_entry_for_sp(thread, callee.sp()); 400 assert(ce != nullptr, "callee.sp(): " INTPTR_FORMAT, p2i(callee.sp())); 401 402 log_develop_debug(continuations)("continuation_bottom_sender: [%d] callee: " INTPTR_FORMAT " sender_sp: " INTPTR_FORMAT, 403 thread->osthread()->thread_id(), p2i(callee.sp()), p2i(sender_sp)); 404 405 frame entry = ce->to_frame(); 406 if (callee.is_interpreted_frame()) { 407 entry.set_sp(sender_sp); // sp != unextended_sp 408 } 409 return entry; 410 } 411 412 address Continuation::get_top_return_pc_post_barrier(JavaThread* thread, address pc) { 413 ContinuationEntry* ce; 414 if (thread != nullptr && is_return_barrier_entry(pc) && (ce = thread->last_continuation()) != nullptr) { 415 return ce->entry_pc(); 416 } 417 return pc; 418 } 419 420 void Continuation::set_cont_fastpath_thread_state(JavaThread* thread) { 421 assert(thread != nullptr, ""); 422 bool fast = !thread->is_interp_only_mode(); 423 thread->set_cont_fastpath_thread_state(fast); 424 } 425 426 void Continuation::notify_deopt(JavaThread* thread, intptr_t* sp) { 427 ContinuationEntry* entry = thread->last_continuation(); 428 429 if (entry == nullptr) { 430 return; 431 } 432 433 if (is_sp_in_continuation(entry, sp)) { 434 thread->push_cont_fastpath(sp); 435 return; 436 } 437 438 ContinuationEntry* prev; 439 do { 440 prev = entry; 441 entry = entry->parent(); 442 } while (entry != nullptr && !is_sp_in_continuation(entry, sp)); 443 444 if (entry == nullptr) { 445 return; 446 } 447 assert(is_sp_in_continuation(entry, sp), ""); 448 if (sp > prev->parent_cont_fastpath()) { 449 prev->set_parent_cont_fastpath(sp); 450 } 451 } 452 453 #ifndef PRODUCT 454 void Continuation::describe(FrameValues &values) { 455 JavaThread* thread = JavaThread::active(); 456 if (thread != nullptr) { 457 for (ContinuationEntry* ce = thread->last_continuation(); ce != nullptr; ce = ce->parent()) { 458 intptr_t* bottom = ce->entry_sp(); 459 if (bottom != nullptr) { 460 values.describe(-1, bottom, "continuation entry"); 461 } 462 } 463 } 464 } 465 #endif 466 467 #ifdef ASSERT 468 void Continuation::debug_verify_continuation(oop contOop) { 469 if (!VerifyContinuations) { 470 return; 471 } 472 assert(contOop != nullptr, ""); 473 assert(oopDesc::is_oop(contOop), ""); 474 ContinuationWrapper cont(contOop); 475 476 assert(oopDesc::is_oop_or_null(cont.tail()), ""); 477 assert(cont.chunk_invariant(), ""); 478 479 bool nonempty_chunk = false; 480 size_t max_size = 0; 481 int num_chunks = 0; 482 int num_frames = 0; 483 int num_interpreted_frames = 0; 484 int num_oops = 0; 485 486 for (stackChunkOop chunk = cont.tail(); chunk != nullptr; chunk = chunk->parent()) { 487 log_develop_trace(continuations)("debug_verify_continuation chunk %d", num_chunks); 488 chunk->verify(&max_size, &num_oops, &num_frames, &num_interpreted_frames); 489 if (!chunk->is_empty()) { 490 nonempty_chunk = true; 491 } 492 num_chunks++; 493 } 494 495 const bool is_empty = cont.is_empty(); 496 assert(!nonempty_chunk || !is_empty, ""); 497 assert(is_empty == (!nonempty_chunk && cont.last_frame().is_empty()), ""); 498 } 499 500 void Continuation::print(oop continuation) { print_on(tty, continuation); } 501 502 void Continuation::print_on(outputStream* st, oop continuation) { 503 ContinuationWrapper cont(continuation); 504 505 st->print_cr("CONTINUATION: " PTR_FORMAT " done: %d", 506 continuation->identity_hash(), jdk_internal_vm_Continuation::done(continuation)); 507 st->print_cr("CHUNKS:"); 508 for (stackChunkOop chunk = cont.tail(); chunk != nullptr; chunk = chunk->parent()) { 509 st->print("* "); 510 chunk->print_on(true, st); 511 } 512 } 513 #endif // ASSERT 514 515 516 void continuations_init() { Continuations::init(); } 517 518 void Continuations::init() { 519 Continuation::init(); 520 } 521 522 bool Continuations::enabled() { 523 return VMContinuations; 524 } 525 526 #define CC (char*) /*cast a literal from (const char*)*/ 527 #define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f) 528 529 static JNINativeMethod CONT_methods[] = { 530 {CC"pin", CC"()V", FN_PTR(CONT_pin)}, 531 {CC"unpin", CC"()V", FN_PTR(CONT_unpin)}, 532 {CC"isPinned0", CC"(Ljdk/internal/vm/ContinuationScope;)I", FN_PTR(CONT_isPinned0)}, 533 }; 534 535 void CONT_RegisterNativeMethods(JNIEnv *env, jclass cls) { 536 JavaThread* thread = JavaThread::current(); 537 ThreadToNativeFromVM trans(thread); 538 int status = env->RegisterNatives(cls, CONT_methods, sizeof(CONT_methods)/sizeof(JNINativeMethod)); 539 guarantee(status == JNI_OK, "register jdk.internal.vm.Continuation natives"); 540 guarantee(!env->ExceptionCheck(), "register jdk.internal.vm.Continuation natives"); 541 }