From 4e2cb098017bf073335ebb02b1bc0a36828cd720 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 22 Jul 2015 16:17:51 -0700 Subject: [PATCH] Add uninterruptible role Example error: cannot call function 'CopyOf' while mutex 'uninterruptible_' is held TODO: Add annotation to more locations. Bug: 20072211 Change-Id: I1bbf5a77e3deeafa5898df529cb7cb53a6d010d2 --- runtime/base/mutex.h | 24 ++++++++++++++--- runtime/class_linker.cc | 4 +-- runtime/class_linker.h | 62 ++++++++++++++++++++++--------------------- runtime/gc/heap.h | 9 ++++--- runtime/mirror/array.h | 9 ++++--- runtime/mirror/class.h | 6 ++--- runtime/mirror/object.h | 3 ++- runtime/mirror/object_array.h | 6 ++--- runtime/thread.cc | 13 ++++----- runtime/thread.h | 14 +++++----- 10 files changed, 86 insertions(+), 64 deletions(-) diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index 6f82f28a1..d0504d993 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -471,11 +471,11 @@ class ConditionVariable { // upon destruction. class SCOPED_CAPABILITY MutexLock { public: - explicit MutexLock(Thread* self, Mutex& mu) EXCLUSIVE_LOCK_FUNCTION(mu) : self_(self), mu_(mu) { + explicit MutexLock(Thread* self, Mutex& mu) ACQUIRE(mu) : self_(self), mu_(mu) { mu_.ExclusiveLock(self_); } - ~MutexLock() UNLOCK_FUNCTION() { + ~MutexLock() RELEASE() { mu_.ExclusiveUnlock(self_); } @@ -491,12 +491,12 @@ class SCOPED_CAPABILITY MutexLock { // construction and releases it upon destruction. class SCOPED_CAPABILITY ReaderMutexLock { public: - explicit ReaderMutexLock(Thread* self, ReaderWriterMutex& mu) EXCLUSIVE_LOCK_FUNCTION(mu) : + explicit ReaderMutexLock(Thread* self, ReaderWriterMutex& mu) ACQUIRE(mu) : self_(self), mu_(mu) { mu_.SharedLock(self_); } - ~ReaderMutexLock() UNLOCK_FUNCTION() { + ~ReaderMutexLock() RELEASE() { mu_.SharedUnlock(self_); } @@ -531,6 +531,17 @@ class SCOPED_CAPABILITY WriterMutexLock { // "WriterMutexLock mu(lock)". #define WriterMutexLock(x) static_assert(0, "WriterMutexLock declaration missing variable name") +// For StartNoThreadSuspension and EndNoThreadSuspension. +class CAPABILITY("role") Role { + public: + void Acquire() ACQUIRE() {} + void Release() RELEASE() {} + const Role& operator!() const { return *this; } +}; + +class Uninterruptible : public Role { +}; + // Global mutexes corresponding to the levels above. class Locks { public: @@ -666,6 +677,11 @@ class Locks { static Mutex* lambda_table_lock_ ACQUIRED_AFTER(mutator_lock_); }; +class Roles { + public: + static Uninterruptible uninterruptible_; +}; + } // namespace art #endif // ART_RUNTIME_BASE_MUTEX_H_ diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index e0be5af77..fc59d5019 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -3447,8 +3447,7 @@ std::string ClassLinker::GetDescriptorForProxy(mirror::Class* proxy_class) { return DotToDescriptor(name->ToModifiedUtf8().c_str()); } -ArtMethod* ClassLinker::FindMethodForProxy(mirror::Class* proxy_class, - ArtMethod* proxy_method) { +ArtMethod* ClassLinker::FindMethodForProxy(mirror::Class* proxy_class, ArtMethod* proxy_method) { DCHECK(proxy_class->IsProxyClass()); DCHECK(proxy_method->IsProxyMethod()); { @@ -4984,6 +4983,7 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self, Handle klass self, old_virtuals, old_method_count * method_size, new_method_count * method_size)); if (UNLIKELY(virtuals == nullptr)) { self->AssertPendingOOMException(); + self->EndAssertNoThreadSuspension(old_cause); return false; } ScopedArenaUnorderedMap move_table(allocator.Adapter()); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 0239b64a8..05a809e52 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -195,16 +195,16 @@ class ClassLinker { // result in the DexCache. The referrer is used to identity the // target DexCache and ClassLoader to use for resolution. mirror::Class* ResolveType(const DexFile& dex_file, uint16_t type_idx, mirror::Class* referrer) - SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_); // Resolve a Type with the given index from the DexFile, storing the // result in the DexCache. The referrer is used to identify the // target DexCache and ClassLoader to use for resolution. mirror::Class* ResolveType(uint16_t type_idx, ArtMethod* referrer) - SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_); mirror::Class* ResolveType(uint16_t type_idx, ArtField* referrer) - SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_); // Resolve a type with the given ID from the DexFile, storing the // result in DexCache. The ClassLoader is used to search for the @@ -213,7 +213,7 @@ class ClassLinker { mirror::Class* ResolveType(const DexFile& dex_file, uint16_t type_idx, Handle dex_cache, Handle class_loader) - SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_); // Resolve a method with a given ID from the DexFile, storing the // result in DexCache. The ClassLinker and ClassLoader are used as @@ -224,19 +224,19 @@ class ClassLinker { Handle dex_cache, Handle class_loader, ArtMethod* referrer, InvokeType type) - SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_); ArtMethod* GetResolvedMethod(uint32_t method_idx, ArtMethod* referrer) SHARED_REQUIRES(Locks::mutator_lock_); ArtMethod* ResolveMethod(Thread* self, uint32_t method_idx, ArtMethod* referrer, InvokeType type) - SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_); ArtField* GetResolvedField(uint32_t field_idx, mirror::Class* field_declaring_class) SHARED_REQUIRES(Locks::mutator_lock_); ArtField* GetResolvedField(uint32_t field_idx, mirror::DexCache* dex_cache) SHARED_REQUIRES(Locks::mutator_lock_); ArtField* ResolveField(uint32_t field_idx, ArtMethod* referrer, bool is_static) - SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_); // Resolve a field with a given ID from the DexFile, storing the // result in DexCache. The ClassLinker and ClassLoader are used as @@ -246,7 +246,7 @@ class ClassLinker { ArtField* ResolveField(const DexFile& dex_file, uint32_t field_idx, Handle dex_cache, Handle class_loader, bool is_static) - SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_); // Resolve a field with a given ID from the DexFile, storing the // result in DexCache. The ClassLinker and ClassLoader are used as @@ -255,7 +255,7 @@ class ClassLinker { ArtField* ResolveFieldJLS(const DexFile& dex_file, uint32_t field_idx, Handle dex_cache, Handle class_loader) - SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_); // Get shorty from method index without resolution. Used to do handlerization. const char* MethodShorty(uint32_t method_idx, ArtMethod* referrer, uint32_t* length) @@ -266,11 +266,12 @@ class ClassLinker { // given the restriction that no execution is possible. bool EnsureInitialized(Thread* self, Handle c, bool can_init_fields, bool can_init_parents) - SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_); // Initializes classes that have instances in the image but that have // methods so they could not be initialized by the compiler. - void RunRootClinits() SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); + void RunRootClinits() SHARED_REQUIRES(Locks::mutator_lock_) + REQUIRES(!dex_lock_, !Roles::uninterruptible_); void RegisterDexFile(const DexFile& dex_file) REQUIRES(!dex_lock_) SHARED_REQUIRES(Locks::mutator_lock_); @@ -330,33 +331,33 @@ class ClassLinker { REQUIRES(!dex_lock_, !Locks::mutator_lock_); // Allocate an instance of a java.lang.Object. - mirror::Object* AllocObject(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_); + mirror::Object* AllocObject(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) + REQUIRES(!Roles::uninterruptible_); // TODO: replace this with multiple methods that allocate the correct managed type. template mirror::ObjectArray* AllocObjectArray(Thread* self, size_t length) - SHARED_REQUIRES(Locks::mutator_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); mirror::ObjectArray* AllocClassArray(Thread* self, size_t length) - SHARED_REQUIRES(Locks::mutator_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); mirror::ObjectArray* AllocStringArray(Thread* self, size_t length) - SHARED_REQUIRES(Locks::mutator_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); + + ArtField* AllocArtFieldArray(Thread* self, size_t length); ArtMethod* AllocArtMethodArray(Thread* self, size_t length); mirror::PointerArray* AllocPointerArray(Thread* self, size_t length) - SHARED_REQUIRES(Locks::mutator_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); mirror::IfTable* AllocIfTable(Thread* self, size_t ifcount) - SHARED_REQUIRES(Locks::mutator_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - ArtField* AllocArtFieldArray(Thread* self, size_t length) - SHARED_REQUIRES(Locks::mutator_lock_); - - mirror::ObjectArray* AllocStackTraceElementArray(Thread* self, - size_t length) - SHARED_REQUIRES(Locks::mutator_lock_); + mirror::ObjectArray* AllocStackTraceElementArray( + Thread* self, size_t length) SHARED_REQUIRES(Locks::mutator_lock_) + REQUIRES(!Roles::uninterruptible_); void VerifyClass(Thread* self, Handle klass) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); @@ -479,28 +480,29 @@ class ClassLinker { REQUIRES(!dex_lock_) SHARED_REQUIRES(Locks::mutator_lock_); - void FinishInit(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); + void FinishInit(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) + REQUIRES(!dex_lock_, !Roles::uninterruptible_); // For early bootstrapping by Init mirror::Class* AllocClass(Thread* self, mirror::Class* java_lang_Class, uint32_t class_size) - SHARED_REQUIRES(Locks::mutator_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); // Alloc* convenience functions to avoid needing to pass in mirror::Class* // values that are known to the ClassLinker such as // kObjectArrayClass and kJavaLangString etc. mirror::Class* AllocClass(Thread* self, uint32_t class_size) - SHARED_REQUIRES(Locks::mutator_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); mirror::DexCache* AllocDexCache(Thread* self, const DexFile& dex_file) - SHARED_REQUIRES(Locks::mutator_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); mirror::Class* CreatePrimitiveClass(Thread* self, Primitive::Type type) - SHARED_REQUIRES(Locks::mutator_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); mirror::Class* InitializePrimitiveClass(mirror::Class* primitive_class, Primitive::Type type) - SHARED_REQUIRES(Locks::mutator_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); mirror::Class* CreateArrayClass(Thread* self, const char* descriptor, size_t hash, Handle class_loader) - SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_, !Roles::uninterruptible_); void AppendToBootClassPath(Thread* self, const DexFile& dex_file) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 790a98cd6..09c18b8f7 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -189,7 +189,8 @@ class Heap { mirror::Object* AllocObject(Thread* self, mirror::Class* klass, size_t num_bytes, const PreFenceVisitor& pre_fence_visitor) SHARED_REQUIRES(Locks::mutator_lock_) - REQUIRES(!*gc_complete_lock_, !*pending_task_lock_, !*backtrace_lock_) { + REQUIRES(!*gc_complete_lock_, !*pending_task_lock_, !*backtrace_lock_, + !Roles::uninterruptible_) { return AllocObjectWithAllocator( self, klass, num_bytes, GetCurrentAllocator(), pre_fence_visitor); } @@ -198,7 +199,8 @@ class Heap { mirror::Object* AllocNonMovableObject(Thread* self, mirror::Class* klass, size_t num_bytes, const PreFenceVisitor& pre_fence_visitor) SHARED_REQUIRES(Locks::mutator_lock_) - REQUIRES(!*gc_complete_lock_, !*pending_task_lock_, !*backtrace_lock_) { + REQUIRES(!*gc_complete_lock_, !*pending_task_lock_, !*backtrace_lock_, + !Roles::uninterruptible_) { return AllocObjectWithAllocator( self, klass, num_bytes, GetCurrentNonMovingAllocator(), pre_fence_visitor); } @@ -208,7 +210,8 @@ class Heap { Thread* self, mirror::Class* klass, size_t byte_count, AllocatorType allocator, const PreFenceVisitor& pre_fence_visitor) SHARED_REQUIRES(Locks::mutator_lock_) - REQUIRES(!*gc_complete_lock_, !*pending_task_lock_, !*backtrace_lock_); + REQUIRES(!*gc_complete_lock_, !*pending_task_lock_, !*backtrace_lock_, + !Roles::uninterruptible_); AllocatorType GetCurrentAllocator() const { return current_allocator_; diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h index 08c12e974..d21cfae34 100644 --- a/runtime/mirror/array.h +++ b/runtime/mirror/array.h @@ -39,11 +39,11 @@ class MANAGED Array : public Object { template ALWAYS_INLINE static Array* Alloc(Thread* self, Class* array_class, int32_t component_count, size_t component_size_shift, gc::AllocatorType allocator_type) - SHARED_REQUIRES(Locks::mutator_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); static Array* CreateMultiArray(Thread* self, Handle element_class, Handle dimensions) - SHARED_REQUIRES(Locks::mutator_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); template @@ -84,7 +84,8 @@ class MANAGED Array : public Object { template ALWAYS_INLINE bool CheckIsValidIndex(int32_t index) SHARED_REQUIRES(Locks::mutator_lock_); - Array* CopyOf(Thread* self, int32_t new_length) SHARED_REQUIRES(Locks::mutator_lock_); + Array* CopyOf(Thread* self, int32_t new_length) SHARED_REQUIRES(Locks::mutator_lock_) + REQUIRES(!Roles::uninterruptible_); protected: void ThrowArrayStoreException(Object* object) SHARED_REQUIRES(Locks::mutator_lock_); @@ -107,7 +108,7 @@ class MANAGED PrimitiveArray : public Array { typedef T ElementType; static PrimitiveArray* Alloc(Thread* self, size_t length) - SHARED_REQUIRES(Locks::mutator_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); const T* GetData() const ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) { return reinterpret_cast(GetRawData(sizeof(T), 0)); diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index be87dee81..f1eb9be7f 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -479,12 +479,12 @@ class MANAGED Class FINAL : public Object { // Creates a raw object instance but does not invoke the default constructor. template ALWAYS_INLINE Object* Alloc(Thread* self, gc::AllocatorType allocator_type) - SHARED_REQUIRES(Locks::mutator_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); Object* AllocObject(Thread* self) - SHARED_REQUIRES(Locks::mutator_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); Object* AllocNonMovableObject(Thread* self) - SHARED_REQUIRES(Locks::mutator_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); template diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index 8b541fdf5..eea9f3751 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -112,7 +112,8 @@ class MANAGED LOCKABLE Object { ReadBarrierOption kReadBarrierOption = kWithReadBarrier> size_t SizeOf() SHARED_REQUIRES(Locks::mutator_lock_); - Object* Clone(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_); + Object* Clone(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) + REQUIRES(!Roles::uninterruptible_); int32_t IdentityHashCode() const SHARED_REQUIRES(Locks::mutator_lock_) diff --git a/runtime/mirror/object_array.h b/runtime/mirror/object_array.h index cee17e993..607b00048 100644 --- a/runtime/mirror/object_array.h +++ b/runtime/mirror/object_array.h @@ -32,10 +32,10 @@ class MANAGED ObjectArray: public Array { static ObjectArray* Alloc(Thread* self, Class* object_array_class, int32_t length, gc::AllocatorType allocator_type) - SHARED_REQUIRES(Locks::mutator_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); static ObjectArray* Alloc(Thread* self, Class* object_array_class, int32_t length) - SHARED_REQUIRES(Locks::mutator_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); T* Get(int32_t i) ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_); @@ -81,7 +81,7 @@ class MANAGED ObjectArray: public Array { SHARED_REQUIRES(Locks::mutator_lock_); ObjectArray* CopyOf(Thread* self, int32_t new_length) - SHARED_REQUIRES(Locks::mutator_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); // TODO fix thread safety analysis broken by the use of template. This should be // SHARED_REQUIRES(Locks::mutator_lock_). diff --git a/runtime/thread.cc b/runtime/thread.cc index 2af31ccd7..6949b0bd3 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1808,26 +1808,23 @@ class BuildInternalStackTraceVisitor : public StackVisitor { trace_(nullptr), pointer_size_(Runtime::Current()->GetClassLinker()->GetImagePointerSize()) {} - bool Init(int depth) - SHARED_REQUIRES(Locks::mutator_lock_) { + bool Init(int depth) SHARED_REQUIRES(Locks::mutator_lock_) ACQUIRE(Roles::uninterruptible_) { // Allocate method trace with format [method pointers][pcs]. auto* cl = Runtime::Current()->GetClassLinker(); trace_ = cl->AllocPointerArray(self_, depth * 2); + const char* last_no_suspend_cause = + self_->StartAssertNoThreadSuspension("Building internal stack trace"); if (trace_ == nullptr) { self_->AssertPendingOOMException(); return false; } // If We are called from native, use non-transactional mode. - const char* last_no_suspend_cause = - self_->StartAssertNoThreadSuspension("Building internal stack trace"); CHECK(last_no_suspend_cause == nullptr) << last_no_suspend_cause; return true; } - virtual ~BuildInternalStackTraceVisitor() { - if (trace_ != nullptr) { - self_->EndAssertNoThreadSuspension(nullptr); - } + virtual ~BuildInternalStackTraceVisitor() RELEASE(Roles::uninterruptible_) { + self_->EndAssertNoThreadSuspension(nullptr); } bool VisitFrame() SHARED_REQUIRES(Locks::mutator_lock_) { diff --git a/runtime/thread.h b/runtime/thread.h index 06934ea75..0aa4b5b86 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -253,12 +253,13 @@ class Thread { // Transition from runnable into a state where mutator privileges are denied. Releases share of // mutator lock. void TransitionFromRunnableToSuspended(ThreadState new_state) - REQUIRES(!Locks::thread_suspend_count_lock_) + REQUIRES(!Locks::thread_suspend_count_lock_, !Roles::uninterruptible_) UNLOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE; // Once called thread suspension will cause an assertion failure. - const char* StartAssertNoThreadSuspension(const char* cause) { + const char* StartAssertNoThreadSuspension(const char* cause) ACQUIRE(Roles::uninterruptible_) { + Roles::uninterruptible_.Acquire(); // No-op. if (kIsDebugBuild) { CHECK(cause != nullptr); const char* previous_cause = tlsPtr_.last_no_thread_suspension_cause; @@ -271,13 +272,14 @@ class Thread { } // End region where no thread suspension is expected. - void EndAssertNoThreadSuspension(const char* old_cause) { + void EndAssertNoThreadSuspension(const char* old_cause) RELEASE(Roles::uninterruptible_) { if (kIsDebugBuild) { CHECK(old_cause != nullptr || tls32_.no_thread_suspension == 1); CHECK_GT(tls32_.no_thread_suspension, 0U); tls32_.no_thread_suspension--; tlsPtr_.last_no_thread_suspension_cause = old_cause; } + Roles::uninterruptible_.Release(); // No-op. } void AssertThreadSuspensionIsAllowable(bool check_locks = true) const; @@ -1342,12 +1344,12 @@ class Thread { DISALLOW_COPY_AND_ASSIGN(Thread); }; -class ScopedAssertNoThreadSuspension { +class SCOPED_CAPABILITY ScopedAssertNoThreadSuspension { public: - ScopedAssertNoThreadSuspension(Thread* self, const char* cause) + ScopedAssertNoThreadSuspension(Thread* self, const char* cause) ACQUIRE(Roles::uninterruptible_) : self_(self), old_cause_(self->StartAssertNoThreadSuspension(cause)) { } - ~ScopedAssertNoThreadSuspension() { + ~ScopedAssertNoThreadSuspension() RELEASE(Roles::uninterruptible_) { self_->EndAssertNoThreadSuspension(old_cause_); } Thread* Self() { -- 2.11.0