8270085: Suspend during block transition may deadlock if lock held
Co-authored-by: Robbin Ehn <rehn@openjdk.org> Co-authored-by: Patricio Chilano Mateo <pchilanomate@openjdk.org> Reviewed-by: dcubed, dholmes, coleenp
This commit is contained in:
parent
39b486db6d
commit
e7f9009315
@ -2076,7 +2076,32 @@ WB_ENTRY(void, WB_AsyncHandshakeWalkStack(JNIEnv* env, jobject wb, jobject threa
|
|||||||
}
|
}
|
||||||
WB_END
|
WB_END
|
||||||
|
|
||||||
//Some convenience methods to deal with objects from java
|
static volatile int _emulated_lock = 0;
|
||||||
|
|
||||||
|
WB_ENTRY(void, WB_LockAndBlock(JNIEnv* env, jobject wb, jboolean suspender))
|
||||||
|
JavaThread* self = JavaThread::current();
|
||||||
|
|
||||||
|
{
|
||||||
|
// Before trying to acquire the lock transition into a safepoint safe state.
|
||||||
|
// Otherwise if either suspender or suspendee blocks for a safepoint
|
||||||
|
// in ~ThreadBlockInVM the other one could loop forever trying to acquire
|
||||||
|
// the lock without allowing the safepoint to progress.
|
||||||
|
ThreadBlockInVM tbivm(self);
|
||||||
|
|
||||||
|
// We will deadlock here if we are 'suspender' and 'suspendee'
|
||||||
|
// suspended in ~ThreadBlockInVM. This verifies we only suspend
|
||||||
|
// at the right place.
|
||||||
|
while (Atomic::cmpxchg(&_emulated_lock, 0, 1) != 0) {}
|
||||||
|
assert(_emulated_lock == 1, "Must be locked");
|
||||||
|
|
||||||
|
// Sleep much longer in suspendee to force situation where
|
||||||
|
// 'suspender' is waiting above to acquire lock.
|
||||||
|
os::naked_short_sleep(suspender ? 1 : 10);
|
||||||
|
}
|
||||||
|
Atomic::store(&_emulated_lock, 0);
|
||||||
|
WB_END
|
||||||
|
|
||||||
|
// Some convenience methods to deal with objects from java
|
||||||
int WhiteBox::offset_for_field(const char* field_name, oop object,
|
int WhiteBox::offset_for_field(const char* field_name, oop object,
|
||||||
Symbol* signature_symbol) {
|
Symbol* signature_symbol) {
|
||||||
assert(field_name != NULL && strlen(field_name) > 0, "Field name not valid");
|
assert(field_name != NULL && strlen(field_name) > 0, "Field name not valid");
|
||||||
@ -2566,6 +2591,7 @@ static JNINativeMethod methods[] = {
|
|||||||
{CC"clearInlineCaches0", CC"(Z)V", (void*)&WB_ClearInlineCaches },
|
{CC"clearInlineCaches0", CC"(Z)V", (void*)&WB_ClearInlineCaches },
|
||||||
{CC"handshakeWalkStack", CC"(Ljava/lang/Thread;Z)I", (void*)&WB_HandshakeWalkStack },
|
{CC"handshakeWalkStack", CC"(Ljava/lang/Thread;Z)I", (void*)&WB_HandshakeWalkStack },
|
||||||
{CC"asyncHandshakeWalkStack", CC"(Ljava/lang/Thread;)V", (void*)&WB_AsyncHandshakeWalkStack },
|
{CC"asyncHandshakeWalkStack", CC"(Ljava/lang/Thread;)V", (void*)&WB_AsyncHandshakeWalkStack },
|
||||||
|
{CC"lockAndBlock", CC"(Z)V", (void*)&WB_LockAndBlock},
|
||||||
{CC"checkThreadObjOfTerminatingThread", CC"(Ljava/lang/Thread;)V", (void*)&WB_CheckThreadObjOfTerminatingThread },
|
{CC"checkThreadObjOfTerminatingThread", CC"(Ljava/lang/Thread;)V", (void*)&WB_CheckThreadObjOfTerminatingThread },
|
||||||
{CC"verifyFrames", CC"(ZZ)V", (void*)&WB_VerifyFrames },
|
{CC"verifyFrames", CC"(ZZ)V", (void*)&WB_VerifyFrames },
|
||||||
{CC"addCompilerDirective", CC"(Ljava/lang/String;)I",
|
{CC"addCompilerDirective", CC"(Ljava/lang/String;)I",
|
||||||
|
@ -77,6 +77,7 @@ class HandshakeOperation : public CHeapObj<mtThread> {
|
|||||||
int32_t pending_threads() { return Atomic::load(&_pending_threads); }
|
int32_t pending_threads() { return Atomic::load(&_pending_threads); }
|
||||||
const char* name() { return _handshake_cl->name(); }
|
const char* name() { return _handshake_cl->name(); }
|
||||||
bool is_async() { return _handshake_cl->is_async(); }
|
bool is_async() { return _handshake_cl->is_async(); }
|
||||||
|
bool is_suspend() { return _handshake_cl->is_suspend(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsyncHandshakeOperation : public HandshakeOperation {
|
class AsyncHandshakeOperation : public HandshakeOperation {
|
||||||
@ -376,6 +377,7 @@ void Handshake::execute(HandshakeClosure* hs_cl, JavaThread* target) {
|
|||||||
// Check for pending handshakes to avoid possible deadlocks where our
|
// Check for pending handshakes to avoid possible deadlocks where our
|
||||||
// target is trying to handshake us.
|
// target is trying to handshake us.
|
||||||
if (SafepointMechanism::should_process(self)) {
|
if (SafepointMechanism::should_process(self)) {
|
||||||
|
// Will not suspend here.
|
||||||
ThreadBlockInVM tbivm(self);
|
ThreadBlockInVM tbivm(self);
|
||||||
}
|
}
|
||||||
hsy.process();
|
hsy.process();
|
||||||
@ -425,11 +427,19 @@ bool HandshakeState::operation_pending(HandshakeOperation* op) {
|
|||||||
return _queue.contains(mo);
|
return _queue.contains(mo);
|
||||||
}
|
}
|
||||||
|
|
||||||
HandshakeOperation* HandshakeState::get_op_for_self() {
|
static bool no_suspend_filter(HandshakeOperation* op) {
|
||||||
|
return !op->is_suspend();
|
||||||
|
}
|
||||||
|
|
||||||
|
HandshakeOperation* HandshakeState::get_op_for_self(bool allow_suspend) {
|
||||||
assert(_handshakee == Thread::current(), "Must be called by self");
|
assert(_handshakee == Thread::current(), "Must be called by self");
|
||||||
assert(_lock.owned_by_self(), "Lock must be held");
|
assert(_lock.owned_by_self(), "Lock must be held");
|
||||||
|
if (allow_suspend) {
|
||||||
return _queue.peek();
|
return _queue.peek();
|
||||||
};
|
} else {
|
||||||
|
return _queue.peek(no_suspend_filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool non_self_queue_filter(HandshakeOperation* op) {
|
static bool non_self_queue_filter(HandshakeOperation* op) {
|
||||||
return !op->is_async();
|
return !op->is_async();
|
||||||
@ -441,6 +451,11 @@ bool HandshakeState::have_non_self_executable_operation() {
|
|||||||
return _queue.contains(non_self_queue_filter);
|
return _queue.contains(non_self_queue_filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HandshakeState::has_a_non_suspend_operation() {
|
||||||
|
MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag);
|
||||||
|
return _queue.contains(no_suspend_filter);
|
||||||
|
}
|
||||||
|
|
||||||
HandshakeOperation* HandshakeState::get_op() {
|
HandshakeOperation* HandshakeState::get_op() {
|
||||||
assert(_handshakee != Thread::current(), "Must not be called by self");
|
assert(_handshakee != Thread::current(), "Must not be called by self");
|
||||||
assert(_lock.owned_by_self(), "Lock must be held");
|
assert(_lock.owned_by_self(), "Lock must be held");
|
||||||
@ -454,25 +469,22 @@ void HandshakeState::remove_op(HandshakeOperation* op) {
|
|||||||
assert(ret == op, "Popped op must match requested op");
|
assert(ret == op, "Popped op must match requested op");
|
||||||
};
|
};
|
||||||
|
|
||||||
bool HandshakeState::process_by_self() {
|
bool HandshakeState::process_by_self(bool allow_suspend) {
|
||||||
assert(Thread::current() == _handshakee, "should call from _handshakee");
|
assert(Thread::current() == _handshakee, "should call from _handshakee");
|
||||||
assert(!_handshakee->is_terminated(), "should not be a terminated thread");
|
assert(!_handshakee->is_terminated(), "should not be a terminated thread");
|
||||||
assert(_handshakee->thread_state() != _thread_blocked, "should not be in a blocked state");
|
assert(_handshakee->thread_state() != _thread_blocked, "should not be in a blocked state");
|
||||||
assert(_handshakee->thread_state() != _thread_in_native, "should not be in native");
|
assert(_handshakee->thread_state() != _thread_in_native, "should not be in native");
|
||||||
|
|
||||||
ThreadInVMForHandshake tivm(_handshakee);
|
ThreadInVMForHandshake tivm(_handshakee);
|
||||||
{
|
|
||||||
// Handshakes cannot safely safepoint.
|
// Handshakes cannot safely safepoint.
|
||||||
// The exception to this rule is the asynchronous suspension handshake.
|
// The exception to this rule is the asynchronous suspension handshake.
|
||||||
// It by-passes the NSV by manually doing the transition.
|
// It by-passes the NSV by manually doing the transition.
|
||||||
NoSafepointVerifier nsv;
|
NoSafepointVerifier nsv;
|
||||||
return process_self_inner();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HandshakeState::process_self_inner() {
|
|
||||||
while (has_operation()) {
|
while (has_operation()) {
|
||||||
MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag);
|
MutexLocker ml(&_lock, Mutex::_no_safepoint_check_flag);
|
||||||
HandshakeOperation* op = get_op_for_self();
|
|
||||||
|
HandshakeOperation* op = get_op_for_self(allow_suspend);
|
||||||
if (op != NULL) {
|
if (op != NULL) {
|
||||||
assert(op->_target == NULL || op->_target == Thread::current(), "Wrong thread");
|
assert(op->_target == NULL || op->_target == Thread::current(), "Wrong thread");
|
||||||
bool async = op->is_async();
|
bool async = op->is_async();
|
||||||
@ -621,6 +633,7 @@ class ThreadSelfSuspensionHandshake : public AsyncHandshakeClosure {
|
|||||||
assert(current == Thread::current(), "Must be self executed.");
|
assert(current == Thread::current(), "Must be self executed.");
|
||||||
current->handshake_state()->do_self_suspend();
|
current->handshake_state()->do_self_suspend();
|
||||||
}
|
}
|
||||||
|
virtual bool is_suspend() { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
bool HandshakeState::suspend_with_handshake() {
|
bool HandshakeState::suspend_with_handshake() {
|
||||||
@ -667,8 +680,15 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
bool HandshakeState::suspend() {
|
bool HandshakeState::suspend() {
|
||||||
|
JavaThread* self = JavaThread::current();
|
||||||
SuspendThreadHandshake st;
|
SuspendThreadHandshake st;
|
||||||
Handshake::execute(&st, _handshakee);
|
Handshake::execute(&st, _handshakee);
|
||||||
|
if (_handshakee == self) {
|
||||||
|
// If target is the current thread we need to call this to do the
|
||||||
|
// actual suspend since Handshake::execute() above only installed
|
||||||
|
// the asynchronous handshake.
|
||||||
|
SafepointMechanism::process_if_requested(self);
|
||||||
|
}
|
||||||
return st.did_suspend();
|
return st.did_suspend();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,6 +49,7 @@ class HandshakeClosure : public ThreadClosure, public CHeapObj<mtThread> {
|
|||||||
virtual ~HandshakeClosure() {}
|
virtual ~HandshakeClosure() {}
|
||||||
const char* name() const { return _name; }
|
const char* name() const { return _name; }
|
||||||
virtual bool is_async() { return false; }
|
virtual bool is_async() { return false; }
|
||||||
|
virtual bool is_suspend() { return false; }
|
||||||
virtual void do_thread(Thread* thread) = 0;
|
virtual void do_thread(Thread* thread) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -92,15 +93,8 @@ class HandshakeState {
|
|||||||
bool possibly_can_process_handshake();
|
bool possibly_can_process_handshake();
|
||||||
bool can_process_handshake();
|
bool can_process_handshake();
|
||||||
|
|
||||||
// Returns false if the JavaThread finished all its handshake operations.
|
|
||||||
// If the method returns true there is still potential work to be done,
|
|
||||||
// but we need to check for a safepoint before.
|
|
||||||
// (This is due to a suspension handshake which put the JavaThread in blocked
|
|
||||||
// state so a safepoint may be in-progress.)
|
|
||||||
bool process_self_inner();
|
|
||||||
|
|
||||||
bool have_non_self_executable_operation();
|
bool have_non_self_executable_operation();
|
||||||
HandshakeOperation* get_op_for_self();
|
HandshakeOperation* get_op_for_self(bool allow_suspend);
|
||||||
HandshakeOperation* get_op();
|
HandshakeOperation* get_op();
|
||||||
void remove_op(HandshakeOperation* op);
|
void remove_op(HandshakeOperation* op);
|
||||||
|
|
||||||
@ -123,10 +117,14 @@ class HandshakeState {
|
|||||||
bool has_operation() {
|
bool has_operation() {
|
||||||
return !_queue.is_empty();
|
return !_queue.is_empty();
|
||||||
}
|
}
|
||||||
|
bool has_a_non_suspend_operation();
|
||||||
|
|
||||||
bool operation_pending(HandshakeOperation* op);
|
bool operation_pending(HandshakeOperation* op);
|
||||||
|
|
||||||
bool process_by_self();
|
// If the method returns true we need to check for a possible safepoint.
|
||||||
|
// This is due to a suspension handshake which put the JavaThread in blocked
|
||||||
|
// state so a safepoint may be in-progress.
|
||||||
|
bool process_by_self(bool allow_suspend);
|
||||||
|
|
||||||
enum ProcessResult {
|
enum ProcessResult {
|
||||||
_no_operation = 0,
|
_no_operation = 0,
|
||||||
|
@ -243,8 +243,10 @@ template <typename PRE_PROC>
|
|||||||
class ThreadBlockInVMPreprocess : public ThreadStateTransition {
|
class ThreadBlockInVMPreprocess : public ThreadStateTransition {
|
||||||
private:
|
private:
|
||||||
PRE_PROC& _pr;
|
PRE_PROC& _pr;
|
||||||
|
bool _allow_suspend;
|
||||||
public:
|
public:
|
||||||
ThreadBlockInVMPreprocess(JavaThread* thread, PRE_PROC& pr) : ThreadStateTransition(thread), _pr(pr) {
|
ThreadBlockInVMPreprocess(JavaThread* thread, PRE_PROC& pr, bool allow_suspend = true)
|
||||||
|
: ThreadStateTransition(thread), _pr(pr), _allow_suspend(allow_suspend) {
|
||||||
assert(thread->thread_state() == _thread_in_vm, "coming from wrong thread state");
|
assert(thread->thread_state() == _thread_in_vm, "coming from wrong thread state");
|
||||||
thread->check_possible_safepoint();
|
thread->check_possible_safepoint();
|
||||||
// Once we are blocked vm expects stack to be walkable
|
// Once we are blocked vm expects stack to be walkable
|
||||||
@ -257,9 +259,9 @@ class ThreadBlockInVMPreprocess : public ThreadStateTransition {
|
|||||||
// Change to transition state and ensure it is seen by the VM thread.
|
// Change to transition state and ensure it is seen by the VM thread.
|
||||||
_thread->set_thread_state_fence(_thread_blocked_trans);
|
_thread->set_thread_state_fence(_thread_blocked_trans);
|
||||||
|
|
||||||
if (SafepointMechanism::should_process(_thread)) {
|
if (SafepointMechanism::should_process(_thread, _allow_suspend)) {
|
||||||
_pr(_thread);
|
_pr(_thread);
|
||||||
SafepointMechanism::process_if_requested(_thread);
|
SafepointMechanism::process_if_requested(_thread, _allow_suspend);
|
||||||
}
|
}
|
||||||
|
|
||||||
_thread->set_thread_state(_thread_in_vm);
|
_thread->set_thread_state(_thread_in_vm);
|
||||||
@ -289,7 +291,7 @@ class ThreadBlockInVM {
|
|||||||
ThreadBlockInVMPreprocess<InFlightMutexRelease> _tbivmpp;
|
ThreadBlockInVMPreprocess<InFlightMutexRelease> _tbivmpp;
|
||||||
public:
|
public:
|
||||||
ThreadBlockInVM(JavaThread* thread, Mutex** in_flight_mutex_addr = NULL)
|
ThreadBlockInVM(JavaThread* thread, Mutex** in_flight_mutex_addr = NULL)
|
||||||
: _ifmr(in_flight_mutex_addr), _tbivmpp(thread, _ifmr) {}
|
: _ifmr(in_flight_mutex_addr), _tbivmpp(thread, _ifmr, /* allow_suspend= */ false) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Debug class instantiated in JRT_ENTRY macro.
|
// Debug class instantiated in JRT_ENTRY macro.
|
||||||
|
@ -76,29 +76,6 @@ void SafepointMechanism::default_initialize() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SafepointMechanism::process(JavaThread *thread) {
|
|
||||||
bool need_rechecking;
|
|
||||||
do {
|
|
||||||
if (global_poll()) {
|
|
||||||
// Any load in ::block() must not pass the global poll load.
|
|
||||||
// Otherwise we might load an old safepoint counter (for example).
|
|
||||||
OrderAccess::loadload();
|
|
||||||
SafepointSynchronize::block(thread);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The call to on_safepoint fixes the thread's oops and the first few frames.
|
|
||||||
//
|
|
||||||
// The call has been carefully placed here to cater to a few situations:
|
|
||||||
// 1) After we exit from block after a global poll
|
|
||||||
// 2) After a thread races with the disarming of the global poll and transitions from native/blocked
|
|
||||||
// 3) Before the handshake code is run
|
|
||||||
StackWatermarkSet::on_safepoint(thread);
|
|
||||||
|
|
||||||
need_rechecking = thread->handshake_state()->has_operation() && thread->handshake_state()->process_by_self();
|
|
||||||
|
|
||||||
} while (need_rechecking);
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t SafepointMechanism::compute_poll_word(bool armed, uintptr_t stack_watermark) {
|
uintptr_t SafepointMechanism::compute_poll_word(bool armed, uintptr_t stack_watermark) {
|
||||||
if (armed) {
|
if (armed) {
|
||||||
log_debug(stackbarrier)("Computed armed for tid %d", Thread::current()->osthread()->thread_id());
|
log_debug(stackbarrier)("Computed armed for tid %d", Thread::current()->osthread()->thread_id());
|
||||||
@ -134,12 +111,31 @@ void SafepointMechanism::update_poll_values(JavaThread* thread) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SafepointMechanism::process_if_requested_slow(JavaThread *thread) {
|
void SafepointMechanism::process(JavaThread *thread, bool allow_suspend) {
|
||||||
// Read global poll and has_handshake after local poll
|
// Read global poll and has_handshake after local poll
|
||||||
OrderAccess::loadload();
|
OrderAccess::loadload();
|
||||||
|
|
||||||
// local poll already checked, if used.
|
// local poll already checked, if used.
|
||||||
process(thread);
|
bool need_rechecking;
|
||||||
|
do {
|
||||||
|
if (global_poll()) {
|
||||||
|
// Any load in ::block() must not pass the global poll load.
|
||||||
|
// Otherwise we might load an old safepoint counter (for example).
|
||||||
|
OrderAccess::loadload();
|
||||||
|
SafepointSynchronize::block(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The call to on_safepoint fixes the thread's oops and the first few frames.
|
||||||
|
//
|
||||||
|
// The call has been carefully placed here to cater to a few situations:
|
||||||
|
// 1) After we exit from block after a global poll
|
||||||
|
// 2) After a thread races with the disarming of the global poll and transitions from native/blocked
|
||||||
|
// 3) Before the handshake code is run
|
||||||
|
StackWatermarkSet::on_safepoint(thread);
|
||||||
|
|
||||||
|
need_rechecking = thread->handshake_state()->has_operation() && thread->handshake_state()->process_by_self(allow_suspend);
|
||||||
|
} while (need_rechecking);
|
||||||
|
|
||||||
update_poll_values(thread);
|
update_poll_values(thread);
|
||||||
OrderAccess::cross_modify_fence();
|
OrderAccess::cross_modify_fence();
|
||||||
}
|
}
|
||||||
|
@ -50,8 +50,9 @@ class SafepointMechanism : public AllStatic {
|
|||||||
|
|
||||||
static inline bool global_poll();
|
static inline bool global_poll();
|
||||||
|
|
||||||
static void process(JavaThread *thread);
|
static void process(JavaThread *thread, bool allow_suspend);
|
||||||
static void process_if_requested_slow(JavaThread *thread);
|
|
||||||
|
static inline bool should_process_no_suspend(JavaThread* thread);
|
||||||
|
|
||||||
static void default_initialize();
|
static void default_initialize();
|
||||||
|
|
||||||
@ -60,7 +61,7 @@ class SafepointMechanism : public AllStatic {
|
|||||||
static uintptr_t compute_poll_word(bool armed, uintptr_t stack_watermark);
|
static uintptr_t compute_poll_word(bool armed, uintptr_t stack_watermark);
|
||||||
|
|
||||||
const static intptr_t _poll_bit = 1;
|
const static intptr_t _poll_bit = 1;
|
||||||
public:
|
public:
|
||||||
static inline bool local_poll_armed(JavaThread* thread);
|
static inline bool local_poll_armed(JavaThread* thread);
|
||||||
static intptr_t poll_bit() { return _poll_bit; }
|
static intptr_t poll_bit() { return _poll_bit; }
|
||||||
|
|
||||||
@ -79,10 +80,10 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Call this method to see if this thread should block for a safepoint or process handshake.
|
// Call this method to see if this thread should block for a safepoint or process handshake.
|
||||||
static inline bool should_process(JavaThread* thread);
|
static inline bool should_process(JavaThread* thread, bool allow_suspend = true);
|
||||||
|
|
||||||
// Processes a pending requested operation.
|
// Processes a pending requested operation.
|
||||||
static inline void process_if_requested(JavaThread* thread);
|
static inline void process_if_requested(JavaThread* thread, bool allow_suspend = true);
|
||||||
static inline void process_if_requested_with_exit_check(JavaThread* thread, bool check_asyncs);
|
static inline void process_if_requested_with_exit_check(JavaThread* thread, bool check_asyncs);
|
||||||
// Compute what the poll values should be and install them.
|
// Compute what the poll values should be and install them.
|
||||||
static void update_poll_values(JavaThread* thread);
|
static void update_poll_values(JavaThread* thread);
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include "runtime/safepointMechanism.hpp"
|
#include "runtime/safepointMechanism.hpp"
|
||||||
|
|
||||||
#include "runtime/atomic.hpp"
|
#include "runtime/atomic.hpp"
|
||||||
|
#include "runtime/handshake.hpp"
|
||||||
#include "runtime/safepoint.hpp"
|
#include "runtime/safepoint.hpp"
|
||||||
#include "runtime/thread.inline.hpp"
|
#include "runtime/thread.inline.hpp"
|
||||||
|
|
||||||
@ -61,11 +62,29 @@ bool SafepointMechanism::global_poll() {
|
|||||||
return (SafepointSynchronize::_state != SafepointSynchronize::_not_synchronized);
|
return (SafepointSynchronize::_state != SafepointSynchronize::_not_synchronized);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SafepointMechanism::should_process(JavaThread* thread) {
|
bool SafepointMechanism::should_process_no_suspend(JavaThread* thread) {
|
||||||
return local_poll_armed(thread);
|
if (global_poll() || thread->handshake_state()->has_a_non_suspend_operation()) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// We ignore suspend requests if any and just check before returning if we need
|
||||||
|
// to fix the thread's oops and first few frames due to a possible safepoint.
|
||||||
|
StackWatermarkSet::on_safepoint(thread);
|
||||||
|
update_poll_values(thread);
|
||||||
|
OrderAccess::cross_modify_fence();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SafepointMechanism::process_if_requested(JavaThread* thread) {
|
bool SafepointMechanism::should_process(JavaThread* thread, bool allow_suspend) {
|
||||||
|
if (!local_poll_armed(thread)) {
|
||||||
|
return false;
|
||||||
|
} else if (allow_suspend) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return should_process_no_suspend(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SafepointMechanism::process_if_requested(JavaThread* thread, bool allow_suspend) {
|
||||||
|
|
||||||
// Macos/aarch64 should be in the right state for safepoint (e.g.
|
// Macos/aarch64 should be in the right state for safepoint (e.g.
|
||||||
// deoptimization needs WXWrite). Crashes caused by the wrong state rarely
|
// deoptimization needs WXWrite). Crashes caused by the wrong state rarely
|
||||||
@ -77,7 +96,7 @@ void SafepointMechanism::process_if_requested(JavaThread* thread) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (local_poll_armed(thread)) {
|
if (local_poll_armed(thread)) {
|
||||||
process_if_requested_slow(thread);
|
process(thread, allow_suspend);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
58
test/hotspot/jtreg/runtime/handshake/SuspendBlocked.java
Normal file
58
test/hotspot/jtreg/runtime/handshake/SuspendBlocked.java
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test SuspendBlocked
|
||||||
|
* @bug 8270085
|
||||||
|
* @library /test/lib
|
||||||
|
* @build SuspendBlocked
|
||||||
|
* @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox
|
||||||
|
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI SuspendBlocked
|
||||||
|
*/
|
||||||
|
|
||||||
|
import jdk.test.lib.Asserts;
|
||||||
|
import sun.hotspot.WhiteBox;
|
||||||
|
|
||||||
|
public class SuspendBlocked {
|
||||||
|
|
||||||
|
public static void main(String... args) throws Exception {
|
||||||
|
Thread suspend_thread = new Thread(() -> run_loop());
|
||||||
|
suspend_thread.start();
|
||||||
|
WhiteBox wb = WhiteBox.getWhiteBox();
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
suspend_thread.suspend();
|
||||||
|
wb.lockAndBlock(/* suspender= */ true);
|
||||||
|
suspend_thread.resume();
|
||||||
|
Thread.sleep(1);
|
||||||
|
}
|
||||||
|
suspend_thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void run_loop() {
|
||||||
|
WhiteBox wb = WhiteBox.getWhiteBox();
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
wb.lockAndBlock(/* suspender= */ false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -607,6 +607,8 @@ public class WhiteBox {
|
|||||||
public native int handshakeWalkStack(Thread t, boolean all_threads);
|
public native int handshakeWalkStack(Thread t, boolean all_threads);
|
||||||
public native void asyncHandshakeWalkStack(Thread t);
|
public native void asyncHandshakeWalkStack(Thread t);
|
||||||
|
|
||||||
|
public native void lockAndBlock(boolean suspender);
|
||||||
|
|
||||||
// Returns true on linux if library has the noexecstack flag set.
|
// Returns true on linux if library has the noexecstack flag set.
|
||||||
public native boolean checkLibSpecifiesNoexecstack(String libfilename);
|
public native boolean checkLibSpecifiesNoexecstack(String libfilename);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user