Skip to content

Commit 8257853

Browse files
fiskpull[bot]
authored andcommitted
8310644: Make panama memory segment close use async handshakes
Reviewed-by: jvernee, mcimadamore, pchilanomate
1 parent 147c41c commit 8257853

File tree

8 files changed

+159
-146
lines changed

8 files changed

+159
-146
lines changed

src/hotspot/share/prims/scopedMemoryAccess.cpp

Lines changed: 72 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -35,54 +35,72 @@
3535
#include "runtime/sharedRuntime.hpp"
3636
#include "runtime/vframe.inline.hpp"
3737

38-
class CloseScopedMemoryFindOopClosure : public OopClosure {
39-
oop _deopt;
40-
bool _found;
41-
42-
public:
43-
CloseScopedMemoryFindOopClosure(jobject deopt) :
44-
_deopt(JNIHandles::resolve(deopt)),
45-
_found(false) {}
46-
47-
template <typename T>
48-
void do_oop_work(T* p) {
49-
if (_found) {
50-
return;
38+
static bool is_in_scoped_access(JavaThread* jt, oop session) {
39+
const int max_critical_stack_depth = 10;
40+
int depth = 0;
41+
for (vframeStream stream(jt); !stream.at_end(); stream.next()) {
42+
Method* m = stream.method();
43+
if (m->is_scoped()) {
44+
StackValueCollection* locals = stream.asJavaVFrame()->locals();
45+
for (int i = 0; i < locals->size(); i++) {
46+
StackValue* var = locals->at(i);
47+
if (var->type() == T_OBJECT) {
48+
if (var->get_obj() == session) {
49+
assert(depth < max_critical_stack_depth, "can't have more than %d critical frames", max_critical_stack_depth);
50+
return true;
51+
}
52+
}
53+
}
54+
break;
5155
}
52-
if (RawAccess<>::oop_load(p) == _deopt) {
53-
_found = true;
56+
depth++;
57+
#ifndef ASSERT
58+
if (depth >= max_critical_stack_depth) {
59+
break;
5460
}
61+
#endif
5562
}
5663

57-
virtual void do_oop(oop* p) {
58-
do_oop_work(p);
59-
}
64+
return false;
65+
}
66+
67+
class ScopedAsyncExceptionHandshake : public AsyncExceptionHandshake {
68+
OopHandle _session;
6069

61-
virtual void do_oop(narrowOop* p) {
62-
do_oop_work(p);
70+
public:
71+
ScopedAsyncExceptionHandshake(OopHandle& session, OopHandle& error)
72+
: AsyncExceptionHandshake(error),
73+
_session(session) {}
74+
75+
~ScopedAsyncExceptionHandshake() {
76+
_session.release(Universe::vm_global());
6377
}
6478

65-
bool found() {
66-
return _found;
79+
virtual void do_thread(Thread* thread) {
80+
JavaThread* jt = JavaThread::cast(thread);
81+
ResourceMark rm;
82+
if (is_in_scoped_access(jt, _session.resolve())) {
83+
// Throw exception to unwind out from the scoped access
84+
AsyncExceptionHandshake::do_thread(thread);
85+
}
6786
}
6887
};
6988

7089
class CloseScopedMemoryClosure : public HandshakeClosure {
71-
jobject _deopt;
90+
jobject _session;
91+
jobject _error;
7292

7393
public:
74-
jboolean _found;
75-
76-
CloseScopedMemoryClosure(jobject deopt, jobject exception)
94+
CloseScopedMemoryClosure(jobject session, jobject error)
7795
: HandshakeClosure("CloseScopedMemory")
78-
, _deopt(deopt)
79-
, _found(false) {}
96+
, _session(session)
97+
, _error(error) {}
8098

8199
void do_thread(Thread* thread) {
82-
83100
JavaThread* jt = JavaThread::cast(thread);
84101

85102
if (!jt->has_last_Java_frame()) {
103+
// No frames; not in a scoped memory access
86104
return;
87105
}
88106

@@ -97,44 +115,27 @@ class CloseScopedMemoryClosure : public HandshakeClosure {
97115
}
98116

99117
ResourceMark rm;
100-
if (_deopt != nullptr && last_frame.is_compiled_frame() && last_frame.can_be_deoptimized()) {
101-
CloseScopedMemoryFindOopClosure cl(_deopt);
102-
CompiledMethod* cm = last_frame.cb()->as_compiled_method();
103-
104-
/* FIXME: this doesn't work if reachability fences are violated by C2
105-
last_frame.oops_do(&cl, nullptr, &register_map);
106-
if (cl.found()) {
107-
//Found the deopt oop in a compiled method; deoptimize.
108-
Deoptimization::deoptimize(jt, last_frame);
109-
}
110-
so... we unconditionally deoptimize, for now: */
118+
if (last_frame.is_compiled_frame() && last_frame.can_be_deoptimized()) {
119+
// FIXME: we would like to conditionally deoptimize only if the corresponding
120+
// _session is reachable from the frame, but reachabilityFence doesn't currently
121+
// work the way it should. Therefore we deopt unconditionally for now.
111122
Deoptimization::deoptimize(jt, last_frame);
112123
}
113124

114-
const int max_critical_stack_depth = 10;
115-
int depth = 0;
116-
for (vframeStream stream(jt); !stream.at_end(); stream.next()) {
117-
Method* m = stream.method();
118-
if (m->is_scoped()) {
119-
StackValueCollection* locals = stream.asJavaVFrame()->locals();
120-
for (int i = 0; i < locals->size(); i++) {
121-
StackValue* var = locals->at(i);
122-
if (var->type() == T_OBJECT) {
123-
if (var->get_obj() == JNIHandles::resolve(_deopt)) {
124-
assert(depth < max_critical_stack_depth, "can't have more than %d critical frames", max_critical_stack_depth);
125-
_found = true;
126-
return;
127-
}
128-
}
129-
}
130-
break;
131-
}
132-
depth++;
133-
#ifndef ASSERT
134-
if (depth >= max_critical_stack_depth) {
135-
break;
136-
}
137-
#endif
125+
if (jt->has_async_exception_condition()) {
126+
// Target thread just about to throw an async exception using async handshakes,
127+
// we will then unwind out from the scoped memory access.
128+
return;
129+
}
130+
131+
if (is_in_scoped_access(jt, JNIHandles::resolve(_session))) {
132+
// We have found that the target thread is inside of a scoped access.
133+
// An asynchronous handshake is sent to the target thread, telling it
134+
// to throw an exception, which will unwind the target thread out from
135+
// the scoped access.
136+
OopHandle session(Universe::vm_global(), JNIHandles::resolve(_session));
137+
OopHandle error(Universe::vm_global(), JNIHandles::resolve(_error));
138+
jt->install_async_exception(new ScopedAsyncExceptionHandshake(session, error));
138139
}
139140
}
140141
};
@@ -146,34 +147,33 @@ class CloseScopedMemoryClosure : public HandshakeClosure {
146147
* class annotated with the '@Scoped' annotation), and whose local variables mention the session being
147148
* closed (deopt), this method returns false, signalling that the session cannot be closed safely.
148149
*/
149-
JVM_ENTRY(jboolean, ScopedMemoryAccess_closeScope(JNIEnv *env, jobject receiver, jobject deopt, jobject exception))
150-
CloseScopedMemoryClosure cl(deopt, exception);
150+
JVM_ENTRY(void, ScopedMemoryAccess_closeScope(JNIEnv *env, jobject receiver, jobject session, jobject error))
151+
CloseScopedMemoryClosure cl(session, error);
151152
Handshake::execute(&cl);
152-
return !cl._found;
153153
JVM_END
154154

155155
/// JVM_RegisterUnsafeMethods
156156

157157
#define PKG_MISC "Ljdk/internal/misc/"
158158
#define PKG_FOREIGN "Ljdk/internal/foreign/"
159159

160-
#define MEMACCESS "ScopedMemoryAccess"
161-
#define SCOPE PKG_FOREIGN "MemorySessionImpl;"
160+
#define SCOPED_SESSION PKG_FOREIGN "MemorySessionImpl;"
161+
#define SCOPED_ERROR PKG_MISC "ScopedMemoryAccess$ScopedAccessError;"
162162

163163
#define CC (char*) /*cast a literal from (const char*)*/
164164
#define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f)
165165

166166
static JNINativeMethod jdk_internal_misc_ScopedMemoryAccess_methods[] = {
167-
{CC "closeScope0", CC "(" SCOPE ")Z", FN_PTR(ScopedMemoryAccess_closeScope)},
167+
{CC "closeScope0", CC "(" SCOPED_SESSION SCOPED_ERROR ")V", FN_PTR(ScopedMemoryAccess_closeScope)},
168168
};
169169

170170
#undef CC
171171
#undef FN_PTR
172172

173173
#undef PKG_MISC
174174
#undef PKG_FOREIGN
175-
#undef MEMACCESS
176-
#undef SCOPE
175+
#undef SCOPED_SESSION
176+
#undef SCOPED_ERROR
177177

178178
// This function is exported, used by NativeLookup.
179179

src/hotspot/share/prims/unsafe.cpp

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,36 @@
7070
( arrayOopDesc::header_size(T_DOUBLE) * HeapWordSize \
7171
+ ((julong)max_jint * sizeof(double)) )
7272

73-
7473
#define UNSAFE_ENTRY(result_type, header) \
7574
JVM_ENTRY(static result_type, header)
7675

7776
#define UNSAFE_LEAF(result_type, header) \
7877
JVM_LEAF(static result_type, header)
7978

79+
// Note that scoped accesses (cf. scopedMemoryAccess.cpp) can install
80+
// an async handshake on the entry to an Unsafe method. When that happens,
81+
// it is expected that we are not allowed to touch the underlying memory
82+
// that might have gotten unmapped. Therefore, we check at the entry
83+
// to unsafe functions, if we have such async exception conditions,
84+
// and return immediately if that is the case.
85+
//
86+
// We also use NoSafepointVerifier to block potential safepoints.
87+
// It would be problematic if an async exception handshake were installed later on
88+
// during another safepoint in the function, but before the memory access happens,
89+
// as the memory will be freed after the handshake is installed. We must notice
90+
// the installed handshake and return early before doing the memory access to prevent
91+
// accesses to freed memory.
92+
//
93+
// Note also that we MUST do a scoped memory access in the VM (or Java) thread
94+
// state. Since we rely on a handshake to check for threads that are accessing
95+
// scoped memory, and we need the handshaking thread to wait until we get to a
96+
// safepoint, in order to make sure we are not in the middle of accessing memory
97+
// that is about to be freed. (i.e. there can be no UNSAFE_LEAF_SCOPED)
98+
#define UNSAFE_ENTRY_SCOPED(result_type, header) \
99+
JVM_ENTRY(static result_type, header) \
100+
if (thread->has_async_exception_condition()) {return (result_type)0;} \
101+
NoSafepointVerifier nsv;
102+
80103
#define UNSAFE_END JVM_END
81104

82105

@@ -279,11 +302,11 @@ UNSAFE_ENTRY(jobject, Unsafe_GetUncompressedObject(JNIEnv *env, jobject unsafe,
279302

280303
#define DEFINE_GETSETOOP(java_type, Type) \
281304
\
282-
UNSAFE_ENTRY(java_type, Unsafe_Get##Type(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) { \
305+
UNSAFE_ENTRY_SCOPED(java_type, Unsafe_Get##Type(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) { \
283306
return MemoryAccess<java_type>(thread, obj, offset).get(); \
284307
} UNSAFE_END \
285308
\
286-
UNSAFE_ENTRY(void, Unsafe_Put##Type(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, java_type x)) { \
309+
UNSAFE_ENTRY_SCOPED(void, Unsafe_Put##Type(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, java_type x)) { \
287310
MemoryAccess<java_type>(thread, obj, offset).put(x); \
288311
} UNSAFE_END \
289312
\
@@ -302,11 +325,11 @@ DEFINE_GETSETOOP(jdouble, Double);
302325

303326
#define DEFINE_GETSETOOP_VOLATILE(java_type, Type) \
304327
\
305-
UNSAFE_ENTRY(java_type, Unsafe_Get##Type##Volatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) { \
328+
UNSAFE_ENTRY_SCOPED(java_type, Unsafe_Get##Type##Volatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) { \
306329
return MemoryAccess<java_type>(thread, obj, offset).get_volatile(); \
307330
} UNSAFE_END \
308331
\
309-
UNSAFE_ENTRY(void, Unsafe_Put##Type##Volatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, java_type x)) { \
332+
UNSAFE_ENTRY_SCOPED(void, Unsafe_Put##Type##Volatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, java_type x)) { \
310333
MemoryAccess<java_type>(thread, obj, offset).put_volatile(x); \
311334
} UNSAFE_END \
312335
\
@@ -362,7 +385,7 @@ UNSAFE_LEAF(void, Unsafe_FreeMemory0(JNIEnv *env, jobject unsafe, jlong addr)) {
362385
os::free(p);
363386
} UNSAFE_END
364387

365-
UNSAFE_ENTRY(void, Unsafe_SetMemory0(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong size, jbyte value)) {
388+
UNSAFE_ENTRY_SCOPED(void, Unsafe_SetMemory0(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong size, jbyte value)) {
366389
size_t sz = (size_t)size;
367390

368391
oop base = JNIHandles::resolve(obj);
@@ -371,7 +394,7 @@ UNSAFE_ENTRY(void, Unsafe_SetMemory0(JNIEnv *env, jobject unsafe, jobject obj, j
371394
Copy::fill_to_memory_atomic(p, sz, value);
372395
} UNSAFE_END
373396

374-
UNSAFE_ENTRY(void, Unsafe_CopyMemory0(JNIEnv *env, jobject unsafe, jobject srcObj, jlong srcOffset, jobject dstObj, jlong dstOffset, jlong size)) {
397+
UNSAFE_ENTRY_SCOPED(void, Unsafe_CopyMemory0(JNIEnv *env, jobject unsafe, jobject srcObj, jlong srcOffset, jobject dstObj, jlong dstOffset, jlong size)) {
375398
size_t sz = (size_t)size;
376399

377400
oop srcp = JNIHandles::resolve(srcObj);
@@ -390,39 +413,19 @@ UNSAFE_ENTRY(void, Unsafe_CopyMemory0(JNIEnv *env, jobject unsafe, jobject srcOb
390413
}
391414
} UNSAFE_END
392415

393-
// This function is a leaf since if the source and destination are both in native memory
394-
// the copy may potentially be very large, and we don't want to disable GC if we can avoid it.
395-
// If either source or destination (or both) are on the heap, the function will enter VM using
396-
// JVM_ENTRY_FROM_LEAF
397-
UNSAFE_LEAF(void, Unsafe_CopySwapMemory0(JNIEnv *env, jobject unsafe, jobject srcObj, jlong srcOffset, jobject dstObj, jlong dstOffset, jlong size, jlong elemSize)) {
416+
UNSAFE_ENTRY_SCOPED(void, Unsafe_CopySwapMemory0(JNIEnv *env, jobject unsafe, jobject srcObj, jlong srcOffset, jobject dstObj, jlong dstOffset, jlong size, jlong elemSize)) {
398417
size_t sz = (size_t)size;
399418
size_t esz = (size_t)elemSize;
400419

401-
if (srcObj == nullptr && dstObj == nullptr) {
402-
// Both src & dst are in native memory
403-
address src = (address)srcOffset;
404-
address dst = (address)dstOffset;
405-
406-
{
407-
JavaThread* thread = JavaThread::thread_from_jni_environment(env);
408-
GuardUnsafeAccess guard(thread);
409-
Copy::conjoint_swap(src, dst, sz, esz);
410-
}
411-
} else {
412-
// At least one of src/dst are on heap, transition to VM to access raw pointers
413-
414-
JVM_ENTRY_FROM_LEAF(env, void, Unsafe_CopySwapMemory0) {
415-
oop srcp = JNIHandles::resolve(srcObj);
416-
oop dstp = JNIHandles::resolve(dstObj);
420+
oop srcp = JNIHandles::resolve(srcObj);
421+
oop dstp = JNIHandles::resolve(dstObj);
417422

418-
address src = (address)index_oop_from_field_offset_long(srcp, srcOffset);
419-
address dst = (address)index_oop_from_field_offset_long(dstp, dstOffset);
423+
address src = (address)index_oop_from_field_offset_long(srcp, srcOffset);
424+
address dst = (address)index_oop_from_field_offset_long(dstp, dstOffset);
420425

421-
{
422-
GuardUnsafeAccess guard(thread);
423-
Copy::conjoint_swap(src, dst, sz, esz);
424-
}
425-
} JVM_END
426+
{
427+
GuardUnsafeAccess guard(thread);
428+
Copy::conjoint_swap(src, dst, sz, esz);
426429
}
427430
} UNSAFE_END
428431

@@ -718,13 +721,13 @@ UNSAFE_ENTRY(jobject, Unsafe_CompareAndExchangeReference(JNIEnv *env, jobject un
718721
return JNIHandles::make_local(THREAD, res);
719722
} UNSAFE_END
720723

721-
UNSAFE_ENTRY(jint, Unsafe_CompareAndExchangeInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) {
724+
UNSAFE_ENTRY_SCOPED(jint, Unsafe_CompareAndExchangeInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) {
722725
oop p = JNIHandles::resolve(obj);
723726
volatile jint* addr = (volatile jint*)index_oop_from_field_offset_long(p, offset);
724727
return Atomic::cmpxchg(addr, e, x);
725728
} UNSAFE_END
726729

727-
UNSAFE_ENTRY(jlong, Unsafe_CompareAndExchangeLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong e, jlong x)) {
730+
UNSAFE_ENTRY_SCOPED(jlong, Unsafe_CompareAndExchangeLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong e, jlong x)) {
728731
oop p = JNIHandles::resolve(obj);
729732
volatile jlong* addr = (volatile jlong*)index_oop_from_field_offset_long(p, offset);
730733
return Atomic::cmpxchg(addr, e, x);
@@ -739,13 +742,13 @@ UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSetReference(JNIEnv *env, jobject unsafe
739742
return ret == e;
740743
} UNSAFE_END
741744

742-
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSetInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) {
745+
UNSAFE_ENTRY_SCOPED(jboolean, Unsafe_CompareAndSetInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) {
743746
oop p = JNIHandles::resolve(obj);
744747
volatile jint* addr = (volatile jint*)index_oop_from_field_offset_long(p, offset);
745748
return Atomic::cmpxchg(addr, e, x) == e;
746749
} UNSAFE_END
747750

748-
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSetLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong e, jlong x)) {
751+
UNSAFE_ENTRY_SCOPED(jboolean, Unsafe_CompareAndSetLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong e, jlong x)) {
749752
oop p = JNIHandles::resolve(obj);
750753
volatile jlong* addr = (volatile jlong*)index_oop_from_field_offset_long(p, offset);
751754
return Atomic::cmpxchg(addr, e, x) == e;

src/hotspot/share/runtime/interfaceSupport.inline.hpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -406,14 +406,6 @@ extern "C" { \
406406
VM_LEAF_BASE(result_type, header)
407407

408408

409-
#define JVM_ENTRY_FROM_LEAF(env, result_type, header) \
410-
{ { \
411-
JavaThread* thread=JavaThread::thread_from_jni_environment(env); \
412-
ThreadInVMfromNative __tiv(thread); \
413-
debug_only(VMNativeEntryWrapper __vew;) \
414-
VM_ENTRY_BASE_FROM_LEAF(result_type, header, thread)
415-
416-
417409
#define JVM_END } }
418410

419411
#endif // SHARE_RUNTIME_INTERFACESUPPORT_INLINE_HPP

src/hotspot/share/runtime/javaThread.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,9 @@ class JavaThread: public Thread {
225225
friend class AsyncExceptionHandshake;
226226
friend class HandshakeState;
227227

228-
void install_async_exception(AsyncExceptionHandshake* aec = nullptr);
229228
void handle_async_exception(oop java_throwable);
230229
public:
230+
void install_async_exception(AsyncExceptionHandshake* aec = nullptr);
231231
bool has_async_exception_condition();
232232
inline void set_pending_unsafe_access_error();
233233
static void send_async_exception(JavaThread* jt, oop java_throwable);

0 commit comments

Comments
 (0)