summaryrefslogtreecommitdiff
path: root/openjdkjvmti/ti_thread.cc
diff options
context:
space:
mode:
Diffstat (limited to 'openjdkjvmti/ti_thread.cc')
-rw-r--r--openjdkjvmti/ti_thread.cc150
1 files changed, 77 insertions, 73 deletions
diff --git a/openjdkjvmti/ti_thread.cc b/openjdkjvmti/ti_thread.cc
index 15fdfb3ab5..01cc8c7699 100644
--- a/openjdkjvmti/ti_thread.cc
+++ b/openjdkjvmti/ti_thread.cc
@@ -65,30 +65,6 @@ static const char* kJvmtiTlsKey = "JvmtiTlsKey";
art::ArtField* ThreadUtil::context_class_loader_ = nullptr;
-ScopedNoUserCodeSuspension::ScopedNoUserCodeSuspension(art::Thread* self) : self_(self) {
- DCHECK_EQ(self, art::Thread::Current());
- // Loop until we both have the user_code_suspension_locK_ and don't have any pending user_code
- // suspensions.
- do {
- art::Locks::user_code_suspension_lock_->AssertNotHeld(self_);
- ThreadUtil::SuspendCheck(self_);
-
- art::Locks::user_code_suspension_lock_->ExclusiveLock(self_);
- if (ThreadUtil::WouldSuspendForUserCodeLocked(self_)) {
- art::Locks::user_code_suspension_lock_->ExclusiveUnlock(self_);
- continue;
- }
-
- art::Locks::user_code_suspension_lock_->AssertHeld(self_);
-
- return;
- } while (true);
-}
-
-ScopedNoUserCodeSuspension::~ScopedNoUserCodeSuspension() {
- art::Locks::user_code_suspension_lock_->ExclusiveUnlock(self_);
-}
-
struct ThreadCallback : public art::ThreadLifecycleCallback {
jthread GetThreadObject(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_) {
if (self->GetPeer() == nullptr) {
@@ -568,8 +544,17 @@ jvmtiError ThreadUtil::GetThreadState(jvmtiEnv* env ATTRIBUTE_UNUSED,
art::Thread* self = art::Thread::Current();
InternalThreadState state = {};
- {
- ScopedNoUserCodeSuspension snucs(self);
+ // Loop since we need to bail out and try again if we would end up getting suspended while holding
+ // the user_code_suspension_lock_ due to a SuspendReason::kForUserCode. In this situation we
+ // release the lock, wait to get resumed and try again.
+ do {
+ SuspendCheck(self);
+ art::MutexLock ucsl_mu(self, *art::Locks::user_code_suspension_lock_);
+ if (WouldSuspendForUserCodeLocked(self)) {
+ // Make sure we won't be suspended in the middle of holding the thread_suspend_count_lock_ by
+ // a user-code suspension. We retry and do another SuspendCheck to clear this.
+ continue;
+ }
art::ScopedObjectAccess soa(self);
art::MutexLock tll_mu(self, *art::Locks::thread_list_lock_);
jvmtiError err = ERR(INTERNAL);
@@ -578,23 +563,24 @@ jvmtiError ThreadUtil::GetThreadState(jvmtiEnv* env ATTRIBUTE_UNUSED,
return err;
}
state = GetNativeThreadState(target);
- if (state.art_state != art::ThreadState::kStarting) {
- DCHECK(state.native_thread != nullptr);
+ if (state.art_state == art::ThreadState::kStarting) {
+ break;
+ }
+ DCHECK(state.native_thread != nullptr);
- // Translate internal thread state to JVMTI and Java state.
- jint jvmti_state = GetJvmtiThreadStateFromInternal(state);
+ // Translate internal thread state to JVMTI and Java state.
+ jint jvmti_state = GetJvmtiThreadStateFromInternal(state);
- // Java state is derived from nativeGetState.
- // TODO: Our implementation assigns "runnable" to suspended. As such, we will have slightly
- // different mask if a thread got suspended due to user-code. However, this is for
- // consistency with the Java view.
- jint java_state = GetJavaStateFromInternal(state);
+ // Java state is derived from nativeGetState.
+ // TODO: Our implementation assigns "runnable" to suspended. As such, we will have slightly
+ // different mask if a thread got suspended due to user-code. However, this is for
+ // consistency with the Java view.
+ jint java_state = GetJavaStateFromInternal(state);
- *thread_state_ptr = jvmti_state | java_state;
+ *thread_state_ptr = jvmti_state | java_state;
- return ERR(NONE);
- }
- }
+ return ERR(NONE);
+ } while (true);
DCHECK_EQ(state.art_state, art::ThreadState::kStarting);
@@ -871,7 +857,18 @@ jvmtiError ThreadUtil::SuspendOther(art::Thread* self,
// the user_code_suspension_lock_ due to a SuspendReason::kForUserCode. In this situation we
// release the lock, wait to get resumed and try again.
do {
- ScopedNoUserCodeSuspension snucs(self);
+ // Suspend ourself if we have any outstanding suspends. This is so we won't suspend due to
+ // another SuspendThread in the middle of suspending something else potentially causing a
+ // deadlock. We need to do this in the loop because if we ended up back here then we had
+ // outstanding SuspendReason::kForUserCode suspensions and we should wait for them to be cleared
+ // before continuing.
+ SuspendCheck(self);
+ art::MutexLock mu(self, *art::Locks::user_code_suspension_lock_);
+ if (WouldSuspendForUserCodeLocked(self)) {
+ // Make sure we won't be suspended in the middle of holding the thread_suspend_count_lock_ by
+ // a user-code suspension. We retry and do another SuspendCheck to clear this.
+ continue;
+ }
// We are not going to be suspended by user code from now on.
{
art::ScopedObjectAccess soa(self);
@@ -960,44 +957,51 @@ jvmtiError ThreadUtil::ResumeThread(jvmtiEnv* env ATTRIBUTE_UNUSED,
}
art::Thread* self = art::Thread::Current();
art::Thread* target;
-
- // Make sure we won't get suspended ourselves while in the middle of resuming another thread.
- ScopedNoUserCodeSuspension snucs(self);
- // From now on we know we cannot get suspended by user-code.
- {
- // NB This does a SuspendCheck (during thread state change) so we need to make sure we don't
- // have the 'suspend_lock' locked here.
- art::ScopedObjectAccess soa(self);
- art::MutexLock tll_mu(self, *art::Locks::thread_list_lock_);
- jvmtiError err = ERR(INTERNAL);
- if (!GetAliveNativeThread(thread, soa, &target, &err)) {
- return err;
- } else if (target == self) {
- // We would have paused until we aren't suspended anymore due to the ScopedObjectAccess so
- // we can just return THREAD_NOT_SUSPENDED. Unfortunately we cannot do any real DCHECKs
- // about current state since it's all concurrent.
- return ERR(THREAD_NOT_SUSPENDED);
+ // Retry until we know we won't get suspended by user code while resuming something.
+ do {
+ SuspendCheck(self);
+ art::MutexLock ucsl_mu(self, *art::Locks::user_code_suspension_lock_);
+ if (WouldSuspendForUserCodeLocked(self)) {
+ // Make sure we won't be suspended in the middle of holding the thread_suspend_count_lock_ by
+ // a user-code suspension. We retry and do another SuspendCheck to clear this.
+ continue;
}
- // The JVMTI spec requires us to return THREAD_NOT_SUSPENDED if it is alive but we really
- // cannot tell why resume failed.
+ // From now on we know we cannot get suspended by user-code.
{
- art::MutexLock thread_suspend_count_mu(self, *art::Locks::thread_suspend_count_lock_);
- if (target->GetUserCodeSuspendCount() == 0) {
+ // NB This does a SuspendCheck (during thread state change) so we need to make sure we don't
+ // have the 'suspend_lock' locked here.
+ art::ScopedObjectAccess soa(self);
+ art::MutexLock tll_mu(self, *art::Locks::thread_list_lock_);
+ jvmtiError err = ERR(INTERNAL);
+ if (!GetAliveNativeThread(thread, soa, &target, &err)) {
+ return err;
+ } else if (target == self) {
+ // We would have paused until we aren't suspended anymore due to the ScopedObjectAccess so
+ // we can just return THREAD_NOT_SUSPENDED. Unfortunately we cannot do any real DCHECKs
+ // about current state since it's all concurrent.
return ERR(THREAD_NOT_SUSPENDED);
}
+ // The JVMTI spec requires us to return THREAD_NOT_SUSPENDED if it is alive but we really
+ // cannot tell why resume failed.
+ {
+ art::MutexLock thread_suspend_count_mu(self, *art::Locks::thread_suspend_count_lock_);
+ if (target->GetUserCodeSuspendCount() == 0) {
+ return ERR(THREAD_NOT_SUSPENDED);
+ }
+ }
}
- }
- // It is okay that we don't have a thread_list_lock here since we know that the thread cannot
- // die since it is currently held suspended by a SuspendReason::kForUserCode suspend.
- DCHECK(target != self);
- if (!art::Runtime::Current()->GetThreadList()->Resume(target,
- art::SuspendReason::kForUserCode)) {
- // TODO Give a better error.
- // This is most likely THREAD_NOT_SUSPENDED but we cannot really be sure.
- return ERR(INTERNAL);
- } else {
- return OK;
- }
+ // It is okay that we don't have a thread_list_lock here since we know that the thread cannot
+ // die since it is currently held suspended by a SuspendReason::kForUserCode suspend.
+ DCHECK(target != self);
+ if (!art::Runtime::Current()->GetThreadList()->Resume(target,
+ art::SuspendReason::kForUserCode)) {
+ // TODO Give a better error.
+ // This is most likely THREAD_NOT_SUSPENDED but we cannot really be sure.
+ return ERR(INTERNAL);
+ } else {
+ return OK;
+ }
+ } while (true);
}
static bool IsCurrentThread(jthread thr) {