mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-17 09:34:38 +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
|
// java_thread - pre-checked
|
||||||
jvmtiError
|
jvmtiError
|
||||||
JvmtiEnv::PopFrame(JavaThread* java_thread) {
|
JvmtiEnv::PopFrame(JavaThread* java_thread) {
|
||||||
JavaThread* current_thread = JavaThread::current();
|
|
||||||
HandleMark hm(current_thread);
|
|
||||||
uint32_t debug_bits = 0;
|
|
||||||
|
|
||||||
// retrieve or create the state
|
// retrieve or create the state
|
||||||
JvmtiThreadState* state = JvmtiThreadState::state_for(java_thread);
|
JvmtiThreadState* state = JvmtiThreadState::state_for(java_thread);
|
||||||
if (state == NULL) {
|
if (state == NULL) {
|
||||||
return JVMTI_ERROR_THREAD_NOT_ALIVE;
|
return JVMTI_ERROR_THREAD_NOT_ALIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if java_thread is fully suspended
|
// Eagerly reallocate scalar replaced objects.
|
||||||
if (!java_thread->is_thread_fully_suspended(true /* wait for suspend completion */, &debug_bits)) {
|
JavaThread* current_thread = JavaThread::current();
|
||||||
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
|
EscapeBarrier eb(true, current_thread, java_thread);
|
||||||
}
|
if (eb.barrier_active()) {
|
||||||
// Check to see if a PopFrame was already in progress
|
if (java_thread->frames_to_pop_failed_realloc() > 0) {
|
||||||
if (java_thread->popframe_condition() != JavaThread::popframe_inactive) {
|
// VM is in the process of popping the top frame because it has scalar replaced objects which
|
||||||
// Probably possible for JVMTI clients to trigger this, but the
|
// could not be reallocated on the heap.
|
||||||
// JPDA backend shouldn't allow this to happen
|
// Return JVMTI_ERROR_OUT_OF_MEMORY to avoid interfering with the VM.
|
||||||
return JVMTI_ERROR_INTERNAL;
|
return JVMTI_ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
|
if (!eb.deoptimize_objects(1)) {
|
||||||
{
|
// Reallocation of scalar replaced objects failed -> return with error
|
||||||
// Was workaround bug
|
return JVMTI_ERROR_OUT_OF_MEMORY;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (java_thread->frames_to_pop_failed_realloc() > 0) {
|
MutexLocker mu(JvmtiThreadState_lock);
|
||||||
// VM is in the process of popping the top frame because it has scalar replaced objects which
|
UpdateForPopTopFrameClosure op(state);
|
||||||
// could not be reallocated on the heap.
|
if (java_thread == current_thread) {
|
||||||
// Return JVMTI_ERROR_OUT_OF_MEMORY to avoid interfering with the VM.
|
op.doit(java_thread, true /* self */);
|
||||||
return JVMTI_ERROR_OUT_OF_MEMORY;
|
} else {
|
||||||
|
Handshake::execute(&op, java_thread);
|
||||||
}
|
}
|
||||||
|
return op.result();
|
||||||
{
|
|
||||||
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])) {
|
|
||||||
// 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);
|
|
||||||
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 */
|
} /* end PopFrame */
|
||||||
|
|
||||||
|
|
||||||
|
@ -1791,46 +1707,19 @@ JvmtiEnv::GetFrameLocation(JavaThread* java_thread, jint depth, jmethodID* metho
|
||||||
// depth - pre-checked as non-negative
|
// depth - pre-checked as non-negative
|
||||||
jvmtiError
|
jvmtiError
|
||||||
JvmtiEnv::NotifyFramePop(JavaThread* java_thread, jint depth) {
|
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);
|
JvmtiThreadState *state = JvmtiThreadState::state_for(java_thread);
|
||||||
if (state == NULL) {
|
if (state == NULL) {
|
||||||
return JVMTI_ERROR_THREAD_NOT_ALIVE;
|
return JVMTI_ERROR_THREAD_NOT_ALIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!java_thread->is_thread_fully_suspended(true, &debug_bits)) {
|
SetFramePopClosure op(this, state, depth);
|
||||||
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.
|
|
||||||
MutexLocker mu(JvmtiThreadState_lock);
|
MutexLocker mu(JvmtiThreadState_lock);
|
||||||
if (java_thread == JavaThread::current()) {
|
if (java_thread == JavaThread::current()) {
|
||||||
int frame_number = state->count_frames() - depth;
|
op.doit(java_thread, true /* self */);
|
||||||
state->env_thread_state(this)->set_frame_pop(frame_number);
|
|
||||||
} else {
|
} else {
|
||||||
SetFramePopClosure op(this, state, depth);
|
|
||||||
Handshake::execute(&op, java_thread);
|
Handshake::execute(&op, java_thread);
|
||||||
err = op.result();
|
|
||||||
}
|
}
|
||||||
return err;
|
return op.result();
|
||||||
} /* end NotifyFramePop */
|
} /* end NotifyFramePop */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -47,16 +47,16 @@
|
||||||
#include "runtime/interfaceSupport.inline.hpp"
|
#include "runtime/interfaceSupport.inline.hpp"
|
||||||
#include "runtime/jfieldIDWorkaround.hpp"
|
#include "runtime/jfieldIDWorkaround.hpp"
|
||||||
#include "runtime/jniHandles.inline.hpp"
|
#include "runtime/jniHandles.inline.hpp"
|
||||||
#include "runtime/objectMonitor.hpp"
|
|
||||||
#include "runtime/objectMonitor.inline.hpp"
|
#include "runtime/objectMonitor.inline.hpp"
|
||||||
#include "runtime/signature.hpp"
|
#include "runtime/signature.hpp"
|
||||||
#include "runtime/thread.inline.hpp"
|
#include "runtime/thread.inline.hpp"
|
||||||
#include "runtime/threadSMR.hpp"
|
#include "runtime/threadSMR.hpp"
|
||||||
#include "runtime/vframe.hpp"
|
#include "runtime/vframe.inline.hpp"
|
||||||
#include "runtime/vframe_hp.hpp"
|
#include "runtime/vframe_hp.hpp"
|
||||||
#include "runtime/vmThread.hpp"
|
#include "runtime/vmThread.hpp"
|
||||||
#include "runtime/vmOperations.hpp"
|
#include "runtime/vmOperations.hpp"
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// JvmtiEnvBase
|
// JvmtiEnvBase
|
||||||
|
@ -1306,17 +1306,10 @@ VM_GetAllStackTraces::doit() {
|
||||||
// HandleMark must be defined in the caller only.
|
// HandleMark must be defined in the caller only.
|
||||||
// It is to keep a ret_ob_h handle alive after return to the caller.
|
// It is to keep a ret_ob_h handle alive after return to the caller.
|
||||||
jvmtiError
|
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) {
|
jvalue value, TosState tos, Handle* ret_ob_h) {
|
||||||
ResourceMark rm(current_thread);
|
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);
|
vframe *vf = vframeForNoProcess(java_thread, 0);
|
||||||
NULL_CHECK(vf, JVMTI_ERROR_NO_MORE_FRAMES);
|
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;
|
return JVMTI_ERROR_OPAQUE_FRAME;
|
||||||
}
|
}
|
||||||
Deoptimization::deoptimize_frame(java_thread, jvf->fr().id());
|
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
|
// Get information about method return type
|
||||||
|
@ -1382,26 +1369,56 @@ JvmtiEnvBase::check_top_frame(JavaThread* current_thread, JavaThread* java_threa
|
||||||
|
|
||||||
jvmtiError
|
jvmtiError
|
||||||
JvmtiEnvBase::force_early_return(JavaThread* java_thread, jvalue value, TosState tos) {
|
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
|
// retrieve or create the state
|
||||||
JvmtiThreadState* state = JvmtiThreadState::state_for(java_thread);
|
JvmtiThreadState* state = JvmtiThreadState::state_for(java_thread);
|
||||||
if (state == NULL) {
|
if (state == NULL) {
|
||||||
return JVMTI_ERROR_THREAD_NOT_ALIVE;
|
return JVMTI_ERROR_THREAD_NOT_ALIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if java_thread is fully suspended
|
// Eagerly reallocate scalar replaced objects.
|
||||||
if (!java_thread->is_thread_fully_suspended(true /* wait for suspend completion */, &debug_bits)) {
|
JavaThread* current_thread = JavaThread::current();
|
||||||
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
|
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
|
// 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
|
// Probably possible for JVMTI clients to trigger this, but the
|
||||||
// JPDA backend shouldn't allow this to happen
|
// JPDA backend shouldn't allow this to happen
|
||||||
return JVMTI_ERROR_INTERNAL;
|
_result = JVMTI_ERROR_INTERNAL;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
// The same as for PopFrame. Workaround bug:
|
// The same as for PopFrame. Workaround bug:
|
||||||
|
@ -1411,15 +1428,17 @@ JvmtiEnvBase::force_early_return(JavaThread* java_thread, jvalue value, TosState
|
||||||
// frame error.
|
// frame error.
|
||||||
OSThread* osThread = java_thread->osthread();
|
OSThread* osThread = java_thread->osthread();
|
||||||
if (osThread->get_state() == MONITOR_WAIT) {
|
if (osThread->get_state() == MONITOR_WAIT) {
|
||||||
return JVMTI_ERROR_OPAQUE_FRAME;
|
_result = JVMTI_ERROR_OPAQUE_FRAME;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle ret_ob_h;
|
Handle ret_ob_h;
|
||||||
jvmtiError err = check_top_frame(current_thread, java_thread, value, tos, &ret_ob_h);
|
_result = JvmtiEnvBase::check_top_frame(current_thread, java_thread, _value, _tos, &ret_ob_h);
|
||||||
if (err != JVMTI_ERROR_NONE) {
|
if (_result != JVMTI_ERROR_NONE) {
|
||||||
return err;
|
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");
|
"return object oop must not be NULL if jobject is not NULL");
|
||||||
|
|
||||||
// Update the thread state to reflect that the top frame must be
|
// 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.
|
// thread is resumed and right before returning from VM to Java.
|
||||||
// (see call_VM_base() in assembler_<cpu>.cpp).
|
// (see call_VM_base() in assembler_<cpu>.cpp).
|
||||||
|
|
||||||
state->set_earlyret_pending();
|
_state->set_earlyret_pending();
|
||||||
state->set_earlyret_oop(ret_ob_h());
|
_state->set_earlyret_oop(ret_ob_h());
|
||||||
state->set_earlyret_value(value, tos);
|
_state->set_earlyret_value(_value, _tos);
|
||||||
|
|
||||||
// Set pending step flag for this early return.
|
// Set pending step flag for this early return.
|
||||||
// It is cleared when next step event is posted.
|
// It is cleared when next step event is posted.
|
||||||
state->set_pending_step_for_earlyret();
|
_state->set_pending_step_for_earlyret();
|
||||||
|
}
|
||||||
return JVMTI_ERROR_NONE;
|
|
||||||
} /* end force_early_return */
|
|
||||||
|
|
||||||
void
|
void
|
||||||
JvmtiMonitorClosure::do_monitor(ObjectMonitor* mon) {
|
JvmtiMonitorClosure::do_monitor(ObjectMonitor* mon) {
|
||||||
|
@ -1517,24 +1534,126 @@ JvmtiModuleClosure::get_all_modules(JvmtiEnv* env, jint* module_count_ptr, jobje
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
UpdateForPopTopFrameClosure::do_thread(Thread *target) {
|
UpdateForPopTopFrameClosure::doit(Thread *target, bool self) {
|
||||||
JavaThread* jt = _state->get_thread();
|
Thread* current_thread = Thread::current();
|
||||||
assert(jt == target, "just checking");
|
HandleMark hm(current_thread);
|
||||||
if (!jt->is_exiting() && jt->threadObj() != NULL) {
|
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();
|
_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;
|
_result = JVMTI_ERROR_NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SetFramePopClosure::do_thread(Thread *target) {
|
SetFramePopClosure::doit(Thread *target, bool self) {
|
||||||
JavaThread* jt = _state->get_thread();
|
ResourceMark rm;
|
||||||
assert(jt == target, "just checking");
|
JavaThread* java_thread = target->as_Java_thread();
|
||||||
if (!jt->is_exiting() && jt->threadObj() != NULL) {
|
|
||||||
int frame_number = _state->count_frames() - _depth;
|
assert(_state->get_thread() == java_thread, "Must be");
|
||||||
_state->env_thread_state((JvmtiEnvBase*)_env)->set_frame_pop(frame_number);
|
|
||||||
_result = JVMTI_ERROR_NONE;
|
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
|
void
|
||||||
|
|
|
@ -286,9 +286,9 @@ class JvmtiEnvBase : public CHeapObj<mtInternal> {
|
||||||
javaVFrame *jvf,
|
javaVFrame *jvf,
|
||||||
GrowableArray<jvmtiMonitorStackDepthInfo*>* owned_monitors_list,
|
GrowableArray<jvmtiMonitorStackDepthInfo*>* owned_monitors_list,
|
||||||
jint depth);
|
jint depth);
|
||||||
vframe* vframeForNoProcess(JavaThread* java_thread, jint depth);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
static vframe* vframeForNoProcess(JavaThread* java_thread, jint depth);
|
||||||
|
|
||||||
// get a field descriptor for the specified class and field
|
// get a field descriptor for the specified class and field
|
||||||
static bool get_field_descriptor(Klass* k, jfieldID field, fieldDescriptor* fd);
|
static bool get_field_descriptor(Klass* k, jfieldID field, fieldDescriptor* fd);
|
||||||
|
|
||||||
|
@ -306,8 +306,8 @@ class JvmtiEnvBase : public CHeapObj<mtInternal> {
|
||||||
jobject *monitor_ptr);
|
jobject *monitor_ptr);
|
||||||
jvmtiError get_owned_monitors(JavaThread *calling_thread, JavaThread* java_thread,
|
jvmtiError get_owned_monitors(JavaThread *calling_thread, JavaThread* java_thread,
|
||||||
GrowableArray<jvmtiMonitorStackDepthInfo*> *owned_monitors_list);
|
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);
|
jvalue value, TosState tos, Handle* ret_ob_h);
|
||||||
jvmtiError force_early_return(JavaThread* java_thread, jvalue value, TosState tos);
|
jvmtiError force_early_return(JavaThread* java_thread, jvalue value, TosState tos);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -346,6 +346,23 @@ class JvmtiHandshakeClosure : public HandshakeClosure {
|
||||||
jvmtiError result() { return _result; }
|
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.
|
// HandshakeClosure to update for pop top frame.
|
||||||
class UpdateForPopTopFrameClosure : public JvmtiHandshakeClosure {
|
class UpdateForPopTopFrameClosure : public JvmtiHandshakeClosure {
|
||||||
private:
|
private:
|
||||||
|
@ -354,8 +371,11 @@ private:
|
||||||
public:
|
public:
|
||||||
UpdateForPopTopFrameClosure(JvmtiThreadState* state)
|
UpdateForPopTopFrameClosure(JvmtiThreadState* state)
|
||||||
: JvmtiHandshakeClosure("UpdateForPopTopFrame"),
|
: JvmtiHandshakeClosure("UpdateForPopTopFrame"),
|
||||||
_state(state) {}
|
_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.
|
// HandshakeClosure to set frame pop.
|
||||||
|
@ -371,7 +391,10 @@ public:
|
||||||
_env(env),
|
_env(env),
|
||||||
_state(state),
|
_state(state),
|
||||||
_depth(depth) {}
|
_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.
|
// HandshakeClosure to get monitor information with stack depth.
|
||||||
|
|
|
@ -1749,8 +1749,10 @@ address Deoptimization::deoptimize_for_missing_exception_handler(CompiledMethod*
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void Deoptimization::deoptimize_frame_internal(JavaThread* thread, intptr_t* id, DeoptReason reason) {
|
void Deoptimization::deoptimize_frame_internal(JavaThread* thread, intptr_t* id, DeoptReason reason) {
|
||||||
assert(thread == Thread::current() || SafepointSynchronize::is_at_safepoint(),
|
assert(thread == Thread::current() ||
|
||||||
"can only deoptimize other thread at a safepoint");
|
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.
|
// Compute frame and register map based on thread and sp.
|
||||||
RegisterMap reg_map(thread, false);
|
RegisterMap reg_map(thread, false);
|
||||||
frame fr = thread->last_frame();
|
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) {
|
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);
|
Deoptimization::deoptimize_frame_internal(thread, id, reason);
|
||||||
} else {
|
} else {
|
||||||
VM_DeoptimizeFrame deopt(thread, id, reason);
|
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
|
// Check if an external suspend request has completed (or has been
|
||||||
// cancelled). Returns true if the thread is externally suspended and
|
// cancelled). Returns true if the thread is externally suspended and
|
||||||
// false otherwise.
|
// false otherwise.
|
||||||
//
|
bool JavaThread::is_ext_suspend_completed() {
|
||||||
// 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 did_trans_retry = false; // only do thread_in_native_trans retry once
|
bool did_trans_retry = false; // only do thread_in_native_trans retry once
|
||||||
bool do_trans_retry; // flag to force the retry
|
bool do_trans_retry; // flag to force the retry
|
||||||
|
|
||||||
*bits |= 0x00000001;
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
do_trans_retry = false;
|
do_trans_retry = false;
|
||||||
|
|
||||||
if (is_exiting()) {
|
if (is_exiting()) {
|
||||||
// Thread is in the process of exiting. This is always checked
|
// Thread is in the process of exiting. This is always checked
|
||||||
// first to reduce the risk of dereferencing a freed JavaThread.
|
// first to reduce the risk of dereferencing a freed JavaThread.
|
||||||
*bits |= 0x00000100;
|
|
||||||
return false;
|
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
|
// Suspend request is cancelled. This is always checked before
|
||||||
// is_ext_suspended() to reduce the risk of a rogue resume
|
// is_ext_suspended() to reduce the risk of a rogue resume
|
||||||
// confusing the thread that made the suspend request.
|
// confusing the thread that made the suspend request.
|
||||||
*bits |= 0x00000200;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_ext_suspended()) {
|
if (is_ext_suspended()) {
|
||||||
// thread is suspended
|
// thread is suspended
|
||||||
*bits |= 0x00000400;
|
|
||||||
return true;
|
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
|
// Return true since we wouldn't be here unless there was still an
|
||||||
// external suspend request.
|
// external suspend request.
|
||||||
*bits |= 0x00001000;
|
|
||||||
return true;
|
return true;
|
||||||
} else if (save_state == _thread_in_native && frame_anchor()->walkable()) {
|
} else if (save_state == _thread_in_native && frame_anchor()->walkable()) {
|
||||||
// Threads running native code will self-suspend on native==>VM/Java
|
// Threads running native code will self-suspend on native==>VM/Java
|
||||||
// transitions. If its stack is walkable (should always be the case
|
// transitions. If its stack is walkable (should always be the case
|
||||||
// unless this function is called before the actual java_suspend()
|
// unless this function is called before the actual java_suspend()
|
||||||
// call), then the wait is done.
|
// call), then the wait is done.
|
||||||
*bits |= 0x00002000;
|
|
||||||
return true;
|
return true;
|
||||||
} else if (!called_by_wait && !did_trans_retry &&
|
} else if (!did_trans_retry &&
|
||||||
save_state == _thread_in_native_trans &&
|
save_state == _thread_in_native_trans &&
|
||||||
frame_anchor()->walkable()) {
|
frame_anchor()->walkable()) {
|
||||||
// The thread is transitioning from thread_in_native to another
|
// The thread is transitioning from thread_in_native to another
|
||||||
// thread state. check_safepoint_and_suspend_for_native_trans()
|
// thread state. check_safepoint_and_suspend_for_native_trans()
|
||||||
// will force the thread to self-suspend. If it hasn't gotten
|
// will force the thread to self-suspend. If it hasn't gotten
|
||||||
// there yet we may have caught the thread in-between the native
|
// there yet we may have caught the thread in-between the native
|
||||||
// code check above and the self-suspend. Lucky us. If we were
|
// code check above and the self-suspend.
|
||||||
// called by wait_for_ext_suspend_completion(), then it
|
|
||||||
// will be doing the retries so we don't have to.
|
|
||||||
//
|
//
|
||||||
// Since we use the saved thread state in the if-statement above,
|
// Since we use the saved thread state in the if-statement above,
|
||||||
// there is a chance that the thread has already transitioned to
|
// 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
|
// make a single unnecessary pass through the logic below. This
|
||||||
// doesn't hurt anything since we still do the trans retry.
|
// doesn't hurt anything since we still do the trans retry.
|
||||||
|
|
||||||
*bits |= 0x00004000;
|
|
||||||
|
|
||||||
// Once the thread leaves thread_in_native_trans for another
|
// Once the thread leaves thread_in_native_trans for another
|
||||||
// thread state, we break out of this retry loop. We shouldn't
|
// thread state, we break out of this retry loop. We shouldn't
|
||||||
// need this flag to prevent us from getting back here, but
|
// 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)
|
// (if we're a JavaThread - the WatcherThread can also call this)
|
||||||
// and increase delay with each retry
|
// and increase delay with each retry
|
||||||
if (Thread::current()->is_Java_thread()) {
|
if (Thread::current()->is_Java_thread()) {
|
||||||
SR_lock()->wait(i * delay);
|
SR_lock()->wait(i * SuspendRetryDelay);
|
||||||
} else {
|
} 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
|
// 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;
|
break;
|
||||||
}
|
}
|
||||||
} // end retry loop
|
} // end retry loop
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
} while (do_trans_retry);
|
} while (do_trans_retry);
|
||||||
|
|
||||||
*bits |= 0x00000010;
|
|
||||||
return false;
|
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
|
// GC Support
|
||||||
bool Thread::claim_par_threads_do(uintx claim_token) {
|
bool Thread::claim_par_threads_do(uintx claim_token) {
|
||||||
uintx token = _threads_do_token;
|
uintx token = _threads_do_token;
|
||||||
|
@ -2489,12 +2275,11 @@ void JavaThread::java_suspend() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// suspend is done
|
// suspend is done
|
||||||
uint32_t debug_bits = 0;
|
|
||||||
// Warning: is_ext_suspend_completed() may temporarily drop the
|
// Warning: is_ext_suspend_completed() may temporarily drop the
|
||||||
// SR_lock to allow the thread to reach a stable thread state if
|
// SR_lock to allow the thread to reach a stable thread state if
|
||||||
// it is currently in a transient thread state.
|
// it is currently in a transient thread state.
|
||||||
if (is_ext_suspend_completed(false /* !called_by_wait */,
|
if (is_ext_suspend_completed()) {
|
||||||
SuspendRetryDelay, &debug_bits)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2548,9 +2333,7 @@ int JavaThread::java_suspend_self() {
|
||||||
if (this->is_suspend_equivalent()) {
|
if (this->is_suspend_equivalent()) {
|
||||||
// If we are self-suspending as a result of the lifting of a
|
// If we are self-suspending as a result of the lifting of a
|
||||||
// suspend equivalent condition, then the suspend_equivalent
|
// suspend equivalent condition, then the suspend_equivalent
|
||||||
// flag is not cleared until we set the ext_suspended flag so
|
// flag is not cleared until we set the ext_suspended flag.
|
||||||
// that wait_for_ext_suspend_completion() returns consistent
|
|
||||||
// results.
|
|
||||||
this->clear_suspend_equivalent();
|
this->clear_suspend_equivalent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1383,26 +1383,7 @@ class JavaThread: public Thread {
|
||||||
// Check for async exception in addition to safepoint and suspend request.
|
// Check for async exception in addition to safepoint and suspend request.
|
||||||
static void check_special_condition_for_native_trans(JavaThread *thread);
|
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();
|
||||||
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);
|
|
||||||
|
|
||||||
inline void set_external_suspend();
|
inline void set_external_suspend();
|
||||||
inline void clear_external_suspend();
|
inline void clear_external_suspend();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue