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