mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-17 01:24:33 +02:00
8223312: Utilize handshakes instead of is_thread_fully_suspended
Reviewed-by: dholmes, rrich, dcubed, eosterlund
This commit is contained in:
parent
cc50c8d4f1
commit
4634dbef6d
6 changed files with 235 additions and 437 deletions
|
@ -1644,120 +1644,36 @@ JvmtiEnv::GetFrameCount(JavaThread* java_thread, jint* count_ptr) {
|
|||
// java_thread - pre-checked
|
||||
jvmtiError
|
||||
JvmtiEnv::PopFrame(JavaThread* java_thread) {
|
||||
JavaThread* current_thread = JavaThread::current();
|
||||
HandleMark hm(current_thread);
|
||||
uint32_t debug_bits = 0;
|
||||
|
||||
// retrieve or create the state
|
||||
JvmtiThreadState* state = JvmtiThreadState::state_for(java_thread);
|
||||
if (state == NULL) {
|
||||
return JVMTI_ERROR_THREAD_NOT_ALIVE;
|
||||
}
|
||||
|
||||
// Check if java_thread is fully suspended
|
||||
if (!java_thread->is_thread_fully_suspended(true /* wait for suspend completion */, &debug_bits)) {
|
||||
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
|
||||
}
|
||||
// Check to see if a PopFrame was already in progress
|
||||
if (java_thread->popframe_condition() != JavaThread::popframe_inactive) {
|
||||
// Probably possible for JVMTI clients to trigger this, but the
|
||||
// JPDA backend shouldn't allow this to happen
|
||||
return JVMTI_ERROR_INTERNAL;
|
||||
}
|
||||
|
||||
{
|
||||
// Was workaround bug
|
||||
// 4812902: popFrame hangs if the method is waiting at a synchronize
|
||||
// Catch this condition and return an error to avoid hanging.
|
||||
// Now JVMTI spec allows an implementation to bail out with an opaque frame error.
|
||||
OSThread* osThread = java_thread->osthread();
|
||||
if (osThread->get_state() == MONITOR_WAIT) {
|
||||
return JVMTI_ERROR_OPAQUE_FRAME;
|
||||
}
|
||||
}
|
||||
|
||||
// Eagerly reallocate scalar replaced objects.
|
||||
JavaThread* current_thread = JavaThread::current();
|
||||
EscapeBarrier eb(true, current_thread, java_thread);
|
||||
if (eb.barrier_active()) {
|
||||
if (java_thread->frames_to_pop_failed_realloc() > 0) {
|
||||
// VM is in the process of popping the top frame because it has scalar replaced objects which
|
||||
// could not be reallocated on the heap.
|
||||
// Return JVMTI_ERROR_OUT_OF_MEMORY to avoid interfering with the VM.
|
||||
return JVMTI_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
{
|
||||
ResourceMark rm(current_thread);
|
||||
// Check if there are more than one Java frame in this thread, that the top two frames
|
||||
// are Java (not native) frames, and that there is no intervening VM frame
|
||||
int frame_count = 0;
|
||||
bool is_interpreted[2];
|
||||
intptr_t *frame_sp[2];
|
||||
// The 2-nd arg of constructor is needed to stop iterating at java entry frame.
|
||||
for (vframeStream vfs(java_thread, true, false /* process_frames */); !vfs.at_end(); vfs.next()) {
|
||||
methodHandle mh(current_thread, vfs.method());
|
||||
if (mh->is_native()) return(JVMTI_ERROR_OPAQUE_FRAME);
|
||||
is_interpreted[frame_count] = vfs.is_interpreted_frame();
|
||||
frame_sp[frame_count] = vfs.frame_id();
|
||||
if (++frame_count > 1) break;
|
||||
}
|
||||
if (frame_count < 2) {
|
||||
// We haven't found two adjacent non-native Java frames on the top.
|
||||
// There can be two situations here:
|
||||
// 1. There are no more java frames
|
||||
// 2. Two top java frames are separated by non-java native frames
|
||||
if(vframeForNoProcess(java_thread, 1) == NULL) {
|
||||
return JVMTI_ERROR_NO_MORE_FRAMES;
|
||||
} else {
|
||||
// Intervening non-java native or VM frames separate java frames.
|
||||
// Current implementation does not support this. See bug #5031735.
|
||||
// In theory it is possible to pop frames in such cases.
|
||||
return JVMTI_ERROR_OPAQUE_FRAME;
|
||||
}
|
||||
}
|
||||
|
||||
// If any of the top 2 frames is a compiled one, need to deoptimize it
|
||||
EscapeBarrier eb(!is_interpreted[0] || !is_interpreted[1], current_thread, java_thread);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (!is_interpreted[i]) {
|
||||
Deoptimization::deoptimize_frame(java_thread, frame_sp[i]);
|
||||
// Eagerly reallocate scalar replaced objects.
|
||||
if (!eb.deoptimize_objects(frame_sp[i])) {
|
||||
if (!eb.deoptimize_objects(1)) {
|
||||
// Reallocation of scalar replaced objects failed -> return with error
|
||||
return JVMTI_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the thread state to reflect that the top frame is popped
|
||||
// so that cur_stack_depth is maintained properly and all frameIDs
|
||||
// are invalidated.
|
||||
// The current frame will be popped later when the suspended thread
|
||||
// is resumed and right before returning from VM to Java.
|
||||
// (see call_VM_base() in assembler_<cpu>.cpp).
|
||||
|
||||
// It's fine to update the thread state here because no JVMTI events
|
||||
// shall be posted for this PopFrame.
|
||||
|
||||
// It is only safe to perform the direct operation on the current
|
||||
// thread. All other usage needs to use a handshake for safety.
|
||||
{
|
||||
MutexLocker mu(JvmtiThreadState_lock);
|
||||
if (java_thread == JavaThread::current()) {
|
||||
state->update_for_pop_top_frame();
|
||||
} else {
|
||||
UpdateForPopTopFrameClosure op(state);
|
||||
if (java_thread == current_thread) {
|
||||
op.doit(java_thread, true /* self */);
|
||||
} else {
|
||||
Handshake::execute(&op, java_thread);
|
||||
if (op.result() != JVMTI_ERROR_NONE) {
|
||||
}
|
||||
return op.result();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
java_thread->set_popframe_condition(JavaThread::popframe_pending_bit);
|
||||
// Set pending step flag for this popframe and it is cleared when next
|
||||
// step event is posted.
|
||||
state->set_pending_step_for_popframe();
|
||||
}
|
||||
|
||||
return JVMTI_ERROR_NONE;
|
||||
} /* end PopFrame */
|
||||
|
||||
|
||||
|
@ -1791,46 +1707,19 @@ JvmtiEnv::GetFrameLocation(JavaThread* java_thread, jint depth, jmethodID* metho
|
|||
// depth - pre-checked as non-negative
|
||||
jvmtiError
|
||||
JvmtiEnv::NotifyFramePop(JavaThread* java_thread, jint depth) {
|
||||
jvmtiError err = JVMTI_ERROR_NONE;
|
||||
ResourceMark rm;
|
||||
uint32_t debug_bits = 0;
|
||||
|
||||
JvmtiThreadState *state = JvmtiThreadState::state_for(java_thread);
|
||||
if (state == NULL) {
|
||||
return JVMTI_ERROR_THREAD_NOT_ALIVE;
|
||||
}
|
||||
|
||||
if (!java_thread->is_thread_fully_suspended(true, &debug_bits)) {
|
||||
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
|
||||
}
|
||||
|
||||
if (TraceJVMTICalls) {
|
||||
JvmtiSuspendControl::print();
|
||||
}
|
||||
|
||||
vframe *vf = vframeForNoProcess(java_thread, depth);
|
||||
if (vf == NULL) {
|
||||
return JVMTI_ERROR_NO_MORE_FRAMES;
|
||||
}
|
||||
|
||||
if (!vf->is_java_frame() || ((javaVFrame*) vf)->method()->is_native()) {
|
||||
return JVMTI_ERROR_OPAQUE_FRAME;
|
||||
}
|
||||
|
||||
assert(vf->frame_pointer() != NULL, "frame pointer mustn't be NULL");
|
||||
|
||||
// It is only safe to perform the direct operation on the current
|
||||
// thread. All other usage needs to use a vm-safepoint-op for safety.
|
||||
SetFramePopClosure op(this, state, depth);
|
||||
MutexLocker mu(JvmtiThreadState_lock);
|
||||
if (java_thread == JavaThread::current()) {
|
||||
int frame_number = state->count_frames() - depth;
|
||||
state->env_thread_state(this)->set_frame_pop(frame_number);
|
||||
op.doit(java_thread, true /* self */);
|
||||
} else {
|
||||
SetFramePopClosure op(this, state, depth);
|
||||
Handshake::execute(&op, java_thread);
|
||||
err = op.result();
|
||||
}
|
||||
return err;
|
||||
return op.result();
|
||||
} /* end NotifyFramePop */
|
||||
|
||||
|
||||
|
|
|
@ -47,16 +47,16 @@
|
|||
#include "runtime/interfaceSupport.inline.hpp"
|
||||
#include "runtime/jfieldIDWorkaround.hpp"
|
||||
#include "runtime/jniHandles.inline.hpp"
|
||||
#include "runtime/objectMonitor.hpp"
|
||||
#include "runtime/objectMonitor.inline.hpp"
|
||||
#include "runtime/signature.hpp"
|
||||
#include "runtime/thread.inline.hpp"
|
||||
#include "runtime/threadSMR.hpp"
|
||||
#include "runtime/vframe.hpp"
|
||||
#include "runtime/vframe.inline.hpp"
|
||||
#include "runtime/vframe_hp.hpp"
|
||||
#include "runtime/vmThread.hpp"
|
||||
#include "runtime/vmOperations.hpp"
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
//
|
||||
// JvmtiEnvBase
|
||||
|
@ -1306,17 +1306,10 @@ VM_GetAllStackTraces::doit() {
|
|||
// HandleMark must be defined in the caller only.
|
||||
// It is to keep a ret_ob_h handle alive after return to the caller.
|
||||
jvmtiError
|
||||
JvmtiEnvBase::check_top_frame(JavaThread* current_thread, JavaThread* java_thread,
|
||||
JvmtiEnvBase::check_top_frame(Thread* current_thread, JavaThread* java_thread,
|
||||
jvalue value, TosState tos, Handle* ret_ob_h) {
|
||||
ResourceMark rm(current_thread);
|
||||
|
||||
if (java_thread->frames_to_pop_failed_realloc() > 0) {
|
||||
// VM is in the process of popping the top frame because it has scalar replaced objects
|
||||
// which could not be reallocated on the heap.
|
||||
// Return JVMTI_ERROR_OUT_OF_MEMORY to avoid interfering with the VM.
|
||||
return JVMTI_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
vframe *vf = vframeForNoProcess(java_thread, 0);
|
||||
NULL_CHECK(vf, JVMTI_ERROR_NO_MORE_FRAMES);
|
||||
|
||||
|
@ -1331,12 +1324,6 @@ JvmtiEnvBase::check_top_frame(JavaThread* current_thread, JavaThread* java_threa
|
|||
return JVMTI_ERROR_OPAQUE_FRAME;
|
||||
}
|
||||
Deoptimization::deoptimize_frame(java_thread, jvf->fr().id());
|
||||
// Eagerly reallocate scalar replaced objects.
|
||||
EscapeBarrier eb(true, current_thread, java_thread);
|
||||
if (!eb.deoptimize_objects(jvf->fr().id())) {
|
||||
// Reallocation of scalar replaced objects failed -> return with error
|
||||
return JVMTI_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
// Get information about method return type
|
||||
|
@ -1382,26 +1369,56 @@ JvmtiEnvBase::check_top_frame(JavaThread* current_thread, JavaThread* java_threa
|
|||
|
||||
jvmtiError
|
||||
JvmtiEnvBase::force_early_return(JavaThread* java_thread, jvalue value, TosState tos) {
|
||||
JavaThread* current_thread = JavaThread::current();
|
||||
HandleMark hm(current_thread);
|
||||
uint32_t debug_bits = 0;
|
||||
|
||||
// retrieve or create the state
|
||||
JvmtiThreadState* state = JvmtiThreadState::state_for(java_thread);
|
||||
if (state == NULL) {
|
||||
return JVMTI_ERROR_THREAD_NOT_ALIVE;
|
||||
}
|
||||
|
||||
// Check if java_thread is fully suspended
|
||||
if (!java_thread->is_thread_fully_suspended(true /* wait for suspend completion */, &debug_bits)) {
|
||||
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
|
||||
// Eagerly reallocate scalar replaced objects.
|
||||
JavaThread* current_thread = JavaThread::current();
|
||||
EscapeBarrier eb(true, current_thread, java_thread);
|
||||
if (eb.barrier_active()) {
|
||||
if (java_thread->frames_to_pop_failed_realloc() > 0) {
|
||||
// VM is in the process of popping the top frame because it has scalar replaced objects
|
||||
// which could not be reallocated on the heap.
|
||||
// Return JVMTI_ERROR_OUT_OF_MEMORY to avoid interfering with the VM.
|
||||
return JVMTI_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
if (!eb.deoptimize_objects(0)) {
|
||||
// Reallocation of scalar replaced objects failed -> return with error
|
||||
return JVMTI_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
SetForceEarlyReturn op(state, value, tos);
|
||||
if (java_thread == current_thread) {
|
||||
op.doit(java_thread, true /* self */);
|
||||
} else {
|
||||
Handshake::execute(&op, java_thread);
|
||||
}
|
||||
return op.result();
|
||||
}
|
||||
|
||||
void
|
||||
SetForceEarlyReturn::doit(Thread *target, bool self) {
|
||||
JavaThread* java_thread = target->as_Java_thread();
|
||||
Thread* current_thread = Thread::current();
|
||||
HandleMark hm(current_thread);
|
||||
|
||||
if (!self) {
|
||||
if (!java_thread->is_external_suspend()) {
|
||||
_result = JVMTI_ERROR_THREAD_NOT_SUSPENDED;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check to see if a ForceEarlyReturn was already in progress
|
||||
if (state->is_earlyret_pending()) {
|
||||
if (_state->is_earlyret_pending()) {
|
||||
// Probably possible for JVMTI clients to trigger this, but the
|
||||
// JPDA backend shouldn't allow this to happen
|
||||
return JVMTI_ERROR_INTERNAL;
|
||||
_result = JVMTI_ERROR_INTERNAL;
|
||||
return;
|
||||
}
|
||||
{
|
||||
// The same as for PopFrame. Workaround bug:
|
||||
|
@ -1411,15 +1428,17 @@ JvmtiEnvBase::force_early_return(JavaThread* java_thread, jvalue value, TosState
|
|||
// frame error.
|
||||
OSThread* osThread = java_thread->osthread();
|
||||
if (osThread->get_state() == MONITOR_WAIT) {
|
||||
return JVMTI_ERROR_OPAQUE_FRAME;
|
||||
_result = JVMTI_ERROR_OPAQUE_FRAME;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Handle ret_ob_h;
|
||||
jvmtiError err = check_top_frame(current_thread, java_thread, value, tos, &ret_ob_h);
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
return err;
|
||||
_result = JvmtiEnvBase::check_top_frame(current_thread, java_thread, _value, _tos, &ret_ob_h);
|
||||
if (_result != JVMTI_ERROR_NONE) {
|
||||
return;
|
||||
}
|
||||
assert(tos != atos || value.l == NULL || ret_ob_h() != NULL,
|
||||
assert(_tos != atos || _value.l == NULL || ret_ob_h() != NULL,
|
||||
"return object oop must not be NULL if jobject is not NULL");
|
||||
|
||||
// Update the thread state to reflect that the top frame must be
|
||||
|
@ -1428,16 +1447,14 @@ JvmtiEnvBase::force_early_return(JavaThread* java_thread, jvalue value, TosState
|
|||
// thread is resumed and right before returning from VM to Java.
|
||||
// (see call_VM_base() in assembler_<cpu>.cpp).
|
||||
|
||||
state->set_earlyret_pending();
|
||||
state->set_earlyret_oop(ret_ob_h());
|
||||
state->set_earlyret_value(value, tos);
|
||||
_state->set_earlyret_pending();
|
||||
_state->set_earlyret_oop(ret_ob_h());
|
||||
_state->set_earlyret_value(_value, _tos);
|
||||
|
||||
// Set pending step flag for this early return.
|
||||
// It is cleared when next step event is posted.
|
||||
state->set_pending_step_for_earlyret();
|
||||
|
||||
return JVMTI_ERROR_NONE;
|
||||
} /* end force_early_return */
|
||||
_state->set_pending_step_for_earlyret();
|
||||
}
|
||||
|
||||
void
|
||||
JvmtiMonitorClosure::do_monitor(ObjectMonitor* mon) {
|
||||
|
@ -1517,25 +1534,127 @@ JvmtiModuleClosure::get_all_modules(JvmtiEnv* env, jint* module_count_ptr, jobje
|
|||
}
|
||||
|
||||
void
|
||||
UpdateForPopTopFrameClosure::do_thread(Thread *target) {
|
||||
JavaThread* jt = _state->get_thread();
|
||||
assert(jt == target, "just checking");
|
||||
if (!jt->is_exiting() && jt->threadObj() != NULL) {
|
||||
UpdateForPopTopFrameClosure::doit(Thread *target, bool self) {
|
||||
Thread* current_thread = Thread::current();
|
||||
HandleMark hm(current_thread);
|
||||
JavaThread* java_thread = target->as_Java_thread();
|
||||
assert(java_thread == _state->get_thread(), "Must be");
|
||||
|
||||
if (!self && !java_thread->is_external_suspend()) {
|
||||
_result = JVMTI_ERROR_THREAD_NOT_SUSPENDED;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check to see if a PopFrame was already in progress
|
||||
if (java_thread->popframe_condition() != JavaThread::popframe_inactive) {
|
||||
// Probably possible for JVMTI clients to trigger this, but the
|
||||
// JPDA backend shouldn't allow this to happen
|
||||
_result = JVMTI_ERROR_INTERNAL;
|
||||
return;
|
||||
}
|
||||
|
||||
// Was workaround bug
|
||||
// 4812902: popFrame hangs if the method is waiting at a synchronize
|
||||
// Catch this condition and return an error to avoid hanging.
|
||||
// Now JVMTI spec allows an implementation to bail out with an opaque frame error.
|
||||
OSThread* osThread = java_thread->osthread();
|
||||
if (osThread->get_state() == MONITOR_WAIT) {
|
||||
_result = JVMTI_ERROR_OPAQUE_FRAME;
|
||||
return;
|
||||
}
|
||||
|
||||
ResourceMark rm(current_thread);
|
||||
// Check if there is more than one Java frame in this thread, that the top two frames
|
||||
// are Java (not native) frames, and that there is no intervening VM frame
|
||||
int frame_count = 0;
|
||||
bool is_interpreted[2];
|
||||
intptr_t *frame_sp[2];
|
||||
// The 2-nd arg of constructor is needed to stop iterating at java entry frame.
|
||||
for (vframeStream vfs(java_thread, true, false /* process_frames */); !vfs.at_end(); vfs.next()) {
|
||||
methodHandle mh(current_thread, vfs.method());
|
||||
if (mh->is_native()) {
|
||||
_result = JVMTI_ERROR_OPAQUE_FRAME;
|
||||
return;
|
||||
}
|
||||
is_interpreted[frame_count] = vfs.is_interpreted_frame();
|
||||
frame_sp[frame_count] = vfs.frame_id();
|
||||
if (++frame_count > 1) break;
|
||||
}
|
||||
if (frame_count < 2) {
|
||||
// We haven't found two adjacent non-native Java frames on the top.
|
||||
// There can be two situations here:
|
||||
// 1. There are no more java frames
|
||||
// 2. Two top java frames are separated by non-java native frames
|
||||
if(JvmtiEnvBase::vframeForNoProcess(java_thread, 1) == NULL) {
|
||||
_result = JVMTI_ERROR_NO_MORE_FRAMES;
|
||||
return;
|
||||
} else {
|
||||
// Intervening non-java native or VM frames separate java frames.
|
||||
// Current implementation does not support this. See bug #5031735.
|
||||
// In theory it is possible to pop frames in such cases.
|
||||
_result = JVMTI_ERROR_OPAQUE_FRAME;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If any of the top 2 frames is a compiled one, need to deoptimize it
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (!is_interpreted[i]) {
|
||||
Deoptimization::deoptimize_frame(java_thread, frame_sp[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the thread state to reflect that the top frame is popped
|
||||
// so that cur_stack_depth is maintained properly and all frameIDs
|
||||
// are invalidated.
|
||||
// The current frame will be popped later when the suspended thread
|
||||
// is resumed and right before returning from VM to Java.
|
||||
// (see call_VM_base() in assembler_<cpu>.cpp).
|
||||
|
||||
// It's fine to update the thread state here because no JVMTI events
|
||||
// shall be posted for this PopFrame.
|
||||
|
||||
if (!java_thread->is_exiting() && java_thread->threadObj() != NULL) {
|
||||
_state->update_for_pop_top_frame();
|
||||
java_thread->set_popframe_condition(JavaThread::popframe_pending_bit);
|
||||
// Set pending step flag for this popframe and it is cleared when next
|
||||
// step event is posted.
|
||||
_state->set_pending_step_for_popframe();
|
||||
_result = JVMTI_ERROR_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SetFramePopClosure::do_thread(Thread *target) {
|
||||
JavaThread* jt = _state->get_thread();
|
||||
assert(jt == target, "just checking");
|
||||
if (!jt->is_exiting() && jt->threadObj() != NULL) {
|
||||
SetFramePopClosure::doit(Thread *target, bool self) {
|
||||
ResourceMark rm;
|
||||
JavaThread* java_thread = target->as_Java_thread();
|
||||
|
||||
assert(_state->get_thread() == java_thread, "Must be");
|
||||
|
||||
if (!self && !java_thread->is_external_suspend()) {
|
||||
_result = JVMTI_ERROR_THREAD_NOT_SUSPENDED;
|
||||
return;
|
||||
}
|
||||
|
||||
vframe *vf = JvmtiEnvBase::vframeForNoProcess(java_thread, _depth);
|
||||
if (vf == NULL) {
|
||||
_result = JVMTI_ERROR_NO_MORE_FRAMES;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!vf->is_java_frame() || ((javaVFrame*) vf)->method()->is_native()) {
|
||||
_result = JVMTI_ERROR_OPAQUE_FRAME;
|
||||
return;
|
||||
}
|
||||
|
||||
assert(vf->frame_pointer() != NULL, "frame pointer mustn't be NULL");
|
||||
if (java_thread->is_exiting() || java_thread->threadObj() == NULL) {
|
||||
return; /* JVMTI_ERROR_THREAD_NOT_ALIVE (default) */
|
||||
}
|
||||
int frame_number = _state->count_frames() - _depth;
|
||||
_state->env_thread_state((JvmtiEnvBase*)_env)->set_frame_pop(frame_number);
|
||||
_result = JVMTI_ERROR_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GetOwnedMonitorInfoClosure::do_thread(Thread *target) {
|
||||
|
|
|
@ -286,9 +286,9 @@ class JvmtiEnvBase : public CHeapObj<mtInternal> {
|
|||
javaVFrame *jvf,
|
||||
GrowableArray<jvmtiMonitorStackDepthInfo*>* owned_monitors_list,
|
||||
jint depth);
|
||||
vframe* vframeForNoProcess(JavaThread* java_thread, jint depth);
|
||||
|
||||
public:
|
||||
static vframe* vframeForNoProcess(JavaThread* java_thread, jint depth);
|
||||
|
||||
// get a field descriptor for the specified class and field
|
||||
static bool get_field_descriptor(Klass* k, jfieldID field, fieldDescriptor* fd);
|
||||
|
||||
|
@ -306,7 +306,7 @@ class JvmtiEnvBase : public CHeapObj<mtInternal> {
|
|||
jobject *monitor_ptr);
|
||||
jvmtiError get_owned_monitors(JavaThread *calling_thread, JavaThread* java_thread,
|
||||
GrowableArray<jvmtiMonitorStackDepthInfo*> *owned_monitors_list);
|
||||
jvmtiError check_top_frame(JavaThread* current_thread, JavaThread* java_thread,
|
||||
static jvmtiError check_top_frame(Thread* current_thread, JavaThread* java_thread,
|
||||
jvalue value, TosState tos, Handle* ret_ob_h);
|
||||
jvmtiError force_early_return(JavaThread* java_thread, jvalue value, TosState tos);
|
||||
};
|
||||
|
@ -346,6 +346,23 @@ class JvmtiHandshakeClosure : public HandshakeClosure {
|
|||
jvmtiError result() { return _result; }
|
||||
};
|
||||
|
||||
class SetForceEarlyReturn : public JvmtiHandshakeClosure {
|
||||
private:
|
||||
JvmtiThreadState* _state;
|
||||
jvalue _value;
|
||||
TosState _tos;
|
||||
public:
|
||||
SetForceEarlyReturn(JvmtiThreadState* state, jvalue value, TosState tos)
|
||||
: JvmtiHandshakeClosure("SetForceEarlyReturn"),
|
||||
_state(state),
|
||||
_value(value),
|
||||
_tos(tos) {}
|
||||
void do_thread(Thread *target) {
|
||||
doit(target, false /* self */);
|
||||
}
|
||||
void doit(Thread *target, bool self);
|
||||
};
|
||||
|
||||
// HandshakeClosure to update for pop top frame.
|
||||
class UpdateForPopTopFrameClosure : public JvmtiHandshakeClosure {
|
||||
private:
|
||||
|
@ -355,7 +372,10 @@ public:
|
|||
UpdateForPopTopFrameClosure(JvmtiThreadState* state)
|
||||
: JvmtiHandshakeClosure("UpdateForPopTopFrame"),
|
||||
_state(state) {}
|
||||
void do_thread(Thread *target);
|
||||
void do_thread(Thread *target) {
|
||||
doit(target, false /* self */);
|
||||
}
|
||||
void doit(Thread *target, bool self);
|
||||
};
|
||||
|
||||
// HandshakeClosure to set frame pop.
|
||||
|
@ -371,7 +391,10 @@ public:
|
|||
_env(env),
|
||||
_state(state),
|
||||
_depth(depth) {}
|
||||
void do_thread(Thread *target);
|
||||
void do_thread(Thread *target) {
|
||||
doit(target, false /* self */);
|
||||
}
|
||||
void doit(Thread *target, bool self);
|
||||
};
|
||||
|
||||
// HandshakeClosure to get monitor information with stack depth.
|
||||
|
|
|
@ -1749,8 +1749,10 @@ address Deoptimization::deoptimize_for_missing_exception_handler(CompiledMethod*
|
|||
#endif
|
||||
|
||||
void Deoptimization::deoptimize_frame_internal(JavaThread* thread, intptr_t* id, DeoptReason reason) {
|
||||
assert(thread == Thread::current() || SafepointSynchronize::is_at_safepoint(),
|
||||
"can only deoptimize other thread at a safepoint");
|
||||
assert(thread == Thread::current() ||
|
||||
thread->is_handshake_safe_for(Thread::current()) ||
|
||||
SafepointSynchronize::is_at_safepoint(),
|
||||
"can only deoptimize other thread at a safepoint/handshake");
|
||||
// Compute frame and register map based on thread and sp.
|
||||
RegisterMap reg_map(thread, false);
|
||||
frame fr = thread->last_frame();
|
||||
|
@ -1762,7 +1764,8 @@ void Deoptimization::deoptimize_frame_internal(JavaThread* thread, intptr_t* id,
|
|||
|
||||
|
||||
void Deoptimization::deoptimize_frame(JavaThread* thread, intptr_t* id, DeoptReason reason) {
|
||||
if (thread == Thread::current()) {
|
||||
Thread* current = Thread::current();
|
||||
if (thread == current || thread->is_handshake_safe_for(current)) {
|
||||
Deoptimization::deoptimize_frame_internal(thread, id, reason);
|
||||
} else {
|
||||
VM_DeoptimizeFrame deopt(thread, id, reason);
|
||||
|
|
|
@ -534,100 +534,16 @@ void Thread::send_async_exception(oop java_thread, oop java_throwable) {
|
|||
// Check if an external suspend request has completed (or has been
|
||||
// cancelled). Returns true if the thread is externally suspended and
|
||||
// false otherwise.
|
||||
//
|
||||
// The bits parameter returns information about the code path through
|
||||
// the routine. Useful for debugging:
|
||||
//
|
||||
// set in is_ext_suspend_completed():
|
||||
// 0x00000001 - routine was entered
|
||||
// 0x00000010 - routine return false at end
|
||||
// 0x00000100 - thread exited (return false)
|
||||
// 0x00000200 - suspend request cancelled (return false)
|
||||
// 0x00000400 - thread suspended (return true)
|
||||
// 0x00001000 - thread is in a suspend equivalent state (return true)
|
||||
// 0x00002000 - thread is native and walkable (return true)
|
||||
// 0x00004000 - thread is native_trans and walkable (needed retry)
|
||||
//
|
||||
// set in wait_for_ext_suspend_completion():
|
||||
// 0x00010000 - routine was entered
|
||||
// 0x00020000 - suspend request cancelled before loop (return false)
|
||||
// 0x00040000 - thread suspended before loop (return true)
|
||||
// 0x00080000 - suspend request cancelled in loop (return false)
|
||||
// 0x00100000 - thread suspended in loop (return true)
|
||||
// 0x00200000 - suspend not completed during retry loop (return false)
|
||||
|
||||
// Helper class for tracing suspend wait debug bits.
|
||||
//
|
||||
// 0x00000100 indicates that the target thread exited before it could
|
||||
// self-suspend which is not a wait failure. 0x00000200, 0x00020000 and
|
||||
// 0x00080000 each indicate a cancelled suspend request so they don't
|
||||
// count as wait failures either.
|
||||
#define DEBUG_FALSE_BITS (0x00000010 | 0x00200000)
|
||||
|
||||
class TraceSuspendDebugBits : public StackObj {
|
||||
private:
|
||||
JavaThread * jt;
|
||||
bool is_wait;
|
||||
bool called_by_wait; // meaningful when !is_wait
|
||||
uint32_t * bits;
|
||||
|
||||
public:
|
||||
TraceSuspendDebugBits(JavaThread *_jt, bool _is_wait, bool _called_by_wait,
|
||||
uint32_t *_bits) {
|
||||
jt = _jt;
|
||||
is_wait = _is_wait;
|
||||
called_by_wait = _called_by_wait;
|
||||
bits = _bits;
|
||||
}
|
||||
|
||||
~TraceSuspendDebugBits() {
|
||||
if (!is_wait) {
|
||||
#if 1
|
||||
// By default, don't trace bits for is_ext_suspend_completed() calls.
|
||||
// That trace is very chatty.
|
||||
return;
|
||||
#else
|
||||
if (!called_by_wait) {
|
||||
// If tracing for is_ext_suspend_completed() is enabled, then only
|
||||
// trace calls to it from wait_for_ext_suspend_completion()
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (AssertOnSuspendWaitFailure || TraceSuspendWaitFailures) {
|
||||
if (bits != NULL && (*bits & DEBUG_FALSE_BITS) != 0) {
|
||||
MutexLocker ml(Threads_lock); // needed for get_thread_name()
|
||||
ResourceMark rm;
|
||||
|
||||
tty->print_cr(
|
||||
"Failed wait_for_ext_suspend_completion(thread=%s, debug_bits=%x)",
|
||||
jt->get_thread_name(), *bits);
|
||||
|
||||
guarantee(!AssertOnSuspendWaitFailure, "external suspend wait failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
#undef DEBUG_FALSE_BITS
|
||||
|
||||
|
||||
bool JavaThread::is_ext_suspend_completed(bool called_by_wait, int delay,
|
||||
uint32_t *bits) {
|
||||
TraceSuspendDebugBits tsdb(this, false /* !is_wait */, called_by_wait, bits);
|
||||
|
||||
bool JavaThread::is_ext_suspend_completed() {
|
||||
bool did_trans_retry = false; // only do thread_in_native_trans retry once
|
||||
bool do_trans_retry; // flag to force the retry
|
||||
|
||||
*bits |= 0x00000001;
|
||||
|
||||
do {
|
||||
do_trans_retry = false;
|
||||
|
||||
if (is_exiting()) {
|
||||
// Thread is in the process of exiting. This is always checked
|
||||
// first to reduce the risk of dereferencing a freed JavaThread.
|
||||
*bits |= 0x00000100;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -635,13 +551,11 @@ bool JavaThread::is_ext_suspend_completed(bool called_by_wait, int delay,
|
|||
// Suspend request is cancelled. This is always checked before
|
||||
// is_ext_suspended() to reduce the risk of a rogue resume
|
||||
// confusing the thread that made the suspend request.
|
||||
*bits |= 0x00000200;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_ext_suspended()) {
|
||||
// thread is suspended
|
||||
*bits |= 0x00000400;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -669,25 +583,21 @@ bool JavaThread::is_ext_suspend_completed(bool called_by_wait, int delay,
|
|||
//
|
||||
// Return true since we wouldn't be here unless there was still an
|
||||
// external suspend request.
|
||||
*bits |= 0x00001000;
|
||||
return true;
|
||||
} else if (save_state == _thread_in_native && frame_anchor()->walkable()) {
|
||||
// Threads running native code will self-suspend on native==>VM/Java
|
||||
// transitions. If its stack is walkable (should always be the case
|
||||
// unless this function is called before the actual java_suspend()
|
||||
// call), then the wait is done.
|
||||
*bits |= 0x00002000;
|
||||
return true;
|
||||
} else if (!called_by_wait && !did_trans_retry &&
|
||||
} else if (!did_trans_retry &&
|
||||
save_state == _thread_in_native_trans &&
|
||||
frame_anchor()->walkable()) {
|
||||
// The thread is transitioning from thread_in_native to another
|
||||
// thread state. check_safepoint_and_suspend_for_native_trans()
|
||||
// will force the thread to self-suspend. If it hasn't gotten
|
||||
// there yet we may have caught the thread in-between the native
|
||||
// code check above and the self-suspend. Lucky us. If we were
|
||||
// called by wait_for_ext_suspend_completion(), then it
|
||||
// will be doing the retries so we don't have to.
|
||||
// code check above and the self-suspend.
|
||||
//
|
||||
// Since we use the saved thread state in the if-statement above,
|
||||
// there is a chance that the thread has already transitioned to
|
||||
|
@ -695,8 +605,6 @@ bool JavaThread::is_ext_suspend_completed(bool called_by_wait, int delay,
|
|||
// make a single unnecessary pass through the logic below. This
|
||||
// doesn't hurt anything since we still do the trans retry.
|
||||
|
||||
*bits |= 0x00004000;
|
||||
|
||||
// Once the thread leaves thread_in_native_trans for another
|
||||
// thread state, we break out of this retry loop. We shouldn't
|
||||
// need this flag to prevent us from getting back here, but
|
||||
|
@ -716,9 +624,9 @@ bool JavaThread::is_ext_suspend_completed(bool called_by_wait, int delay,
|
|||
// (if we're a JavaThread - the WatcherThread can also call this)
|
||||
// and increase delay with each retry
|
||||
if (Thread::current()->is_Java_thread()) {
|
||||
SR_lock()->wait(i * delay);
|
||||
SR_lock()->wait(i * SuspendRetryDelay);
|
||||
} else {
|
||||
SR_lock()->wait_without_safepoint_check(i * delay);
|
||||
SR_lock()->wait_without_safepoint_check(i * SuspendRetryDelay);
|
||||
}
|
||||
|
||||
// check the actual thread state instead of what we saved above
|
||||
|
@ -729,134 +637,12 @@ bool JavaThread::is_ext_suspend_completed(bool called_by_wait, int delay,
|
|||
break;
|
||||
}
|
||||
} // end retry loop
|
||||
|
||||
|
||||
}
|
||||
} while (do_trans_retry);
|
||||
|
||||
*bits |= 0x00000010;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait for an external suspend request to complete (or be cancelled).
|
||||
// Returns true if the thread is externally suspended and false otherwise.
|
||||
//
|
||||
bool JavaThread::wait_for_ext_suspend_completion(int retries, int delay,
|
||||
uint32_t *bits) {
|
||||
TraceSuspendDebugBits tsdb(this, true /* is_wait */,
|
||||
false /* !called_by_wait */, bits);
|
||||
|
||||
// local flag copies to minimize SR_lock hold time
|
||||
bool is_suspended;
|
||||
bool pending;
|
||||
uint32_t reset_bits;
|
||||
|
||||
// set a marker so is_ext_suspend_completed() knows we are the caller
|
||||
*bits |= 0x00010000;
|
||||
|
||||
// We use reset_bits to reinitialize the bits value at the top of
|
||||
// each retry loop. This allows the caller to make use of any
|
||||
// unused bits for their own marking purposes.
|
||||
reset_bits = *bits;
|
||||
|
||||
{
|
||||
MutexLocker ml(SR_lock(), Mutex::_no_safepoint_check_flag);
|
||||
is_suspended = is_ext_suspend_completed(true /* called_by_wait */,
|
||||
delay, bits);
|
||||
pending = is_external_suspend();
|
||||
}
|
||||
// must release SR_lock to allow suspension to complete
|
||||
|
||||
if (!pending) {
|
||||
// A cancelled suspend request is the only false return from
|
||||
// is_ext_suspend_completed() that keeps us from entering the
|
||||
// retry loop.
|
||||
*bits |= 0x00020000;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_suspended) {
|
||||
*bits |= 0x00040000;
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 1; i <= retries; i++) {
|
||||
*bits = reset_bits; // reinit to only track last retry
|
||||
|
||||
// We used to do an "os::yield_all(i)" call here with the intention
|
||||
// that yielding would increase on each retry. However, the parameter
|
||||
// is ignored on Linux which means the yield didn't scale up. Waiting
|
||||
// on the SR_lock below provides a much more predictable scale up for
|
||||
// the delay. It also provides a simple/direct point to check for any
|
||||
// safepoint requests from the VMThread
|
||||
|
||||
{
|
||||
Thread* t = Thread::current();
|
||||
MonitorLocker ml(SR_lock(),
|
||||
t->is_Java_thread() ? Mutex::_safepoint_check_flag : Mutex::_no_safepoint_check_flag);
|
||||
// wait with safepoint check (if we're a JavaThread - the WatcherThread
|
||||
// can also call this) and increase delay with each retry
|
||||
ml.wait(i * delay);
|
||||
|
||||
is_suspended = is_ext_suspend_completed(true /* called_by_wait */,
|
||||
delay, bits);
|
||||
|
||||
// It is possible for the external suspend request to be cancelled
|
||||
// (by a resume) before the actual suspend operation is completed.
|
||||
// Refresh our local copy to see if we still need to wait.
|
||||
pending = is_external_suspend();
|
||||
}
|
||||
|
||||
if (!pending) {
|
||||
// A cancelled suspend request is the only false return from
|
||||
// is_ext_suspend_completed() that keeps us from staying in the
|
||||
// retry loop.
|
||||
*bits |= 0x00080000;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_suspended) {
|
||||
*bits |= 0x00100000;
|
||||
return true;
|
||||
}
|
||||
} // end retry loop
|
||||
|
||||
// thread did not suspend after all our retries
|
||||
*bits |= 0x00200000;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Called from API entry points which perform stack walking. If the
|
||||
// associated JavaThread is the current thread, then wait_for_suspend
|
||||
// is not used. Otherwise, it determines if we should wait for the
|
||||
// "other" thread to complete external suspension. (NOTE: in future
|
||||
// releases the suspension mechanism should be reimplemented so this
|
||||
// is not necessary.)
|
||||
//
|
||||
bool
|
||||
JavaThread::is_thread_fully_suspended(bool wait_for_suspend, uint32_t *bits) {
|
||||
if (this != Thread::current()) {
|
||||
// "other" threads require special handling.
|
||||
if (wait_for_suspend) {
|
||||
// We are allowed to wait for the external suspend to complete
|
||||
// so give the other thread a chance to get suspended.
|
||||
if (!wait_for_ext_suspend_completion(SuspendRetryCount,
|
||||
SuspendRetryDelay, bits)) {
|
||||
// Didn't make it so let the caller know.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// We aren't allowed to wait for the external suspend to complete
|
||||
// so if the other thread isn't externally suspended we need to
|
||||
// let the caller know.
|
||||
else if (!is_ext_suspend_completed_with_lock(bits)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// GC Support
|
||||
bool Thread::claim_par_threads_do(uintx claim_token) {
|
||||
uintx token = _threads_do_token;
|
||||
|
@ -2489,12 +2275,11 @@ void JavaThread::java_suspend() {
|
|||
}
|
||||
|
||||
// suspend is done
|
||||
uint32_t debug_bits = 0;
|
||||
|
||||
// Warning: is_ext_suspend_completed() may temporarily drop the
|
||||
// SR_lock to allow the thread to reach a stable thread state if
|
||||
// it is currently in a transient thread state.
|
||||
if (is_ext_suspend_completed(false /* !called_by_wait */,
|
||||
SuspendRetryDelay, &debug_bits)) {
|
||||
if (is_ext_suspend_completed()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -2548,9 +2333,7 @@ int JavaThread::java_suspend_self() {
|
|||
if (this->is_suspend_equivalent()) {
|
||||
// If we are self-suspending as a result of the lifting of a
|
||||
// suspend equivalent condition, then the suspend_equivalent
|
||||
// flag is not cleared until we set the ext_suspended flag so
|
||||
// that wait_for_ext_suspend_completion() returns consistent
|
||||
// results.
|
||||
// flag is not cleared until we set the ext_suspended flag.
|
||||
this->clear_suspend_equivalent();
|
||||
}
|
||||
|
||||
|
|
|
@ -1383,26 +1383,7 @@ class JavaThread: public Thread {
|
|||
// Check for async exception in addition to safepoint and suspend request.
|
||||
static void check_special_condition_for_native_trans(JavaThread *thread);
|
||||
|
||||
bool is_ext_suspend_completed(bool called_by_wait, int delay, uint32_t *bits);
|
||||
bool is_ext_suspend_completed_with_lock(uint32_t *bits) {
|
||||
MutexLocker ml(SR_lock(), Mutex::_no_safepoint_check_flag);
|
||||
// Warning: is_ext_suspend_completed() may temporarily drop the
|
||||
// SR_lock to allow the thread to reach a stable thread state if
|
||||
// it is currently in a transient thread state.
|
||||
return is_ext_suspend_completed(false /* !called_by_wait */,
|
||||
SuspendRetryDelay, bits);
|
||||
}
|
||||
|
||||
// We cannot allow wait_for_ext_suspend_completion() to run forever or
|
||||
// we could hang. SuspendRetryCount and SuspendRetryDelay are normally
|
||||
// passed as the count and delay parameters. Experiments with specific
|
||||
// calls to wait_for_ext_suspend_completion() can be done by passing
|
||||
// other values in the code. Experiments with all calls can be done
|
||||
// via the appropriate -XX options.
|
||||
bool wait_for_ext_suspend_completion(int count, int delay, uint32_t *bits);
|
||||
|
||||
// test for suspend - most (all?) of these should go away
|
||||
bool is_thread_fully_suspended(bool wait_for_suspend, uint32_t *bits);
|
||||
bool is_ext_suspend_completed();
|
||||
|
||||
inline void set_external_suspend();
|
||||
inline void clear_external_suspend();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue