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