/* * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. * */ #include "precompiled.hpp" #include "classfile/vmSymbols.hpp" #include "jni.h" #include "jvm.h" #include "oops/access.inline.hpp" #include "oops/oop.inline.hpp" #include "prims/stackwalk.hpp" #include "runtime/deoptimization.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/jniHandles.inline.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/vframe.inline.hpp" class CloseScopedMemoryFindOopClosure : public OopClosure { oop _deopt; bool _found; public: CloseScopedMemoryFindOopClosure(jobject deopt) : _deopt(JNIHandles::resolve(deopt)), _found(false) {} template void do_oop_work(T* p) { if (_found) { return; } if (RawAccess<>::oop_load(p) == _deopt) { _found = true; } } virtual void do_oop(oop* p) { do_oop_work(p); } virtual void do_oop(narrowOop* p) { do_oop_work(p); } bool found() { return _found; } }; class CloseScopedMemoryClosure : public HandshakeClosure { jobject _deopt; public: jboolean _found; CloseScopedMemoryClosure(jobject deopt, jobject exception) : HandshakeClosure("CloseScopedMemory") , _deopt(deopt) , _found(false) {} void do_thread(Thread* thread) { JavaThread* jt = JavaThread::cast(thread); if (!jt->has_last_Java_frame()) { return; } frame last_frame = jt->last_frame(); RegisterMap register_map(jt, RegisterMap::UpdateMap::include, RegisterMap::ProcessFrames::include, RegisterMap::WalkContinuation::skip); if (last_frame.is_safepoint_blob_frame()) { last_frame = last_frame.sender(®ister_map); } ResourceMark rm; if (_deopt != nullptr && last_frame.is_compiled_frame() && last_frame.can_be_deoptimized()) { CloseScopedMemoryFindOopClosure cl(_deopt); CompiledMethod* cm = last_frame.cb()->as_compiled_method(); /* FIXME: this doesn't work if reachability fences are violated by C2 last_frame.oops_do(&cl, nullptr, ®ister_map); if (cl.found()) { //Found the deopt oop in a compiled method; deoptimize. Deoptimization::deoptimize(jt, last_frame); } so... we unconditionally deoptimize, for now: */ Deoptimization::deoptimize(jt, last_frame); } const int max_critical_stack_depth = 10; int depth = 0; for (vframeStream stream(jt); !stream.at_end(); stream.next()) { Method* m = stream.method(); if (m->is_scoped()) { StackValueCollection* locals = stream.asJavaVFrame()->locals(); for (int i = 0; i < locals->size(); i++) { StackValue* var = locals->at(i); if (var->type() == T_OBJECT) { if (var->get_obj() == JNIHandles::resolve(_deopt)) { assert(depth < max_critical_stack_depth, "can't have more than %d critical frames", max_critical_stack_depth); _found = true; return; } } } break; } depth++; #ifndef ASSERT if (depth >= max_critical_stack_depth) { break; } #endif } } }; /* * This function performs a thread-local handshake against all threads running at the time * the given session (deopt) was closed. If the handshake for a given thread is processed while * one or more threads is found inside a scoped method (that is, a method inside the ScopedMemoryAccess * class annotated with the '@Scoped' annotation), and whose local variables mention the session being * closed (deopt), this method returns false, signalling that the session cannot be closed safely. */ JVM_ENTRY(jboolean, ScopedMemoryAccess_closeScope(JNIEnv *env, jobject receiver, jobject deopt, jobject exception)) CloseScopedMemoryClosure cl(deopt, exception); Handshake::execute(&cl); return !cl._found; JVM_END /// JVM_RegisterUnsafeMethods #define PKG_MISC "Ljdk/internal/misc/" #define PKG_FOREIGN "Ljdk/internal/foreign/" #define MEMACCESS "ScopedMemoryAccess" #define SCOPE PKG_FOREIGN "MemorySessionImpl;" #define CC (char*) /*cast a literal from (const char*)*/ #define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f) static JNINativeMethod jdk_internal_misc_ScopedMemoryAccess_methods[] = { {CC "closeScope0", CC "(" SCOPE ")Z", FN_PTR(ScopedMemoryAccess_closeScope)}, }; #undef CC #undef FN_PTR #undef PKG_MISC #undef PKG_FOREIGN #undef MEMACCESS #undef SCOPE // This function is exported, used by NativeLookup. JVM_ENTRY(void, JVM_RegisterJDKInternalMiscScopedMemoryAccessMethods(JNIEnv *env, jclass scopedMemoryAccessClass)) ThreadToNativeFromVM ttnfv(thread); int ok = env->RegisterNatives(scopedMemoryAccessClass, jdk_internal_misc_ScopedMemoryAccess_methods, sizeof(jdk_internal_misc_ScopedMemoryAccess_methods)/sizeof(JNINativeMethod)); guarantee(ok == 0, "register jdk.internal.misc.ScopedMemoryAccess natives"); JVM_END