diff options
Diffstat (limited to 'openjdkjvmti/ti_thread.cc')
-rw-r--r-- | openjdkjvmti/ti_thread.cc | 150 |
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) { |