mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 23:34:52 +02:00
8265753: Remove manual JavaThread transitions to blocked
Reviewed-by: dcubed, rrich, dholmes, pchilanomate
This commit is contained in:
parent
6eb9114582
commit
97ec5ad0a6
8 changed files with 205 additions and 236 deletions
|
@ -3191,31 +3191,7 @@ JvmtiEnv::RawMonitorEnter(JvmtiRawMonitor * rmonitor) {
|
|||
// in thread.cpp.
|
||||
JvmtiPendingMonitors::enter(rmonitor);
|
||||
} else {
|
||||
Thread* thread = Thread::current();
|
||||
if (thread->is_Java_thread()) {
|
||||
JavaThread* current_thread = thread->as_Java_thread();
|
||||
|
||||
/* Transition to thread_blocked without entering vm state */
|
||||
/* This is really evil. Normally you can't undo _thread_blocked */
|
||||
/* transitions like this because it would cause us to miss a */
|
||||
/* safepoint but since the thread was already in _thread_in_native */
|
||||
/* the thread is not leaving a safepoint safe state and it will */
|
||||
/* block when it tries to return from native. We can't safepoint */
|
||||
/* block in here because we could deadlock the vmthread. Blech. */
|
||||
|
||||
JavaThreadState state = current_thread->thread_state();
|
||||
assert(state == _thread_in_native, "Must be _thread_in_native");
|
||||
// frame should already be walkable since we are in native
|
||||
assert(!current_thread->has_last_Java_frame() ||
|
||||
current_thread->frame_anchor()->walkable(), "Must be walkable");
|
||||
current_thread->set_thread_state(_thread_blocked);
|
||||
|
||||
rmonitor->raw_enter(current_thread);
|
||||
// restore state, still at a safepoint safe state
|
||||
current_thread->set_thread_state(state);
|
||||
} else {
|
||||
rmonitor->raw_enter(thread);
|
||||
}
|
||||
rmonitor->raw_enter(Thread::current());
|
||||
}
|
||||
return JVMTI_ERROR_NONE;
|
||||
} /* end RawMonitorEnter */
|
||||
|
|
|
@ -43,11 +43,13 @@ void JvmtiPendingMonitors::transition_raw_monitors() {
|
|||
"Java thread has not been created yet or more than one java thread "
|
||||
"is running. Raw monitor transition will not work");
|
||||
JavaThread* current_java_thread = JavaThread::current();
|
||||
assert(current_java_thread->thread_state() == _thread_in_vm, "Must be in vm");
|
||||
{
|
||||
ThreadToNativeFromVM ttnfvm(current_java_thread);
|
||||
for (int i = 0; i < count(); i++) {
|
||||
JvmtiRawMonitor* rmonitor = monitors()->at(i);
|
||||
rmonitor->raw_enter(current_java_thread);
|
||||
}
|
||||
}
|
||||
// pending monitors are converted to real monitor so delete them all.
|
||||
dispose();
|
||||
}
|
||||
|
@ -60,7 +62,6 @@ JvmtiRawMonitor::JvmtiRawMonitor(const char* name) : _owner(NULL),
|
|||
_recursions(0),
|
||||
_entry_list(NULL),
|
||||
_wait_set(NULL),
|
||||
_waiters(0),
|
||||
_magic(JVMTI_RM_MAGIC),
|
||||
_name(NULL) {
|
||||
#ifdef ASSERT
|
||||
|
@ -217,11 +218,12 @@ inline void JvmtiRawMonitor::dequeue_waiter(QNode& node) {
|
|||
// simple_wait is not quite so simple as we have to deal with the interaction
|
||||
// with the Thread interrupt state, which resides in the java.lang.Thread object.
|
||||
// That state must only be accessed while _thread_in_vm and requires proper thread-state
|
||||
// transitions. However, we cannot perform such transitions whilst we hold the RawMonitor,
|
||||
// else we can deadlock with the VMThread (which may also use RawMonitors as part of
|
||||
// executing various callbacks).
|
||||
// transitions.
|
||||
// Returns M_OK usually, but M_INTERRUPTED if the thread is a JavaThread and was
|
||||
// interrupted.
|
||||
// Note:
|
||||
// - simple_wait never reenters the monitor.
|
||||
// - A JavaThread must be in native.
|
||||
int JvmtiRawMonitor::simple_wait(Thread* self, jlong millis) {
|
||||
guarantee(_owner == self , "invariant");
|
||||
guarantee(_recursions == 0, "invariant");
|
||||
|
@ -235,8 +237,10 @@ int JvmtiRawMonitor::simple_wait(Thread* self, jlong millis) {
|
|||
int ret = M_OK;
|
||||
if (self->is_Java_thread()) {
|
||||
JavaThread* jt = self->as_Java_thread();
|
||||
// Transition to VM so we can check interrupt state
|
||||
ThreadInVMfromNative tivm(jt);
|
||||
guarantee(jt->thread_state() == _thread_in_native, "invariant");
|
||||
{
|
||||
// This transition must be after we exited the monitor.
|
||||
ThreadInVMfromNative tivmfn(jt);
|
||||
if (jt->is_interrupted(true)) {
|
||||
ret = M_INTERRUPTED;
|
||||
} else {
|
||||
|
@ -251,6 +255,7 @@ int JvmtiRawMonitor::simple_wait(Thread* self, jlong millis) {
|
|||
if (jt->is_interrupted(true)) {
|
||||
ret = M_INTERRUPTED;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (millis <= 0) {
|
||||
self->_ParkEvent->park();
|
||||
|
@ -261,10 +266,6 @@ int JvmtiRawMonitor::simple_wait(Thread* self, jlong millis) {
|
|||
|
||||
dequeue_waiter(node);
|
||||
|
||||
simple_enter(self);
|
||||
guarantee(_owner == self, "invariant");
|
||||
guarantee(_recursions == 0, "invariant");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -306,75 +307,37 @@ void JvmtiRawMonitor::simple_notify(Thread* self, bool all) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Any JavaThread will enter here with state _thread_blocked unless we
|
||||
// are in single-threaded mode during startup.
|
||||
void JvmtiRawMonitor::ExitOnSuspend::operator()(JavaThread* current) {
|
||||
// We must exit the monitor in case of a safepoint.
|
||||
_rm->simple_exit(current);
|
||||
_rm_exited = true;
|
||||
}
|
||||
|
||||
// JavaThreads will enter here with state _thread_in_native.
|
||||
void JvmtiRawMonitor::raw_enter(Thread* self) {
|
||||
void* contended;
|
||||
JavaThread* jt = NULL;
|
||||
// don't enter raw monitor if thread is being externally suspended, it will
|
||||
// surprise the suspender if a "suspended" thread can still enter monitor
|
||||
if (self->is_Java_thread()) {
|
||||
jt = self->as_Java_thread();
|
||||
while (true) {
|
||||
// To pause suspend requests while in blocked we must block handshakes.
|
||||
jt->handshake_state()->lock();
|
||||
// Suspend request flag can only be set in handshakes.
|
||||
// By blocking handshakes, suspend request flag cannot change its value.
|
||||
if (!jt->handshake_state()->is_suspended()) {
|
||||
contended = Atomic::cmpxchg(&_owner, (Thread*)NULL, jt);
|
||||
jt->handshake_state()->unlock();
|
||||
break;
|
||||
}
|
||||
jt->handshake_state()->unlock();
|
||||
|
||||
// We may only be in states other than _thread_blocked when we are
|
||||
// in single-threaded mode during startup.
|
||||
guarantee(jt->thread_state() == _thread_blocked, "invariant");
|
||||
|
||||
jt->set_thread_state_fence(_thread_blocked_trans);
|
||||
SafepointMechanism::process_if_requested(jt);
|
||||
// We should transition to thread_in_vm and then to thread_in_vm_trans,
|
||||
// but those are always treated the same as _thread_blocked_trans.
|
||||
jt->set_thread_state(_thread_blocked);
|
||||
}
|
||||
} else {
|
||||
contended = Atomic::cmpxchg(&_owner, (Thread*)NULL, self);
|
||||
}
|
||||
|
||||
if (contended == self) {
|
||||
// TODO Atomic::load on _owner field
|
||||
if (_owner == self) {
|
||||
_recursions++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (contended == NULL) {
|
||||
guarantee(_owner == self, "invariant");
|
||||
guarantee(_recursions == 0, "invariant");
|
||||
return;
|
||||
}
|
||||
|
||||
self->set_current_pending_raw_monitor(this);
|
||||
|
||||
if (!self->is_Java_thread()) {
|
||||
simple_enter(self);
|
||||
} else {
|
||||
// In multi-threaded mode, we must enter this method blocked.
|
||||
guarantee(jt->thread_state() == _thread_blocked, "invariant");
|
||||
JavaThread* jt = self->as_Java_thread();
|
||||
guarantee(jt->thread_state() == _thread_in_native, "invariant");
|
||||
ThreadInVMfromNative tivmfn(jt);
|
||||
for (;;) {
|
||||
ExitOnSuspend eos(this);
|
||||
{
|
||||
ThreadBlockInVMPreprocess<ExitOnSuspend> tbivmp(jt, eos);
|
||||
simple_enter(jt);
|
||||
if (!SafepointMechanism::should_process(jt)) {
|
||||
// Not suspended so we're done here.
|
||||
}
|
||||
if (!eos.monitor_exited()) {
|
||||
break;
|
||||
}
|
||||
if (!jt->is_suspended()) {
|
||||
// Not suspended so we're done here.
|
||||
break;
|
||||
}
|
||||
simple_exit(jt);
|
||||
jt->set_thread_state_fence(_thread_blocked_trans);
|
||||
SafepointMechanism::process_if_requested(jt);
|
||||
// We should transition to thread_in_vm and then to thread_in_vm_trans,
|
||||
// but those are always treated the same as _thread_blocked_trans.
|
||||
jt->set_thread_state(_thread_blocked);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -411,37 +374,34 @@ int JvmtiRawMonitor::raw_wait(jlong millis, Thread* self) {
|
|||
|
||||
intptr_t save = _recursions;
|
||||
_recursions = 0;
|
||||
_waiters++;
|
||||
ret = simple_wait(self, millis);
|
||||
_recursions = save;
|
||||
_waiters--;
|
||||
|
||||
guarantee(self == _owner, "invariant");
|
||||
|
||||
if (self->is_Java_thread()) {
|
||||
// Now we need to re-enter the monitor. For JavaThreads
|
||||
// we need to manage suspend requests.
|
||||
if (self->is_Java_thread()) { // JavaThread re-enter
|
||||
JavaThread* jt = self->as_Java_thread();
|
||||
guarantee(jt->thread_state() == _thread_in_native, "invariant");
|
||||
ThreadInVMfromNative tivmfn(jt);
|
||||
for (;;) {
|
||||
if (!SafepointMechanism::should_process(jt)) {
|
||||
// Not suspended so we're done here:
|
||||
ExitOnSuspend eos(this);
|
||||
{
|
||||
ThreadBlockInVMPreprocess<ExitOnSuspend> tbivmp(jt, eos);
|
||||
simple_enter(jt);
|
||||
}
|
||||
if (!eos.monitor_exited()) {
|
||||
break;
|
||||
}
|
||||
simple_exit(jt);
|
||||
jt->set_thread_state_fence(_thread_in_native_trans);
|
||||
SafepointMechanism::process_if_requested(jt);
|
||||
}
|
||||
if (jt->is_interrupted(true)) {
|
||||
ret = M_INTERRUPTED;
|
||||
}
|
||||
// We should transition to thread_in_vm and then to thread_in_vm_trans,
|
||||
// but those are always treated the same as _thread_in_native_trans.
|
||||
jt->set_thread_state(_thread_in_native);
|
||||
simple_enter(jt);
|
||||
}
|
||||
guarantee(jt == _owner, "invariant");
|
||||
} else {
|
||||
} else { // Non-JavaThread re-enter
|
||||
assert(ret != M_INTERRUPTED, "Only JavaThreads can be interrupted");
|
||||
simple_enter(self);
|
||||
}
|
||||
|
||||
_recursions = save;
|
||||
|
||||
guarantee(self == _owner, "invariant");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,21 @@
|
|||
// A simplified version of the ObjectMonitor code.
|
||||
//
|
||||
|
||||
// Important note:
|
||||
// Raw monitors can be used in callbacks which happen during safepoint by the VM
|
||||
// thread (e.g., heapRootCallback). This means we may not transition/safepoint
|
||||
// poll in many cases, else the agent JavaThread can deadlock with the VM thread,
|
||||
// as this old comment says:
|
||||
// "We can't safepoint block in here because we could deadlock the vmthread. Blech."
|
||||
// The rules are:
|
||||
// - We must never safepoint poll if raw monitor is owned.
|
||||
// - We may safepoint poll before it is owned and after it has been released.
|
||||
// If this were the only thing we needed to think about we could just stay in
|
||||
// native for all operations. However we need to honor a suspend request, not
|
||||
// entering a monitor if suspended, and check for interrupts. Honoring a suspend
|
||||
// request and reading the interrupt flag must be done from VM state
|
||||
// (a safepoint unsafe state).
|
||||
|
||||
class JvmtiRawMonitor : public CHeapObj<mtSynchronizer> {
|
||||
|
||||
// Helper class to allow Threads to be linked into queues.
|
||||
|
@ -59,7 +74,6 @@ class JvmtiRawMonitor : public CHeapObj<mtSynchronizer> {
|
|||
// The list is actually composed of nodes,
|
||||
// acting as proxies for Threads.
|
||||
QNode* volatile _wait_set; // Threads wait()ing on the monitor
|
||||
volatile jint _waiters; // number of waiting threads
|
||||
int _magic;
|
||||
char* _name;
|
||||
// JVMTI_RM_MAGIC is set in contructor and unset in destructor.
|
||||
|
@ -75,6 +89,16 @@ class JvmtiRawMonitor : public CHeapObj<mtSynchronizer> {
|
|||
int simple_wait(Thread* self, jlong millis);
|
||||
void simple_notify(Thread* self, bool all);
|
||||
|
||||
class ExitOnSuspend {
|
||||
protected:
|
||||
JvmtiRawMonitor* _rm;
|
||||
bool _rm_exited;
|
||||
public:
|
||||
ExitOnSuspend(JvmtiRawMonitor* rm) : _rm(rm), _rm_exited(false) {}
|
||||
void operator()(JavaThread* current);
|
||||
bool monitor_exited() { return _rm_exited; }
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
// return codes
|
||||
|
|
|
@ -600,14 +600,6 @@ HandshakeState::ProcessResult HandshakeState::try_process(HandshakeOperation* ma
|
|||
return pr_ret;
|
||||
}
|
||||
|
||||
void HandshakeState::lock() {
|
||||
_lock.lock_without_safepoint_check();
|
||||
}
|
||||
|
||||
void HandshakeState::unlock() {
|
||||
_lock.unlock();
|
||||
}
|
||||
|
||||
void HandshakeState::do_self_suspend() {
|
||||
assert(Thread::current() == _handshakee, "should call from _handshakee");
|
||||
assert(_lock.owned_by_self(), "Lock must be held");
|
||||
|
|
|
@ -73,7 +73,6 @@ class JvmtiRawMonitor;
|
|||
// operation is only done by either VMThread/Handshaker on behalf of the
|
||||
// JavaThread or by the target JavaThread itself.
|
||||
class HandshakeState {
|
||||
friend JvmtiRawMonitor;
|
||||
friend ThreadSelfSuspensionHandshake;
|
||||
friend SuspendThreadHandshake;
|
||||
friend JavaThread;
|
||||
|
@ -103,9 +102,6 @@ class HandshakeState {
|
|||
HandshakeOperation* pop_for_self();
|
||||
HandshakeOperation* pop();
|
||||
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
public:
|
||||
HandshakeState(JavaThread* thread);
|
||||
|
||||
|
|
|
@ -202,7 +202,14 @@ class ThreadInVMfromNative : public ThreadStateTransition {
|
|||
trans_from_native(_thread_in_vm);
|
||||
}
|
||||
~ThreadInVMfromNative() {
|
||||
trans(_thread_in_vm, _thread_in_native);
|
||||
assert(_thread->thread_state() == _thread_in_vm, "coming from wrong thread state");
|
||||
// We cannot assert !_thread->owns_locks() since we have valid cases where
|
||||
// we call known native code using this wrapper holding locks.
|
||||
_thread->check_possible_safepoint();
|
||||
// Once we are in native vm expects stack to be walkable
|
||||
_thread->frame_anchor()->make_walkable(_thread);
|
||||
OrderAccess::storestore(); // Keep thread_state change and make_walkable() separate.
|
||||
_thread->set_thread_state(_thread_in_native);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -226,47 +233,61 @@ class ThreadToNativeFromVM : public ThreadStateTransition {
|
|||
}
|
||||
};
|
||||
|
||||
// Perform a transition to _thread_blocked and take a call-back to be executed before
|
||||
// SafepointMechanism::process_if_requested when returning to the VM. This allows us
|
||||
// to perform an "undo" action if we might block processing a safepoint/handshake operation
|
||||
// (such as thread suspension).
|
||||
template <typename PRE_PROC>
|
||||
class ThreadBlockInVMPreprocess : public ThreadStateTransition {
|
||||
private:
|
||||
PRE_PROC& _pr;
|
||||
public:
|
||||
ThreadBlockInVMPreprocess(JavaThread* thread, PRE_PROC& pr) : ThreadStateTransition(thread), _pr(pr) {
|
||||
assert(thread->thread_state() == _thread_in_vm, "coming from wrong thread state");
|
||||
thread->check_possible_safepoint();
|
||||
// Once we are blocked vm expects stack to be walkable
|
||||
thread->frame_anchor()->make_walkable(thread);
|
||||
OrderAccess::storestore(); // Keep thread_state change and make_walkable() separate.
|
||||
thread->set_thread_state(_thread_blocked);
|
||||
}
|
||||
~ThreadBlockInVMPreprocess() {
|
||||
assert(_thread->thread_state() == _thread_blocked, "coming from wrong thread state");
|
||||
// Change to transition state and ensure it is seen by the VM thread.
|
||||
_thread->set_thread_state_fence(_thread_blocked_trans);
|
||||
|
||||
if (SafepointMechanism::should_process(_thread)) {
|
||||
_pr(_thread);
|
||||
SafepointMechanism::process_if_requested(_thread);
|
||||
}
|
||||
|
||||
_thread->set_thread_state(_thread_in_vm);
|
||||
}
|
||||
};
|
||||
|
||||
class InFlightMutexRelease {
|
||||
private:
|
||||
Mutex** _in_flight_mutex_addr;
|
||||
public:
|
||||
InFlightMutexRelease(Mutex** in_flight_mutex_addr) : _in_flight_mutex_addr(in_flight_mutex_addr) {}
|
||||
void operator()(JavaThread* current) {
|
||||
if (_in_flight_mutex_addr != NULL && *_in_flight_mutex_addr != NULL) {
|
||||
(*_in_flight_mutex_addr)->release_for_safepoint();
|
||||
*_in_flight_mutex_addr = NULL;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Parameter in_flight_mutex_addr is only used by class Mutex to avoid certain deadlock
|
||||
// scenarios while making transitions that might block for a safepoint or handshake.
|
||||
// It's the address of a pointer to the mutex we are trying to acquire. This will be used to
|
||||
// access and release said mutex when transitioning back from blocked to vm (destructor) in
|
||||
// case we need to stop for a safepoint or handshake.
|
||||
class ThreadBlockInVM : public ThreadStateTransition {
|
||||
private:
|
||||
Mutex** _in_flight_mutex_addr;
|
||||
|
||||
class ThreadBlockInVM {
|
||||
InFlightMutexRelease _ifmr;
|
||||
ThreadBlockInVMPreprocess<InFlightMutexRelease> _tbivmpp;
|
||||
public:
|
||||
ThreadBlockInVM(JavaThread* thread, Mutex** in_flight_mutex_addr = NULL)
|
||||
: ThreadStateTransition(thread), _in_flight_mutex_addr(in_flight_mutex_addr) {
|
||||
assert(thread->thread_state() == _thread_in_vm, "coming from wrong thread state");
|
||||
thread->check_possible_safepoint();
|
||||
// Once we are blocked vm expects stack to be walkable
|
||||
thread->frame_anchor()->make_walkable(thread);
|
||||
thread->set_thread_state(_thread_blocked);
|
||||
}
|
||||
~ThreadBlockInVM() {
|
||||
assert(_thread->thread_state() == _thread_blocked, "coming from wrong thread state");
|
||||
// Change to transition state and ensure it is seen by the VM thread.
|
||||
_thread->set_thread_state_fence(_thread_blocked_trans);
|
||||
|
||||
if (SafepointMechanism::should_process(_thread)) {
|
||||
if (_in_flight_mutex_addr != NULL) {
|
||||
release_mutex();
|
||||
}
|
||||
SafepointMechanism::process_if_requested(_thread);
|
||||
}
|
||||
|
||||
_thread->set_thread_state(_thread_in_vm);
|
||||
}
|
||||
|
||||
void release_mutex() {
|
||||
Mutex* in_flight_mutex = *_in_flight_mutex_addr;
|
||||
if (in_flight_mutex != NULL) {
|
||||
in_flight_mutex->release_for_safepoint();
|
||||
*_in_flight_mutex_addr = NULL;
|
||||
}
|
||||
}
|
||||
: _ifmr(in_flight_mutex_addr), _tbivmpp(thread, _ifmr) {}
|
||||
};
|
||||
|
||||
// Debug class instantiated in JRT_ENTRY macro.
|
||||
|
|
|
@ -307,6 +307,27 @@ oop ObjectMonitor::object_peek() const {
|
|||
return _object.peek();
|
||||
}
|
||||
|
||||
void ObjectMonitor::ExitOnSuspend::operator()(JavaThread* current) {
|
||||
if (current->is_suspended()) {
|
||||
_om->_recursions = 0;
|
||||
_om->_succ = NULL;
|
||||
// Don't need a full fence after clearing successor here because of the call to exit().
|
||||
_om->exit(current, false /* not_suspended */);
|
||||
_om_exited = true;
|
||||
|
||||
current->set_current_pending_monitor(_om);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectMonitor::ClearSuccOnSuspend::operator()(JavaThread* current) {
|
||||
if (current->is_suspended()) {
|
||||
if (_om->_succ == current) {
|
||||
_om->_succ = NULL;
|
||||
OrderAccess::fence(); // always do a full fence when successor is cleared
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Enter support
|
||||
|
||||
|
@ -406,46 +427,29 @@ bool ObjectMonitor::enter(JavaThread* current) {
|
|||
|
||||
assert(current->thread_state() == _thread_in_vm, "invariant");
|
||||
|
||||
current->frame_anchor()->make_walkable(current);
|
||||
// Thread must be walkable before it is blocked.
|
||||
// Read in reverse order.
|
||||
OrderAccess::storestore();
|
||||
for (;;) {
|
||||
current->set_thread_state(_thread_blocked);
|
||||
ExitOnSuspend eos(this);
|
||||
{
|
||||
ThreadBlockInVMPreprocess<ExitOnSuspend> tbivs(current, eos);
|
||||
EnterI(current);
|
||||
current->set_thread_state_fence(_thread_blocked_trans);
|
||||
if (SafepointMechanism::should_process(current) &&
|
||||
current->is_suspended()) {
|
||||
// We have acquired the contended monitor, but while we were
|
||||
// waiting another thread suspended us. We don't want to enter
|
||||
// the monitor while suspended because that would surprise the
|
||||
// thread that suspended us.
|
||||
_recursions = 0;
|
||||
_succ = NULL;
|
||||
// Don't need a full fence after clearing successor here because of the call to exit().
|
||||
exit(current, false /* not_suspended */);
|
||||
SafepointMechanism::process_if_requested(current);
|
||||
// Since we are going to _thread_blocked we skip setting _thread_in_vm here.
|
||||
} else {
|
||||
// Only exit path from for loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
current->set_current_pending_monitor(NULL);
|
||||
|
||||
// We cleared the pending monitor info since we've just gotten past
|
||||
// the enter-check-for-suspend dance and we now own the monitor free
|
||||
// and clear, i.e., it is no longer pending.
|
||||
// We can go to a safepoint at the end of this block. If we
|
||||
// do a thread dump during that safepoint, then this thread will show
|
||||
// as having "-locked" the monitor, but the OS and java.lang.Thread
|
||||
// states will still report that the thread is blocked trying to
|
||||
// acquire it.
|
||||
// If there is a suspend request, ExitOnSuspend will exit the OM
|
||||
// and set the OM as pending.
|
||||
}
|
||||
if (!eos.exited()) {
|
||||
// ExitOnSuspend did not exit the OM
|
||||
assert(owner_raw() == current, "invariant");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Completed the tranisition.
|
||||
SafepointMechanism::process_if_requested(current);
|
||||
current->set_thread_state(_thread_in_vm);
|
||||
// We've just gotten past the enter-check-for-suspend dance and we now own
|
||||
// the monitor free and clear.
|
||||
}
|
||||
|
||||
add_to_contentions(-1);
|
||||
|
@ -969,21 +973,11 @@ void ObjectMonitor::ReenterI(JavaThread* current, ObjectWaiter* currentNode) {
|
|||
|
||||
assert(current->thread_state() == _thread_in_vm, "invariant");
|
||||
|
||||
current->frame_anchor()->make_walkable(current);
|
||||
// Thread must be walkable before it is blocked.
|
||||
// Read in reverse order.
|
||||
OrderAccess::storestore();
|
||||
current->set_thread_state(_thread_blocked);
|
||||
{
|
||||
ClearSuccOnSuspend csos(this);
|
||||
ThreadBlockInVMPreprocess<ClearSuccOnSuspend> tbivs(current, csos);
|
||||
current->_ParkEvent->park();
|
||||
current->set_thread_state_fence(_thread_blocked_trans);
|
||||
if (SafepointMechanism::should_process(current)) {
|
||||
if (_succ == current) {
|
||||
_succ = NULL;
|
||||
OrderAccess::fence(); // always do a full fence when successor is cleared
|
||||
}
|
||||
SafepointMechanism::process_if_requested(current);
|
||||
}
|
||||
current->set_thread_state(_thread_in_vm);
|
||||
}
|
||||
|
||||
// Try again, but just so we distinguish between futile wakeups and
|
||||
|
@ -1541,11 +1535,8 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
|
|||
assert(current->thread_state() == _thread_in_vm, "invariant");
|
||||
|
||||
{
|
||||
current->frame_anchor()->make_walkable(current);
|
||||
// Thread must be walkable before it is blocked.
|
||||
// Read in reverse order.
|
||||
OrderAccess::storestore();
|
||||
current->set_thread_state(_thread_blocked);
|
||||
ClearSuccOnSuspend csos(this);
|
||||
ThreadBlockInVMPreprocess<ClearSuccOnSuspend> tbivs(current, csos);
|
||||
if (interrupted || HAS_PENDING_EXCEPTION) {
|
||||
// Intentionally empty
|
||||
} else if (node._notified == 0) {
|
||||
|
@ -1555,15 +1546,6 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
|
|||
ret = current->_ParkEvent->park(millis);
|
||||
}
|
||||
}
|
||||
current->set_thread_state_fence(_thread_blocked_trans);
|
||||
if (SafepointMechanism::should_process(current)) {
|
||||
if (_succ == current) {
|
||||
_succ = NULL;
|
||||
OrderAccess::fence(); // always do a full fence when successor is cleared
|
||||
}
|
||||
SafepointMechanism::process_if_requested(current);
|
||||
}
|
||||
current->set_thread_state(_thread_in_vm);
|
||||
}
|
||||
|
||||
// Node may be on the WaitSet, the EntryList (or cxq), or in transition
|
||||
|
|
|
@ -302,6 +302,24 @@ class ObjectMonitor : public CHeapObj<mtInternal> {
|
|||
// returns false and throws IllegalMonitorStateException (IMSE).
|
||||
bool check_owner(TRAPS);
|
||||
|
||||
private:
|
||||
class ExitOnSuspend {
|
||||
protected:
|
||||
ObjectMonitor* _om;
|
||||
bool _om_exited;
|
||||
public:
|
||||
ExitOnSuspend(ObjectMonitor* om) : _om(om), _om_exited(false) {}
|
||||
void operator()(JavaThread* current);
|
||||
bool exited() { return _om_exited; }
|
||||
};
|
||||
class ClearSuccOnSuspend {
|
||||
protected:
|
||||
ObjectMonitor* _om;
|
||||
public:
|
||||
ClearSuccOnSuspend(ObjectMonitor* om) : _om(om) {}
|
||||
void operator()(JavaThread* current);
|
||||
};
|
||||
public:
|
||||
bool enter(JavaThread* current);
|
||||
void exit(JavaThread* current, bool not_suspended = true);
|
||||
void wait(jlong millis, bool interruptible, TRAPS);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue