diff options
author | 2021-11-22 16:31:57 +0000 | |
---|---|---|
committer | 2021-11-25 09:26:35 +0000 | |
commit | ddf4fd3c37af160b5a1f7e83212b837f50e13e81 (patch) | |
tree | 61f9223f33f3191f6e6416d717a3a13405235413 | |
parent | c3e004d1c8c58c1311beb1bcdd8211f5d4d5a009 (diff) |
Always access Thread state and flags as 32-bit location.
Rewrite access to Thread's state and flags to use 32-bit
atomic operations. Avoid `volatile` accesses that prevent
compiler optimizations.
Change `ThreadState` and `ThreadFlag` to `enum class`es.
Golem results for art-opt-cc (higher is better):
linux-ia32 before after
NativeDowncallStaticNormal 28.162 35.323 (+25.43%)
NativeDowncallStaticNormal6 26.447 32.951 (+24.59%)
NativeDowncallStaticNormalRefs6
NativeDowncallVirtualNormal 27.972 35.027 (+25.22%)
NativeDowncallVirtualNormal6 26.096 32.131 (+23.13%)
NativeDowncallVirtualNormalRefs6 25.922 31.873 (+22.95%)
linux-x64 before after
NativeDowncallStaticNormal 26.987 34.380 (+27.40%)
NativeDowncallStaticNormal6 25.424 31.096 (+22.31%)
NativeDowncallStaticNormalRefs6 25.086 30.602 (+21.99%)
NativeDowncallVirtualNormal 26.812 33.234 (+23.95%)
NativeDowncallVirtualNormal6 25.086 30.617 (+22.05%)
NativeDowncallVirtualNormalRefs6 25.086 30.602 (+21.99%)
linux-armv7 before after
NativeDowncallStaticNormal 7.2394 7.9523 (+9.848%)
NativeDowncallStaticNormal6 6.8527 7.4888 (+9.283%)
NativeDowncallStaticNormalRefs6 6.3976 6.9444 (+8.547%)
NativeDowncallVirtualNormal 7.2081 7.9130 (+9.779%)
NativeDowncallVirtualNormal6 6.8527 7.4888 (+9.283%)
NativeDowncallVirtualNormalRefs6 6.3168 6.8527 (+8.483%)
linux-armv8 before after
NativeDowncallStaticNormal 7.0389 7.5973 (+7.933%)
NativeDowncallStaticNormal6 6.8527 7.3783 (+7.670%)
NativeDowncallStaticNormalRefs6 6.2924 6.8226 (+8.427%)
NativeDowncallVirtualNormal 6.8527 7.3783 (+7.670%)
NativeDowncallVirtualNormal6 6.5604 7.0423 (+7.344%)
NativeDowncallVirtualNormalRefs6 6.1408 6.5329 (+6.386%)
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Test: run-gtests.sh
Test: testrunner.py --target --optimizing --interpreter
Bug: 172332525
Bug: 143299880
Change-Id: Ib55d457ad8f5d9e1159b681dfd279d1f9cfb2af7
78 files changed, 557 insertions, 437 deletions
diff --git a/adbconnection/adbconnection.cc b/adbconnection/adbconnection.cc index cca4485cdd..f9ebe40e15 100644 --- a/adbconnection/adbconnection.cc +++ b/adbconnection/adbconnection.cc @@ -183,7 +183,7 @@ AdbConnectionState::~AdbConnectionState() { static jobject CreateAdbConnectionThread(art::Thread* thr) { JNIEnv* env = thr->GetJniEnv(); // Move to native state to talk with the jnienv api. - art::ScopedThreadStateChange stsc(thr, art::kNative); + art::ScopedThreadStateChange stsc(thr, art::ThreadState::kNative); ScopedLocalRef<jstring> thr_name(env, env->NewStringUTF(kAdbConnectionThreadName)); ScopedLocalRef<jobject> thr_group( env, @@ -528,9 +528,9 @@ bool AdbConnectionState::SetupAdbConnection() { void AdbConnectionState::RunPollLoop(art::Thread* self) { DCHECK(IsDebuggingPossible() || art::Runtime::Current()->IsProfileableFromShell()); CHECK_NE(agent_name_, ""); - CHECK_EQ(self->GetState(), art::kNative); + CHECK_EQ(self->GetState(), art::ThreadState::kNative); art::Locks::mutator_lock_->AssertNotHeld(self); - self->SetState(art::kWaitingInMainDebuggerLoop); + self->SetState(art::ThreadState::kWaitingInMainDebuggerLoop); // shutting_down_ set by StopDebuggerThreads while (!shutting_down_) { // First, connect to adbd if we haven't already. diff --git a/cmdline/cmdline.h b/cmdline/cmdline.h index 5821496ec6..b8ca7d0efc 100644 --- a/cmdline/cmdline.h +++ b/cmdline/cmdline.h @@ -127,7 +127,7 @@ static Runtime* StartRuntime(const char* boot_image_location, // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start, // give it away now and then switch to a more manageable ScopedObjectAccess. - Thread::Current()->TransitionFromRunnableToSuspended(kNative); + Thread::Current()->TransitionFromRunnableToSuspended(ThreadState::kNative); return Runtime::Current(); } diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc index 2f96d44977..d63ae1737f 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -536,9 +536,9 @@ mirror::Object* JniCompilerTest::JniMethodEndWithReferenceSynchronizedOverride( static void expectValidThreadState() { // Normal JNI always transitions to "Native". Other JNIs stay in the "Runnable" state. if (IsCurrentJniNormal()) { - EXPECT_EQ(kNative, Thread::Current()->GetState()); + EXPECT_EQ(ThreadState::kNative, Thread::Current()->GetState()); } else { - EXPECT_EQ(kRunnable, Thread::Current()->GetState()); + EXPECT_EQ(ThreadState::kRunnable, Thread::Current()->GetState()); } } diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 2cf2571acd..bcb5ac5084 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -1994,7 +1994,8 @@ void InstructionCodeGeneratorARM64::GenerateSuspendCheck(HSuspendCheck* instruct UseScratchRegisterScope temps(codegen_->GetVIXLAssembler()); Register temp = temps.AcquireW(); - __ Ldrh(temp, MemOperand(tr, Thread::ThreadFlagsOffset<kArm64PointerSize>().SizeValue())); + __ Ldr(temp, MemOperand(tr, Thread::ThreadFlagsOffset<kArm64PointerSize>().SizeValue())); + static_assert(static_cast<std::underlying_type_t<ThreadState>>(ThreadState::kRunnable) == 0u); if (successor == nullptr) { __ Cbnz(temp, slow_path->GetEntryLabel()); __ Bind(slow_path->GetReturnLabel()); diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 62c285d91c..aa06c5a35c 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -7181,7 +7181,8 @@ void InstructionCodeGeneratorARMVIXL::GenerateSuspendCheck(HSuspendCheck* instru UseScratchRegisterScope temps(GetVIXLAssembler()); vixl32::Register temp = temps.Acquire(); GetAssembler()->LoadFromOffset( - kLoadUnsignedHalfword, temp, tr, Thread::ThreadFlagsOffset<kArmPointerSize>().Int32Value()); + kLoadWord, temp, tr, Thread::ThreadFlagsOffset<kArmPointerSize>().Int32Value()); + static_assert(static_cast<std::underlying_type_t<ThreadState>>(ThreadState::kRunnable) == 0u); if (successor == nullptr) { __ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel()); __ Bind(slow_path->GetReturnLabel()); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 11c15d6219..758a4712f4 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -6699,7 +6699,8 @@ void InstructionCodeGeneratorX86::GenerateSuspendCheck(HSuspendCheck* instructio DCHECK_EQ(slow_path->GetSuccessor(), successor); } - __ fs()->cmpw(Address::Absolute(Thread::ThreadFlagsOffset<kX86PointerSize>().Int32Value()), + static_assert(static_cast<std::underlying_type_t<ThreadState>>(ThreadState::kRunnable) == 0u); + __ fs()->cmpl(Address::Absolute(Thread::ThreadFlagsOffset<kX86PointerSize>().Int32Value()), Immediate(0)); if (successor == nullptr) { __ j(kNotEqual, slow_path->GetEntryLabel()); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index e601b402cb..c402e83b9c 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -6016,7 +6016,8 @@ void InstructionCodeGeneratorX86_64::GenerateSuspendCheck(HSuspendCheck* instruc DCHECK_EQ(slow_path->GetSuccessor(), successor); } - __ gs()->cmpw(Address::Absolute(Thread::ThreadFlagsOffset<kX86_64PointerSize>().Int32Value(), + static_assert(static_cast<std::underlying_type_t<ThreadState>>(ThreadState::kRunnable) == 0u); + __ gs()->cmpl(Address::Absolute(Thread::ThreadFlagsOffset<kX86_64PointerSize>().Int32Value(), /* no_rip= */ true), Immediate(0)); if (successor == nullptr) { diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 16abf9d37d..6d7a953534 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -1043,7 +1043,7 @@ CompiledMethod* OptimizingCompiler::Compile(const dex::CodeItem* code_item, // All signature polymorphic methods are native. DCHECK(method == nullptr || !method->IsSignaturePolymorphic()); // Go to native so that we don't block GC during compilation. - ScopedThreadSuspension sts(soa.Self(), kNative); + ScopedThreadSuspension sts(soa.Self(), ThreadState::kNative); // Try to compile a fully intrinsified implementation. if (method != nullptr && UNLIKELY(method->IsIntrinsic())) { DCHECK(compiler_options.IsBootImage()); @@ -1159,7 +1159,7 @@ CompiledMethod* OptimizingCompiler::JniCompile(uint32_t access_flags, compiling_class); CodeVectorAllocator code_allocator(&allocator); // Go to native so that we don't block GC during compilation. - ScopedThreadSuspension sts(soa.Self(), kNative); + ScopedThreadSuspension sts(soa.Self(), ThreadState::kNative); std::unique_ptr<CodeGenerator> codegen( TryCompileIntrinsic(&allocator, &arena_stack, @@ -1328,7 +1328,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, compiling_class); // Go to native so that we don't block GC during compilation. - ScopedThreadSuspension sts(self, kNative); + ScopedThreadSuspension sts(self, ThreadState::kNative); codegen.reset( TryCompile(&allocator, &arena_stack, diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc index bd8aa083eb..2b3c2dd40a 100644 --- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc +++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc @@ -1053,11 +1053,12 @@ void ArmVIXLJNIMacroAssembler::GetCurrentThread(FrameOffset dest_offset) { void ArmVIXLJNIMacroAssembler::SuspendCheck(JNIMacroLabel* label) { UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); vixl32::Register scratch = temps.Acquire(); - asm_.LoadFromOffset(kLoadUnsignedHalfword, + asm_.LoadFromOffset(kLoadWord, scratch, tr, Thread::ThreadFlagsOffset<kArmPointerSize>().Int32Value()); + static_assert(static_cast<std::underlying_type_t<ThreadState>>(ThreadState::kRunnable) == 0u); ___ Cmp(scratch, 0); ___ BPreferNear(ne, ArmVIXLJNIMacroLabel::Cast(label)->AsArm()); // TODO: think about using CBNZ here. diff --git a/compiler/utils/arm64/jni_macro_assembler_arm64.cc b/compiler/utils/arm64/jni_macro_assembler_arm64.cc index 561cbbd54b..e2d29fd19a 100644 --- a/compiler/utils/arm64/jni_macro_assembler_arm64.cc +++ b/compiler/utils/arm64/jni_macro_assembler_arm64.cc @@ -892,7 +892,8 @@ void Arm64JNIMacroAssembler::CreateJObject(FrameOffset out_off, void Arm64JNIMacroAssembler::SuspendCheck(JNIMacroLabel* label) { UseScratchRegisterScope temps(asm_.GetVIXLAssembler()); Register scratch = temps.AcquireW(); - ___ Ldrh(scratch, MEM_OP(reg_x(TR), Thread::ThreadFlagsOffset<kArm64PointerSize>().Int32Value())); + ___ Ldr(scratch, MEM_OP(reg_x(TR), Thread::ThreadFlagsOffset<kArm64PointerSize>().Int32Value())); + static_assert(static_cast<std::underlying_type_t<ThreadState>>(ThreadState::kRunnable) == 0u); ___ Cbnz(scratch, Arm64JNIMacroLabel::Cast(label)->AsArm64()); } diff --git a/compiler/utils/x86/jni_macro_assembler_x86.cc b/compiler/utils/x86/jni_macro_assembler_x86.cc index 7dff279944..904cca4030 100644 --- a/compiler/utils/x86/jni_macro_assembler_x86.cc +++ b/compiler/utils/x86/jni_macro_assembler_x86.cc @@ -590,7 +590,8 @@ void X86JNIMacroAssembler::GetCurrentThread(FrameOffset offset) { } void X86JNIMacroAssembler::SuspendCheck(JNIMacroLabel* label) { - __ fs()->cmpw(Address::Absolute(Thread::ThreadFlagsOffset<kX86PointerSize>()), Immediate(0)); + static_assert(static_cast<std::underlying_type_t<ThreadState>>(ThreadState::kRunnable) == 0u); + __ fs()->cmpl(Address::Absolute(Thread::ThreadFlagsOffset<kX86PointerSize>()), Immediate(0)); __ j(kNotEqual, X86JNIMacroLabel::Cast(label)->AsX86()); } diff --git a/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc b/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc index 2da1b470ac..2fb27977cb 100644 --- a/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc +++ b/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc @@ -672,7 +672,8 @@ void X86_64JNIMacroAssembler::GetCurrentThread(FrameOffset offset) { } void X86_64JNIMacroAssembler::SuspendCheck(JNIMacroLabel* label) { - __ gs()->cmpw(Address::Absolute(Thread::ThreadFlagsOffset<kX86_64PointerSize>(), true), + static_assert(static_cast<std::underlying_type_t<ThreadState>>(ThreadState::kRunnable) == 0u); + __ gs()->cmpl(Address::Absolute(Thread::ThreadFlagsOffset<kX86_64PointerSize>(), true), Immediate(0)); __ j(kNotEqual, X86_64JNIMacroLabel::Cast(label)->AsX86_64()); } diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index fe46552b4d..217afa9a54 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -2680,7 +2680,7 @@ class Dex2Oat final { // Runtime::Create acquired the mutator_lock_ that is normally given away when we // Runtime::Start, give it away now so that we don't starve GC. - self->TransitionFromRunnableToSuspended(kNative); + self->TransitionFromRunnableToSuspended(ThreadState::kNative); WatchDog::SetRuntime(runtime_.get()); diff --git a/dex2oat/driver/compiler_driver.cc b/dex2oat/driver/compiler_driver.cc index 54294302ed..6939b0c274 100644 --- a/dex2oat/driver/compiler_driver.cc +++ b/dex2oat/driver/compiler_driver.cc @@ -1426,7 +1426,7 @@ class ParallelCompilationManager { // Ensure we're suspended while we're blocked waiting for the other threads to finish (worker // thread destructor's called below perform join). - CHECK_NE(self->GetState(), kRunnable); + CHECK_NE(self->GetState(), ThreadState::kRunnable); // Wait for all the worker threads to finish. thread_pool_->Wait(self, true, false); @@ -2537,7 +2537,7 @@ static void CompileDexFile(CompilerDriver* driver, } // Go to native so that we don't block GC during compilation. - ScopedThreadSuspension sts(soa.Self(), kNative); + ScopedThreadSuspension sts(soa.Self(), ThreadState::kNative); // Compile direct and virtual methods. int64_t previous_method_idx = -1; diff --git a/dex2oat/linker/image_write_read_test.cc b/dex2oat/linker/image_write_read_test.cc index 2966f19d32..3e3dac1f28 100644 --- a/dex2oat/linker/image_write_read_test.cc +++ b/dex2oat/linker/image_write_read_test.cc @@ -80,7 +80,7 @@ void ImageWriteReadTest::TestWriteRead(ImageHeader::StorageMode storage_mode, runtime_.reset(Runtime::Current()); // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start, // give it away now and then switch to a more managable ScopedObjectAccess. - Thread::Current()->TransitionFromRunnableToSuspended(kNative); + Thread::Current()->TransitionFromRunnableToSuspended(ThreadState::kNative); ScopedObjectAccess soa(Thread::Current()); ASSERT_TRUE(runtime_.get() != nullptr); class_linker_ = runtime_->GetClassLinker(); diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc index 99e8286ad5..0cc2cdb03a 100644 --- a/dexoptanalyzer/dexoptanalyzer.cc +++ b/dexoptanalyzer/dexoptanalyzer.cc @@ -284,7 +284,7 @@ class DexoptAnalyzer final { } // Runtime::Create acquired the mutator_lock_ that is normally given away when we // Runtime::Start. Give it away now. - Thread::Current()->TransitionFromRunnableToSuspended(kNative); + Thread::Current()->TransitionFromRunnableToSuspended(ThreadState::kNative); return true; } diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 365d1e89d1..7e21f64c7c 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1831,7 +1831,7 @@ class ImageDumper { // Since FlushAllocStack() above resets the (active) allocation // stack. Need to revoke the thread-local allocation stacks that // point into it. - ScopedThreadSuspension sts(self, kNative); + ScopedThreadSuspension sts(self, ThreadState::kNative); ScopedSuspendAll ssa(__FUNCTION__); heap->RevokeAllThreadLocalAllocationStacks(self); } diff --git a/openjdkjvm/OpenjdkJvm.cc b/openjdkjvm/OpenjdkJvm.cc index 9b514af452..a9fa0fc6c7 100644 --- a/openjdkjvm/OpenjdkJvm.cc +++ b/openjdkjvm/OpenjdkJvm.cc @@ -367,7 +367,8 @@ JNIEXPORT void JVM_Sleep(JNIEnv* env, jclass threadClass ATTRIBUTE_UNUSED, jobject java_lock, jlong millis) { art::ScopedFastNativeObjectAccess soa(env); art::ObjPtr<art::mirror::Object> lock = soa.Decode<art::mirror::Object>(java_lock); - art::Monitor::Wait(art::Thread::Current(), lock.Ptr(), millis, 0, true, art::kSleeping); + art::Monitor::Wait( + art::Thread::Current(), lock.Ptr(), millis, 0, true, art::ThreadState::kSleeping); } JNIEXPORT jobject JVM_CurrentThread(JNIEnv* env, jclass unused ATTRIBUTE_UNUSED) { diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc index cf28a71932..f30a21d04f 100644 --- a/openjdkjvmti/deopt_manager.cc +++ b/openjdkjvmti/deopt_manager.cc @@ -219,14 +219,14 @@ bool DeoptManager::MethodHasBreakpointsLocked(art::ArtMethod* method) { void DeoptManager::RemoveDeoptimizeAllMethods() { art::Thread* self = art::Thread::Current(); - art::ScopedThreadSuspension sts(self, art::kSuspended); + art::ScopedThreadSuspension sts(self, art::ThreadState::kSuspended); deoptimization_status_lock_.ExclusiveLock(self); RemoveDeoptimizeAllMethodsLocked(self); } void DeoptManager::AddDeoptimizeAllMethods() { art::Thread* self = art::Thread::Current(); - art::ScopedThreadSuspension sts(self, art::kSuspended); + art::ScopedThreadSuspension sts(self, art::ThreadState::kSuspended); deoptimization_status_lock_.ExclusiveLock(self); AddDeoptimizeAllMethodsLocked(self); } @@ -240,7 +240,7 @@ void DeoptManager::AddMethodBreakpoint(art::ArtMethod* method) { method = method->GetCanonicalMethod(); bool is_default = method->IsDefault(); - art::ScopedThreadSuspension sts(self, art::kSuspended); + art::ScopedThreadSuspension sts(self, art::ThreadState::kSuspended); deoptimization_status_lock_.ExclusiveLock(self); { breakpoint_status_lock_.ExclusiveLock(self); @@ -280,7 +280,7 @@ void DeoptManager::RemoveMethodBreakpoint(art::ArtMethod* method) { method = method->GetCanonicalMethod(); bool is_default = method->IsDefault(); - art::ScopedThreadSuspension sts(self, art::kSuspended); + art::ScopedThreadSuspension sts(self, art::ThreadState::kSuspended); // Ideally we should do a ScopedSuspendAll right here to get the full mutator_lock_ that we might // need but since that is very heavy we will instead just use a condition variable to make sure we // don't race with ourselves. @@ -452,7 +452,7 @@ jvmtiError DeoptManager::RemoveDeoptimizeThreadMethods(art::ScopedObjectAccessUn void DeoptManager::RemoveDeoptimizationRequester() { art::Thread* self = art::Thread::Current(); - art::ScopedThreadStateChange sts(self, art::kSuspended); + art::ScopedThreadStateChange sts(self, art::ThreadState::kSuspended); deoptimization_status_lock_.ExclusiveLock(self); DCHECK_GT(deopter_count_, 0u) << "Removing deoptimization requester without any being present"; deopter_count_--; @@ -468,7 +468,7 @@ void DeoptManager::RemoveDeoptimizationRequester() { void DeoptManager::AddDeoptimizationRequester() { art::Thread* self = art::Thread::Current(); - art::ScopedThreadStateChange stsc(self, art::kSuspended); + art::ScopedThreadStateChange stsc(self, art::ThreadState::kSuspended); deoptimization_status_lock_.ExclusiveLock(self); deopter_count_++; if (deopter_count_ == 1) { @@ -487,7 +487,7 @@ void DeoptManager::AddDeoptimizationRequester() { void DeoptManager::DeoptimizeThread(art::Thread* target) { // We might or might not be running on the target thread (self) so get Thread::Current // directly. - art::ScopedThreadSuspension sts(art::Thread::Current(), art::kSuspended); + art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kSuspended); art::gc::ScopedGCCriticalSection sgccs(art::Thread::Current(), art::gc::GcCause::kGcCauseDebugger, art::gc::CollectorType::kCollectorTypeDebugger); diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc index 924a0d84d7..334588348c 100644 --- a/openjdkjvmti/ti_class.cc +++ b/openjdkjvmti/ti_class.cc @@ -368,7 +368,7 @@ struct ClassCallback : public art::ClassLoadCallback { heap->IncrementDisableMovingGC(self); } { - art::ScopedThreadSuspension sts(self, art::kWaitingForVisitObjects); + art::ScopedThreadSuspension sts(self, art::ThreadState::kWaitingForVisitObjects); art::ScopedSuspendAll ssa("FixupTempClass"); art::mirror::Class* input = temp_klass.Get(); diff --git a/openjdkjvmti/ti_heap.cc b/openjdkjvmti/ti_heap.cc index bd9d2ddd08..2a1d44207a 100644 --- a/openjdkjvmti/ti_heap.cc +++ b/openjdkjvmti/ti_heap.cc @@ -1403,7 +1403,7 @@ jvmtiError HeapUtil::FollowReferences(jvmtiEnv* env, { art::ScopedObjectAccess soa(self); // Now we know we have the shared lock. art::jni::ScopedEnableSuspendAllJniIdQueries sjni; // make sure we can get JNI ids. - art::ScopedThreadSuspension sts(self, art::kWaitingForVisitObjects); + art::ScopedThreadSuspension sts(self, art::ThreadState::kWaitingForVisitObjects); art::ScopedSuspendAll ssa("FollowReferences"); art::ObjPtr<art::mirror::Class> class_filter = klass == nullptr diff --git a/openjdkjvmti/ti_monitor.cc b/openjdkjvmti/ti_monitor.cc index 2ca5057f85..f244cc114d 100644 --- a/openjdkjvmti/ti_monitor.cc +++ b/openjdkjvmti/ti_monitor.cc @@ -376,39 +376,42 @@ jvmtiError MonitorUtil::GetCurrentContendedMonitor(jvmtiEnv* env ATTRIBUTE_UNUSE switch (target_thread->GetState()) { // These three we are actually currently waiting on a monitor and have sent the appropriate // events (if anyone is listening). - case art::kBlocked: - case art::kTimedWaiting: - case art::kWaiting: { + case art::ThreadState::kBlocked: + case art::ThreadState::kTimedWaiting: + case art::ThreadState::kWaiting: { out_ = art::GcRoot<art::mirror::Object>(art::Monitor::GetContendedMonitor(target_thread)); return; } - case art::kTerminated: - case art::kRunnable: - case art::kSleeping: - case art::kWaitingForLockInflation: - case art::kWaitingForTaskProcessor: - case art::kWaitingForGcToComplete: - case art::kWaitingForCheckPointsToRun: - case art::kWaitingPerformingGc: - case art::kWaitingForDebuggerSend: - case art::kWaitingForDebuggerToAttach: - case art::kWaitingInMainDebuggerLoop: - case art::kWaitingForDebuggerSuspension: - case art::kWaitingForJniOnLoad: - case art::kWaitingForSignalCatcherOutput: - case art::kWaitingInMainSignalCatcherLoop: - case art::kWaitingForDeoptimization: - case art::kWaitingForMethodTracingStart: - case art::kWaitingForVisitObjects: - case art::kWaitingForGetObjectsAllocated: - case art::kWaitingWeakGcRootRead: - case art::kWaitingForGcThreadFlip: - case art::kNativeForAbort: - case art::kStarting: - case art::kNative: - case art::kSuspended: { + case art::ThreadState::kTerminated: + case art::ThreadState::kRunnable: + case art::ThreadState::kSleeping: + case art::ThreadState::kWaitingForLockInflation: + case art::ThreadState::kWaitingForTaskProcessor: + case art::ThreadState::kWaitingForGcToComplete: + case art::ThreadState::kWaitingForCheckPointsToRun: + case art::ThreadState::kWaitingPerformingGc: + case art::ThreadState::kWaitingForDebuggerSend: + case art::ThreadState::kWaitingForDebuggerToAttach: + case art::ThreadState::kWaitingInMainDebuggerLoop: + case art::ThreadState::kWaitingForDebuggerSuspension: + case art::ThreadState::kWaitingForJniOnLoad: + case art::ThreadState::kWaitingForSignalCatcherOutput: + case art::ThreadState::kWaitingInMainSignalCatcherLoop: + case art::ThreadState::kWaitingForDeoptimization: + case art::ThreadState::kWaitingForMethodTracingStart: + case art::ThreadState::kWaitingForVisitObjects: + case art::ThreadState::kWaitingForGetObjectsAllocated: + case art::ThreadState::kWaitingWeakGcRootRead: + case art::ThreadState::kWaitingForGcThreadFlip: + case art::ThreadState::kNativeForAbort: + case art::ThreadState::kStarting: + case art::ThreadState::kNative: + case art::ThreadState::kSuspended: { // We aren't currently (explicitly) waiting for a monitor so just return null. return; + case art::ThreadState::kObsoleteRunnable: + LOG(FATAL) << "UNREACHABLE"; // Obsolete value. + UNREACHABLE(); } } } diff --git a/openjdkjvmti/ti_object.cc b/openjdkjvmti/ti_object.cc index 344ae88546..eb1140df4f 100644 --- a/openjdkjvmti/ti_object.cc +++ b/openjdkjvmti/ti_object.cc @@ -91,7 +91,7 @@ jvmtiError ObjectUtil::GetObjectMonitorUsage( std::vector<jthread> notify_wait; { art::ScopedObjectAccess soa(self); // Now we know we have the shared lock. - art::ScopedThreadSuspension sts(self, art::kNative); + art::ScopedThreadSuspension sts(self, art::ThreadState::kNative); art::ScopedSuspendAll ssa("GetObjectMonitorUsage", /*long_suspend=*/false); art::ObjPtr<art::mirror::Object> target(self->DecodeJObject(obj)); // This gets the list of threads trying to lock or wait on the monitor. diff --git a/openjdkjvmti/ti_thread.cc b/openjdkjvmti/ti_thread.cc index a9a6ee8895..f31759ee34 100644 --- a/openjdkjvmti/ti_thread.cc +++ b/openjdkjvmti/ti_thread.cc @@ -174,7 +174,7 @@ void ThreadUtil::VMInitEventSent() { static void WaitForSystemDaemonStart(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_) { { - art::ScopedThreadStateChange strc(self, art::kNative); + art::ScopedThreadStateChange strc(self, art::ThreadState::kNative); JNIEnv* jni = self->GetJniEnv(); jni->CallStaticVoidMethod(art::WellKnownClasses::java_lang_Daemons, art::WellKnownClasses::java_lang_Daemons_waitForDaemonStart); @@ -487,6 +487,7 @@ static jint GetJvmtiThreadStateFromInternal(const InternalThreadState& state) { jvmti_state |= (JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_INDEFINITELY); break; + case art::ThreadState::kObsoleteRunnable: // Obsolete value. case art::ThreadState::kStarting: case art::ThreadState::kTerminated: // We only call this if we are alive so we shouldn't see either of these states. @@ -539,6 +540,9 @@ static jint GetJavaStateFromInternal(const InternalThreadState& state) { case art::ThreadState::kWaitingForGcThreadFlip: case art::ThreadState::kNativeForAbort: return JVMTI_JAVA_LANG_THREAD_STATE_WAITING; + + case art::ThreadState::kObsoleteRunnable: + break; // Obsolete value. } LOG(FATAL) << "Unreachable"; UNREACHABLE(); diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 9dda50cdc9..5b1a4761cb 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -319,7 +319,7 @@ void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* if (kIsDebugBuild) { self->AssertThreadSuspensionIsAllowable(); - CHECK_EQ(kRunnable, self->GetState()); + CHECK_EQ(ThreadState::kRunnable, self->GetState()); CHECK_STREQ(GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty(), shorty); } diff --git a/runtime/cha.cc b/runtime/cha.cc index 392b35cd81..03025dc835 100644 --- a/runtime/cha.cc +++ b/runtime/cha.cc @@ -249,7 +249,7 @@ class CHACheckpoint final : public Closure { void WaitForThreadsToRunThroughCheckpoint(size_t threads_running_checkpoint) { Thread* self = Thread::Current(); - ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun); + ScopedThreadStateChange tsc(self, ThreadState::kWaitingForCheckPointsToRun); barrier_.Increment(self, threads_running_checkpoint); } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 45562c46c1..5410bb027d 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2550,7 +2550,7 @@ ObjPtr<mirror::Class> ClassLinker::EnsureResolved(Thread* self, } { // Handle wrapper deals with klass moving. - ScopedThreadSuspension sts(self, kSuspended); + ScopedThreadSuspension sts(self, ThreadState::kSuspended); if (index < kNumYieldIterations) { sched_yield(); } else { @@ -2935,7 +2935,7 @@ ObjPtr<mirror::Class> ClassLinker::FindClass(Thread* self, soa.Env(), soa.AddLocalReference<jobject>(class_loader.Get())); ScopedLocalRef<jobject> result(soa.Env(), nullptr); { - ScopedThreadStateChange tsc(self, kNative); + ScopedThreadStateChange tsc(self, ThreadState::kNative); ScopedLocalRef<jobject> class_name_object( soa.Env(), soa.Env()->NewStringUTF(class_name_string.c_str())); if (class_name_object.get() == nullptr) { @@ -3228,7 +3228,7 @@ ObjPtr<mirror::Class> ClassLinker::DefineClass(Thread* self, // We must be in the kRunnable state to prevent instrumentation from // suspending all threads to update entrypoints while we are doing it // for this class. - DCHECK_EQ(self->GetState(), kRunnable); + DCHECK_EQ(self->GetState(), ThreadState::kRunnable); Runtime::Current()->GetInstrumentation()->InstallStubsForClass(h_new_class.Get()); } @@ -7439,7 +7439,7 @@ class ClassLinker::LinkMethodsHelper { if (methods != old_methods && old_methods != nullptr) { // Need to make sure the GC is not running since it could be scanning the methods we are // about to overwrite. - ScopedThreadStateChange tsc(self_, kSuspended); + ScopedThreadStateChange tsc(self_, ThreadState::kSuspended); gc::ScopedGCCriticalSection gcs(self_, gc::kGcCauseClassLinker, gc::kCollectorTypeClassLinker); diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index 3f643e8db8..f949759df2 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -130,7 +130,7 @@ void CommonRuntimeTestImpl::SetUp() { // Runtime::Create acquired the mutator_lock_ that is normally given away when we // Runtime::Start, give it away now and then switch to a more managable ScopedObjectAccess. - Thread::Current()->TransitionFromRunnableToSuspended(kNative); + Thread::Current()->TransitionFromRunnableToSuspended(ThreadState::kNative); // Get the boot class path from the runtime so it can be used in tests. boot_class_path_ = class_linker_->GetBootClassPath(); diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 2da3e418d3..af365310ef 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -268,7 +268,7 @@ void Dbg::DdmBroadcast(bool connect) { VLOG(jdwp) << "Broadcasting DDM " << (connect ? "connect" : "disconnect") << "..."; Thread* self = Thread::Current(); - if (self->GetState() != kRunnable) { + if (self->GetState() != ThreadState::kRunnable) { LOG(ERROR) << "DDM broadcast in thread state " << self->GetState(); /* try anyway? */ } @@ -747,7 +747,7 @@ void Dbg::DdmSendHeapSegments(bool native) { context.SetChunkOverhead(0); // Need to acquire the mutator lock before the heap bitmap lock with exclusive access since // RosAlloc's internal logic doesn't know to release and reacquire the heap bitmap lock. - ScopedThreadSuspension sts(self, kSuspended); + ScopedThreadSuspension sts(self, ThreadState::kSuspended); ScopedSuspendAll ssa(__FUNCTION__); ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); space->AsRosAllocSpace()->Walk(HeapChunkContext::HeapChunkJavaCallback, &context); @@ -759,7 +759,7 @@ void Dbg::DdmSendHeapSegments(bool native) { } else if (space->IsRegionSpace()) { heap->IncrementDisableMovingGC(self); { - ScopedThreadSuspension sts(self, kSuspended); + ScopedThreadSuspension sts(self, ThreadState::kSuspended); ScopedSuspendAll ssa(__FUNCTION__); ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); context.SetChunkOverhead(0); diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc index 95072130a9..4fa37e5a9c 100644 --- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc @@ -66,7 +66,7 @@ extern void JniMethodStart(Thread* self) { } // Transition out of runnable. - self->TransitionFromRunnableToSuspended(kNative); + self->TransitionFromRunnableToSuspended(ThreadState::kNative); } // TODO: NO_THREAD_SAFETY_ANALYSIS due to different control paths depending on fast JNI. diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc index 66fb3c7b5c..f8bd213d53 100644 --- a/runtime/fault_handler.cc +++ b/runtime/fault_handler.cc @@ -309,7 +309,7 @@ bool FaultManager::IsInGeneratedCode(siginfo_t* siginfo, void* context, bool che } ThreadState state = thread->GetState(); - if (state != kRunnable) { + if (state != ThreadState::kRunnable) { VLOG(signals) << "not runnable"; return false; } diff --git a/runtime/gc/accounting/mod_union_table_test.cc b/runtime/gc/accounting/mod_union_table_test.cc index 1cd719d460..e42682a112 100644 --- a/runtime/gc/accounting/mod_union_table_test.cc +++ b/runtime/gc/accounting/mod_union_table_test.cc @@ -188,7 +188,7 @@ void ModUnionTableTest::RunTest(ModUnionTableFactory::TableType type) { "other space", 128 * KB, 4 * MB, 4 * MB, /*can_move_objects=*/ false)); ASSERT_TRUE(other_space.get() != nullptr); { - ScopedThreadSuspension sts(self, kSuspended); + ScopedThreadSuspension sts(self, ThreadState::kSuspended); ScopedSuspendAll ssa("Add image space"); heap->AddSpace(other_space.get()); } @@ -260,7 +260,7 @@ void ModUnionTableTest::RunTest(ModUnionTableFactory::TableType type) { std::ostringstream oss2; table->Dump(oss2); // Remove the space we added so it doesn't persist to the next test. - ScopedThreadSuspension sts(self, kSuspended); + ScopedThreadSuspension sts(self, ThreadState::kSuspended); ScopedSuspendAll ssa("Add image space"); heap->RemoveSpace(other_space.get()); } diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index af02da8714..3a60191310 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -266,7 +266,9 @@ class ConcurrentCopying::ActivateReadBarrierEntrypointsCheckpoint : public Closu void Run(Thread* thread) override NO_THREAD_SAFETY_ANALYSIS { // Note: self is not necessarily equal to thread since thread may be suspended. Thread* self = Thread::Current(); - DCHECK(thread == self || thread->IsSuspended() || thread->GetState() == kWaitingPerformingGc) + DCHECK(thread == self || + thread->IsSuspended() || + thread->GetState() == ThreadState::kWaitingPerformingGc) << thread->GetState() << " thread " << thread << " self " << self; // Switch to the read barrier entrypoints. thread->SetReadBarrierEntrypoints(); @@ -307,7 +309,7 @@ void ConcurrentCopying::ActivateReadBarrierEntrypoints() { if (barrier_count == 0) { return; } - ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun); + ScopedThreadStateChange tsc(self, ThreadState::kWaitingForCheckPointsToRun); gc_barrier_->Increment(self, barrier_count); } @@ -462,7 +464,9 @@ class ConcurrentCopying::ThreadFlipVisitor : public Closure, public RootVisitor void Run(Thread* thread) override REQUIRES_SHARED(Locks::mutator_lock_) { // Note: self is not necessarily equal to thread since thread may be suspended. Thread* self = Thread::Current(); - CHECK(thread == self || thread->IsSuspended() || thread->GetState() == kWaitingPerformingGc) + CHECK(thread == self || + thread->IsSuspended() || + thread->GetState() == ThreadState::kWaitingPerformingGc) << thread->GetState() << " thread " << thread << " self " << self; thread->SetIsGcMarkingAndUpdateEntrypoints(true); if (use_tlab_ && thread->HasTlab()) { @@ -768,7 +772,7 @@ void ConcurrentCopying::FlipThreadRoots() { &thread_flip_visitor, &flip_callback, this, GetHeap()->GetGcPauseListener()); { - ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun); + ScopedThreadStateChange tsc(self, ThreadState::kWaitingForCheckPointsToRun); gc_barrier_->Increment(self, barrier_count); } is_asserting_to_space_invariant_ = true; @@ -980,7 +984,9 @@ class ConcurrentCopying::RevokeThreadLocalMarkStackCheckpoint : public Closure { void Run(Thread* thread) override NO_THREAD_SAFETY_ANALYSIS { // Note: self is not necessarily equal to thread since thread may be suspended. Thread* const self = Thread::Current(); - CHECK(thread == self || thread->IsSuspended() || thread->GetState() == kWaitingPerformingGc) + CHECK(thread == self || + thread->IsSuspended() || + thread->GetState() == ThreadState::kWaitingPerformingGc) << thread->GetState() << " thread " << thread << " self " << self; // Revoke thread local mark stacks. { @@ -1047,7 +1053,7 @@ void ConcurrentCopying::CaptureThreadRootsForMarking() { } Locks::mutator_lock_->SharedUnlock(self); { - ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun); + ScopedThreadStateChange tsc(self, ThreadState::kWaitingForCheckPointsToRun); gc_barrier_->Increment(self, barrier_count); } Locks::mutator_lock_->SharedLock(self); @@ -1718,7 +1724,9 @@ class ConcurrentCopying::DisableMarkingCheckpoint : public Closure { void Run(Thread* thread) override NO_THREAD_SAFETY_ANALYSIS { // Note: self is not necessarily equal to thread since thread may be suspended. Thread* self = Thread::Current(); - DCHECK(thread == self || thread->IsSuspended() || thread->GetState() == kWaitingPerformingGc) + DCHECK(thread == self || + thread->IsSuspended() || + thread->GetState() == ThreadState::kWaitingPerformingGc) << thread->GetState() << " thread " << thread << " self " << self; // Disable the thread-local is_gc_marking flag. // Note a thread that has just started right before this checkpoint may have already this flag @@ -1771,7 +1779,7 @@ void ConcurrentCopying::IssueDisableMarkingCheckpoint() { // Release locks then wait for all mutator threads to pass the barrier. Locks::mutator_lock_->SharedUnlock(self); { - ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun); + ScopedThreadStateChange tsc(self, ThreadState::kWaitingForCheckPointsToRun); gc_barrier_->Increment(self, barrier_count); } Locks::mutator_lock_->SharedLock(self); @@ -2075,7 +2083,7 @@ void ConcurrentCopying::RevokeThreadLocalMarkStacks(bool disable_weak_ref_access } Locks::mutator_lock_->SharedUnlock(self); { - ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun); + ScopedThreadStateChange tsc(self, ThreadState::kWaitingForCheckPointsToRun); gc_barrier_->Increment(self, barrier_count); } Locks::mutator_lock_->SharedLock(self); diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index ebac36dc7b..e3cd1eefe9 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -1163,7 +1163,9 @@ class MarkSweep::CheckpointMarkThreadRoots : public Closure, public RootVisitor ScopedTrace trace("Marking thread roots"); // Note: self is not necessarily equal to thread since thread may be suspended. Thread* const self = Thread::Current(); - CHECK(thread == self || thread->IsSuspended() || thread->GetState() == kWaitingPerformingGc) + CHECK(thread == self || + thread->IsSuspended() || + thread->GetState() == ThreadState::kWaitingPerformingGc) << thread->GetState() << " thread " << thread << " self " << self; thread->VisitRoots(this, kVisitRootFlagAllRoots); if (revoke_ros_alloc_thread_local_buffers_at_checkpoint_) { @@ -1197,7 +1199,7 @@ void MarkSweep::MarkRootsCheckpoint(Thread* self, Locks::heap_bitmap_lock_->ExclusiveUnlock(self); Locks::mutator_lock_->SharedUnlock(self); { - ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun); + ScopedThreadStateChange tsc(self, ThreadState::kWaitingForCheckPointsToRun); gc_barrier_->Increment(self, barrier_count); } Locks::mutator_lock_->SharedLock(self); diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index d191031767..0848f34d0b 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -156,13 +156,13 @@ void SemiSpace::MarkingPhase() { Locks::mutator_lock_->AssertExclusiveHeld(self_); // Store the stack traces into the runtime fault string in case we Get a heap corruption // related crash later. - ThreadState old_state = self_->SetStateUnsafe(kRunnable); + ThreadState old_state = self_->SetStateUnsafe(ThreadState::kRunnable); std::ostringstream oss; Runtime* runtime = Runtime::Current(); runtime->GetThreadList()->DumpForSigQuit(oss); runtime->GetThreadList()->DumpNativeStacks(oss); runtime->SetFaultMessage(oss.str()); - CHECK_EQ(self_->SetStateUnsafe(old_state), kRunnable); + CHECK_EQ(self_->SetStateUnsafe(old_state), ThreadState::kRunnable); } // Revoke the thread local buffers since the GC may allocate into a RosAllocSpace and this helps // to prevent fragmentation. diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index 50cfc6ec0b..9017f30fb5 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -57,7 +57,7 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, CheckPreconditionsForAllocObject(klass, byte_count); // Since allocation can cause a GC which will need to SuspendAll, make sure all allocations are // done in the runnable state where suspension is expected. - CHECK_EQ(self->GetState(), kRunnable); + CHECK_EQ(self->GetState(), ThreadState::kRunnable); self->AssertThreadSuspensionIsAllowable(); self->AssertNoPendingException(); // Make sure to preserve klass. diff --git a/runtime/gc/heap-visit-objects-inl.h b/runtime/gc/heap-visit-objects-inl.h index b6ccb277cd..e20d981fa3 100644 --- a/runtime/gc/heap-visit-objects-inl.h +++ b/runtime/gc/heap-visit-objects-inl.h @@ -50,7 +50,7 @@ inline void Heap::VisitObjects(Visitor&& visitor) { // IncrementDisableMovingGC() and threads are suspended. IncrementDisableMovingGC(self); { - ScopedThreadSuspension sts(self, kWaitingForVisitObjects); + ScopedThreadSuspension sts(self, ThreadState::kWaitingForVisitObjects); ScopedSuspendAll ssa(__FUNCTION__); VisitObjectsInternalRegionSpace(visitor); VisitObjectsInternal(visitor); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index cb2a64889a..100754a46d 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -942,7 +942,7 @@ bool Heap::IsCompilingBoot() const { void Heap::IncrementDisableMovingGC(Thread* self) { // Need to do this holding the lock to prevent races where the GC is about to run / running when // we attempt to disable it. - ScopedThreadStateChange tsc(self, kWaitingForGcToComplete); + ScopedThreadStateChange tsc(self, ThreadState::kWaitingForGcToComplete); MutexLock mu(self, *gc_complete_lock_); ++disable_moving_gc_count_; if (IsMovingGc(collector_type_running_)) { @@ -966,7 +966,7 @@ void Heap::IncrementDisableThreadFlip(Thread* self) { // counter. The global counter is incremented only once for a thread for the outermost enter. return; } - ScopedThreadStateChange tsc(self, kWaitingForGcThreadFlip); + ScopedThreadStateChange tsc(self, ThreadState::kWaitingForGcThreadFlip); MutexLock mu(self, *thread_flip_lock_); thread_flip_cond_->CheckSafeToWait(self); bool has_waited = false; @@ -1013,7 +1013,7 @@ void Heap::ThreadFlipBegin(Thread* self) { // Supposed to be called by GC. Set thread_flip_running_ to be true. If disable_thread_flip_count_ // > 0, block. Otherwise, go ahead. CHECK(kUseReadBarrier); - ScopedThreadStateChange tsc(self, kWaitingForGcThreadFlip); + ScopedThreadStateChange tsc(self, ThreadState::kWaitingForGcThreadFlip); MutexLock mu(self, *thread_flip_lock_); thread_flip_cond_->CheckSafeToWait(self); bool has_waited = false; @@ -1559,7 +1559,7 @@ void Heap::TrimIndirectReferenceTables(Thread* self) { // TODO: May also want to look for entirely empty pages maintained by SmallIrtAllocator. Barrier barrier(0); TrimIndirectReferenceTableClosure closure(&barrier); - ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun); + ScopedThreadStateChange tsc(self, ThreadState::kWaitingForCheckPointsToRun); size_t barrier_count = Runtime::Current()->GetThreadList()->RunCheckpoint(&closure); if (barrier_count != 0) { barrier.Increment(self, barrier_count); @@ -1569,7 +1569,7 @@ void Heap::TrimIndirectReferenceTables(Thread* self) { void Heap::StartGC(Thread* self, GcCause cause, CollectorType collector_type) { // Need to do this before acquiring the locks since we don't want to get suspended while // holding any locks. - ScopedThreadStateChange tsc(self, kWaitingForGcToComplete); + ScopedThreadStateChange tsc(self, ThreadState::kWaitingForGcToComplete); MutexLock mu(self, *gc_complete_lock_); // Ensure there is only one GC at a time. WaitForGcToCompleteLocked(cause, self); @@ -2000,7 +2000,7 @@ void Heap::SetTargetHeapUtilization(float target) { size_t Heap::GetObjectsAllocated() const { Thread* const self = Thread::Current(); - ScopedThreadStateChange tsc(self, kWaitingForGetObjectsAllocated); + ScopedThreadStateChange tsc(self, ThreadState::kWaitingForGetObjectsAllocated); // Prevent GC running during GetObjectsAllocated since we may get a checkpoint request that tells // us to suspend while we are doing SuspendAll. b/35232978 gc::ScopedGCCriticalSection gcs(Thread::Current(), @@ -2085,10 +2085,10 @@ HomogeneousSpaceCompactResult Heap::PerformHomogeneousSpaceCompact() { // Inc requested homogeneous space compaction. count_requested_homogeneous_space_compaction_++; // Store performed homogeneous space compaction at a new request arrival. - ScopedThreadStateChange tsc(self, kWaitingPerformingGc); + ScopedThreadStateChange tsc(self, ThreadState::kWaitingPerformingGc); Locks::mutator_lock_->AssertNotHeld(self); { - ScopedThreadStateChange tsc2(self, kWaitingForGcToComplete); + ScopedThreadStateChange tsc2(self, ThreadState::kWaitingForGcToComplete); MutexLock mu(self, *gc_complete_lock_); // Ensure there is only one GC at a time. WaitForGcToCompleteLocked(kGcCauseHomogeneousSpaceCompact, self); @@ -2641,7 +2641,7 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, // here is full GC. } } - ScopedThreadStateChange tsc(self, kWaitingPerformingGc); + ScopedThreadStateChange tsc(self, ThreadState::kWaitingPerformingGc); Locks::mutator_lock_->AssertNotHeld(self); if (self->IsHandlingStackOverflow()) { // If we are throwing a stack overflow error we probably don't have enough remaining stack @@ -2653,7 +2653,7 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, bool compacting_gc; { gc_complete_lock_->AssertNotHeld(self); - ScopedThreadStateChange tsc2(self, kWaitingForGcToComplete); + ScopedThreadStateChange tsc2(self, ThreadState::kWaitingForGcToComplete); MutexLock mu(self, *gc_complete_lock_); // Ensure there is only one GC at a time. WaitForGcToCompleteLocked(gc_cause, self); @@ -3437,7 +3437,7 @@ void Heap::PreSweepingGcVerification(collector::GarbageCollector* gc) { // reachable objects. if (verify_pre_sweeping_heap_) { TimingLogger::ScopedTiming t2("(Paused)PostSweepingVerifyHeapReferences", timings); - CHECK_NE(self->GetState(), kRunnable); + CHECK_NE(self->GetState(), ThreadState::kRunnable); { WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); // Swapping bound bitmaps does nothing. @@ -3501,7 +3501,7 @@ void Heap::RosAllocVerification(TimingLogger* timings, const char* name) { } collector::GcType Heap::WaitForGcToComplete(GcCause cause, Thread* self) { - ScopedThreadStateChange tsc(self, kWaitingForGcToComplete); + ScopedThreadStateChange tsc(self, ThreadState::kWaitingForGcToComplete); MutexLock mu(self, *gc_complete_lock_); return WaitForGcToCompleteLocked(cause, self); } diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc index 7564c8942c..4f7cc71f6b 100644 --- a/runtime/gc/space/dlmalloc_space.cc +++ b/runtime/gc/space/dlmalloc_space.cc @@ -367,7 +367,7 @@ bool DlMallocSpace::LogFragmentationAllocFailure(std::ostream& os, // lock, temporarily release the shared access to the mutator // lock here by transitioning to the suspended state. Locks::mutator_lock_->AssertSharedHeld(self); - ScopedThreadSuspension sts(self, kSuspended); + ScopedThreadSuspension sts(self, ThreadState::kSuspended); Walk(MSpaceChunkCallback, &max_contiguous_allocation); if (failed_alloc_bytes > max_contiguous_allocation) { os << "; failed due to fragmentation (largest possible contiguous allocation " diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index fefba2766c..fe387e20f7 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -1008,7 +1008,7 @@ class ImageSpace::Loader { if (use_parallel) { ScopedTrace trace("Waiting for workers"); // Go to native since we don't want to suspend while holding the mutator lock. - ScopedThreadSuspension sts(Thread::Current(), kNative); + ScopedThreadSuspension sts(Thread::Current(), ThreadState::kNative); pool->Wait(self, true, false); } const uint64_t time = NanoTime() - start; diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc index fc9cad0a05..80430bd26b 100644 --- a/runtime/gc/space/rosalloc_space.cc +++ b/runtime/gc/space/rosalloc_space.cc @@ -396,7 +396,7 @@ void RosAllocSpace::InspectAllRosAlloc(void (*callback)(void *start, void *end, // The mutators are not suspended yet and we have a shared access // to the mutator lock. Temporarily release the shared access by // transitioning to the suspend state, and suspend the mutators. - ScopedThreadSuspension sts(self, kSuspended); + ScopedThreadSuspension sts(self, ThreadState::kSuspended); InspectAllRosAllocWithSuspendAll(callback, arg, do_null_callback_at_end); } else { // The mutators are not suspended yet. Suspend the mutators. diff --git a/runtime/gc/space/space_create_test.cc b/runtime/gc/space/space_create_test.cc index 4849d6c9ee..25bc12e85b 100644 --- a/runtime/gc/space/space_create_test.cc +++ b/runtime/gc/space/space_create_test.cc @@ -168,7 +168,7 @@ TEST_P(SpaceCreateTest, ZygoteSpaceTestBody) { gc::Heap* heap = Runtime::Current()->GetHeap(); space::Space* old_space = space; { - ScopedThreadSuspension sts(self, kSuspended); + ScopedThreadSuspension sts(self, ThreadState::kSuspended); ScopedSuspendAll ssa("Add image space"); heap->RemoveSpace(old_space); } diff --git a/runtime/gc/space/space_test.h b/runtime/gc/space/space_test.h index e40ee50853..4b01e83b38 100644 --- a/runtime/gc/space/space_test.h +++ b/runtime/gc/space/space_test.h @@ -46,7 +46,7 @@ class SpaceTest : public Super { heap->RevokeAllThreadLocalBuffers(); } { - ScopedThreadStateChange sts(Thread::Current(), kSuspended); + ScopedThreadStateChange sts(Thread::Current(), ThreadState::kSuspended); ScopedSuspendAll ssa("Add image space"); heap->AddSpace(space); } @@ -237,7 +237,7 @@ void SpaceTest<Super>::SizeFootPrintGrowthLimitAndTrimBody(MallocSpace* space, size_t free_increment = 96; while (true) { { - ScopedThreadStateChange tsc(self, kNative); + ScopedThreadStateChange tsc(self, ThreadState::kNative); // Give the space a haircut. space->Trim(); } diff --git a/runtime/gc/task_processor.cc b/runtime/gc/task_processor.cc index 64e8322a0d..494cf2bb79 100644 --- a/runtime/gc/task_processor.cc +++ b/runtime/gc/task_processor.cc @@ -39,14 +39,14 @@ TaskProcessor::~TaskProcessor() { } void TaskProcessor::AddTask(Thread* self, HeapTask* task) { - ScopedThreadStateChange tsc(self, kWaitingForTaskProcessor); + ScopedThreadStateChange tsc(self, ThreadState::kWaitingForTaskProcessor); MutexLock mu(self, lock_); tasks_.insert(task); cond_.Signal(self); } HeapTask* TaskProcessor::GetTask(Thread* self) { - ScopedThreadStateChange tsc(self, kWaitingForTaskProcessor); + ScopedThreadStateChange tsc(self, ThreadState::kWaitingForTaskProcessor); MutexLock mu(self, lock_); while (true) { if (tasks_.empty()) { diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc index d35847efe9..cfca51e7cd 100644 --- a/runtime/instrumentation_test.cc +++ b/runtime/instrumentation_test.cc @@ -187,7 +187,7 @@ class InstrumentationTest : public CommonRuntimeTest { void CheckConfigureStubs(const char* key, Instrumentation::InstrumentationLevel level) { ScopedObjectAccess soa(Thread::Current()); instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation(); - ScopedThreadSuspension sts(soa.Self(), kSuspended); + ScopedThreadSuspension sts(soa.Self(), ThreadState::kSuspended); gc::ScopedGCCriticalSection gcs(soa.Self(), gc::kGcCauseInstrumentation, gc::kCollectorTypeInstrumentation); @@ -216,7 +216,7 @@ class InstrumentationTest : public CommonRuntimeTest { instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation(); TestInstrumentationListener listener; { - ScopedThreadSuspension sts(soa.Self(), kSuspended); + ScopedThreadSuspension sts(soa.Self(), ThreadState::kSuspended); ScopedSuspendAll ssa("Add instrumentation listener"); instr->AddListener(&listener, instrumentation_event); } @@ -240,7 +240,7 @@ class InstrumentationTest : public CommonRuntimeTest { listener.Reset(); { - ScopedThreadSuspension sts(soa.Self(), kSuspended); + ScopedThreadSuspension sts(soa.Self(), ThreadState::kSuspended); ScopedSuspendAll ssa("Remove instrumentation listener"); instr->RemoveListener(&listener, instrumentation_event); } @@ -263,7 +263,7 @@ class InstrumentationTest : public CommonRuntimeTest { REQUIRES_SHARED(Locks::mutator_lock_) { Runtime* runtime = Runtime::Current(); instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation(); - ScopedThreadSuspension sts(self, kSuspended); + ScopedThreadSuspension sts(self, ThreadState::kSuspended); gc::ScopedGCCriticalSection gcs(self, gc::kGcCauseInstrumentation, gc::kCollectorTypeInstrumentation); @@ -279,7 +279,7 @@ class InstrumentationTest : public CommonRuntimeTest { REQUIRES_SHARED(Locks::mutator_lock_) { Runtime* runtime = Runtime::Current(); instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation(); - ScopedThreadSuspension sts(self, kSuspended); + ScopedThreadSuspension sts(self, ThreadState::kSuspended); gc::ScopedGCCriticalSection gcs(self, gc::kGcCauseInstrumentation, gc::kCollectorTypeInstrumentation); @@ -294,7 +294,7 @@ class InstrumentationTest : public CommonRuntimeTest { REQUIRES_SHARED(Locks::mutator_lock_) { Runtime* runtime = Runtime::Current(); instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation(); - ScopedThreadSuspension sts(self, kSuspended); + ScopedThreadSuspension sts(self, ThreadState::kSuspended); gc::ScopedGCCriticalSection gcs(self, gc::kGcCauseInstrumentation, gc::kCollectorTypeInstrumentation); @@ -309,7 +309,7 @@ class InstrumentationTest : public CommonRuntimeTest { REQUIRES_SHARED(Locks::mutator_lock_) { Runtime* runtime = Runtime::Current(); instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation(); - ScopedThreadSuspension sts(self, kSuspended); + ScopedThreadSuspension sts(self, ThreadState::kSuspended); gc::ScopedGCCriticalSection gcs(self, gc::kGcCauseInstrumentation, gc::kCollectorTypeInstrumentation); @@ -324,7 +324,7 @@ class InstrumentationTest : public CommonRuntimeTest { REQUIRES_SHARED(Locks::mutator_lock_) { Runtime* runtime = Runtime::Current(); instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation(); - ScopedThreadSuspension sts(self, kSuspended); + ScopedThreadSuspension sts(self, ThreadState::kSuspended); gc::ScopedGCCriticalSection gcs(self, gc::kGcCauseInstrumentation, gc::kCollectorTypeInstrumentation); @@ -336,7 +336,7 @@ class InstrumentationTest : public CommonRuntimeTest { REQUIRES_SHARED(Locks::mutator_lock_) { Runtime* runtime = Runtime::Current(); instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation(); - ScopedThreadSuspension sts(self, kSuspended); + ScopedThreadSuspension sts(self, ThreadState::kSuspended); gc::ScopedGCCriticalSection gcs(self, gc::kGcCauseInstrumentation, gc::kCollectorTypeInstrumentation); diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc index 6af04550a4..25c45cba6a 100644 --- a/runtime/intern_table.cc +++ b/runtime/intern_table.cc @@ -187,7 +187,7 @@ void InternTable::BroadcastForNewInterns() { void InternTable::WaitUntilAccessible(Thread* self) { Locks::intern_table_lock_->ExclusiveUnlock(self); { - ScopedThreadSuspension sts(self, kWaitingWeakGcRootRead); + ScopedThreadSuspension sts(self, ThreadState::kWaitingWeakGcRootRead); MutexLock mu(self, *Locks::intern_table_lock_); while ((!kUseReadBarrier && weak_root_state_ == gc::kWeakRootStateNoReadsOrWrites) || (kUseReadBarrier && !self->GetWeakRefAccessEnabled())) { diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 299892ec92..0cce09ee9e 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -61,7 +61,7 @@ static void InterpreterJni(Thread* self, soa.AddLocalReference<jclass>(method->GetDeclaringClass())); jobject jresult; { - ScopedThreadStateChange tsc(self, kNative); + ScopedThreadStateChange tsc(self, ThreadState::kNative); jresult = fn(soa.Env(), klass.get()); } result->SetL(soa.Decode<mirror::Object>(jresult)); @@ -70,28 +70,28 @@ static void InterpreterJni(Thread* self, fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni()); ScopedLocalRef<jclass> klass(soa.Env(), soa.AddLocalReference<jclass>(method->GetDeclaringClass())); - ScopedThreadStateChange tsc(self, kNative); + ScopedThreadStateChange tsc(self, ThreadState::kNative); fn(soa.Env(), klass.get()); } else if (shorty == "Z") { using fntype = jboolean(JNIEnv*, jclass); fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni()); ScopedLocalRef<jclass> klass(soa.Env(), soa.AddLocalReference<jclass>(method->GetDeclaringClass())); - ScopedThreadStateChange tsc(self, kNative); + ScopedThreadStateChange tsc(self, ThreadState::kNative); result->SetZ(fn(soa.Env(), klass.get())); } else if (shorty == "BI") { using fntype = jbyte(JNIEnv*, jclass, jint); fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni()); ScopedLocalRef<jclass> klass(soa.Env(), soa.AddLocalReference<jclass>(method->GetDeclaringClass())); - ScopedThreadStateChange tsc(self, kNative); + ScopedThreadStateChange tsc(self, ThreadState::kNative); result->SetB(fn(soa.Env(), klass.get(), args[0])); } else if (shorty == "II") { using fntype = jint(JNIEnv*, jclass, jint); fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni()); ScopedLocalRef<jclass> klass(soa.Env(), soa.AddLocalReference<jclass>(method->GetDeclaringClass())); - ScopedThreadStateChange tsc(self, kNative); + ScopedThreadStateChange tsc(self, ThreadState::kNative); result->SetI(fn(soa.Env(), klass.get(), args[0])); } else if (shorty == "LL") { using fntype = jobject(JNIEnv*, jclass, jobject); @@ -102,7 +102,7 @@ static void InterpreterJni(Thread* self, soa.AddLocalReference<jobject>(ObjArg(args[0]))); jobject jresult; { - ScopedThreadStateChange tsc(self, kNative); + ScopedThreadStateChange tsc(self, ThreadState::kNative); jresult = fn(soa.Env(), klass.get(), arg0.get()); } result->SetL(soa.Decode<mirror::Object>(jresult)); @@ -111,7 +111,7 @@ static void InterpreterJni(Thread* self, fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni()); ScopedLocalRef<jclass> klass(soa.Env(), soa.AddLocalReference<jclass>(method->GetDeclaringClass())); - ScopedThreadStateChange tsc(self, kNative); + ScopedThreadStateChange tsc(self, ThreadState::kNative); result->SetI(fn(soa.Env(), klass.get(), args[0], args[1])); } else if (shorty == "ILI") { using fntype = jint(JNIEnv*, jclass, jobject, jint); @@ -121,7 +121,7 @@ static void InterpreterJni(Thread* self, soa.AddLocalReference<jclass>(method->GetDeclaringClass())); ScopedLocalRef<jobject> arg0(soa.Env(), soa.AddLocalReference<jobject>(ObjArg(args[0]))); - ScopedThreadStateChange tsc(self, kNative); + ScopedThreadStateChange tsc(self, ThreadState::kNative); result->SetI(fn(soa.Env(), klass.get(), arg0.get(), args[1])); } else if (shorty == "SIZ") { using fntype = jshort(JNIEnv*, jclass, jint, jboolean); @@ -129,14 +129,14 @@ static void InterpreterJni(Thread* self, reinterpret_cast<fntype*>(const_cast<void*>(method->GetEntryPointFromJni())); ScopedLocalRef<jclass> klass(soa.Env(), soa.AddLocalReference<jclass>(method->GetDeclaringClass())); - ScopedThreadStateChange tsc(self, kNative); + ScopedThreadStateChange tsc(self, ThreadState::kNative); result->SetS(fn(soa.Env(), klass.get(), args[0], args[1])); } else if (shorty == "VIZ") { using fntype = void(JNIEnv*, jclass, jint, jboolean); fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni()); ScopedLocalRef<jclass> klass(soa.Env(), soa.AddLocalReference<jclass>(method->GetDeclaringClass())); - ScopedThreadStateChange tsc(self, kNative); + ScopedThreadStateChange tsc(self, ThreadState::kNative); fn(soa.Env(), klass.get(), args[0], args[1]); } else if (shorty == "ZLL") { using fntype = jboolean(JNIEnv*, jclass, jobject, jobject); @@ -147,7 +147,7 @@ static void InterpreterJni(Thread* self, soa.AddLocalReference<jobject>(ObjArg(args[0]))); ScopedLocalRef<jobject> arg1(soa.Env(), soa.AddLocalReference<jobject>(ObjArg(args[1]))); - ScopedThreadStateChange tsc(self, kNative); + ScopedThreadStateChange tsc(self, ThreadState::kNative); result->SetZ(fn(soa.Env(), klass.get(), arg0.get(), arg1.get())); } else if (shorty == "ZILL") { using fntype = jboolean(JNIEnv*, jclass, jint, jobject, jobject); @@ -158,7 +158,7 @@ static void InterpreterJni(Thread* self, soa.AddLocalReference<jobject>(ObjArg(args[1]))); ScopedLocalRef<jobject> arg2(soa.Env(), soa.AddLocalReference<jobject>(ObjArg(args[2]))); - ScopedThreadStateChange tsc(self, kNative); + ScopedThreadStateChange tsc(self, ThreadState::kNative); result->SetZ(fn(soa.Env(), klass.get(), args[0], arg1.get(), arg2.get())); } else if (shorty == "VILII") { using fntype = void(JNIEnv*, jclass, jint, jobject, jint, jint); @@ -167,7 +167,7 @@ static void InterpreterJni(Thread* self, soa.AddLocalReference<jclass>(method->GetDeclaringClass())); ScopedLocalRef<jobject> arg1(soa.Env(), soa.AddLocalReference<jobject>(ObjArg(args[1]))); - ScopedThreadStateChange tsc(self, kNative); + ScopedThreadStateChange tsc(self, ThreadState::kNative); fn(soa.Env(), klass.get(), args[0], arg1.get(), args[2], args[3]); } else if (shorty == "VLILII") { using fntype = void(JNIEnv*, jclass, jobject, jint, jobject, jint, jint); @@ -178,7 +178,7 @@ static void InterpreterJni(Thread* self, soa.AddLocalReference<jobject>(ObjArg(args[0]))); ScopedLocalRef<jobject> arg2(soa.Env(), soa.AddLocalReference<jobject>(ObjArg(args[2]))); - ScopedThreadStateChange tsc(self, kNative); + ScopedThreadStateChange tsc(self, ThreadState::kNative); fn(soa.Env(), klass.get(), arg0.get(), args[1], arg2.get(), args[3], args[4]); } else { LOG(FATAL) << "Do something with static native method: " << method->PrettyMethod() @@ -192,7 +192,7 @@ static void InterpreterJni(Thread* self, soa.AddLocalReference<jobject>(receiver)); jobject jresult; { - ScopedThreadStateChange tsc(self, kNative); + ScopedThreadStateChange tsc(self, ThreadState::kNative); jresult = fn(soa.Env(), rcvr.get()); } result->SetL(soa.Decode<mirror::Object>(jresult)); @@ -201,7 +201,7 @@ static void InterpreterJni(Thread* self, fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni()); ScopedLocalRef<jobject> rcvr(soa.Env(), soa.AddLocalReference<jobject>(receiver)); - ScopedThreadStateChange tsc(self, kNative); + ScopedThreadStateChange tsc(self, ThreadState::kNative); fn(soa.Env(), rcvr.get()); } else if (shorty == "LL") { using fntype = jobject(JNIEnv*, jobject, jobject); @@ -212,17 +212,17 @@ static void InterpreterJni(Thread* self, soa.AddLocalReference<jobject>(ObjArg(args[0]))); jobject jresult; { - ScopedThreadStateChange tsc(self, kNative); + ScopedThreadStateChange tsc(self, ThreadState::kNative); jresult = fn(soa.Env(), rcvr.get(), arg0.get()); } result->SetL(soa.Decode<mirror::Object>(jresult)); - ScopedThreadStateChange tsc(self, kNative); + ScopedThreadStateChange tsc(self, ThreadState::kNative); } else if (shorty == "III") { using fntype = jint(JNIEnv*, jobject, jint, jint); fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni()); ScopedLocalRef<jobject> rcvr(soa.Env(), soa.AddLocalReference<jobject>(receiver)); - ScopedThreadStateChange tsc(self, kNative); + ScopedThreadStateChange tsc(self, ThreadState::kNative); result->SetI(fn(soa.Env(), rcvr.get(), args[0], args[1])); } else { LOG(FATAL) << "Do something with native method: " << method->PrettyMethod() diff --git a/runtime/interpreter/mterp/arm64ng/main.S b/runtime/interpreter/mterp/arm64ng/main.S index ffd1b13805..53792c0921 100644 --- a/runtime/interpreter/mterp/arm64ng/main.S +++ b/runtime/interpreter/mterp/arm64ng/main.S @@ -309,8 +309,8 @@ END \name add x2, x2, #-1 strh w2, [x0, #ART_METHOD_HOTNESS_COUNT_OFFSET] // Otherwise, do a suspend check. - ldr x0, [xSELF, #THREAD_FLAGS_OFFSET] - ands x0, x0, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST + ldr w0, [xSELF, #THREAD_FLAGS_OFFSET] + tst w0, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST b.eq 1b EXPORT_PC bl art_quick_test_suspend @@ -435,8 +435,8 @@ END \name cbz w2, 2f add x2, x2, #-1 strh w2, [x0, #ART_METHOD_HOTNESS_COUNT_OFFSET] - ldr x0, [xSELF, #THREAD_FLAGS_OFFSET] - tst x0, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST + ldr w0, [xSELF, #THREAD_FLAGS_OFFSET] + tst w0, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST b.ne 3f 1: FETCH_INST diff --git a/runtime/interpreter/mterp/armng/main.S b/runtime/interpreter/mterp/armng/main.S index e21c64f8f9..e26d9222fa 100644 --- a/runtime/interpreter/mterp/armng/main.S +++ b/runtime/interpreter/mterp/armng/main.S @@ -318,7 +318,7 @@ END \name strh r2, [r0, #ART_METHOD_HOTNESS_COUNT_OFFSET] // Otherwise, do a suspend check. ldr r0, [rSELF, #THREAD_FLAGS_OFFSET] - ands r0, r0, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST + tst r0, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST beq 1b EXPORT_PC bl art_quick_test_suspend diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 3fcb10abad..5cf08f9107 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -568,7 +568,7 @@ void JitCodeCache::WaitUntilInlineCacheAccessible(Thread* self) { if (IsWeakAccessEnabled(self)) { return; } - ScopedThreadSuspension sts(self, kWaitingWeakGcRootRead); + ScopedThreadSuspension sts(self, ThreadState::kWaitingWeakGcRootRead); MutexLock mu(self, *Locks::jit_lock_); while (!IsWeakAccessEnabled(self)) { inline_cache_cond_.Wait(self); @@ -625,7 +625,7 @@ void JitCodeCache::WaitForPotentialCollectionToCompleteRunnable(Thread* self) { while (collection_in_progress_) { Locks::jit_lock_->Unlock(self); { - ScopedThreadSuspension sts(self, kSuspended); + ScopedThreadSuspension sts(self, ThreadState::kSuspended); MutexLock mu(self, *Locks::jit_lock_); WaitForPotentialCollectionToComplete(self); } @@ -943,7 +943,7 @@ bool JitCodeCache::Reserve(Thread* self, while (true) { bool at_max_capacity = false; { - ScopedThreadSuspension sts(self, kSuspended); + ScopedThreadSuspension sts(self, ThreadState::kSuspended); MutexLock mu(self, *Locks::jit_lock_); WaitForPotentialCollectionToComplete(self); ScopedCodeCacheWrite ccw(*region); @@ -1069,7 +1069,7 @@ void JitCodeCache::MarkCompiledCodeOnThreadStacks(Thread* self) { threads_running_checkpoint = Runtime::Current()->GetThreadList()->RunCheckpoint(&closure); // Now that we have run our checkpoint, move to a suspended state and wait // for other threads to run the checkpoint. - ScopedThreadSuspension sts(self, kSuspended); + ScopedThreadSuspension sts(self, ThreadState::kSuspended); if (threads_running_checkpoint != 0) { barrier.Increment(self, threads_running_checkpoint); } @@ -1100,7 +1100,7 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { ScopedTrace trace(__FUNCTION__); // Wait for an existing collection, or let everyone know we are starting one. { - ScopedThreadSuspension sts(self, kSuspended); + ScopedThreadSuspension sts(self, ThreadState::kSuspended); MutexLock mu(self, *Locks::jit_lock_); if (!garbage_collect_code_) { private_region_.IncreaseCodeCacheCapacity(); diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index a5cb81be82..cea654fe17 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -788,7 +788,7 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods(bool startup) { // Release the mutator lock. We shall need to re-acquire the lock for a moment to // destroy the `VariableSizedHandleScope` inside the `helper` which shall be // conveniently handled by destroying `sts`, then `helper` and then `soa`. - ScopedThreadSuspension sts(self, kNative); + ScopedThreadSuspension sts(self, ThreadState::kNative); // Get back to the previous thread priority. We shall not increase the priority // for the short time we need to re-acquire mutator lock for `helper` destructor. sdp.reset(); diff --git a/runtime/jni/java_vm_ext.cc b/runtime/jni/java_vm_ext.cc index 053afc96e3..dd45c32e02 100644 --- a/runtime/jni/java_vm_ext.cc +++ b/runtime/jni/java_vm_ext.cc @@ -285,7 +285,7 @@ class Libraries { const char* shorty = m->GetShorty(); { // Go to suspended since dlsym may block for a long time if other threads are using dlopen. - ScopedThreadSuspension sts(self, kNative); + ScopedThreadSuspension sts(self, ThreadState::kNative); void* native_code = FindNativeMethodInternal(self, declaring_class_loader_allocator, shorty, @@ -353,7 +353,7 @@ class Libraries { } } } - ScopedThreadSuspension sts(self, kNative); + ScopedThreadSuspension sts(self, ThreadState::kNative); // Do this without holding the jni libraries lock to prevent possible deadlocks. UnloadLibraries(self->GetJniEnv()->GetVm(), unload_libraries); for (auto library : unload_libraries) { @@ -575,7 +575,7 @@ void JavaVMExt::JniAbort(const char* jni_function_name, const char* msg) { check_jni_abort_hook_(check_jni_abort_hook_data_, os.str()); } else { // Ensure that we get a native stack trace for this thread. - ScopedThreadSuspension sts(self, kNative); + ScopedThreadSuspension sts(self, ThreadState::kNative); LOG(FATAL) << os.str(); UNREACHABLE(); } diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index 2476b135ff..c679fded32 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -100,7 +100,7 @@ inline void Object::NotifyAll(Thread* self) { } inline void Object::Wait(Thread* self, int64_t ms, int32_t ns) { - Monitor::Wait(self, this, ms, ns, true, kTimedWaiting); + Monitor::Wait(self, this, ms, ns, true, ThreadState::kTimedWaiting); } inline uint32_t Object::GetMarkBit() { diff --git a/runtime/monitor.cc b/runtime/monitor.cc index f2189e16ac..0cad79b6e3 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -515,7 +515,8 @@ void Monitor::Lock(Thread* self) { } self->SetMonitorEnterObject(GetObject().Ptr()); { - ScopedThreadSuspension tsc(self, kBlocked); // Change to blocked and give up mutator_lock_. + // Change to blocked and give up mutator_lock_. + ScopedThreadSuspension tsc(self, ThreadState::kBlocked); // Acquire monitor_lock_ without mutator_lock_, expecting to block this time. // We already tried spinning above. The shutdown procedure currently assumes we stop @@ -827,7 +828,9 @@ void Monitor::SignalWaiterAndReleaseMonitorLock(Thread* self) { void Monitor::Wait(Thread* self, int64_t ms, int32_t ns, bool interruptShouldThrow, ThreadState why) { DCHECK(self != nullptr); - DCHECK(why == kTimedWaiting || why == kWaiting || why == kSleeping); + DCHECK(why == ThreadState::kTimedWaiting || + why == ThreadState::kWaiting || + why == ThreadState::kSleeping); // Make sure that we hold the lock. if (owner_.load(std::memory_order_relaxed) != self) { @@ -837,8 +840,8 @@ void Monitor::Wait(Thread* self, int64_t ms, int32_t ns, // We need to turn a zero-length timed wait into a regular wait because // Object.wait(0, 0) is defined as Object.wait(0), which is defined as Object.wait(). - if (why == kTimedWaiting && (ms == 0 && ns == 0)) { - why = kWaiting; + if (why == ThreadState::kTimedWaiting && (ms == 0 && ns == 0)) { + why = ThreadState::kWaiting; } // Enforce the timeout range. @@ -900,10 +903,10 @@ void Monitor::Wait(Thread* self, int64_t ms, int32_t ns, was_interrupted = true; } else { // Wait for a notification or a timeout to occur. - if (why == kWaiting) { + if (why == ThreadState::kWaiting) { self->GetWaitConditionVariable()->Wait(self); } else { - DCHECK(why == kTimedWaiting || why == kSleeping) << why; + DCHECK(why == ThreadState::kTimedWaiting || why == ThreadState::kSleeping) << why; timed_out = self->GetWaitConditionVariable()->TimedWait(self, ms, ns); } was_interrupted = self->IsInterrupted(); @@ -1065,7 +1068,7 @@ void Monitor::InflateThinLocked(Thread* self, Handle<mirror::Object> obj, LockWo bool timed_out; Thread* owner; { - ScopedThreadSuspension sts(self, kWaitingForLockInflation); + ScopedThreadSuspension sts(self, ThreadState::kWaitingForLockInflation); owner = thread_list->SuspendThreadByThreadId(owner_thread_id, SuspendReason::kInternal, &timed_out); @@ -1388,9 +1391,9 @@ ThreadState Monitor::FetchState(const Thread* thread, ThreadState state = thread->GetState(); switch (state) { - case kWaiting: - case kTimedWaiting: - case kSleeping: + case ThreadState::kWaiting: + case ThreadState::kTimedWaiting: + case ThreadState::kSleeping: { Thread* self = Thread::Current(); MutexLock mu(self, *thread->GetWaitMutex()); @@ -1401,8 +1404,8 @@ ThreadState Monitor::FetchState(const Thread* thread, } break; - case kBlocked: - case kWaitingForLockInflation: + case ThreadState::kBlocked: + case ThreadState::kWaitingForLockInflation: { ObjPtr<mirror::Object> lock_object = thread->GetMonitorEnterObject(); if (lock_object != nullptr) { diff --git a/runtime/monitor_objects_stack_visitor.cc b/runtime/monitor_objects_stack_visitor.cc index 61f4159a90..2e75e37bd1 100644 --- a/runtime/monitor_objects_stack_visitor.cc +++ b/runtime/monitor_objects_stack_visitor.cc @@ -47,16 +47,16 @@ bool MonitorObjectsStackVisitor::VisitFrame() { &monitor_object, &lock_owner_tid); switch (state) { - case kWaiting: - case kTimedWaiting: + case ThreadState::kWaiting: + case ThreadState::kTimedWaiting: VisitWaitingObject(monitor_object, state); break; - case kSleeping: + case ThreadState::kSleeping: VisitSleepingObject(monitor_object); break; - case kBlocked: - case kWaitingForLockInflation: + case ThreadState::kBlocked: + case ThreadState::kWaitingForLockInflation: VisitBlockedOnObject(monitor_object, state, lock_owner_tid); break; diff --git a/runtime/monitor_test.cc b/runtime/monitor_test.cc index 8ddd50f031..66008f3ff2 100644 --- a/runtime/monitor_test.cc +++ b/runtime/monitor_test.cc @@ -265,7 +265,7 @@ static void CommonWaitSetup(MonitorTest* test, ClassLinker* class_linker, uint64 } // Need to drop the mutator lock to allow barriers. - ScopedThreadSuspension sts(soa.Self(), kNative); + ScopedThreadSuspension sts(soa.Self(), ThreadState::kNative); ThreadPool thread_pool(pool_name, 3); thread_pool.AddTask(self, new CreateTask(test, create_sleep, c_millis, c_expected)); if (interrupt) { @@ -361,7 +361,7 @@ TEST_F(MonitorTest, TestTryLock) { // Test failure case. thread_pool.AddTask(self, new TryLockTask(obj1)); thread_pool.StartWorkers(self); - ScopedThreadSuspension sts(self, kSuspended); + ScopedThreadSuspension sts(self, ThreadState::kSuspended); thread_pool.Wait(Thread::Current(), /*do_work=*/false, /*may_hold_locks=*/false); } // Test that the trylock actually locks the object. diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc index e88516e248..71078c9ad2 100644 --- a/runtime/native/dalvik_system_VMStack.cc +++ b/runtime/native/dalvik_system_VMStack.cc @@ -55,7 +55,7 @@ static ResultT GetThreadStack(const ScopedFastNativeObjectAccess& soa, return nullptr; } // Suspend thread to build stack trace. - ScopedThreadSuspension sts(soa.Self(), kNative); + ScopedThreadSuspension sts(soa.Self(), ThreadState::kNative); ThreadList* thread_list = Runtime::Current()->GetThreadList(); bool timed_out; Thread* thread = thread_list->SuspendThreadByPeer(peer, diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc index c3b4fe09de..570c554782 100644 --- a/runtime/native/java_lang_Thread.cc +++ b/runtime/native/java_lang_Thread.cc @@ -71,41 +71,43 @@ static jint Thread_nativeGetStatus(JNIEnv* env, jobject java_thread, jboolean ha const jint kJavaTerminated = 5; ScopedObjectAccess soa(env); - ThreadState internal_thread_state = (has_been_started ? kTerminated : kStarting); + ThreadState internal_thread_state = + (has_been_started ? ThreadState::kTerminated : ThreadState::kStarting); MutexLock mu(soa.Self(), *Locks::thread_list_lock_); Thread* thread = Thread::FromManagedThread(soa, java_thread); if (thread != nullptr) { internal_thread_state = thread->GetState(); } switch (internal_thread_state) { - case kTerminated: return kJavaTerminated; - case kRunnable: return kJavaRunnable; - case kTimedWaiting: return kJavaTimedWaiting; - case kSleeping: return kJavaTimedWaiting; - case kBlocked: return kJavaBlocked; - case kWaiting: return kJavaWaiting; - case kStarting: return kJavaNew; - case kNative: return kJavaRunnable; - case kWaitingForTaskProcessor: return kJavaWaiting; - case kWaitingForLockInflation: return kJavaWaiting; - case kWaitingForGcToComplete: return kJavaWaiting; - case kWaitingPerformingGc: return kJavaWaiting; - case kWaitingForCheckPointsToRun: return kJavaWaiting; - case kWaitingForDebuggerSend: return kJavaWaiting; - case kWaitingForDebuggerToAttach: return kJavaWaiting; - case kWaitingInMainDebuggerLoop: return kJavaWaiting; - case kWaitingForDebuggerSuspension: return kJavaWaiting; - case kWaitingForDeoptimization: return kJavaWaiting; - case kWaitingForGetObjectsAllocated: return kJavaWaiting; - case kWaitingForJniOnLoad: return kJavaWaiting; - case kWaitingForSignalCatcherOutput: return kJavaWaiting; - case kWaitingInMainSignalCatcherLoop: return kJavaWaiting; - case kWaitingForMethodTracingStart: return kJavaWaiting; - case kWaitingForVisitObjects: return kJavaWaiting; - case kWaitingWeakGcRootRead: return kJavaRunnable; - case kWaitingForGcThreadFlip: return kJavaWaiting; - case kNativeForAbort: return kJavaWaiting; - case kSuspended: return kJavaRunnable; + case ThreadState::kTerminated: return kJavaTerminated; + case ThreadState::kRunnable: return kJavaRunnable; + case ThreadState::kObsoleteRunnable: break; // Obsolete value. + case ThreadState::kTimedWaiting: return kJavaTimedWaiting; + case ThreadState::kSleeping: return kJavaTimedWaiting; + case ThreadState::kBlocked: return kJavaBlocked; + case ThreadState::kWaiting: return kJavaWaiting; + case ThreadState::kStarting: return kJavaNew; + case ThreadState::kNative: return kJavaRunnable; + case ThreadState::kWaitingForTaskProcessor: return kJavaWaiting; + case ThreadState::kWaitingForLockInflation: return kJavaWaiting; + case ThreadState::kWaitingForGcToComplete: return kJavaWaiting; + case ThreadState::kWaitingPerformingGc: return kJavaWaiting; + case ThreadState::kWaitingForCheckPointsToRun: return kJavaWaiting; + case ThreadState::kWaitingForDebuggerSend: return kJavaWaiting; + case ThreadState::kWaitingForDebuggerToAttach: return kJavaWaiting; + case ThreadState::kWaitingInMainDebuggerLoop: return kJavaWaiting; + case ThreadState::kWaitingForDebuggerSuspension: return kJavaWaiting; + case ThreadState::kWaitingForDeoptimization: return kJavaWaiting; + case ThreadState::kWaitingForGetObjectsAllocated: return kJavaWaiting; + case ThreadState::kWaitingForJniOnLoad: return kJavaWaiting; + case ThreadState::kWaitingForSignalCatcherOutput: return kJavaWaiting; + case ThreadState::kWaitingInMainSignalCatcherLoop: return kJavaWaiting; + case ThreadState::kWaitingForMethodTracingStart: return kJavaWaiting; + case ThreadState::kWaitingForVisitObjects: return kJavaWaiting; + case ThreadState::kWaitingWeakGcRootRead: return kJavaRunnable; + case ThreadState::kWaitingForGcThreadFlip: return kJavaWaiting; + case ThreadState::kNativeForAbort: return kJavaWaiting; + case ThreadState::kSuspended: return kJavaRunnable; // Don't add a 'default' here so the compiler can spot incompatible enum changes. } LOG(ERROR) << "Unexpected thread state: " << internal_thread_state; @@ -180,7 +182,7 @@ static void Thread_setPriority0(JNIEnv* env, jobject java_thread, jint new_prior static void Thread_sleep(JNIEnv* env, jclass, jobject java_lock, jlong ms, jint ns) { ScopedFastNativeObjectAccess soa(env); ObjPtr<mirror::Object> lock = soa.Decode<mirror::Object>(java_lock); - Monitor::Wait(Thread::Current(), lock.Ptr(), ms, ns, true, kSleeping); + Monitor::Wait(Thread::Current(), lock.Ptr(), ms, ns, true, ThreadState::kSleeping); } /* diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc index f74e1206f5..081ec2043a 100644 --- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc @@ -109,38 +109,40 @@ static constexpr uint8_t ToJdwpThreadStatus(ThreadState state) { TS_WAIT = 4, // (in Object.wait()) }; switch (state) { - case kBlocked: + case ThreadState::kBlocked: return TS_MONITOR; - case kNative: - case kRunnable: - case kSuspended: + case ThreadState::kNative: + case ThreadState::kRunnable: + case ThreadState::kSuspended: return TS_RUNNING; - case kSleeping: + case ThreadState::kObsoleteRunnable: + break; // Obsolete value. + case ThreadState::kSleeping: return TS_SLEEPING; - case kStarting: - case kTerminated: + case ThreadState::kStarting: + case ThreadState::kTerminated: return TS_ZOMBIE; - case kTimedWaiting: - case kWaitingForTaskProcessor: - case kWaitingForLockInflation: - case kWaitingForCheckPointsToRun: - case kWaitingForDebuggerSend: - case kWaitingForDebuggerSuspension: - case kWaitingForDebuggerToAttach: - case kWaitingForDeoptimization: - case kWaitingForGcToComplete: - case kWaitingForGetObjectsAllocated: - case kWaitingForJniOnLoad: - case kWaitingForMethodTracingStart: - case kWaitingForSignalCatcherOutput: - case kWaitingForVisitObjects: - case kWaitingInMainDebuggerLoop: - case kWaitingInMainSignalCatcherLoop: - case kWaitingPerformingGc: - case kWaitingWeakGcRootRead: - case kWaitingForGcThreadFlip: - case kNativeForAbort: - case kWaiting: + case ThreadState::kTimedWaiting: + case ThreadState::kWaitingForTaskProcessor: + case ThreadState::kWaitingForLockInflation: + case ThreadState::kWaitingForCheckPointsToRun: + case ThreadState::kWaitingForDebuggerSend: + case ThreadState::kWaitingForDebuggerSuspension: + case ThreadState::kWaitingForDebuggerToAttach: + case ThreadState::kWaitingForDeoptimization: + case ThreadState::kWaitingForGcToComplete: + case ThreadState::kWaitingForGetObjectsAllocated: + case ThreadState::kWaitingForJniOnLoad: + case ThreadState::kWaitingForMethodTracingStart: + case ThreadState::kWaitingForSignalCatcherOutput: + case ThreadState::kWaitingForVisitObjects: + case ThreadState::kWaitingInMainDebuggerLoop: + case ThreadState::kWaitingInMainSignalCatcherLoop: + case ThreadState::kWaitingPerformingGc: + case ThreadState::kWaitingWeakGcRootRead: + case ThreadState::kWaitingForGcThreadFlip: + case ThreadState::kNativeForAbort: + case ThreadState::kWaiting: return TS_WAIT; // Don't add a 'default' here so the compiler can spot incompatible enum changes. } diff --git a/runtime/native/scoped_fast_native_object_access-inl.h b/runtime/native/scoped_fast_native_object_access-inl.h index 20ff76ea27..0b8ad117d2 100644 --- a/runtime/native/scoped_fast_native_object_access-inl.h +++ b/runtime/native/scoped_fast_native_object_access-inl.h @@ -29,7 +29,7 @@ inline ScopedFastNativeObjectAccess::ScopedFastNativeObjectAccess(JNIEnv* env) Locks::mutator_lock_->AssertSharedHeld(Self()); DCHECK((*Self()->GetManagedStack()->GetTopQuickFrame())->IsFastNative()); // Don't work with raw objects in non-runnable states. - DCHECK_EQ(Self()->GetState(), kRunnable); + DCHECK_EQ(Self()->GetState(), ThreadState::kRunnable); } } // namespace art diff --git a/runtime/oat.h b/runtime/oat.h index 0b6bf7db91..8a97cddd71 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr std::array<uint8_t, 4> kOatMagic { { 'o', 'a', 't', '\n' } }; - // Last oat version changed reason: JNI: Rewrite locking for synchronized methods. - static constexpr std::array<uint8_t, 4> kOatVersion { { '2', '1', '3', '\0' } }; + // Last oat version changed reason: Always access thread state and flags as a 32-bit location. + static constexpr std::array<uint8_t, 4> kOatVersion { { '2', '1', '4', '\0' } }; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; static constexpr const char* kDebuggableKey = "debuggable"; diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index 7166dd15c5..dfa4518120 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -263,7 +263,7 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( // Add image space has a race condition since other threads could be reading from the // spaces array. { - ScopedThreadSuspension sts(self, kSuspended); + ScopedThreadSuspension sts(self, ThreadState::kSuspended); gc::ScopedGCCriticalSection gcs(self, gc::kGcCauseAddRemoveAppImageSpace, gc::kCollectorTypeAddRemoveAppImageSpace); @@ -290,7 +290,7 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( LOG(INFO) << "Failed to add image file " << temp_error_msg; dex_files.clear(); { - ScopedThreadSuspension sts(self, kSuspended); + ScopedThreadSuspension sts(self, ThreadState::kSuspended); gc::ScopedGCCriticalSection gcs(self, gc::kGcCauseAddRemoveAppImageSpace, gc::kCollectorTypeAddRemoveAppImageSpace); diff --git a/runtime/object_lock.cc b/runtime/object_lock.cc index 744bc42858..9e02484f15 100644 --- a/runtime/object_lock.cc +++ b/runtime/object_lock.cc @@ -35,7 +35,7 @@ ObjectLock<T>::~ObjectLock() { template <typename T> void ObjectLock<T>::WaitIgnoringInterrupts() { - Monitor::Wait(self_, obj_.Get(), 0, 0, false, kWaiting); + Monitor::Wait(self_, obj_.Get(), 0, 0, false, ThreadState::kWaiting); } template <typename T> diff --git a/runtime/reflection.cc b/runtime/reflection.cc index f642bcb092..a7290a2919 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -879,7 +879,7 @@ ObjPtr<mirror::Object> BoxPrimitive(Primitive::Type src_class, const JValue& val } ScopedObjectAccessUnchecked soa(Thread::Current()); - DCHECK_EQ(soa.Self()->GetState(), kRunnable); + DCHECK_EQ(soa.Self()->GetState(), ThreadState::kRunnable); ArgArray arg_array(shorty, 2); JValue result; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 0f6b521ad1..bbc649263c 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -654,7 +654,7 @@ void Runtime::Abort(const char* msg) { { // Ensure that we don't have multiple threads trying to abort at once, // which would result in significantly worse diagnostics. - ScopedThreadStateChange tsc(Thread::Current(), kNativeForAbort); + ScopedThreadStateChange tsc(Thread::Current(), ThreadState::kNativeForAbort); Locks::abort_lock_->ExclusiveLock(Thread::Current()); } @@ -718,7 +718,7 @@ void Runtime::PostZygoteFork() { void Runtime::CallExitHook(jint status) { if (exit_ != nullptr) { - ScopedThreadStateChange tsc(Thread::Current(), kNative); + ScopedThreadStateChange tsc(Thread::Current(), ThreadState::kNative); exit_(status); LOG(WARNING) << "Exit hook returned instead of exiting!"; } @@ -883,7 +883,7 @@ bool Runtime::Start() { // Restore main thread state to kNative as expected by native code. Thread* self = Thread::Current(); - self->TransitionFromRunnableToSuspended(kNative); + self->TransitionFromRunnableToSuspended(ThreadState::kNative); started_ = true; @@ -990,7 +990,7 @@ bool Runtime::Start() { finished_starting_ = true; if (trace_config_.get() != nullptr && trace_config_->trace_file != "") { - ScopedThreadStateChange tsc(self, kWaitingForMethodTracingStart); + ScopedThreadStateChange tsc(self, ThreadState::kWaitingForMethodTracingStart); Trace::Start(trace_config_->trace_file.c_str(), static_cast<int>(trace_config_->trace_file_size), 0, @@ -1195,7 +1195,7 @@ void Runtime::StartDaemonThreads() { Thread* self = Thread::Current(); // Must be in the kNative state for calling native methods. - CHECK_EQ(self->GetState(), kNative); + CHECK_EQ(self->GetState(), ThreadState::kNative); JNIEnv* env = self->GetJniEnv(); env->CallStaticVoidMethod(WellKnownClasses::java_lang_Daemons, @@ -2086,7 +2086,7 @@ void Runtime::InitNativeMethods() { JNIEnv* env = self->GetJniEnv(); // Must be in the kNative state for calling native methods (JNI_OnLoad code). - CHECK_EQ(self->GetState(), kNative); + CHECK_EQ(self->GetState(), ThreadState::kNative); // Set up the native methods provided by the runtime itself. RegisterRuntimeNativeMethods(env); diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc index e49deaec49..7619750032 100644 --- a/runtime/runtime_callbacks_test.cc +++ b/runtime/runtime_callbacks_test.cc @@ -55,7 +55,7 @@ class RuntimeCallbacksTest : public CommonRuntimeTest { Thread* self = Thread::Current(); ScopedObjectAccess soa(self); - ScopedThreadSuspension sts(self, kWaitingForDebuggerToAttach); + ScopedThreadSuspension sts(self, ThreadState::kWaitingForDebuggerToAttach); ScopedSuspendAll ssa("RuntimeCallbacksTest SetUp"); AddListener(); } @@ -64,7 +64,7 @@ class RuntimeCallbacksTest : public CommonRuntimeTest { { Thread* self = Thread::Current(); ScopedObjectAccess soa(self); - ScopedThreadSuspension sts(self, kWaitingForDebuggerToAttach); + ScopedThreadSuspension sts(self, ThreadState::kWaitingForDebuggerToAttach); ScopedSuspendAll ssa("RuntimeCallbacksTest TearDown"); RemoveListener(); } @@ -523,7 +523,7 @@ TEST_F(MonitorWaitCallbacksTest, WaitUnlocked) { /*ms=*/0, /*ns=*/0, /*interruptShouldThrow=*/false, - /*why=*/kWaiting); + /*why=*/ThreadState::kWaiting); } } ASSERT_TRUE(cb_.saw_wait_start_); diff --git a/runtime/scoped_thread_state_change-inl.h b/runtime/scoped_thread_state_change-inl.h index 04be224c5f..d601952e9f 100644 --- a/runtime/scoped_thread_state_change-inl.h +++ b/runtime/scoped_thread_state_change-inl.h @@ -34,7 +34,7 @@ inline ScopedThreadStateChange::ScopedThreadStateChange(Thread* self, ThreadStat : self_(self), thread_state_(new_thread_state), expected_has_no_thread_(false) { if (UNLIKELY(self_ == nullptr)) { // Value chosen arbitrarily and won't be used in the destructor since thread_ == null. - old_thread_state_ = kTerminated; + old_thread_state_ = ThreadState::kTerminated; Runtime* runtime = Runtime::Current(); CHECK(runtime == nullptr || !runtime->IsStarted() || runtime->IsShuttingDown(self_)); } else { @@ -43,9 +43,9 @@ inline ScopedThreadStateChange::ScopedThreadStateChange(Thread* self, ThreadStat // in the suspend count (this will be handled in the runnable transitions). old_thread_state_ = self->GetState(); if (old_thread_state_ != new_thread_state) { - if (new_thread_state == kRunnable) { + if (new_thread_state == ThreadState::kRunnable) { self_->TransitionFromSuspendedToRunnable(); - } else if (old_thread_state_ == kRunnable) { + } else if (old_thread_state_ == ThreadState::kRunnable) { self_->TransitionFromRunnableToSuspended(new_thread_state); } else { // A suspended transition to another effectively suspended transition, ok to use Unsafe. @@ -60,9 +60,9 @@ inline ScopedThreadStateChange::~ScopedThreadStateChange() { ScopedThreadChangeDestructorCheck(); } else { if (old_thread_state_ != thread_state_) { - if (old_thread_state_ == kRunnable) { + if (old_thread_state_ == ThreadState::kRunnable) { self_->TransitionFromSuspendedToRunnable(); - } else if (thread_state_ == kRunnable) { + } else if (thread_state_ == ThreadState::kRunnable) { self_->TransitionFromRunnableToSuspended(old_thread_state_); } else { // A suspended transition to another effectively suspended transition, ok to use Unsafe. @@ -90,7 +90,7 @@ inline ObjPtr<T> ScopedObjectAccessAlreadyRunnable::Decode(jobject obj) const { } inline bool ScopedObjectAccessAlreadyRunnable::IsRunnable() const { - return self_->GetState() == kRunnable; + return self_->GetState() == ThreadState::kRunnable; } inline ScopedObjectAccessAlreadyRunnable::ScopedObjectAccessAlreadyRunnable(JNIEnv* env) @@ -102,13 +102,13 @@ inline ScopedObjectAccessAlreadyRunnable::ScopedObjectAccessAlreadyRunnable(Thre vm_(env_ != nullptr ? env_->GetVm() : nullptr) {} inline ScopedObjectAccessUnchecked::ScopedObjectAccessUnchecked(JNIEnv* env) - : ScopedObjectAccessAlreadyRunnable(env), tsc_(Self(), kRunnable) { + : ScopedObjectAccessAlreadyRunnable(env), tsc_(Self(), ThreadState::kRunnable) { Self()->VerifyStack(); Locks::mutator_lock_->AssertSharedHeld(Self()); } inline ScopedObjectAccessUnchecked::ScopedObjectAccessUnchecked(Thread* self) - : ScopedObjectAccessAlreadyRunnable(self), tsc_(self, kRunnable) { + : ScopedObjectAccessAlreadyRunnable(self), tsc_(self, ThreadState::kRunnable) { Self()->VerifyStack(); Locks::mutator_lock_->AssertSharedHeld(Self()); } diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h index 08cb5b4689..7416d181bc 100644 --- a/runtime/scoped_thread_state_change.h +++ b/runtime/scoped_thread_state_change.h @@ -55,12 +55,12 @@ class ScopedThreadStateChange : public ValueObject { ScopedThreadStateChange() {} Thread* const self_ = nullptr; - const ThreadState thread_state_ = kTerminated; + const ThreadState thread_state_ = ThreadState::kTerminated; private: void ScopedThreadChangeDestructorCheck(); - ThreadState old_thread_state_ = kTerminated; + ThreadState old_thread_state_ = ThreadState::kTerminated; const bool expected_has_no_thread_ = true; friend class ScopedObjectAccessUnchecked; diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc index f76a0d0b7b..70cebafe4b 100644 --- a/runtime/signal_catcher.cc +++ b/runtime/signal_catcher.cc @@ -104,7 +104,7 @@ bool SignalCatcher::ShouldHalt() { } void SignalCatcher::Output(const std::string& s) { - ScopedThreadStateChange tsc(Thread::Current(), kWaitingForSignalCatcherOutput); + ScopedThreadStateChange tsc(Thread::Current(), ThreadState::kWaitingForSignalCatcherOutput); palette_status_t status = PaletteWriteCrashThreadStacks(s.data(), s.size()); if (status == PALETTE_STATUS_OK) { LOG(INFO) << "Wrote stack traces to tombstoned"; @@ -149,7 +149,7 @@ void SignalCatcher::HandleSigUsr1() { } int SignalCatcher::WaitForSignal(Thread* self, SignalSet& signals) { - ScopedThreadStateChange tsc(self, kWaitingInMainSignalCatcherLoop); + ScopedThreadStateChange tsc(self, ThreadState::kWaitingInMainSignalCatcherLoop); // Signals for sigwait() must be blocked but not ignored. We // block signals like SIGQUIT for all threads, so the condition @@ -177,7 +177,7 @@ void* SignalCatcher::Run(void* arg) { !runtime->IsAotCompiler())); Thread* self = Thread::Current(); - DCHECK_NE(self->GetState(), kRunnable); + DCHECK_NE(self->GetState(), ThreadState::kRunnable); { MutexLock mu(self, signal_catcher->lock_); signal_catcher->thread_ = self; diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h index f5bf5fb47b..3ac129275f 100644 --- a/runtime/thread-inl.h +++ b/runtime/thread-inl.h @@ -52,11 +52,12 @@ inline void Thread::AllowThreadSuspension() { inline void Thread::CheckSuspend() { DCHECK_EQ(Thread::Current(), this); for (;;) { - if (ReadFlag(kCheckpointRequest)) { + StateAndFlags state_and_flags(tls32_.state_and_flags.load(std::memory_order_relaxed)); + if (state_and_flags.IsFlagSet(ThreadFlag::kCheckpointRequest)) { RunCheckpointFunction(); - } else if (ReadFlag(kSuspendRequest)) { + } else if (state_and_flags.IsFlagSet(ThreadFlag::kSuspendRequest)) { FullSuspendCheck(); - } else if (ReadFlag(kEmptyCheckpointRequest)) { + } else if (state_and_flags.IsFlagSet(ThreadFlag::kEmptyCheckpointRequest)) { RunEmptyCheckpoint(); } else { break; @@ -68,7 +69,7 @@ inline void Thread::CheckEmptyCheckpointFromWeakRefAccess(BaseMutex* cond_var_mu Thread* self = Thread::Current(); DCHECK_EQ(self, this); for (;;) { - if (ReadFlag(kEmptyCheckpointRequest)) { + if (ReadFlag(ThreadFlag::kEmptyCheckpointRequest)) { RunEmptyCheckpoint(); // Check we hold only an expected mutex when accessing weak ref. if (kIsDebugBuild) { @@ -92,7 +93,7 @@ inline void Thread::CheckEmptyCheckpointFromWeakRefAccess(BaseMutex* cond_var_mu inline void Thread::CheckEmptyCheckpointFromMutex() { DCHECK_EQ(Thread::Current(), this); for (;;) { - if (ReadFlag(kEmptyCheckpointRequest)) { + if (ReadFlag(ThreadFlag::kEmptyCheckpointRequest)) { RunEmptyCheckpoint(); } else { break; @@ -103,21 +104,29 @@ inline void Thread::CheckEmptyCheckpointFromMutex() { inline ThreadState Thread::SetState(ThreadState new_state) { // Should only be used to change between suspended states. // Cannot use this code to change into or from Runnable as changing to Runnable should - // fail if old_state_and_flags.suspend_request is true and changing from Runnable might + // fail if the `ThreadFlag::kSuspendRequest` is set and changing from Runnable might // miss passing an active suspend barrier. - DCHECK_NE(new_state, kRunnable); + DCHECK_NE(new_state, ThreadState::kRunnable); if (kIsDebugBuild && this != Thread::Current()) { std::string name; GetThreadName(name); LOG(FATAL) << "Thread \"" << name << "\"(" << this << " != Thread::Current()=" << Thread::Current() << ") changing state to " << new_state; } - union StateAndFlags old_state_and_flags; - old_state_and_flags.as_int = tls32_.state_and_flags.as_int; - CHECK_NE(old_state_and_flags.as_struct.state, kRunnable) << new_state << " " << *this << " " - << *Thread::Current(); - tls32_.state_and_flags.as_struct.state = new_state; - return static_cast<ThreadState>(old_state_and_flags.as_struct.state); + + while (true) { + StateAndFlags old_state_and_flags(tls32_.state_and_flags.load(std::memory_order_relaxed)); + CHECK_NE(old_state_and_flags.GetState(), ThreadState::kRunnable) + << new_state << " " << *this << " " << *Thread::Current(); + StateAndFlags new_state_and_flags = old_state_and_flags; + new_state_and_flags.SetState(new_state); + bool done = + tls32_.state_and_flags.CompareAndSetWeakRelaxed(old_state_and_flags.GetValue(), + new_state_and_flags.GetValue()); + if (done) { + return static_cast<ThreadState>(old_state_and_flags.GetState()); + } + } } inline bool Thread::IsThreadSuspensionAllowable() const { @@ -182,31 +191,29 @@ inline void Thread::AssertThreadSuspensionIsAllowable(bool check_locks) const { } inline void Thread::TransitionToSuspendedAndRunCheckpoints(ThreadState new_state) { - DCHECK_NE(new_state, kRunnable); - DCHECK_EQ(GetState(), kRunnable); - union StateAndFlags old_state_and_flags; - union StateAndFlags new_state_and_flags; + DCHECK_NE(new_state, ThreadState::kRunnable); while (true) { - old_state_and_flags.as_int = tls32_.state_and_flags.as_int; - if (UNLIKELY((old_state_and_flags.as_struct.flags & kCheckpointRequest) != 0)) { + StateAndFlags old_state_and_flags(tls32_.state_and_flags.load(std::memory_order_relaxed)); + DCHECK_EQ(old_state_and_flags.GetState(), ThreadState::kRunnable); + if (UNLIKELY(old_state_and_flags.IsFlagSet(ThreadFlag::kCheckpointRequest))) { RunCheckpointFunction(); continue; } - if (UNLIKELY((old_state_and_flags.as_struct.flags & kEmptyCheckpointRequest) != 0)) { + if (UNLIKELY(old_state_and_flags.IsFlagSet(ThreadFlag::kEmptyCheckpointRequest))) { RunEmptyCheckpoint(); continue; } // Change the state but keep the current flags (kCheckpointRequest is clear). - DCHECK_EQ((old_state_and_flags.as_struct.flags & kCheckpointRequest), 0); - DCHECK_EQ((old_state_and_flags.as_struct.flags & kEmptyCheckpointRequest), 0); - new_state_and_flags.as_struct.flags = old_state_and_flags.as_struct.flags; - new_state_and_flags.as_struct.state = new_state; + DCHECK(!old_state_and_flags.IsFlagSet(ThreadFlag::kCheckpointRequest)); + DCHECK(!old_state_and_flags.IsFlagSet(ThreadFlag::kEmptyCheckpointRequest)); + StateAndFlags new_state_and_flags = old_state_and_flags; + new_state_and_flags.SetState(new_state); // CAS the value, ensuring that prior memory operations are visible to any thread // that observes that we are suspended. bool done = - tls32_.state_and_flags.as_atomic_int.CompareAndSetWeakRelease(old_state_and_flags.as_int, - new_state_and_flags.as_int); + tls32_.state_and_flags.CompareAndSetWeakRelease(old_state_and_flags.GetValue(), + new_state_and_flags.GetValue()); if (LIKELY(done)) { break; } @@ -215,11 +222,12 @@ inline void Thread::TransitionToSuspendedAndRunCheckpoints(ThreadState new_state inline void Thread::PassActiveSuspendBarriers() { while (true) { - uint16_t current_flags = tls32_.state_and_flags.as_struct.flags; - if (LIKELY((current_flags & - (kCheckpointRequest | kEmptyCheckpointRequest | kActiveSuspendBarrier)) == 0)) { + StateAndFlags state_and_flags(tls32_.state_and_flags.load(std::memory_order_relaxed)); + if (LIKELY(!state_and_flags.IsFlagSet(ThreadFlag::kCheckpointRequest) && + !state_and_flags.IsFlagSet(ThreadFlag::kEmptyCheckpointRequest) && + !state_and_flags.IsFlagSet(ThreadFlag::kActiveSuspendBarrier))) { break; - } else if ((current_flags & kActiveSuspendBarrier) != 0) { + } else if (state_and_flags.IsFlagSet(ThreadFlag::kActiveSuspendBarrier)) { PassActiveSuspendBarriers(this); } else { // Impossible @@ -241,38 +249,33 @@ inline void Thread::TransitionFromRunnableToSuspended(ThreadState new_state) { } inline ThreadState Thread::TransitionFromSuspendedToRunnable() { - union StateAndFlags old_state_and_flags; - old_state_and_flags.as_int = tls32_.state_and_flags.as_int; - int16_t old_state = old_state_and_flags.as_struct.state; - DCHECK_NE(static_cast<ThreadState>(old_state), kRunnable); - do { + StateAndFlags old_state_and_flags(tls32_.state_and_flags.load(std::memory_order_relaxed)); + ThreadState old_state = old_state_and_flags.GetState(); + DCHECK_NE(old_state, ThreadState::kRunnable); + while (true) { GetMutatorLock()->AssertNotHeld(this); // Otherwise we starve GC. - old_state_and_flags.as_int = tls32_.state_and_flags.as_int; - DCHECK_EQ(old_state_and_flags.as_struct.state, old_state); - if (LIKELY(old_state_and_flags.as_struct.flags == 0)) { - // Optimize for the return from native code case - this is the fast path. - // Atomically change from suspended to runnable if no suspend request pending. - union StateAndFlags new_state_and_flags; - new_state_and_flags.as_int = old_state_and_flags.as_int; - new_state_and_flags.as_struct.state = kRunnable; - + // Optimize for the return from native code case - this is the fast path. + // Atomically change from suspended to runnable if no suspend request pending. + StateAndFlags new_state_and_flags = old_state_and_flags; + new_state_and_flags.SetState(ThreadState::kRunnable); + static_assert(static_cast<std::underlying_type_t<ThreadState>>(ThreadState::kRunnable) == 0u); + if (LIKELY(new_state_and_flags.GetValue() == 0u)) { // No flags set? // CAS the value with a memory barrier. - if (LIKELY(tls32_.state_and_flags.as_atomic_int.CompareAndSetWeakAcquire( - old_state_and_flags.as_int, - new_state_and_flags.as_int))) { + if (LIKELY(tls32_.state_and_flags.CompareAndSetWeakAcquire(old_state_and_flags.GetValue(), + new_state_and_flags.GetValue()))) { // Mark the acquisition of a share of the mutator lock. GetMutatorLock()->TransitionFromSuspendedToRunnable(this); break; } - } else if ((old_state_and_flags.as_struct.flags & kActiveSuspendBarrier) != 0) { + } else if (old_state_and_flags.IsFlagSet(ThreadFlag::kActiveSuspendBarrier)) { PassActiveSuspendBarriers(this); - } else if ((old_state_and_flags.as_struct.flags & - (kCheckpointRequest | kEmptyCheckpointRequest)) != 0) { + } else if (UNLIKELY(old_state_and_flags.IsFlagSet(ThreadFlag::kCheckpointRequest) || + old_state_and_flags.IsFlagSet(ThreadFlag::kEmptyCheckpointRequest))) { // Impossible LOG(FATAL) << "Transitioning to runnable with checkpoint flag, " - << " flags=" << old_state_and_flags.as_struct.flags - << " state=" << old_state_and_flags.as_struct.state; - } else if ((old_state_and_flags.as_struct.flags & kSuspendRequest) != 0) { + << " flags=" << new_state_and_flags.GetValue() // State set to kRunnable = 0. + << " state=" << old_state_and_flags.GetState(); + } else if (old_state_and_flags.IsFlagSet(ThreadFlag::kSuspendRequest)) { // Wait while our suspend count is non-zero. // We pass null to the MutexLock as we may be in a situation where the @@ -286,17 +289,22 @@ inline ThreadState Thread::TransitionFromSuspendedToRunnable() { } MutexLock mu(thread_to_pass, *Locks::thread_suspend_count_lock_); ScopedTransitioningToRunnable scoped_transitioning_to_runnable(this); - old_state_and_flags.as_int = tls32_.state_and_flags.as_int; - DCHECK_EQ(old_state_and_flags.as_struct.state, old_state); - while ((old_state_and_flags.as_struct.flags & kSuspendRequest) != 0) { + // Reload state and flags after locking the mutex. + old_state_and_flags.SetValue(tls32_.state_and_flags.load(std::memory_order_relaxed)); + DCHECK_EQ(old_state, old_state_and_flags.GetState()); + while (old_state_and_flags.IsFlagSet(ThreadFlag::kSuspendRequest)) { // Re-check when Thread::resume_cond_ is notified. Thread::resume_cond_->Wait(thread_to_pass); - old_state_and_flags.as_int = tls32_.state_and_flags.as_int; - DCHECK_EQ(old_state_and_flags.as_struct.state, old_state); + // Reload state and flags after waiting. + old_state_and_flags.SetValue(tls32_.state_and_flags.load(std::memory_order_relaxed)); + DCHECK_EQ(old_state, old_state_and_flags.GetState()); } DCHECK_EQ(GetSuspendCount(), 0); } - } while (true); + // Reload state and flags. + old_state_and_flags.SetValue(tls32_.state_and_flags.load(std::memory_order_relaxed)); + DCHECK_EQ(old_state, old_state_and_flags.GetState()); + } // Run the flip function, if set. Closure* flip_func = GetFlipFunction(); if (flip_func != nullptr) { @@ -344,7 +352,7 @@ inline void Thread::RevokeThreadLocalAllocationStack() { if (kIsDebugBuild) { // Note: self is not necessarily equal to this thread since thread may be suspended. Thread* self = Thread::Current(); - DCHECK(this == self || IsSuspended() || GetState() == kWaitingPerformingGc) + DCHECK(this == self || IsSuspended() || GetState() == ThreadState::kWaitingPerformingGc) << GetState() << " thread " << this << " self " << self; } tlsPtr_.thread_local_alloc_stack_end = nullptr; diff --git a/runtime/thread.cc b/runtime/thread.cc index 46aa38e035..c7ce0de86c 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1003,8 +1003,8 @@ Thread* Thread::Attach(const char* thread_name, bool as_daemon, PeerAction peer_ self->InitStringEntryPoints(); - CHECK_NE(self->GetState(), kRunnable); - self->SetState(kNative); + CHECK_NE(self->GetState(), ThreadState::kRunnable); + self->SetState(ThreadState::kNative); // Run the action that is acting on the peer. if (!peer_action(self)) { @@ -1444,7 +1444,7 @@ bool Thread::ModifySuspendCountInternal(Thread* self, return false; } - uint16_t flags = kSuspendRequest; + uint32_t flags = enum_cast<uint32_t>(ThreadFlag::kSuspendRequest); if (delta > 0 && suspend_barrier != nullptr) { uint32_t available_barrier = kMaxSuspendBarriers; for (uint32_t i = 0; i < kMaxSuspendBarriers; ++i) { @@ -1458,7 +1458,7 @@ bool Thread::ModifySuspendCountInternal(Thread* self, return false; } tlsPtr_.active_suspend_barriers[available_barrier] = suspend_barrier; - flags |= kActiveSuspendBarrier; + flags |= enum_cast<uint32_t>(ThreadFlag::kActiveSuspendBarrier); } tls32_.suspend_count += delta; @@ -1471,10 +1471,10 @@ bool Thread::ModifySuspendCountInternal(Thread* self, } if (tls32_.suspend_count == 0) { - AtomicClearFlag(kSuspendRequest); + AtomicClearFlag(ThreadFlag::kSuspendRequest); } else { // Two bits might be set simultaneously. - tls32_.state_and_flags.as_atomic_int.fetch_or(flags, std::memory_order_seq_cst); + tls32_.state_and_flags.fetch_or(flags, std::memory_order_seq_cst); TriggerSuspend(); } return true; @@ -1488,7 +1488,7 @@ bool Thread::PassActiveSuspendBarriers(Thread* self) { AtomicInteger* pass_barriers[kMaxSuspendBarriers]; { MutexLock mu(self, *Locks::thread_suspend_count_lock_); - if (!ReadFlag(kActiveSuspendBarrier)) { + if (!ReadFlag(ThreadFlag::kActiveSuspendBarrier)) { // quick exit test: the barriers have already been claimed - this is // possible as there may be a race to claim and it doesn't matter // who wins. @@ -1503,7 +1503,7 @@ bool Thread::PassActiveSuspendBarriers(Thread* self) { pass_barriers[i] = tlsPtr_.active_suspend_barriers[i]; tlsPtr_.active_suspend_barriers[i] = nullptr; } - AtomicClearFlag(kActiveSuspendBarrier); + AtomicClearFlag(ThreadFlag::kActiveSuspendBarrier); } uint32_t barrier_count = 0; @@ -1530,7 +1530,7 @@ bool Thread::PassActiveSuspendBarriers(Thread* self) { } void Thread::ClearSuspendBarrier(AtomicInteger* target) { - CHECK(ReadFlag(kActiveSuspendBarrier)); + CHECK(ReadFlag(ThreadFlag::kActiveSuspendBarrier)); bool clear_flag = true; for (uint32_t i = 0; i < kMaxSuspendBarriers; ++i) { AtomicInteger* ptr = tlsPtr_.active_suspend_barriers[i]; @@ -1541,7 +1541,7 @@ void Thread::ClearSuspendBarrier(AtomicInteger* target) { } } if (LIKELY(clear_flag)) { - AtomicClearFlag(kActiveSuspendBarrier); + AtomicClearFlag(ThreadFlag::kActiveSuspendBarrier); } } @@ -1559,7 +1559,7 @@ void Thread::RunCheckpointFunction() { } else { // No overflow checkpoints. Clear the kCheckpointRequest flag tlsPtr_.checkpoint_function = nullptr; - AtomicClearFlag(kCheckpointRequest); + AtomicClearFlag(ThreadFlag::kCheckpointRequest); } } // Outside the lock, run the checkpoint function. @@ -1570,24 +1570,22 @@ void Thread::RunCheckpointFunction() { void Thread::RunEmptyCheckpoint() { DCHECK_EQ(Thread::Current(), this); - AtomicClearFlag(kEmptyCheckpointRequest); + AtomicClearFlag(ThreadFlag::kEmptyCheckpointRequest); Runtime::Current()->GetThreadList()->EmptyCheckpointBarrier()->Pass(this); } bool Thread::RequestCheckpoint(Closure* function) { - union StateAndFlags old_state_and_flags; - old_state_and_flags.as_int = tls32_.state_and_flags.as_int; - if (old_state_and_flags.as_struct.state != kRunnable) { + StateAndFlags old_state_and_flags(tls32_.state_and_flags.load(std::memory_order_relaxed)); + if (old_state_and_flags.GetState() != ThreadState::kRunnable) { return false; // Fail, thread is suspended and so can't run a checkpoint. } // We must be runnable to request a checkpoint. - DCHECK_EQ(old_state_and_flags.as_struct.state, kRunnable); - union StateAndFlags new_state_and_flags; - new_state_and_flags.as_int = old_state_and_flags.as_int; - new_state_and_flags.as_struct.flags |= kCheckpointRequest; - bool success = tls32_.state_and_flags.as_atomic_int.CompareAndSetStrongSequentiallyConsistent( - old_state_and_flags.as_int, new_state_and_flags.as_int); + DCHECK_EQ(old_state_and_flags.GetState(), ThreadState::kRunnable); + StateAndFlags new_state_and_flags = old_state_and_flags; + new_state_and_flags.SetFlag(ThreadFlag::kCheckpointRequest); + bool success = tls32_.state_and_flags.CompareAndSetStrongSequentiallyConsistent( + old_state_and_flags.GetValue(), new_state_and_flags.GetValue()); if (success) { // Succeeded setting checkpoint flag, now insert the actual checkpoint. if (tlsPtr_.checkpoint_function == nullptr) { @@ -1595,28 +1593,26 @@ bool Thread::RequestCheckpoint(Closure* function) { } else { checkpoint_overflow_.push_back(function); } - CHECK_EQ(ReadFlag(kCheckpointRequest), true); + CHECK(ReadFlag(ThreadFlag::kCheckpointRequest)); TriggerSuspend(); } return success; } bool Thread::RequestEmptyCheckpoint() { - union StateAndFlags old_state_and_flags; - old_state_and_flags.as_int = tls32_.state_and_flags.as_int; - if (old_state_and_flags.as_struct.state != kRunnable) { + StateAndFlags old_state_and_flags(tls32_.state_and_flags.load(std::memory_order_relaxed)); + if (old_state_and_flags.GetState() != ThreadState::kRunnable) { // If it's not runnable, we don't need to do anything because it won't be in the middle of a // heap access (eg. the read barrier). return false; } // We must be runnable to request a checkpoint. - DCHECK_EQ(old_state_and_flags.as_struct.state, kRunnable); - union StateAndFlags new_state_and_flags; - new_state_and_flags.as_int = old_state_and_flags.as_int; - new_state_and_flags.as_struct.flags |= kEmptyCheckpointRequest; - bool success = tls32_.state_and_flags.as_atomic_int.CompareAndSetStrongSequentiallyConsistent( - old_state_and_flags.as_int, new_state_and_flags.as_int); + DCHECK_EQ(old_state_and_flags.GetState(), ThreadState::kRunnable); + StateAndFlags new_state_and_flags = old_state_and_flags; + new_state_and_flags.SetFlag(ThreadFlag::kEmptyCheckpointRequest); + bool success = tls32_.state_and_flags.CompareAndSetStrongSequentiallyConsistent( + old_state_and_flags.GetValue(), new_state_and_flags.GetValue()); if (success) { TriggerSuspend(); } @@ -1776,7 +1772,7 @@ void Thread::FullSuspendCheck() { VLOG(threads) << this << " self-suspending"; // Make thread appear suspended to other threads, release mutator_lock_. // Transition to suspended and back to runnable, re-acquire share on mutator_lock_. - ScopedThreadSuspension(this, kSuspended); // NOLINT + ScopedThreadSuspension(this, ThreadState::kSuspended); // NOLINT VLOG(threads) << this << " self-reviving"; } @@ -1879,10 +1875,15 @@ void Thread::DumpState(std::ostream& os, const Thread* thread, pid_t tid) { if (thread != nullptr) { auto suspend_log_fn = [&]() REQUIRES(Locks::thread_suspend_count_lock_) { + StateAndFlags state_and_flags( + thread->tls32_.state_and_flags.load(std::memory_order_relaxed)); + static_assert( + static_cast<std::underlying_type_t<ThreadState>>(ThreadState::kRunnable) == 0u); + state_and_flags.SetState(ThreadState::kRunnable); // Clear state bits. os << " | group=\"" << group_name << "\"" << " sCount=" << thread->tls32_.suspend_count << " ucsCount=" << thread->tls32_.user_code_suspend_count - << " flags=" << thread->tls32_.state_and_flags.as_struct.flags + << " flags=" << state_and_flags.GetValue() << " obj=" << reinterpret_cast<void*>(thread->tlsPtr_.opeer) << " self=" << reinterpret_cast<const void*>(thread) << "\n"; }; @@ -2058,11 +2059,11 @@ struct StackDumpVisitor : public MonitorObjectsStackVisitor { REQUIRES_SHARED(Locks::mutator_lock_) { const char* msg; switch (state) { - case kBlocked: + case ThreadState::kBlocked: msg = " - waiting to lock "; break; - case kWaitingForLockInflation: + case ThreadState::kWaitingForLockInflation: msg = " - waiting for lock inflation of "; break; @@ -2116,12 +2117,14 @@ static bool ShouldShowNativeStack(const Thread* thread) ThreadState state = thread->GetState(); // In native code somewhere in the VM (one of the kWaitingFor* states)? That's interesting. - if (state > kWaiting && state < kStarting) { + if (state > ThreadState::kWaiting && state < ThreadState::kStarting) { return true; } // In an Object.wait variant or Thread.sleep? That's not interesting. - if (state == kTimedWaiting || state == kSleeping || state == kWaiting) { + if (state == ThreadState::kTimedWaiting || + state == ThreadState::kSleeping || + state == ThreadState::kWaiting) { return false; } @@ -2305,8 +2308,10 @@ Thread::Thread(bool daemon) static_assert((sizeof(Thread) % 4) == 0U, "art::Thread has a size which is not a multiple of 4."); - tls32_.state_and_flags.as_struct.flags = 0; - tls32_.state_and_flags.as_struct.state = kNative; + DCHECK_EQ(tls32_.state_and_flags.load(std::memory_order_relaxed), 0u); + StateAndFlags state_and_flags(0u); + state_and_flags.SetState(ThreadState::kNative); + tls32_.state_and_flags.store(state_and_flags.GetValue(), std::memory_order_relaxed); tls32_.interrupted.store(false, std::memory_order_relaxed); // Initialize with no permit; if the java Thread was unparked before being // started, it will unpark itself before calling into java code. @@ -2462,9 +2467,9 @@ Thread::~Thread() { delete tlsPtr_.jni_env; tlsPtr_.jni_env = nullptr; } - CHECK_NE(GetState(), kRunnable); - CHECK(!ReadFlag(kCheckpointRequest)); - CHECK(!ReadFlag(kEmptyCheckpointRequest)); + CHECK_NE(GetState(), ThreadState::kRunnable); + CHECK(!ReadFlag(ThreadFlag::kCheckpointRequest)); + CHECK(!ReadFlag(ThreadFlag::kEmptyCheckpointRequest)); CHECK(tlsPtr_.checkpoint_function == nullptr); CHECK_EQ(checkpoint_overflow_.size(), 0u); CHECK(tlsPtr_.flip_function == nullptr); @@ -2476,7 +2481,7 @@ Thread::~Thread() { "Not all deoptimized frames have been consumed by the debugger."; // We may be deleting a still born thread. - SetStateUnsafe(kTerminated); + SetStateUnsafe(ThreadState::kTerminated); delete wait_cond_; delete wait_mutex_; @@ -2503,7 +2508,7 @@ void Thread::HandleUncaughtExceptions(ScopedObjectAccessAlreadyRunnable& soa) { return; } ScopedLocalRef<jobject> peer(tlsPtr_.jni_env, soa.AddLocalReference<jobject>(tlsPtr_.opeer)); - ScopedThreadStateChange tsc(this, kNative); + ScopedThreadStateChange tsc(this, ThreadState::kNative); // Get and clear the exception. ScopedLocalRef<jthrowable> exception(tlsPtr_.jni_env, tlsPtr_.jni_env->ExceptionOccurred()); @@ -2526,7 +2531,7 @@ void Thread::RemoveFromThreadGroup(ScopedObjectAccessAlreadyRunnable& soa) { if (ogroup != nullptr) { ScopedLocalRef<jobject> group(soa.Env(), soa.AddLocalReference<jobject>(ogroup)); ScopedLocalRef<jobject> peer(soa.Env(), soa.AddLocalReference<jobject>(tlsPtr_.opeer)); - ScopedThreadStateChange tsc(soa.Self(), kNative); + ScopedThreadStateChange tsc(soa.Self(), ThreadState::kNative); tlsPtr_.jni_env->CallVoidMethod(group.get(), WellKnownClasses::java_lang_ThreadGroup_removeThread, peer.get()); @@ -4470,7 +4475,7 @@ bool Thread::IsSystemDaemon() const { std::string Thread::StateAndFlagsAsHexString() const { std::stringstream result_stream; - result_stream << std::hex << tls32_.state_and_flags.as_atomic_int.load(); + result_stream << std::hex << tls32_.state_and_flags.load(std::memory_order_relaxed); return result_stream.str(); } diff --git a/runtime/thread.h b/runtime/thread.h index 94789801b9..f1dd7b8845 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -26,6 +26,8 @@ #include <string> #include "base/atomic.h" +#include "base/bit_field.h" +#include "base/bit_utils.h" #include "base/enums.h" #include "base/locks.h" #include "base/macros.h" @@ -118,12 +120,21 @@ enum ThreadPriority { kMaxThreadPriority = 10, }; -enum ThreadFlag { - kSuspendRequest = 1, // If set implies that suspend_count_ > 0 and the Thread should enter the - // safepoint handler. - kCheckpointRequest = 2, // Request that the thread do some checkpoint work and then continue. - kEmptyCheckpointRequest = 4, // Request that the thread do empty checkpoint and then continue. - kActiveSuspendBarrier = 8, // Register that at least 1 suspend barrier needs to be passed. +enum class ThreadFlag : uint32_t { + // If set, implies that suspend_count_ > 0 and the Thread should enter the safepoint handler. + kSuspendRequest = 1u << 0, + + // Request that the thread do some checkpoint work and then continue. + kCheckpointRequest = 1u << 1, + + // Request that the thread do empty checkpoint and then continue. + kEmptyCheckpointRequest = 1u << 2, + + // Register that at least 1 suspend barrier needs to be passed. + kActiveSuspendBarrier = 1u << 3, + + // Indicates the last flag. Used for checking that the flags do not overlap thread state. + kLastFlag = kActiveSuspendBarrier }; enum class StackedShadowFrameType { @@ -236,9 +247,8 @@ class Thread { REQUIRES_SHARED(Locks::mutator_lock_); ThreadState GetState() const { - DCHECK_GE(tls32_.state_and_flags.as_struct.state, kTerminated); - DCHECK_LE(tls32_.state_and_flags.as_struct.state, kSuspended); - return static_cast<ThreadState>(tls32_.state_and_flags.as_struct.state); + StateAndFlags state_and_flags(tls32_.state_and_flags.load(std::memory_order_relaxed)); + return state_and_flags.GetState(); } ThreadState SetState(ThreadState new_state); @@ -253,10 +263,9 @@ class Thread { } bool IsSuspended() const { - union StateAndFlags state_and_flags; - state_and_flags.as_int = tls32_.state_and_flags.as_atomic_int.load(std::memory_order_relaxed); - return state_and_flags.as_struct.state != kRunnable && - (state_and_flags.as_struct.flags & kSuspendRequest) != 0; + StateAndFlags state_and_flags(tls32_.state_and_flags.load(std::memory_order_relaxed)); + return state_and_flags.GetState() != ThreadState::kRunnable && + state_and_flags.IsFlagSet(ThreadFlag::kSuspendRequest); } void DecrDefineClassCount() { @@ -1097,19 +1106,23 @@ class Thread { REQUIRES(Locks::thread_suspend_count_lock_); bool ReadFlag(ThreadFlag flag) const { - return (tls32_.state_and_flags.as_struct.flags & flag) != 0; + StateAndFlags state_and_flags(tls32_.state_and_flags.load(std::memory_order_relaxed)); + return state_and_flags.IsFlagSet(flag); } bool TestAllFlags() const { - return (tls32_.state_and_flags.as_struct.flags != 0); + StateAndFlags state_and_flags(tls32_.state_and_flags.load(std::memory_order_relaxed)); + static_assert(static_cast<std::underlying_type_t<ThreadState>>(ThreadState::kRunnable) == 0u); + state_and_flags.SetState(ThreadState::kRunnable); // Clear state bits. + return state_and_flags.GetValue() != 0u; } void AtomicSetFlag(ThreadFlag flag) { - tls32_.state_and_flags.as_atomic_int.fetch_or(flag, std::memory_order_seq_cst); + tls32_.state_and_flags.fetch_or(enum_cast<uint32_t>(flag), std::memory_order_seq_cst); } void AtomicClearFlag(ThreadFlag flag) { - tls32_.state_and_flags.as_atomic_int.fetch_and(-1 ^ flag, std::memory_order_seq_cst); + tls32_.state_and_flags.fetch_and(~enum_cast<uint32_t>(flag), std::memory_order_seq_cst); } void ResetQuickAllocEntryPointsForThread(); @@ -1330,10 +1343,15 @@ class Thread { jint thread_priority) REQUIRES_SHARED(Locks::mutator_lock_); - // Avoid use, callers should use SetState. Used only by SignalCatcher::HandleSigQuit and, ~Thread + // Avoid use, callers should use SetState. + // Used only by `Thread` destructor and stack trace collection in semi-space GC (currently + // disabled by `kStoreStackTraces = false`). ThreadState SetStateUnsafe(ThreadState new_state) { - ThreadState old_state = GetState(); - if (old_state == kRunnable && new_state != kRunnable) { + StateAndFlags old_state_and_flags(tls32_.state_and_flags.load(std::memory_order_relaxed)); + ThreadState old_state = old_state_and_flags.GetState(); + if (old_state == new_state) { + // Nothing to do. + } else if (old_state == ThreadState::kRunnable) { // Need to run pending checkpoint and suspend barriers. Run checkpoints in runnable state in // case they need to use a ScopedObjectAccess. If we are holding the mutator lock and a SOA // attempts to TransitionFromSuspendedToRunnable, it results in a deadlock. @@ -1341,7 +1359,18 @@ class Thread { // Since we transitioned to a suspended state, check the pass barrier requests. PassActiveSuspendBarriers(); } else { - tls32_.state_and_flags.as_struct.state = new_state; + while (true) { + StateAndFlags new_state_and_flags = old_state_and_flags; + new_state_and_flags.SetState(new_state); + if (LIKELY(tls32_.state_and_flags.CompareAndSetWeakAcquire( + old_state_and_flags.GetValue(), + new_state_and_flags.GetValue()))) { + break; + } + // Reload state and flags. + old_state_and_flags.SetValue(tls32_.state_and_flags.load(std::memory_order_relaxed)); + DCHECK_EQ(old_state, old_state_and_flags.GetState()); + } } return old_state; } @@ -1440,29 +1469,61 @@ class Thread { void ReleaseLongJumpContextInternal(); - // 32 bits of atomically changed state and flags. Keeping as 32 bits allows and atomic CAS to - // change from being Suspended to Runnable without a suspend request occurring. - union PACKED(4) StateAndFlags { - StateAndFlags() {} - struct PACKED(4) { - // Bitfield of flag values. Must be changed atomically so that flag values aren't lost. See - // ThreadFlag for bit field meanings. - volatile uint16_t flags; - // Holds the ThreadState. May be changed non-atomically between Suspended (ie not Runnable) - // transitions. Changing to Runnable requires that the suspend_request be part of the atomic - // operation. If a thread is suspended and a suspend_request is present, a thread may not - // change to Runnable as a GC or other operation is in progress. - volatile uint16_t state; - } as_struct; - AtomicInteger as_atomic_int; - volatile int32_t as_int; + // Helper class for manipulating the 32 bits of atomically changed state and flags. + class StateAndFlags { + public: + explicit StateAndFlags(uint32_t value) :value_(value) {} + + uint32_t GetValue() const { + return value_; + } + + void SetValue(uint32_t value) { + value_ = value; + } + + bool IsFlagSet(ThreadFlag flag) const { + return (value_ & enum_cast<uint32_t>(flag)) != 0u; + } + + void SetFlag(ThreadFlag flag) { + value_ |= enum_cast<uint32_t>(flag); + } + + void ClearFlag(ThreadFlag flag) { + value_ &= ~enum_cast<uint32_t>(flag); + } + + ThreadState GetState() const { + ThreadState state = ThreadStateField::Decode(value_); + ValidateThreadState(state); + return state; + } + + void SetState(ThreadState state) { + ValidateThreadState(state); + value_ = ThreadStateField::Update(state, value_); + } private: - // gcc does not handle struct with volatile member assignments correctly. - // See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47409 - DISALLOW_COPY_AND_ASSIGN(StateAndFlags); + static void ValidateThreadState(ThreadState state) { + if (kIsDebugBuild && state != ThreadState::kRunnable) { + CHECK_GE(state, ThreadState::kTerminated); + CHECK_LE(state, ThreadState::kSuspended); + CHECK_NE(state, ThreadState::kObsoleteRunnable); + } + } + + // The value holds thread flags and thread state. + uint32_t value_; + + static constexpr size_t kThreadStateBitSize = BitSizeOf<std::underlying_type_t<ThreadState>>(); + static constexpr size_t kThreadStatePosition = BitSizeOf<uint32_t>() - kThreadStateBitSize; + using ThreadStateField = BitField<ThreadState, kThreadStatePosition, kThreadStateBitSize>; + static_assert( + WhichPowerOf2(enum_cast<uint32_t>(ThreadFlag::kLastFlag)) < kThreadStatePosition); }; - static_assert(sizeof(StateAndFlags) == sizeof(int32_t), "Weird state_and_flags size"); + static_assert(sizeof(StateAndFlags) == sizeof(uint32_t), "Unexpected StateAndFlags size"); // Format state and flags as a hex string. For diagnostic output. std::string StateAndFlagsAsHexString() const; @@ -1502,7 +1563,8 @@ class Thread { using bool32_t = uint32_t; explicit tls_32bit_sized_values(bool is_daemon) - : suspend_count(0), + : state_and_flags(0u), + suspend_count(0), thin_lock_thread_id(0), tid(0), daemon(is_daemon), @@ -1518,9 +1580,13 @@ class Thread { make_visibly_initialized_counter(0), define_class_counter(0) {} - union StateAndFlags state_and_flags; - static_assert(sizeof(union StateAndFlags) == sizeof(int32_t), - "Size of state_and_flags and int32 are different"); + // The state and flags field must be changed atomically so that flag values aren't lost. + // See `StateAndFlags` for bit assignments of `ThreadFlag` and `ThreadState` values. + // Keeping the state and flags together allows an atomic CAS to change from being + // Suspended to Runnable without a suspend request occurring. + Atomic<uint32_t> state_and_flags; + static_assert(sizeof(state_and_flags) == sizeof(uint32_t), + "Size of state_and_flags and uint32 are different"); // A non-zero value is used to tell the current thread to enter a safe point // at the next poll. diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 8a48300f42..f50dba8922 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -222,7 +222,7 @@ class DumpCheckpoint final : public Closure { void WaitForThreadsToRunThroughCheckpoint(size_t threads_running_checkpoint) { Thread* self = Thread::Current(); - ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun); + ScopedThreadStateChange tsc(self, ThreadState::kWaitingForCheckPointsToRun); bool timed_out = barrier_.Increment(self, threads_running_checkpoint, kDumpWaitTimeout); if (timed_out) { // Avoid a recursive abort. @@ -338,7 +338,7 @@ size_t ThreadList::RunCheckpoint(Closure* checkpoint_function, Closure* callback break; } else { // The thread is probably suspended, try to make sure that it stays suspended. - if (thread->GetState() == kRunnable) { + if (thread->GetState() == ThreadState::kRunnable) { // Spurious fail, try again. continue; } @@ -419,7 +419,7 @@ void ThreadList::RunEmptyCheckpoint() { } break; } - if (thread->GetState() != kRunnable) { + if (thread->GetState() != ThreadState::kRunnable) { // It's seen suspended, we are done because it must not be in the middle of a mutator // heap access. break; @@ -434,7 +434,7 @@ void ThreadList::RunEmptyCheckpoint() { Runtime::Current()->GetHeap()->GetReferenceProcessor()->BroadcastForSlowPath(self); Runtime::Current()->BroadcastForNewSystemWeaks(/*broadcast_for_checkpoint=*/true); { - ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun); + ScopedThreadStateChange tsc(self, ThreadState::kWaitingForCheckPointsToRun); uint64_t total_wait_time = 0; bool first_iter = true; while (true) { @@ -481,7 +481,7 @@ void ThreadList::RunEmptyCheckpoint() { std::find(runnable_thread_ids.begin(), runnable_thread_ids.end(), tid) != runnable_thread_ids.end(); if (is_in_runnable_thread_ids && - thread->ReadFlag(kEmptyCheckpointRequest)) { + thread->ReadFlag(ThreadFlag::kEmptyCheckpointRequest)) { // Found a runnable thread that hasn't responded to the empty checkpoint request. // Assume it's stuck and safe to dump its stack. thread->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT), @@ -514,7 +514,7 @@ size_t ThreadList::FlipThreadRoots(Closure* thread_flip_visitor, Locks::mutator_lock_->AssertNotHeld(self); Locks::thread_list_lock_->AssertNotHeld(self); Locks::thread_suspend_count_lock_->AssertNotHeld(self); - CHECK_NE(self->GetState(), kRunnable); + CHECK_NE(self->GetState(), ThreadState::kRunnable); collector->GetHeap()->ThreadFlipBegin(self); // Sync with JNI critical calls. @@ -557,7 +557,7 @@ size_t ThreadList::FlipThreadRoots(Closure* thread_flip_visitor, // runnable (both cases waiting inside Thread::TransitionFromSuspendedToRunnable), or waiting // for the thread flip to end at the JNI critical section entry (kWaitingForGcThreadFlip), ThreadState state = thread->GetState(); - if ((state == kWaitingForGcThreadFlip || thread->IsTransitioningToRunnable()) && + if ((state == ThreadState::kWaitingForGcThreadFlip || thread->IsTransitioningToRunnable()) && thread->GetSuspendCount() == 1) { // The thread will resume right after the broadcast. bool updated = thread->ModifySuspendCount(self, -1, nullptr, SuspendReason::kInternal); @@ -667,7 +667,7 @@ void ThreadList::SuspendAllInternal(Thread* self, Locks::thread_list_lock_->AssertNotHeld(self); Locks::thread_suspend_count_lock_->AssertNotHeld(self); if (kDebugLocking && self != nullptr) { - CHECK_NE(self->GetState(), kRunnable); + CHECK_NE(self->GetState(), ThreadState::kRunnable); } // First request that all threads suspend, then wait for them to suspend before @@ -1201,7 +1201,7 @@ void ThreadList::SuspendAllDaemonThreadsForShutdown() { { MutexLock mu(self, *Locks::thread_list_lock_); for (const auto& thread : list_) { - if (thread != self && thread->GetState() == kRunnable) { + if (thread != self && thread->GetState() == ThreadState::kRunnable) { if (!have_complained) { LOG(WARNING) << "daemon thread not yet suspended: " << *thread; have_complained = true; @@ -1236,7 +1236,7 @@ void ThreadList::SuspendAllDaemonThreadsForShutdown() { // touching deallocated memory, but it also prevents mutexes from getting released. Thus we // only do this once we're reasonably sure that no system mutexes are still held. for (const auto& thread : list_) { - DCHECK(thread == self || !all_suspended || thread->GetState() != kRunnable); + DCHECK(thread == self || !all_suspended || thread->GetState() != ThreadState::kRunnable); // In the !all_suspended case, the target is probably sleeping. thread->GetJniEnv()->SetRuntimeDeleted(); // Possibly contended Mutex acquisitions are unsafe after this. @@ -1291,7 +1291,7 @@ void ThreadList::Register(Thread* self) { void ThreadList::Unregister(Thread* self) { DCHECK_EQ(self, Thread::Current()); - CHECK_NE(self->GetState(), kRunnable); + CHECK_NE(self->GetState(), ThreadState::kRunnable); Locks::mutator_lock_->AssertNotHeld(self); VLOG(threads) << "ThreadList::Unregister() " << *self; diff --git a/runtime/thread_state.h b/runtime/thread_state.h index 69b5b5db2b..e03df2752c 100644 --- a/runtime/thread_state.h +++ b/runtime/thread_state.h @@ -25,12 +25,18 @@ namespace art { // When we refer to "a suspended state", or when function names mention "ToSuspended" or // "FromSuspended", we mean any state other than kRunnable, i.e. any state in which the thread is // guaranteed not to access the Java heap. The kSuspended state is merely one of these. -enum ThreadState { +enum class ThreadState : uint8_t { + // `kRunnable` was previously 67 but it is now set to 0 so that we do not need to extract + // flags from the thread's `state_and_flags` to check for any flag being set while Runnable. + // Note: All atomic accesses for a location should use the same data size, + // so the incorrect old approach of reading just 16 bits has been rewritten. + // Java // Thread.State JDWP state kTerminated = 66, // TERMINATED TS_ZOMBIE Thread.run has returned, but Thread* still around - kRunnable, // RUNNABLE TS_RUNNING runnable - kTimedWaiting, // TIMED_WAITING TS_WAIT in Object.wait() with a timeout + kRunnable = 0, // RUNNABLE TS_RUNNING runnable + kObsoleteRunnable = 67, // --- --- obsolete value + kTimedWaiting = 68, // TIMED_WAITING TS_WAIT in Object.wait() with a timeout kSleeping, // TIMED_WAITING TS_SLEEPING in Thread.sleep() kBlocked, // BLOCKED TS_MONITOR blocked on a monitor kWaiting, // WAITING TS_WAIT in Object.wait() diff --git a/test/566-polymorphic-inlining/polymorphic_inline.cc b/test/566-polymorphic-inlining/polymorphic_inline.cc index 34ec3ff86f..02ba04c843 100644 --- a/test/566-polymorphic-inlining/polymorphic_inline.cc +++ b/test/566-polymorphic-inlining/polymorphic_inline.cc @@ -42,7 +42,7 @@ static bool do_checks(ArtMethod* method, ScopedObjectAccess& soa) header = OatQuickMethodHeader::FromEntryPoint(pc); break; } else { - ScopedThreadSuspension sts(soa.Self(), kSuspended); + ScopedThreadSuspension sts(soa.Self(), ThreadState::kSuspended); // Sleep to yield to the compiler thread. usleep(1000); } diff --git a/test/597-deopt-new-string/deopt.cc b/test/597-deopt-new-string/deopt.cc index fe229e4fff..572be28979 100644 --- a/test/597-deopt-new-string/deopt.cc +++ b/test/597-deopt-new-string/deopt.cc @@ -30,7 +30,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_deoptimizeAll( JNIEnv* env, jclass cls ATTRIBUTE_UNUSED) { ScopedObjectAccess soa(env); - ScopedThreadSuspension sts(Thread::Current(), kWaitingForDeoptimization); + ScopedThreadSuspension sts(Thread::Current(), ThreadState::kWaitingForDeoptimization); gc::ScopedGCCriticalSection gcs(Thread::Current(), gc::kGcCauseInstrumentation, gc::kCollectorTypeInstrumentation); @@ -49,7 +49,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_undeoptimizeAll( JNIEnv* env, jclass cls ATTRIBUTE_UNUSED) { ScopedObjectAccess soa(env); - ScopedThreadSuspension sts(Thread::Current(), kWaitingForDeoptimization); + ScopedThreadSuspension sts(Thread::Current(), ThreadState::kWaitingForDeoptimization); gc::ScopedGCCriticalSection gcs(Thread::Current(), gc::kGcCauseInstrumentation, gc::kCollectorTypeInstrumentation); diff --git a/test/706-checker-scheduler/src/Main.java b/test/706-checker-scheduler/src/Main.java index 5a66fbbebc..d4d3923173 100644 --- a/test/706-checker-scheduler/src/Main.java +++ b/test/706-checker-scheduler/src/Main.java @@ -605,7 +605,7 @@ public class Main { /// CHECK: subs /// CHECK: add /// CHECK: adds - /// CHECK: ldrh + /// CHECK: ldr /// CHECK: cmp /// CHECK: beq @@ -613,7 +613,7 @@ public class Main { /// CHECK: sub /// CHECK: add /// CHECK: add - /// CHECK: ldrh + /// CHECK: ldr /// CHECK: cbz private static void testCrossItersDependencies() { int[] data = {1, 2, 3, 0}; diff --git a/tools/cpp-define-generator/thread.def b/tools/cpp-define-generator/thread.def index d472cc455d..fff57550fa 100644 --- a/tools/cpp-define-generator/thread.def +++ b/tools/cpp-define-generator/thread.def @@ -22,9 +22,9 @@ ASM_DEFINE(THREAD_CARD_TABLE_OFFSET, art::Thread::CardTableOffset<art::kRuntimePointerSize>().Int32Value()) ASM_DEFINE(THREAD_CHECKPOINT_REQUEST, - art::kCheckpointRequest) + static_cast<uint32_t>(art::ThreadFlag::kCheckpointRequest)) ASM_DEFINE(THREAD_EMPTY_CHECKPOINT_REQUEST, - art::kEmptyCheckpointRequest) + static_cast<uint32_t>(art::ThreadFlag::kEmptyCheckpointRequest)) ASM_DEFINE(THREAD_EXCEPTION_OFFSET, art::Thread::ExceptionOffset<art::kRuntimePointerSize>().Int32Value()) ASM_DEFINE(THREAD_FLAGS_OFFSET, @@ -56,9 +56,11 @@ ASM_DEFINE(THREAD_ROSALLOC_RUNS_OFFSET, ASM_DEFINE(THREAD_SELF_OFFSET, art::Thread::SelfOffset<art::kRuntimePointerSize>().Int32Value()) ASM_DEFINE(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST, - art::kSuspendRequest | art::kCheckpointRequest | art::kEmptyCheckpointRequest) + static_cast<uint32_t>(art::ThreadFlag::kSuspendRequest) | + static_cast<uint32_t>(art::ThreadFlag::kCheckpointRequest) | + static_cast<uint32_t>(art::ThreadFlag::kEmptyCheckpointRequest)) ASM_DEFINE(THREAD_SUSPEND_REQUEST, - art::kSuspendRequest) + static_cast<uint32_t>(art::ThreadFlag::kSuspendRequest)) ASM_DEFINE(THREAD_TOP_QUICK_FRAME_OFFSET, art::Thread::TopOfManagedStackOffset<art::kRuntimePointerSize>().Int32Value()) ASM_DEFINE(THREAD_ALLOC_OBJECT_ENTRYPOINT_OFFSET, |