diff options
author | 2012-09-27 16:03:43 -0700 | |
---|---|---|
committer | 2012-09-27 17:19:25 -0700 | |
commit | 81d425b0b232962441616f8b14f73620bffef5e5 (patch) | |
tree | cd1e46656269acf20e78817d675e56d9b9133e6c | |
parent | bfaf917edbb1de8d158c3615e0da8ac3143d10c8 (diff) |
Pass self to lock methods.
This avoids frequent recomputation of
Thread::Current/pthread_getspecific.
Also add a futex based reader/writer mutex that is disabled.
Change-Id: I118fdb99ef1d1c4bfda6446ba3a0d8b6ab31eaee
39 files changed, 839 insertions, 609 deletions
diff --git a/build/Android.common.mk b/build/Android.common.mk index c5e45e3c2d..78743a7768 100644 --- a/build/Android.common.mk +++ b/build/Android.common.mk @@ -196,6 +196,7 @@ LIBART_COMMON_SRC_FILES := \ src/jdwp/jdwp_socket.cc \ src/jni_internal.cc \ src/jobject_comparator.cc \ + src/locks.cc \ src/logging.cc \ src/mark_stack.cc \ src/mark_sweep.cc \ @@ -378,6 +379,7 @@ LIBART_ENUM_OPERATOR_OUT_HEADER_FILES := \ src/invoke_type.h \ src/jdwp/jdwp.h \ src/jdwp/jdwp_constants.h \ + src/locks.h \ src/mutex.h \ src/object.h \ src/thread.h \ diff --git a/src/compiler.h b/src/compiler.h index a2cc317160..c5f19f7be9 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -27,6 +27,7 @@ #include "dex_file.h" #include "instruction_set.h" #include "invoke_type.h" +#include "mutex.h" #include "oat_file.h" #include "object.h" #include "runtime.h" diff --git a/src/debugger.cc b/src/debugger.cc index aad75b1493..8477054299 100644 --- a/src/debugger.cc +++ b/src/debugger.cc @@ -2416,7 +2416,7 @@ JDWP::JdwpError Dbg::InvokeMethod(JDWP::ObjectId threadId, JDWP::ObjectId object // Wait for the request to finish executing. while (req->invoke_needed_) { - req->cond_.Wait(req->lock_); + req->cond_.Wait(self, req->lock_); } } VLOG(jdwp) << " Control has returned from event thread"; diff --git a/src/dex_file.h b/src/dex_file.h index fad6fa9cb9..cca3935722 100644 --- a/src/dex_file.h +++ b/src/dex_file.h @@ -26,7 +26,6 @@ #include "logging.h" #include "mem_map.h" #include "modifiers.h" -#include "mutex.h" #include "safe_map.h" #include "stringpiece.h" #include "UniquePtr.h" diff --git a/src/heap.cc b/src/heap.cc index 0f9b65b872..3ab6419f6a 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -461,7 +461,7 @@ bool Heap::IsHeapAddress(const Object* obj) { } bool Heap::IsLiveObjectLocked(const Object* obj) { - Locks::heap_bitmap_lock_->AssertReaderHeld(); + Locks::heap_bitmap_lock_->AssertReaderHeld(Thread::Current()); return IsHeapAddress(obj) && GetLiveBitmap()->Test(obj); } @@ -604,10 +604,7 @@ Object* Heap::Allocate(AllocSpace* space, size_t alloc_size) { // done in the runnable state where suspension is expected. #ifndef NDEBUG Thread* self = Thread::Current(); - { - MutexLock mu(*Locks::thread_suspend_count_lock_); - CHECK_EQ(self->GetState(), kRunnable); - } + DCHECK_EQ(self->GetState(), kRunnable); self->AssertThreadSuspensionIsAllowable(); #endif @@ -618,7 +615,10 @@ Object* Heap::Allocate(AllocSpace* space, size_t alloc_size) { // The allocation failed. If the GC is running, block until it completes, and then retry the // allocation. - GcType last_gc = WaitForConcurrentGcToComplete(); +#ifdef NDEBUG + Thread* self = Thread::Current(); +#endif + GcType last_gc = WaitForConcurrentGcToComplete(self); if (last_gc != kGcTypeNone) { // A GC was in progress and we blocked, retry allocation now that memory has been freed. ptr = TryToAllocate(space, alloc_size, false); @@ -628,9 +628,6 @@ Object* Heap::Allocate(AllocSpace* space, size_t alloc_size) { } // Loop through our different Gc types and try to Gc until we get enough free memory. -#ifdef NDEBUG - Thread* self = Thread::Current(); -#endif for (size_t i = static_cast<size_t>(last_gc) + 1; i < static_cast<size_t>(kGcTypeMax); ++i) { bool run_gc = false; GcType gc_type = static_cast<GcType>(i); @@ -772,14 +769,16 @@ int64_t Heap::CountInstances(Class* c, bool count_assignable) { void Heap::CollectGarbage(bool clear_soft_references) { // Even if we waited for a GC we still need to do another GC since weaks allocated during the // last GC will not have necessarily been cleared. - WaitForConcurrentGcToComplete(); - ScopedThreadStateChange tsc(Thread::Current(), kWaitingPerformingGc); + Thread* self = Thread::Current(); + WaitForConcurrentGcToComplete(self); + ScopedThreadStateChange tsc(self, kWaitingPerformingGc); CollectGarbageInternal(have_zygote_space_ ? kGcTypePartial : kGcTypeFull, clear_soft_references); } void Heap::PreZygoteFork() { static Mutex zygote_creation_lock_("zygote creation lock", kZygoteCreationLock); - MutexLock mu(zygote_creation_lock_); + Thread* self = Thread::Current(); + MutexLock mu(self, zygote_creation_lock_); // Try to see if we have any Zygote spaces. if (have_zygote_space_) { @@ -790,7 +789,7 @@ void Heap::PreZygoteFork() { { // Flush the alloc stack. - WriterMutexLock mu(*Locks::heap_bitmap_lock_); + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); FlushAllocStack(); } @@ -866,31 +865,27 @@ void Heap::UnMarkAllocStack(SpaceBitmap* bitmap, SpaceSetMap* large_objects, Mar } GcType Heap::CollectGarbageInternal(GcType gc_type, bool clear_soft_references) { - Locks::mutator_lock_->AssertNotHeld(); -#ifndef NDEBUG - { - MutexLock mu(*Locks::thread_suspend_count_lock_); - CHECK_EQ(Thread::Current()->GetState(), kWaitingPerformingGc); - } -#endif + Thread* self = Thread::Current(); + Locks::mutator_lock_->AssertNotHeld(self); + DCHECK_EQ(self->GetState(), kWaitingPerformingGc); // Ensure there is only one GC at a time. bool start_collect = false; while (!start_collect) { { - MutexLock mu(*gc_complete_lock_); + MutexLock mu(self, *gc_complete_lock_); if (!is_gc_running_) { is_gc_running_ = true; start_collect = true; } } if (!start_collect) { - WaitForConcurrentGcToComplete(); + WaitForConcurrentGcToComplete(self); // TODO: if another thread beat this one to do the GC, perhaps we should just return here? // Not doing at the moment to ensure soft references are cleared. } } - gc_complete_lock_->AssertNotHeld(); + gc_complete_lock_->AssertNotHeld(self); // We need to do partial GCs every now and then to avoid the heap growing too much and // fragmenting. @@ -902,14 +897,14 @@ GcType Heap::CollectGarbageInternal(GcType gc_type, bool clear_soft_references) } if (concurrent_gc_) { - CollectGarbageConcurrentMarkSweepPlan(gc_type, clear_soft_references); + CollectGarbageConcurrentMarkSweepPlan(self, gc_type, clear_soft_references); } else { - CollectGarbageMarkSweepPlan(gc_type, clear_soft_references); + CollectGarbageMarkSweepPlan(self, gc_type, clear_soft_references); } bytes_since_last_gc_ = 0; { - MutexLock mu(*gc_complete_lock_); + MutexLock mu(self, *gc_complete_lock_); is_gc_running_ = false; last_gc_type_ = gc_type; // Wake anyone who may have been waiting for the GC to complete. @@ -920,7 +915,7 @@ GcType Heap::CollectGarbageInternal(GcType gc_type, bool clear_soft_references) return gc_type; } -void Heap::CollectGarbageMarkSweepPlan(GcType gc_type, bool clear_soft_references) { +void Heap::CollectGarbageMarkSweepPlan(Thread* self, GcType gc_type, bool clear_soft_references) { TimingLogger timings("CollectGarbageInternal", true); std::stringstream gc_type_str; @@ -931,7 +926,7 @@ void Heap::CollectGarbageMarkSweepPlan(GcType gc_type, bool clear_soft_reference ThreadList* thread_list = Runtime::Current()->GetThreadList(); thread_list->SuspendAll(); timings.AddSplit("SuspendAll"); - Locks::mutator_lock_->AssertExclusiveHeld(); + Locks::mutator_lock_->AssertExclusiveHeld(self); size_t bytes_freed = 0; Object* cleared_references = NULL; @@ -942,7 +937,7 @@ void Heap::CollectGarbageMarkSweepPlan(GcType gc_type, bool clear_soft_reference timings.AddSplit("Init"); if (verify_pre_gc_heap_) { - WriterMutexLock mu(*Locks::heap_bitmap_lock_); + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); if (!VerifyHeapReferences()) { LOG(FATAL) << "Pre " << gc_type_str.str() << "Gc verification failed"; } @@ -1049,7 +1044,7 @@ void Heap::CollectGarbageMarkSweepPlan(GcType gc_type, bool clear_soft_reference const bool swap = true; if (swap) { - SwapBitmaps(); + SwapBitmaps(self); } #ifndef NDEBUG @@ -1077,7 +1072,7 @@ void Heap::CollectGarbageMarkSweepPlan(GcType gc_type, bool clear_soft_reference } if (verify_post_gc_heap_) { - WriterMutexLock mu(*Locks::heap_bitmap_lock_); + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); if (!VerifyHeapReferences()) { LOG(FATAL) << "Post " + gc_type_str.str() + "Gc verification failed"; } @@ -1286,7 +1281,7 @@ class VerifyObjectVisitor { // Must do this with mutators suspended since we are directly accessing the allocation stacks. bool Heap::VerifyHeapReferences() { - Locks::mutator_lock_->AssertExclusiveHeld(); + Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current()); // Lets sort our allocation stacks so that we can efficiently binary search them. std::sort(allocation_stack_->Begin(), allocation_stack_->End()); std::sort(live_stack_->Begin(), live_stack_->End()); @@ -1389,7 +1384,7 @@ class VerifyLiveStackReferences { }; bool Heap::VerifyMissingCardMarks() { - Locks::mutator_lock_->AssertExclusiveHeld(); + Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current()); VerifyLiveStackReferences visitor(this); GetLiveBitmap()->Visit(visitor); @@ -1406,12 +1401,12 @@ bool Heap::VerifyMissingCardMarks() { return true; } -void Heap::SwapBitmaps() { +void Heap::SwapBitmaps(Thread* self) { // Swap the live and mark bitmaps for each alloc space. This is needed since sweep re-swaps // these bitmaps. Doing this enables us to sweep with the heap unlocked since new allocations // set the live bit, but since we have the bitmaps reversed at this point, this sets the mark bit // instead, resulting in no new allocated objects being incorrectly freed by sweep. - WriterMutexLock mu(*Locks::heap_bitmap_lock_); + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); for (Spaces::iterator it = spaces_.begin(); it != spaces_.end(); ++it) { Space* space = *it; // We never allocate into zygote spaces. @@ -1438,7 +1433,7 @@ void Heap::SwapStacks() { } } -void Heap::CollectGarbageConcurrentMarkSweepPlan(GcType gc_type, bool clear_soft_references) { +void Heap::CollectGarbageConcurrentMarkSweepPlan(Thread* self, GcType gc_type, bool clear_soft_references) { TimingLogger timings("ConcurrentCollectGarbageInternal", true); uint64_t root_begin = NanoTime(), root_end = 0, dirty_begin = 0, dirty_end = 0; std::stringstream gc_type_str; @@ -1448,7 +1443,7 @@ void Heap::CollectGarbageConcurrentMarkSweepPlan(GcType gc_type, bool clear_soft ThreadList* thread_list = Runtime::Current()->GetThreadList(); thread_list->SuspendAll(); timings.AddSplit("SuspendAll"); - Locks::mutator_lock_->AssertExclusiveHeld(); + Locks::mutator_lock_->AssertExclusiveHeld(self); size_t bytes_freed = 0; Object* cleared_references = NULL; @@ -1460,7 +1455,7 @@ void Heap::CollectGarbageConcurrentMarkSweepPlan(GcType gc_type, bool clear_soft timings.AddSplit("Init"); if (verify_pre_gc_heap_) { - WriterMutexLock mu(*Locks::heap_bitmap_lock_); + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); if (!VerifyHeapReferences()) { LOG(FATAL) << "Pre " << gc_type_str.str() << "Gc verification failed"; } @@ -1472,7 +1467,7 @@ void Heap::CollectGarbageConcurrentMarkSweepPlan(GcType gc_type, bool clear_soft // Check that all objects which reference things in the live stack are on dirty cards. if (verify_missing_card_marks_) { - ReaderMutexLock mu(*Locks::heap_bitmap_lock_); + ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); // Sort the live stack so that we can quickly binary search it later. std::sort(live_stack_->Begin(), live_stack_->End()); if (!VerifyMissingCardMarks()) { @@ -1509,7 +1504,7 @@ void Heap::CollectGarbageConcurrentMarkSweepPlan(GcType gc_type, bool clear_soft } { - WriterMutexLock mu(*Locks::heap_bitmap_lock_); + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); for (Object** it = live_stack_->Begin(); it != live_stack_->End(); ++it) { CHECK(!GetLiveBitmap()->Test(*it)); @@ -1560,11 +1555,11 @@ void Heap::CollectGarbageConcurrentMarkSweepPlan(GcType gc_type, bool clear_soft // Allow mutators to go again, acquire share on mutator_lock_ to continue. thread_list->ResumeAll(); { - ReaderMutexLock reader_lock(*Locks::mutator_lock_); + ReaderMutexLock reader_lock(self, *Locks::mutator_lock_); root_end = NanoTime(); timings.AddSplit("RootEnd"); - WriterMutexLock mu(*Locks::heap_bitmap_lock_); + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); UpdateAndMarkModUnion(timings, gc_type); // Mark everything as live so that sweeping system weak works correctly for sticky mark bit @@ -1585,10 +1580,10 @@ void Heap::CollectGarbageConcurrentMarkSweepPlan(GcType gc_type, bool clear_soft dirty_begin = NanoTime(); thread_list->SuspendAll(); timings.AddSplit("ReSuspend"); - Locks::mutator_lock_->AssertExclusiveHeld(); + Locks::mutator_lock_->AssertExclusiveHeld(self); { - WriterMutexLock mu(*Locks::heap_bitmap_lock_); + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); // Re-mark root set. mark_sweep.ReMarkRoots(); @@ -1607,7 +1602,7 @@ void Heap::CollectGarbageConcurrentMarkSweepPlan(GcType gc_type, bool clear_soft } { - ReaderMutexLock mu(*Locks::heap_bitmap_lock_); + ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); mark_sweep.ProcessReferences(clear_soft_references); timings.AddSplit("ProcessReferences"); @@ -1623,15 +1618,15 @@ void Heap::CollectGarbageConcurrentMarkSweepPlan(GcType gc_type, bool clear_soft // bit instead, resulting in no new allocated objects being incorrectly freed by sweep. const bool swap = true; if (swap) { - SwapBitmaps(); + SwapBitmaps(self); } // Only need to do this if we have the card mark verification on, and only during concurrent GC. if (verify_missing_card_marks_) { - WriterMutexLock mu(*Locks::heap_bitmap_lock_); + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); mark_sweep.SweepArray(timings, allocation_stack_.get(), swap); } else { - WriterMutexLock mu(*Locks::heap_bitmap_lock_); + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); // We only sweep over the live stack, and the live stack should not intersect with the // allocation stack, so it should be safe to UnMark anything in the allocation stack as live. UnMarkAllocStack(alloc_space_->GetLiveBitmap(), large_object_space_->GetLiveObjects(), @@ -1641,13 +1636,13 @@ void Heap::CollectGarbageConcurrentMarkSweepPlan(GcType gc_type, bool clear_soft if (kIsDebugBuild) { // Verify that we only reach marked objects from the image space. - ReaderMutexLock mu(*Locks::heap_bitmap_lock_); + ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); mark_sweep.VerifyImageRoots(); timings.AddSplit("VerifyImageRoots"); } if (verify_post_gc_heap_) { - WriterMutexLock mu(*Locks::heap_bitmap_lock_); + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); if (!VerifyHeapReferences()) { LOG(FATAL) << "Post " << gc_type_str.str() << "Gc verification failed"; } @@ -1656,11 +1651,11 @@ void Heap::CollectGarbageConcurrentMarkSweepPlan(GcType gc_type, bool clear_soft thread_list->ResumeAll(); dirty_end = NanoTime(); - Locks::mutator_lock_->AssertNotHeld(); + Locks::mutator_lock_->AssertNotHeld(self); { // TODO: this lock shouldn't be necessary (it's why we did the bitmap flip above). - WriterMutexLock mu(*Locks::heap_bitmap_lock_); + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); if (gc_type != kGcTypeSticky) { mark_sweep.SweepLargeObjects(swap); timings.AddSplit("SweepLargeObjects"); @@ -1674,7 +1669,7 @@ void Heap::CollectGarbageConcurrentMarkSweepPlan(GcType gc_type, bool clear_soft } if (verify_system_weaks_) { - ReaderMutexLock mu(*Locks::heap_bitmap_lock_); + ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); mark_sweep.VerifySystemWeaks(); timings.AddSplit("VerifySystemWeaks"); } @@ -1714,23 +1709,23 @@ void Heap::CollectGarbageConcurrentMarkSweepPlan(GcType gc_type, bool clear_soft logger->End(); // Next iteration. } -GcType Heap::WaitForConcurrentGcToComplete() { +GcType Heap::WaitForConcurrentGcToComplete(Thread* self) { GcType last_gc_type = kGcTypeNone; if (concurrent_gc_) { bool do_wait; uint64_t wait_start = NanoTime(); { // Check if GC is running holding gc_complete_lock_. - MutexLock mu(*gc_complete_lock_); + MutexLock mu(self, *gc_complete_lock_); do_wait = is_gc_running_; } if (do_wait) { // We must wait, change thread state then sleep on gc_complete_cond_; ScopedThreadStateChange tsc(Thread::Current(), kWaitingForGcToComplete); { - MutexLock mu(*gc_complete_lock_); + MutexLock mu(self, *gc_complete_lock_); while (is_gc_running_) { - gc_complete_cond_->Wait(*gc_complete_lock_); + gc_complete_cond_->Wait(self, *gc_complete_lock_); } last_gc_type = last_gc_type_; } @@ -1809,7 +1804,7 @@ void Heap::GrowForUtilization() { } void Heap::ClearGrowthLimit() { - WaitForConcurrentGcToComplete(); + WaitForConcurrentGcToComplete(Thread::Current()); alloc_space_->ClearGrowthLimit(); } @@ -1941,7 +1936,7 @@ void Heap::RequestConcurrentGC() { requesting_gc_ = false; } -void Heap::ConcurrentGC() { +void Heap::ConcurrentGC(Thread* self) { if (Runtime::Current()->IsShuttingDown() || !concurrent_gc_) { return; } @@ -1949,9 +1944,9 @@ void Heap::ConcurrentGC() { // TODO: We shouldn't need a WaitForConcurrentGcToComplete here since only // concurrent GC resumes threads before the GC is completed and this function // is only called within the GC daemon thread. - if (WaitForConcurrentGcToComplete() == kGcTypeNone) { + if (WaitForConcurrentGcToComplete(self) == kGcTypeNone) { // Start a concurrent GC as one wasn't in progress - ScopedThreadStateChange tsc(Thread::Current(), kWaitingPerformingGc); + ScopedThreadStateChange tsc(self, kWaitingPerformingGc); if (alloc_space_->Size() > min_alloc_space_size_for_sticky_gc_) { CollectGarbageInternal(kGcTypeSticky, false); } else { @@ -1960,8 +1955,8 @@ void Heap::ConcurrentGC() { } } -void Heap::Trim() { - WaitForConcurrentGcToComplete(); +void Heap::Trim(Thread* self) { + WaitForConcurrentGcToComplete(self); alloc_space_->Trim(); } diff --git a/src/heap.h b/src/heap.h index b905952472..0a054e2775 100644 --- a/src/heap.h +++ b/src/heap.h @@ -25,7 +25,7 @@ #include "globals.h" #include "gtest/gtest.h" #include "heap_bitmap.h" -#include "mutex.h" +#include "locks.h" #include "offsets.h" #include "safe_map.h" #include "timing_logger.h" @@ -39,12 +39,13 @@ namespace art { class AllocSpace; class Class; +class ConditionVariable; class HeapBitmap; class ImageSpace; class LargeObjectSpace; class MarkStack; class ModUnionTable; - +class Mutex; class Object; class Space; class SpaceTest; @@ -122,7 +123,7 @@ class LOCKABLE Heap { // Does a concurrent GC, should only be called by the GC daemon thread // through runtime. - void ConcurrentGC(); + void ConcurrentGC(Thread* self); // Implements java.lang.Runtime.maxMemory. int64_t GetMaxMemory(); @@ -159,7 +160,7 @@ class LOCKABLE Heap { // Blocks the caller until the garbage collector becomes idle and returns // true if we waited for the GC to complete. - GcType WaitForConcurrentGcToComplete(); + GcType WaitForConcurrentGcToComplete(Thread* self); const Spaces& GetSpaces() { return spaces_; @@ -247,7 +248,7 @@ class LOCKABLE Heap { void DumpForSigQuit(std::ostream& os); - void Trim(); + void Trim(Thread* self); HeapBitmap* GetLiveBitmap() SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { return live_bitmap_.get(); @@ -303,7 +304,7 @@ class LOCKABLE Heap { void RequestConcurrentGC(); // Swap bitmaps (if we are a full Gc then we swap the zygote bitmap too). - void SwapBitmaps() EXCLUSIVE_LOCKS_REQUIRED(GlobalSynchronization::heap_bitmap_lock_); + void SwapBitmaps(Thread* self) EXCLUSIVE_LOCKS_REQUIRED(GlobalSynchronization::heap_bitmap_lock_); void RecordAllocation(size_t size, const Object* object) LOCKS_EXCLUDED(GlobalSynchronization::heap_bitmap_lock_); @@ -315,10 +316,11 @@ class LOCKABLE Heap { Locks::heap_bitmap_lock_, Locks::mutator_lock_, Locks::thread_suspend_count_lock_); - void CollectGarbageMarkSweepPlan(GcType gc_plan, bool clear_soft_references) + void CollectGarbageMarkSweepPlan(Thread* self, GcType gc_plan, bool clear_soft_references) LOCKS_EXCLUDED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - void CollectGarbageConcurrentMarkSweepPlan(GcType gc_plan, bool clear_soft_references) + void CollectGarbageConcurrentMarkSweepPlan(Thread* self, GcType gc_plan, + bool clear_soft_references) LOCKS_EXCLUDED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); diff --git a/src/jdwp/jdwp_event.cc b/src/jdwp/jdwp_event.cc index 4f11a65331..19f10eb4c3 100644 --- a/src/jdwp/jdwp_event.cc +++ b/src/jdwp/jdwp_event.cc @@ -585,7 +585,8 @@ void JdwpState::SetWaitForEventThread(ObjectId threadId) { bool waited = false; /* this is held for very brief periods; contention is unlikely */ - MutexLock mu(event_thread_lock_); + Thread* self = Thread::Current(); + MutexLock mu(self, event_thread_lock_); /* * If another thread is already doing stuff, wait for it. This can @@ -594,7 +595,7 @@ void JdwpState::SetWaitForEventThread(ObjectId threadId) { while (event_thread_id_ != 0) { VLOG(jdwp) << StringPrintf("event in progress (%#llx), %#llx sleeping", event_thread_id_, threadId); waited = true; - event_thread_cond_.Wait(event_thread_lock_); + event_thread_cond_.Wait(self, event_thread_lock_); } if (waited || threadId != 0) { @@ -1075,7 +1076,7 @@ void JdwpState::DdmSendChunkV(uint32_t type, const iovec* iov, int iov_count) { Thread* self = Thread::Current(); bool safe_to_release_mutator_lock_over_send; for (size_t i=0; i < kMutatorLock; ++i) { - if (self->GetHeldMutex(static_cast<MutexLevel>(i)) != NULL) { + if (self->GetHeldMutex(static_cast<LockLevel>(i)) != NULL) { safe_to_release_mutator_lock_over_send = false; break; } diff --git a/src/jdwp/jdwp_main.cc b/src/jdwp/jdwp_main.cc index 4fec005005..a09c488d4a 100644 --- a/src/jdwp/jdwp_main.cc +++ b/src/jdwp/jdwp_main.cc @@ -118,7 +118,8 @@ JdwpState::JdwpState(const JdwpOptions* options) * the thread is accepting network connections. */ JdwpState* JdwpState::Create(const JdwpOptions* options) { - Locks::mutator_lock_->AssertNotHeld(); + Thread* self = Thread::Current(); + Locks::mutator_lock_->AssertNotHeld(self); UniquePtr<JdwpState> state(new JdwpState(options)); switch (options->transport) { case kJdwpTransportSocket: @@ -157,7 +158,7 @@ JdwpState* JdwpState::Create(const JdwpOptions* options) { * Wait until the thread finishes basic initialization. * TODO: cond vars should be waited upon in a loop */ - state->thread_start_cond_.Wait(state->thread_start_lock_); + state->thread_start_cond_.Wait(self, state->thread_start_lock_); } else { { MutexLock attach_locker(state->attach_lock_); @@ -171,7 +172,7 @@ JdwpState* JdwpState::Create(const JdwpOptions* options) { * Wait until the thread finishes basic initialization. * TODO: cond vars should be waited upon in a loop */ - state->thread_start_cond_.Wait(state->thread_start_lock_); + state->thread_start_cond_.Wait(self, state->thread_start_lock_); /* * For suspend=y, wait for the debugger to connect to us or for us to @@ -182,8 +183,8 @@ JdwpState* JdwpState::Create(const JdwpOptions* options) { * when we wake up. */ { - ScopedThreadStateChange tsc(Thread::Current(), kWaitingForDebuggerToAttach); - state->attach_cond_.Wait(state->attach_lock_); + ScopedThreadStateChange tsc(self, kWaitingForDebuggerToAttach); + state->attach_cond_.Wait(self, state->attach_lock_); } } if (!state->IsActive()) { @@ -294,14 +295,15 @@ void JdwpState::Run() { thread_ = Thread::Current(); run = true; - thread_start_lock_.Lock(); - debug_thread_started_ = true; - thread_start_cond_.Broadcast(); - thread_start_lock_.Unlock(); + { + MutexLock locker(thread_, thread_start_lock_); + debug_thread_started_ = true; + thread_start_cond_.Broadcast(); + } /* set the thread state to kWaitingInMainDebuggerLoop so GCs don't wait for us */ { - MutexLock mu(*Locks::thread_suspend_count_lock_); + MutexLock mu(thread_, *Locks::thread_suspend_count_lock_); CHECK_EQ(thread_->GetState(), kNative); thread_->SetState(kWaitingInMainDebuggerLoop); } @@ -346,7 +348,7 @@ void JdwpState::Run() { while (!Dbg::IsDisposed()) { { // sanity check -- shouldn't happen? - MutexLock mu(*Locks::thread_suspend_count_lock_); + MutexLock mu(thread_, *Locks::thread_suspend_count_lock_); CHECK_EQ(thread_->GetState(), kWaitingInMainDebuggerLoop); } @@ -366,7 +368,7 @@ void JdwpState::Run() { } /* wake anybody who's waiting for us */ - MutexLock mu(attach_lock_); + MutexLock mu(thread_, attach_lock_); attach_cond_.Broadcast(); } } @@ -400,11 +402,8 @@ void JdwpState::Run() { } /* back to native, for thread shutdown */ - { - MutexLock mu(*Locks::thread_suspend_count_lock_); - CHECK_EQ(thread_->GetState(), kWaitingInMainDebuggerLoop); - thread_->SetState(kNative); - } + CHECK_EQ(thread_->GetState(), kWaitingInMainDebuggerLoop); + thread_->SetState(kNative); VLOG(jdwp) << "JDWP: thread detaching and exiting..."; runtime->DetachCurrentThread(); diff --git a/src/jni_compiler_test.cc b/src/jni_compiler_test.cc index 31b14c96be..81941c00de 100644 --- a/src/jni_compiler_test.cc +++ b/src/jni_compiler_test.cc @@ -118,7 +118,7 @@ void Java_MyClassNatives_foo(JNIEnv* env, jobject thisObj) { { MutexLock mu(*Locks::thread_suspend_count_lock_); EXPECT_EQ(kNative, Thread::Current()->GetState()); - Locks::mutator_lock_->AssertNotHeld(); + Locks::mutator_lock_->AssertNotHeld(Thread::Current()); } EXPECT_EQ(Thread::Current()->GetJniEnv(), env); EXPECT_TRUE(thisObj != NULL); diff --git a/src/jni_internal.cc b/src/jni_internal.cc index 77066c43d5..0f93461a63 100644 --- a/src/jni_internal.cc +++ b/src/jni_internal.cc @@ -527,7 +527,7 @@ class SharedLibrary { } else { while (jni_on_load_result_ == kPending) { VLOG(jni) << "[" << *self << " waiting for \"" << path_ << "\" " << "JNI_OnLoad...]"; - jni_on_load_cond_.Wait(jni_on_load_lock_); + jni_on_load_cond_.Wait(self, jni_on_load_lock_); } okay = (jni_on_load_result_ == kOkay); diff --git a/src/locks.cc b/src/locks.cc new file mode 100644 index 0000000000..20bf81cb00 --- /dev/null +++ b/src/locks.cc @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "locks.h" + +#include "mutex.h" + +namespace art { + +ReaderWriterMutex* Locks::mutator_lock_ = NULL; +Mutex* Locks::thread_list_lock_ = NULL; +Mutex* Locks::classlinker_classes_lock_ = NULL; +ReaderWriterMutex* Locks::heap_bitmap_lock_ = NULL; +Mutex* Locks::abort_lock_ = NULL; +Mutex* Locks::logging_lock_ = NULL; +Mutex* Locks::unexpected_signal_lock_ = NULL; +Mutex* Locks::thread_suspend_count_lock_ = NULL; + +void Locks::Init() { + if (logging_lock_ != NULL) { + // Already initialized. + DCHECK(mutator_lock_ != NULL); + DCHECK(thread_list_lock_ != NULL); + DCHECK(classlinker_classes_lock_ != NULL); + DCHECK(heap_bitmap_lock_ != NULL); + DCHECK(abort_lock_ != NULL); + DCHECK(logging_lock_ != NULL); + DCHECK(unexpected_signal_lock_ != NULL); + DCHECK(thread_suspend_count_lock_ != NULL); + } else { + logging_lock_ = new Mutex("logging lock", kLoggingLock, true); + abort_lock_ = new Mutex("abort lock", kAbortLock, true); + DCHECK(mutator_lock_ == NULL); + mutator_lock_ = new ReaderWriterMutex("mutator lock", kMutatorLock); + DCHECK(thread_list_lock_ == NULL); + thread_list_lock_ = new Mutex("thread list lock", kThreadListLock); + DCHECK(classlinker_classes_lock_ == NULL); + classlinker_classes_lock_ = new Mutex("ClassLinker classes lock", kClassLinkerClassesLock); + DCHECK(heap_bitmap_lock_ == NULL); + heap_bitmap_lock_ = new ReaderWriterMutex("heap bitmap lock", kHeapBitmapLock); + DCHECK(unexpected_signal_lock_ == NULL); + unexpected_signal_lock_ = new Mutex("unexpected signal lock", kUnexpectedSignalLock, true); + DCHECK(thread_suspend_count_lock_ == NULL); + thread_suspend_count_lock_ = new Mutex("thread suspend count lock", kThreadSuspendCountLock); + } +} + +} // namespace art diff --git a/src/locks.h b/src/locks.h new file mode 100644 index 0000000000..c5821d8440 --- /dev/null +++ b/src/locks.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_SRC_LOCKS_H_ +#define ART_SRC_LOCKS_H_ + +#include <ostream> + +#include "macros.h" + +namespace art { + +class LOCKABLE Mutex; +class LOCKABLE ReaderWriterMutex; + +// LockLevel is used to impose a lock hierarchy [1] where acquisition of a Mutex at a higher or +// equal level to a lock a thread holds is invalid. The lock hierarchy achieves a cycle free +// partial ordering and thereby cause deadlock situations to fail checks. +// +// [1] http://www.drdobbs.com/parallel/use-lock-hierarchies-to-avoid-deadlock/204801163 +enum LockLevel { + kLoggingLock = 0, + kUnexpectedSignalLock = 1, + kThreadSuspendCountLock = 2, + kAbortLock = 3, + kDefaultMutexLevel = 4, + kJdwpSerialLock = 5, + kAllocSpaceLock = 6, + kLoadLibraryLock = 7, + kClassLinkerClassesLock = 8, + kThreadListLock = 9, + kHeapBitmapLock = 10, + kMonitorLock = 11, + kMutatorLock = 12, + kZygoteCreationLock = 13, + kMaxMutexLevel = kMutatorLock, +}; +std::ostream& operator<<(std::ostream& os, const LockLevel& rhs); + +// Global mutexes corresponding to the levels above. +class Locks { + public: + static void Init(); + + // The mutator_lock_ is used to allow mutators to execute in a shared (reader) mode or to block + // mutators by having an exclusive (writer) owner. In normal execution each mutator thread holds + // a share on the mutator_lock_. The garbage collector may also execute with shared access but + // at times requires exclusive access to the heap (not to be confused with the heap meta-data + // guarded by the heap_lock_ below). When the garbage collector requires exclusive access it asks + // the mutators to suspend themselves which also involves usage of the thread_suspend_count_lock_ + // to cover weaknesses in using ReaderWriterMutexes with ConditionVariables. We use a condition + // variable to wait upon in the suspension logic as releasing and then re-acquiring a share on + // the mutator lock doesn't necessarily allow the exclusive user (e.g the garbage collector) + // chance to acquire the lock. + // + // Thread suspension: + // Shared users | Exclusive user + // (holding mutator lock and in kRunnable state) | .. running .. + // .. running .. | Request thread suspension by: + // .. running .. | - acquiring thread_suspend_count_lock_ + // .. running .. | - incrementing Thread::suspend_count_ on + // .. running .. | all mutator threads + // .. running .. | - releasing thread_suspend_count_lock_ + // .. running .. | Block trying to acquire exclusive mutator lock + // Poll Thread::suspend_count_ and enter full | .. blocked .. + // suspend code. | .. blocked .. + // Change state to kSuspended | .. blocked .. + // x: Release share on mutator_lock_ | Carry out exclusive access + // Acquire thread_suspend_count_lock_ | .. exclusive .. + // while Thread::suspend_count_ > 0 | .. exclusive .. + // - wait on Thread::resume_cond_ | .. exclusive .. + // (releases thread_suspend_count_lock_) | .. exclusive .. + // .. waiting .. | Release mutator_lock_ + // .. waiting .. | Request thread resumption by: + // .. waiting .. | - acquiring thread_suspend_count_lock_ + // .. waiting .. | - decrementing Thread::suspend_count_ on + // .. waiting .. | all mutator threads + // .. waiting .. | - notifying on Thread::resume_cond_ + // - re-acquire thread_suspend_count_lock_ | - releasing thread_suspend_count_lock_ + // Release thread_suspend_count_lock_ | .. running .. + // Acquire share on mutator_lock_ | .. running .. + // - This could block but the thread still | .. running .. + // has a state of kSuspended and so this | .. running .. + // isn't an issue. | .. running .. + // Acquire thread_suspend_count_lock_ | .. running .. + // - we poll here as we're transitioning into | .. running .. + // kRunnable and an individual thread suspend | .. running .. + // request (e.g for debugging) won't try | .. running .. + // to acquire the mutator lock (which would | .. running .. + // block as we hold the mutator lock). This | .. running .. + // poll ensures that if the suspender thought | .. running .. + // we were suspended by incrementing our | .. running .. + // Thread::suspend_count_ and then reading | .. running .. + // our state we go back to waiting on | .. running .. + // Thread::resume_cond_. | .. running .. + // can_go_runnable = Thread::suspend_count_ == 0 | .. running .. + // Release thread_suspend_count_lock_ | .. running .. + // if can_go_runnable | .. running .. + // Change state to kRunnable | .. running .. + // else | .. running .. + // Goto x | .. running .. + // .. running .. | .. running .. + static ReaderWriterMutex* mutator_lock_; + + // Allow reader-writer mutual exclusion on the mark and live bitmaps of the heap. + static ReaderWriterMutex* heap_bitmap_lock_ ACQUIRED_AFTER(mutator_lock_); + + // The thread_list_lock_ guards ThreadList::list_. It is also commonly held to stop threads + // attaching and detaching. + static Mutex* thread_list_lock_ ACQUIRED_AFTER(heap_bitmap_lock_); + + // Guards lists of classes within the class linker. + static Mutex* classlinker_classes_lock_ ACQUIRED_AFTER(thread_list_lock_); + + // When declaring any Mutex add DEFAULT_MUTEX_ACQUIRED_AFTER to use annotalysis to check the code + // doesn't try to hold a higher level Mutex. + #define DEFAULT_MUTEX_ACQUIRED_AFTER ACQUIRED_AFTER(classlinker_classes_lock_) + + // Have an exclusive aborting thread. + static Mutex* abort_lock_ ACQUIRED_AFTER(classlinker_classes_lock_); + + // Allow mutual exclusion when manipulating Thread::suspend_count_. + // TODO: Does the trade-off of a per-thread lock make sense? + static Mutex* thread_suspend_count_lock_ ACQUIRED_AFTER(abort_lock_); + + // One unexpected signal at a time lock. + static Mutex* unexpected_signal_lock_ ACQUIRED_AFTER(thread_suspend_count_lock_); + + // Have an exclusive logging thread. + static Mutex* logging_lock_ ACQUIRED_AFTER(unexpected_signal_lock_); +}; + +} // namespace art + +#endif // ART_SRC_LOCKS_H_ diff --git a/src/logging.cc b/src/logging.cc index a0c07cf227..48785c5ad2 100644 --- a/src/logging.cc +++ b/src/logging.cc @@ -16,6 +16,7 @@ #include "logging.h" +#include "mutex.h" #include "runtime.h" #include "thread.h" #include "utils.h" diff --git a/src/mark_sweep.cc b/src/mark_sweep.cc index 2c280a22d5..cdb73db51e 100644 --- a/src/mark_sweep.cc +++ b/src/mark_sweep.cc @@ -25,10 +25,12 @@ #include "heap.h" #include "indirect_reference_table.h" #include "intern_table.h" +#include "jni_internal.h" #include "logging.h" #include "macros.h" #include "mark_stack.h" #include "monitor.h" +#include "mutex.h" #include "object.h" #include "runtime.h" #include "space.h" @@ -456,7 +458,7 @@ struct SweepCallbackContext { }; void MarkSweep::SweepCallback(size_t num_ptrs, Object** ptrs, void* arg) { - Locks::heap_bitmap_lock_->AssertExclusiveHeld(); + Locks::heap_bitmap_lock_->AssertExclusiveHeld(Thread::Current()); size_t freed_objects = num_ptrs; size_t freed_bytes = 0; @@ -490,7 +492,7 @@ void MarkSweep::SweepCallback(size_t num_ptrs, Object** ptrs, void* arg) { } void MarkSweep::ZygoteSweepCallback(size_t num_ptrs, Object** ptrs, void* arg) { - Locks::heap_bitmap_lock_->AssertExclusiveHeld(); + Locks::heap_bitmap_lock_->AssertExclusiveHeld(Thread::Current()); SweepCallbackContext* context = static_cast<SweepCallbackContext*>(arg); Heap* heap = context->mark_sweep->GetHeap(); diff --git a/src/monitor.cc b/src/monitor.cc index 6a18a90c8d..c0484af1bf 100644 --- a/src/monitor.cc +++ b/src/monitor.cc @@ -124,7 +124,7 @@ Monitor::Monitor(Thread* owner, Object* obj) wait_set_(NULL), locking_method_(NULL), locking_dex_pc_(0) { - monitor_lock_.Lock(); + monitor_lock_.Lock(owner); // Propagate the lock state. uint32_t thin = *obj->GetRawLockWordAddress(); lock_count_ = LW_LOCK_COUNT(thin); @@ -201,7 +201,7 @@ void Monitor::Lock(Thread* self) { return; } - if (!monitor_lock_.TryLock()) { + if (!monitor_lock_.TryLock(self)) { uint64_t waitStart = 0; uint64_t waitEnd = 0; uint32_t wait_threshold = lock_profiling_threshold_; @@ -215,7 +215,7 @@ void Monitor::Lock(Thread* self) { current_locking_method = locking_method_; current_locking_dex_pc = locking_dex_pc_; - monitor_lock_.Lock(); + monitor_lock_.Lock(self); if (wait_threshold != 0) { waitEnd = NanoTime() / 1000; } @@ -343,7 +343,7 @@ bool Monitor::Unlock(Thread* self, bool for_wait) { owner_ = NULL; locking_method_ = NULL; locking_dex_pc_ = 0; - monitor_lock_.Unlock(); + monitor_lock_.Unlock(self); } else { --lock_count_; } @@ -353,7 +353,7 @@ bool Monitor::Unlock(Thread* self, bool for_wait) { DCHECK(owner == NULL); DCHECK(locking_method_ == NULL); DCHECK_EQ(locking_dex_pc_, 0u); - monitor_lock_.Unlock(); + monitor_lock_.Unlock(self); } else { // We don't own this, so we're not allowed to unlock it. // The JNI spec says that we should throw IllegalMonitorStateException @@ -496,9 +496,9 @@ void Monitor::WaitWithLock(Thread* self, int64_t ms, int32_t ns, bool interruptS } else { // Wait for a notification or a timeout to occur. if (!timed) { - self->wait_cond_->Wait(*self->wait_mutex_); + self->wait_cond_->Wait(self, *self->wait_mutex_); } else { - self->wait_cond_->TimedWait(*self->wait_mutex_, ts); + self->wait_cond_->TimedWait(self, *self->wait_mutex_, ts); } if (self->interrupted_) { wasInterrupted = true; @@ -515,7 +515,7 @@ void Monitor::WaitWithLock(Thread* self, int64_t ms, int32_t ns, bool interruptS Lock(self); - self->wait_mutex_->AssertNotHeld(); + self->wait_mutex_->AssertNotHeld(self); /* * We remove our thread from wait set after restoring the count diff --git a/src/mutex.cc b/src/mutex.cc index c066bec2dc..ccb913f3f2 100644 --- a/src/mutex.cc +++ b/src/mutex.cc @@ -18,6 +18,9 @@ #include <errno.h> +#include "cutils/atomic.h" +#include "cutils/atomic-inline.h" +#include "linux/futex.h" #include "logging.h" #include "runtime.h" #include "thread.h" @@ -33,6 +36,16 @@ extern int pthread_mutex_lock(pthread_mutex_t* mutex) EXCLUSIVE_LOCK_FUNCTION(mu extern int pthread_mutex_unlock(pthread_mutex_t* mutex) UNLOCK_FUNCTION(1); extern int pthread_mutex_trylock(pthread_mutex_t* mutex) EXCLUSIVE_TRYLOCK_FUNCTION(0, mutex); +#if ART_USE_FUTEXES +#include "sys/syscall.h" +#ifndef SYS_futex +#define SYS_futex __NR_futex +#endif +int futex(volatile int *uaddr, int op, int val, const struct timespec *timeout, int *, int ) { + return syscall(SYS_futex, uaddr, op, val, timeout, NULL, NULL); +} +#endif // ART_USE_FUTEXES + namespace art { // This works on Mac OS 10.6 but hasn't been tested on older releases. @@ -75,47 +88,9 @@ struct __attribute__((__may_alias__)) glibc_pthread_rwlock_t { // ...other stuff we don't care about. }; -ReaderWriterMutex* Locks::mutator_lock_ = NULL; -Mutex* Locks::thread_list_lock_ = NULL; -Mutex* Locks::classlinker_classes_lock_ = NULL; -ReaderWriterMutex* Locks::heap_bitmap_lock_ = NULL; -Mutex* Locks::abort_lock_ = NULL; -Mutex* Locks::logging_lock_ = NULL; -Mutex* Locks::unexpected_signal_lock_ = NULL; -Mutex* Locks::thread_suspend_count_lock_ = NULL; - -void Locks::Init() { - if (logging_lock_ != NULL) { - // Already initialized. - DCHECK(mutator_lock_ != NULL); - DCHECK(thread_list_lock_ != NULL); - DCHECK(classlinker_classes_lock_ != NULL); - DCHECK(heap_bitmap_lock_ != NULL); - DCHECK(abort_lock_ != NULL); - DCHECK(logging_lock_ != NULL); - DCHECK(unexpected_signal_lock_ != NULL); - DCHECK(thread_suspend_count_lock_ != NULL); - } else { - logging_lock_ = new Mutex("logging lock", kLoggingLock, true); - abort_lock_ = new Mutex("abort lock", kAbortLock, true); - DCHECK(mutator_lock_ == NULL); - mutator_lock_ = new ReaderWriterMutex("mutator lock", kMutatorLock); - DCHECK(thread_list_lock_ == NULL); - thread_list_lock_ = new Mutex("thread list lock", kThreadListLock); - DCHECK(classlinker_classes_lock_ == NULL); - classlinker_classes_lock_ = new Mutex("ClassLinker classes lock", kClassLinkerClassesLock); - DCHECK(heap_bitmap_lock_ == NULL); - heap_bitmap_lock_ = new ReaderWriterMutex("heap bitmap lock", kHeapBitmapLock); - DCHECK(unexpected_signal_lock_ == NULL); - unexpected_signal_lock_ = new Mutex("unexpected signal lock", kUnexpectedSignalLock, true); - DCHECK(thread_suspend_count_lock_ == NULL); - thread_suspend_count_lock_ = new Mutex("thread suspend count lock", kThreadSuspendCountLock); - } -} +BaseMutex::BaseMutex(const char* name, LockLevel level) : level_(level), name_(name) {} -BaseMutex::BaseMutex(const char* name, MutexLevel level) : level_(level), name_(name) {} - -static void CheckUnattachedThread(MutexLevel level) { +static void CheckUnattachedThread(LockLevel level) { // The check below enumerates the cases where we expect not to be able to sanity check locks // on a thread. TODO: tighten this check. if (kDebugLocking) { @@ -126,9 +101,8 @@ static void CheckUnattachedThread(MutexLevel level) { } } -void BaseMutex::RegisterAsLockedWithCurrentThread() { - Thread* self = Thread::Current(); - if (self == NULL) { +void BaseMutex::RegisterAsLocked(Thread* self) { + if (UNLIKELY(self == NULL)) { CheckUnattachedThread(level_); return; } @@ -136,7 +110,7 @@ void BaseMutex::RegisterAsLockedWithCurrentThread() { // Check if a bad Mutex of this level or lower is held. bool bad_mutexes_held = false; for (int i = level_; i >= 0; --i) { - BaseMutex* held_mutex = self->GetHeldMutex(static_cast<MutexLevel>(i)); + BaseMutex* held_mutex = self->GetHeldMutex(static_cast<LockLevel>(i)); if (UNLIKELY(held_mutex != NULL)) { LOG(ERROR) << "Lock level violation: holding \"" << held_mutex->name_ << "\" (level " << i << ") while locking \"" << name_ << "\" (level " << static_cast<int>(level_) << ")"; @@ -155,9 +129,8 @@ void BaseMutex::RegisterAsLockedWithCurrentThread() { } } -void BaseMutex::RegisterAsUnlockedWithCurrentThread() { - Thread* self = Thread::Current(); - if (self == NULL) { +void BaseMutex::RegisterAsUnlocked(Thread* self) { + if (UNLIKELY(self == NULL)) { CheckUnattachedThread(level_); return; } @@ -169,8 +142,7 @@ void BaseMutex::RegisterAsUnlockedWithCurrentThread() { } } -void BaseMutex::CheckSafeToWait() { - Thread* self = Thread::Current(); +void BaseMutex::CheckSafeToWait(Thread* self) { if (self == NULL) { CheckUnattachedThread(level_); return; @@ -180,7 +152,7 @@ void BaseMutex::CheckSafeToWait() { bool bad_mutexes_held = false; for (int i = kMaxMutexLevel; i >= 0; --i) { if (i != level_) { - BaseMutex* held_mutex = self->GetHeldMutex(static_cast<MutexLevel>(i)); + BaseMutex* held_mutex = self->GetHeldMutex(static_cast<LockLevel>(i)); if (held_mutex != NULL) { LOG(ERROR) << "Holding " << held_mutex->name_ << " (level " << i << ") while performing wait on: " @@ -193,7 +165,7 @@ void BaseMutex::CheckSafeToWait() { } } -Mutex::Mutex(const char* name, MutexLevel level, bool recursive) +Mutex::Mutex(const char* name, LockLevel level, bool recursive) : BaseMutex(name, level), recursive_(recursive), recursion_count_(0) { #if defined(__BIONIC__) || defined(__APPLE__) // Use recursive mutexes for bionic and Apple otherwise the @@ -220,27 +192,27 @@ Mutex::~Mutex() { } } -void Mutex::ExclusiveLock() { +void Mutex::ExclusiveLock(Thread* self) { if (kDebugLocking && !recursive_) { - AssertNotHeld(); + AssertNotHeld(self); } - if (!recursive_ || !IsExclusiveHeld()) { + if (!recursive_ || !IsExclusiveHeld(self)) { CHECK_MUTEX_CALL(pthread_mutex_lock, (&mutex_)); - RegisterAsLockedWithCurrentThread(); + RegisterAsLocked(self); } recursion_count_++; if (kDebugLocking) { CHECK(recursion_count_ == 1 || recursive_) << "Unexpected recursion count on mutex: " << name_ << " " << recursion_count_; - AssertHeld(); + AssertHeld(self); } } -bool Mutex::ExclusiveTryLock() { +bool Mutex::ExclusiveTryLock(Thread* self) { if (kDebugLocking && !recursive_) { - AssertNotHeld(); + AssertNotHeld(self); } - if (!recursive_ || !IsExclusiveHeld()) { + if (!recursive_ || !IsExclusiveHeld(self)) { int result = pthread_mutex_trylock(&mutex_); if (result == EBUSY) { return false; @@ -249,32 +221,31 @@ bool Mutex::ExclusiveTryLock() { errno = result; PLOG(FATAL) << "pthread_mutex_trylock failed for " << name_; } - RegisterAsLockedWithCurrentThread(); + RegisterAsLocked(self); } recursion_count_++; if (kDebugLocking) { CHECK(recursion_count_ == 1 || recursive_) << "Unexpected recursion count on mutex: " << name_ << " " << recursion_count_; - AssertHeld(); + AssertHeld(self); } return true; } -void Mutex::ExclusiveUnlock() { - AssertHeld(); +void Mutex::ExclusiveUnlock(Thread* self) { + AssertHeld(self); recursion_count_--; if (!recursive_ || recursion_count_ == 0) { if (kDebugLocking) { CHECK(recursion_count_ == 0 || recursive_) << "Unexpected recursion count on mutex: " << name_ << " " << recursion_count_; } - RegisterAsUnlockedWithCurrentThread(); + RegisterAsUnlocked(self); CHECK_MUTEX_CALL(pthread_mutex_unlock, (&mutex_)); } } -bool Mutex::IsExclusiveHeld() const { - Thread* self = Thread::Current(); +bool Mutex::IsExclusiveHeld(const Thread* self) const { bool result; if (self == NULL || level_ == kMonitorLock) { // Handle unattached threads and monitors. result = (GetExclusiveOwnerTid() == static_cast<uint64_t>(GetTid())); @@ -309,11 +280,24 @@ uint64_t Mutex::GetExclusiveOwnerTid() const { #endif } -ReaderWriterMutex::ReaderWriterMutex(const char* name, MutexLevel level) : BaseMutex(name, level) { +ReaderWriterMutex::ReaderWriterMutex(const char* name, LockLevel level) : + BaseMutex(name, level) +#if ART_USE_FUTEXES + , state_(0), exclusive_owner_(0), num_pending_readers_(0), num_pending_writers_(0) +#endif +{ +#if !ART_USE_FUTEXES CHECK_MUTEX_CALL(pthread_rwlock_init, (&rwlock_, NULL)); +#endif } ReaderWriterMutex::~ReaderWriterMutex() { +#if ART_USE_FUTEXES + CHECK_EQ(state_, 0); + CHECK_EQ(exclusive_owner_, 0U); + CHECK_EQ(num_pending_readers_, 0); + CHECK_EQ(num_pending_writers_, 0); +#else // We can't use CHECK_MUTEX_CALL here because on shutdown a suspended daemon thread // may still be using locks. int rc = pthread_rwlock_destroy(&rwlock_); @@ -323,23 +307,89 @@ ReaderWriterMutex::~ReaderWriterMutex() { bool shutting_down = Runtime::Current()->IsShuttingDown(); PLOG(shutting_down ? WARNING : FATAL) << "pthread_mutex_destroy failed for " << name_; } +#endif } -void ReaderWriterMutex::ExclusiveLock() { - AssertNotExclusiveHeld(); +void ReaderWriterMutex::ExclusiveLock(Thread* self) { + AssertNotExclusiveHeld(self); +#if ART_USE_FUTEXES + bool done = false; + do { + int32_t cur_state = state_; + if (cur_state == 0) { + // Change state from 0 to -1. + done = android_atomic_cmpxchg(0, -1, &state_) == 0; + } else { + // Failed to acquire, hang up. + android_atomic_inc(&num_pending_writers_); + if (futex(&state_, FUTEX_WAIT, cur_state, NULL, NULL, 0) != 0) { + if (errno != EAGAIN) { + PLOG(FATAL) << "futex wait failed for " << name_; + } + } + android_atomic_dec(&num_pending_writers_); + } + } while(!done); + exclusive_owner_ = static_cast<uint64_t>(GetTid()); +#else CHECK_MUTEX_CALL(pthread_rwlock_wrlock, (&rwlock_)); - RegisterAsLockedWithCurrentThread(); - AssertExclusiveHeld(); +#endif + RegisterAsLocked(self); + AssertExclusiveHeld(self); } -void ReaderWriterMutex::ExclusiveUnlock() { - AssertExclusiveHeld(); - RegisterAsUnlockedWithCurrentThread(); +void ReaderWriterMutex::ExclusiveUnlock(Thread* self) { + AssertExclusiveHeld(self); + RegisterAsUnlocked(self); +#if ART_USE_FUTEXES + bool done = false; + do { + int32_t cur_state = state_; + if (cur_state == -1) { + // We're no longer the owner. + exclusive_owner_ = 0; + // Change state from -1 to 0. + done = android_atomic_cmpxchg(-1, 0, &state_) == 0; + if (done) { // cmpxchg may fail due to noise? + // Wake any waiters. + if (num_pending_readers_ > 0 || num_pending_writers_ > 0) { + futex(&state_, FUTEX_WAKE, -1, NULL, NULL, 0); + } + } + } else { + LOG(FATAL) << "Unexpected state_:" << cur_state << " for " << name_; + } + } while(!done); +#else CHECK_MUTEX_CALL(pthread_rwlock_unlock, (&rwlock_)); +#endif } #if HAVE_TIMED_RWLOCK -bool ReaderWriterMutex::ExclusiveLockWithTimeout(const timespec& abs_timeout) { +bool ReaderWriterMutex::ExclusiveLockWithTimeout(Thread* self, const timespec& abs_timeout) { +#if ART_USE_FUTEXES + bool done = false; + do { + int32_t cur_state = state_; + if (cur_state == 0) { + // Change state from 0 to -1. + done = android_atomic_cmpxchg(0, -1, &state_) == 0; + } else { + // Failed to acquire, hang up. + android_atomic_inc(&num_pending_writers_); + if (futex(&state_, FUTEX_WAIT, cur_state, &abs_timeout, NULL, 0) != 0) { + if (errno == ETIMEDOUT) { + android_atomic_dec(&num_pending_writers_); + return false; + } else if (errno != EAGAIN) { + PLOG(FATAL) << "timed futex wait failed for " << name_; + } + } + android_atomic_dec(&num_pending_writers_); + } + } while(!done); + exclusive_owner_ = static_cast<uint64_t>(GetTid()); +#else int result = pthread_rwlock_timedwrlock(&rwlock_, &abs_timeout); if (result == ETIMEDOUT) { return false; @@ -348,19 +398,53 @@ bool ReaderWriterMutex::ExclusiveLockWithTimeout(const timespec& abs_timeout) { errno = result; PLOG(FATAL) << "pthread_rwlock_timedwrlock failed for " << name_; } - RegisterAsLockedWithCurrentThread(); - AssertSharedHeld(); +#endif + RegisterAsLocked(self); + AssertSharedHeld(self); return true; } #endif -void ReaderWriterMutex::SharedLock() { +void ReaderWriterMutex::SharedLock(Thread* self) { +#if ART_USE_FUTEXES + bool done = false; + do { + int32_t cur_state = state_; + if (cur_state >= 0) { + // Add as an extra reader. + done = android_atomic_cmpxchg(cur_state, cur_state + 1, &state_) == 0; + } else { + // Owner holds it exclusively, hang up. + android_atomic_inc(&num_pending_readers_); + if (futex(&state_, FUTEX_WAIT, cur_state, NULL, NULL, 0) != 0) { + if (errno != EAGAIN) { + PLOG(FATAL) << "futex wait failed for " << name_; + } + } + android_atomic_dec(&num_pending_readers_); + } + } while(!done); +#else CHECK_MUTEX_CALL(pthread_rwlock_rdlock, (&rwlock_)); - RegisterAsLockedWithCurrentThread(); - AssertSharedHeld(); +#endif + RegisterAsLocked(self); + AssertSharedHeld(self); } -bool ReaderWriterMutex::SharedTryLock() { +bool ReaderWriterMutex::SharedTryLock(Thread* self) { +#if ART_USE_FUTEXES + bool done = false; + do { + int32_t cur_state = state_; + if (cur_state >= 0) { + // Add as an extra reader. + done = android_atomic_cmpxchg(cur_state, cur_state + 1, &state_) == 0; + } else { + // Owner holds it exclusively. + return false; + } + } while(!done); +#else int result = pthread_rwlock_tryrdlock(&rwlock_); if (result == EBUSY) { return false; @@ -369,32 +453,50 @@ bool ReaderWriterMutex::SharedTryLock() { errno = result; PLOG(FATAL) << "pthread_mutex_trylock failed for " << name_; } - RegisterAsLockedWithCurrentThread(); - AssertSharedHeld(); +#endif + RegisterAsLocked(self); + AssertSharedHeld(self); return true; } -void ReaderWriterMutex::SharedUnlock() { - AssertSharedHeld(); - RegisterAsUnlockedWithCurrentThread(); +void ReaderWriterMutex::SharedUnlock(Thread* self) { + AssertSharedHeld(self); + RegisterAsUnlocked(self); +#if ART_USE_FUTEXES + bool done = false; + do { + int32_t cur_state = state_; + if (LIKELY(cur_state > 0)) { + // Reduce state by 1. + done = android_atomic_cmpxchg(cur_state, cur_state - 1, &state_) == 0; + if (done && (cur_state - 1) == 0) { // cmpxchg may fail due to noise? + if (num_pending_writers_ > 0 || num_pending_readers_ > 0) { + // Wake any exclusive waiters as there are now no readers. + futex(&state_, FUTEX_WAKE, -1, NULL, NULL, 0); + } + } + } else { + LOG(FATAL) << "Unexpected state_:" << cur_state << " for " << name_; + } + } while(!done); +#else CHECK_MUTEX_CALL(pthread_rwlock_unlock, (&rwlock_)); +#endif } -bool ReaderWriterMutex::IsExclusiveHeld() const { +bool ReaderWriterMutex::IsExclusiveHeld(const Thread* self) const { bool result = (GetExclusiveOwnerTid() == static_cast<uint64_t>(GetTid())); if (kDebugLocking) { // Sanity that if the pthread thinks we own the lock the Thread agrees. - Thread* self = Thread::Current(); CHECK((self == NULL) || !result || (self->GetHeldMutex(level_) == this)); } return result; } -bool ReaderWriterMutex::IsSharedHeld() const { - Thread* self = Thread::Current(); +bool ReaderWriterMutex::IsSharedHeld(const Thread* self) const { bool result; if (UNLIKELY(self == NULL)) { // Handle unattached threads. - result = IsExclusiveHeld(); // TODO: a better best effort here. + result = IsExclusiveHeld(self); // TODO: a better best effort here. } else { result = (self->GetHeldMutex(level_) == this); } @@ -402,12 +504,16 @@ bool ReaderWriterMutex::IsSharedHeld() const { } uint64_t ReaderWriterMutex::GetExclusiveOwnerTid() const { +#if ART_USE_FUTEXES + return exclusive_owner_; +#else #if defined(__BIONIC__) return rwlock_.writerThreadId; #elif defined(__GLIBC__) return reinterpret_cast<const glibc_pthread_rwlock_t*>(&rwlock_)->writer; #elif defined(__APPLE__) - const darwin_pthread_rwlock_t* dprwlock = reinterpret_cast<const darwin_pthread_rwlock_t*>(&rwlock_); + const darwin_pthread_rwlock_t* + dprwlock = reinterpret_cast<const darwin_pthread_rwlock_t*>(&rwlock_); pthread_t owner = dprwlock->darwin_pthread_rwlock_owner; if (owner == (pthread_t)0) { return 0; @@ -418,6 +524,7 @@ uint64_t ReaderWriterMutex::GetExclusiveOwnerTid() const { #else #error unsupported C library #endif +#endif } ConditionVariable::ConditionVariable(const std::string& name) : name_(name) { @@ -443,21 +550,21 @@ void ConditionVariable::Signal() { CHECK_MUTEX_CALL(pthread_cond_signal, (&cond_)); } -void ConditionVariable::Wait(Mutex& mutex) { - mutex.CheckSafeToWait(); +void ConditionVariable::Wait(Thread* self, Mutex& mutex) { + mutex.CheckSafeToWait(self); unsigned int old_recursion_count = mutex.recursion_count_; mutex.recursion_count_ = 0; CHECK_MUTEX_CALL(pthread_cond_wait, (&cond_, &mutex.mutex_)); mutex.recursion_count_ = old_recursion_count; } -void ConditionVariable::TimedWait(Mutex& mutex, const timespec& ts) { +void ConditionVariable::TimedWait(Thread* self, Mutex& mutex, const timespec& ts) { #ifdef HAVE_TIMEDWAIT_MONOTONIC #define TIMEDWAIT pthread_cond_timedwait_monotonic #else #define TIMEDWAIT pthread_cond_timedwait #endif - mutex.CheckSafeToWait(); + mutex.CheckSafeToWait(self); unsigned int old_recursion_count = mutex.recursion_count_; mutex.recursion_count_ = 0; int rc = TIMEDWAIT(&cond_, &mutex.mutex_, &ts); diff --git a/src/mutex.h b/src/mutex.h index 85d75ab099..af2b3524eb 100644 --- a/src/mutex.h +++ b/src/mutex.h @@ -24,8 +24,11 @@ #include <string> #include "globals.h" -#include "logging.h" +#include "locks.h" #include "macros.h" +#include "thread.h" + +#define ART_USE_FUTEXES 0 // Currently Darwin doesn't support locks with timeouts. #if !defined(__APPLE__) @@ -38,126 +41,6 @@ namespace art { const bool kDebugLocking = kIsDebugBuild; -class LOCKABLE Mutex; -class LOCKABLE ReaderWriterMutex; - -// MutexLevel is used to impose a lock hierarchy [1] where acquisition of a Mutex at a higher or -// equal level to a lock a thread holds is invalid. The lock hierarchy achieves a cycle free -// partial ordering and thereby cause deadlock situations to fail checks. -// -// [1] http://www.drdobbs.com/parallel/use-lock-hierarchies-to-avoid-deadlock/204801163 -enum MutexLevel { - kLoggingLock = 0, - kUnexpectedSignalLock = 1, - kThreadSuspendCountLock = 2, - kAbortLock = 3, - kDefaultMutexLevel = 4, - kJdwpSerialLock = 5, - kAllocSpaceLock = 6, - kLoadLibraryLock = 7, - kClassLinkerClassesLock = 8, - kThreadListLock = 9, - kHeapBitmapLock = 10, - kMonitorLock = 11, - kMutatorLock = 12, - kZygoteCreationLock = 13, - kMaxMutexLevel = kMutatorLock, -}; -std::ostream& operator<<(std::ostream& os, const MutexLevel& rhs); - -// Global mutexes corresponding to the levels above. -class Locks { - public: - static void Init(); - - // The mutator_lock_ is used to allow mutators to execute in a shared (reader) mode or to block - // mutators by having an exclusive (writer) owner. In normal execution each mutator thread holds - // a share on the mutator_lock_. The garbage collector may also execute with shared access but - // at times requires exclusive access to the heap (not to be confused with the heap meta-data - // guarded by the heap_lock_ below). When the garbage collector requires exclusive access it asks - // the mutators to suspend themselves which also involves usage of the thread_suspend_count_lock_ - // to cover weaknesses in using ReaderWriterMutexes with ConditionVariables. We use a condition - // variable to wait upon in the suspension logic as releasing and then re-acquiring a share on - // the mutator lock doesn't necessarily allow the exclusive user (e.g the garbage collector) - // chance to acquire the lock. - // - // Thread suspension: - // Shared users | Exclusive user - // (holding mutator lock and in kRunnable state) | .. running .. - // .. running .. | Request thread suspension by: - // .. running .. | - acquiring thread_suspend_count_lock_ - // .. running .. | - incrementing Thread::suspend_count_ on - // .. running .. | all mutator threads - // .. running .. | - releasing thread_suspend_count_lock_ - // .. running .. | Block trying to acquire exclusive mutator lock - // Poll Thread::suspend_count_ and enter full | .. blocked .. - // suspend code. | .. blocked .. - // Change state to kSuspended | .. blocked .. - // x: Release share on mutator_lock_ | Carry out exclusive access - // Acquire thread_suspend_count_lock_ | .. exclusive .. - // while Thread::suspend_count_ > 0 | .. exclusive .. - // - wait on Thread::resume_cond_ | .. exclusive .. - // (releases thread_suspend_count_lock_) | .. exclusive .. - // .. waiting .. | Release mutator_lock_ - // .. waiting .. | Request thread resumption by: - // .. waiting .. | - acquiring thread_suspend_count_lock_ - // .. waiting .. | - decrementing Thread::suspend_count_ on - // .. waiting .. | all mutator threads - // .. waiting .. | - notifying on Thread::resume_cond_ - // - re-acquire thread_suspend_count_lock_ | - releasing thread_suspend_count_lock_ - // Release thread_suspend_count_lock_ | .. running .. - // Acquire share on mutator_lock_ | .. running .. - // - This could block but the thread still | .. running .. - // has a state of kSuspended and so this | .. running .. - // isn't an issue. | .. running .. - // Acquire thread_suspend_count_lock_ | .. running .. - // - we poll here as we're transitioning into | .. running .. - // kRunnable and an individual thread suspend | .. running .. - // request (e.g for debugging) won't try | .. running .. - // to acquire the mutator lock (which would | .. running .. - // block as we hold the mutator lock). This | .. running .. - // poll ensures that if the suspender thought | .. running .. - // we were suspended by incrementing our | .. running .. - // Thread::suspend_count_ and then reading | .. running .. - // our state we go back to waiting on | .. running .. - // Thread::resume_cond_. | .. running .. - // can_go_runnable = Thread::suspend_count_ == 0 | .. running .. - // Release thread_suspend_count_lock_ | .. running .. - // if can_go_runnable | .. running .. - // Change state to kRunnable | .. running .. - // else | .. running .. - // Goto x | .. running .. - // .. running .. | .. running .. - static ReaderWriterMutex* mutator_lock_; - - // Allow reader-writer mutual exclusion on the mark and live bitmaps of the heap. - static ReaderWriterMutex* heap_bitmap_lock_ ACQUIRED_AFTER(mutator_lock_); - - // The thread_list_lock_ guards ThreadList::list_. It is also commonly held to stop threads - // attaching and detaching. - static Mutex* thread_list_lock_ ACQUIRED_AFTER(heap_bitmap_lock_); - - // Guards lists of classes within the class linker. - static Mutex* classlinker_classes_lock_ ACQUIRED_AFTER(thread_list_lock_); - - // When declaring any Mutex add DEFAULT_MUTEX_ACQUIRED_AFTER to use annotalysis to check the code - // doesn't try to hold a higher level Mutex. - #define DEFAULT_MUTEX_ACQUIRED_AFTER ACQUIRED_AFTER(classlinker_classes_lock_) - - // Have an exclusive aborting thread. - static Mutex* abort_lock_ ACQUIRED_AFTER(classlinker_classes_lock_); - - // Allow mutual exclusion when manipulating Thread::suspend_count_. - // TODO: Does the trade-off of a per-thread lock make sense? - static Mutex* thread_suspend_count_lock_ ACQUIRED_AFTER(abort_lock_); - - // One unexpected signal at a time lock. - static Mutex* unexpected_signal_lock_ ACQUIRED_AFTER(thread_suspend_count_lock_); - - // Have an exclusive logging thread. - static Mutex* logging_lock_ ACQUIRED_AFTER(unexpected_signal_lock_); -}; - // Base class for all Mutex implementations class BaseMutex { public: @@ -171,13 +54,13 @@ class BaseMutex { protected: friend class ConditionVariable; - BaseMutex(const char* name, MutexLevel level); + BaseMutex(const char* name, LockLevel level); virtual ~BaseMutex() {} - void RegisterAsLockedWithCurrentThread(); - void RegisterAsUnlockedWithCurrentThread(); - void CheckSafeToWait(); + void RegisterAsLocked(Thread* self); + void RegisterAsUnlocked(Thread* self); + void CheckSafeToWait(Thread* self); - const MutexLevel level_; // Support for lock hierarchy. + const LockLevel level_; // Support for lock hierarchy. const std::string name_; }; @@ -195,41 +78,42 @@ class BaseMutex { // an error. Being non-reentrant simplifies Waiting on ConditionVariables. class LOCKABLE Mutex : public BaseMutex { public: - explicit Mutex(const char* name, MutexLevel level = kDefaultMutexLevel, bool recursive = false); + explicit Mutex(const char* name, LockLevel level = kDefaultMutexLevel, bool recursive = false); ~Mutex(); virtual bool IsMutex() const { return true; } // Block until mutex is free then acquire exclusive access. - void ExclusiveLock() EXCLUSIVE_LOCK_FUNCTION(); - void Lock() EXCLUSIVE_LOCK_FUNCTION() { ExclusiveLock(); } + void ExclusiveLock(Thread* self) EXCLUSIVE_LOCK_FUNCTION(); + void Lock(Thread* self) EXCLUSIVE_LOCK_FUNCTION() { ExclusiveLock(self); } // Returns true if acquires exclusive access, false otherwise. - bool ExclusiveTryLock() EXCLUSIVE_TRYLOCK_FUNCTION(true); - bool TryLock() EXCLUSIVE_TRYLOCK_FUNCTION(true) { return ExclusiveTryLock(); } + bool ExclusiveTryLock(Thread* self) EXCLUSIVE_TRYLOCK_FUNCTION(true); + bool TryLock(Thread* self) EXCLUSIVE_TRYLOCK_FUNCTION(true) { return ExclusiveTryLock(self); } // Release exclusive access. - void ExclusiveUnlock() UNLOCK_FUNCTION(); - void Unlock() UNLOCK_FUNCTION() { ExclusiveUnlock(); } + void ExclusiveUnlock(Thread* self) UNLOCK_FUNCTION(); + void Unlock(Thread* self) UNLOCK_FUNCTION() { ExclusiveUnlock(self); } // Is the current thread the exclusive holder of the Mutex. - bool IsExclusiveHeld() const; + bool IsExclusiveHeld(const Thread* self) const; // Assert that the Mutex is exclusively held by the current thread. - void AssertExclusiveHeld() { + void AssertExclusiveHeld(const Thread* self) { if (kDebugLocking) { - CHECK(IsExclusiveHeld()); + CHECK(IsExclusiveHeld(self)); } } - void AssertHeld() { AssertExclusiveHeld(); } + void AssertHeld(const Thread* self) { AssertExclusiveHeld(self); } + void AssertHeld() { AssertExclusiveHeld(Thread::Current()); } // Assert that the Mutex is not held by the current thread. - void AssertNotHeldExclusive() { + void AssertNotHeldExclusive(const Thread* self) { if (kDebugLocking) { - CHECK(!IsExclusiveHeld()); + CHECK(!IsExclusiveHeld(self)); } } - void AssertNotHeld() { AssertNotHeldExclusive(); } + void AssertNotHeld(const Thread* self) { AssertNotHeldExclusive(self); } // Id associated with exclusive owner. uint64_t GetExclusiveOwnerTid() const; @@ -266,79 +150,91 @@ class LOCKABLE Mutex : public BaseMutex { // * for large values of n the SharedLock may block. class LOCKABLE ReaderWriterMutex : public BaseMutex { public: - explicit ReaderWriterMutex(const char* name, MutexLevel level = kDefaultMutexLevel); + explicit ReaderWriterMutex(const char* name, LockLevel level = kDefaultMutexLevel); ~ReaderWriterMutex(); virtual bool IsReaderWriterMutex() const { return true; } // Block until ReaderWriterMutex is free then acquire exclusive access. - void ExclusiveLock() EXCLUSIVE_LOCK_FUNCTION(); - void WriterLock() EXCLUSIVE_LOCK_FUNCTION() { ExclusiveLock(); } + void ExclusiveLock(Thread* self) EXCLUSIVE_LOCK_FUNCTION(); + void WriterLock(Thread* self) EXCLUSIVE_LOCK_FUNCTION() { ExclusiveLock(self); } // Release exclusive access. - void ExclusiveUnlock() UNLOCK_FUNCTION(); - void WriterUnlock() UNLOCK_FUNCTION() { ExclusiveUnlock(); } + void ExclusiveUnlock(Thread* self) UNLOCK_FUNCTION(); + void WriterUnlock(Thread* self) UNLOCK_FUNCTION() { ExclusiveUnlock(self); } // Block until ReaderWriterMutex is free and acquire exclusive access. Returns true on success // or false if timeout is reached. #if HAVE_TIMED_RWLOCK - bool ExclusiveLockWithTimeout(const timespec& abs_timeout) EXCLUSIVE_TRYLOCK_FUNCTION(true); + bool ExclusiveLockWithTimeout(Thread* self, const timespec& abs_timeout) + EXCLUSIVE_TRYLOCK_FUNCTION(true); #endif // Block until ReaderWriterMutex is shared or free then acquire a share on the access. - void SharedLock() SHARED_LOCK_FUNCTION(); - void ReaderLock() SHARED_LOCK_FUNCTION() { SharedLock(); } + void SharedLock(Thread* self) SHARED_LOCK_FUNCTION(); + void ReaderLock(Thread* self) SHARED_LOCK_FUNCTION() { SharedLock(self); } // Try to acquire share of ReaderWriterMutex. - bool SharedTryLock() EXCLUSIVE_TRYLOCK_FUNCTION(true); + bool SharedTryLock(Thread* self) EXCLUSIVE_TRYLOCK_FUNCTION(true); // Release a share of the access. - void SharedUnlock() UNLOCK_FUNCTION(); - void ReaderUnlock() UNLOCK_FUNCTION() { SharedUnlock(); } + void SharedUnlock(Thread* self) UNLOCK_FUNCTION(); + void ReaderUnlock(Thread* self) UNLOCK_FUNCTION() { SharedUnlock(self); } // Is the current thread the exclusive holder of the ReaderWriterMutex. - bool IsExclusiveHeld() const; + bool IsExclusiveHeld(const Thread* self) const; // Assert the current thread has exclusive access to the ReaderWriterMutex. - void AssertExclusiveHeld() { + void AssertExclusiveHeld(const Thread* self) { if (kDebugLocking) { - CHECK(IsExclusiveHeld()); + CHECK(IsExclusiveHeld(self)); } } - void AssertWriterHeld() { AssertExclusiveHeld(); } + void AssertWriterHeld(const Thread* self) { AssertExclusiveHeld(self); } // Assert the current thread doesn't have exclusive access to the ReaderWriterMutex. - void AssertNotExclusiveHeld() { + void AssertNotExclusiveHeld(const Thread* self) { if (kDebugLocking) { - CHECK(!IsExclusiveHeld()); + CHECK(!IsExclusiveHeld(self)); } } - void AssertNotWriterHeld() { AssertNotExclusiveHeld(); } + void AssertNotWriterHeld(const Thread* self) { AssertNotExclusiveHeld(self); } // Is the current thread a shared holder of the ReaderWriterMutex. - bool IsSharedHeld() const; + bool IsSharedHeld(const Thread* self) const; // Assert the current thread has shared access to the ReaderWriterMutex. - void AssertSharedHeld() { + void AssertSharedHeld(const Thread* self) { if (kDebugLocking) { - CHECK(IsSharedHeld()); + CHECK(IsSharedHeld(self)); } } - void AssertReaderHeld() { AssertSharedHeld(); } + void AssertReaderHeld(const Thread* self) { AssertSharedHeld(self); } // Assert the current thread doesn't hold this ReaderWriterMutex either in shared or exclusive // mode. - void AssertNotHeld() { + void AssertNotHeld(const Thread* self) { if (kDebugLocking) { - CHECK(!IsSharedHeld()); + CHECK(!IsSharedHeld(self)); } } // Id associated with exclusive owner. uint64_t GetExclusiveOwnerTid() const; + private: +#if ART_USE_FUTEXES + // -1 implies held exclusive, +ve shared held by state_ many owners. + volatile int32_t state_; + // Exclusive owner. + volatile uint64_t exclusive_owner_; + // Pending readers. + volatile int32_t num_pending_readers_; + // Pending writers. + volatile int32_t num_pending_writers_; +#else pthread_rwlock_t rwlock_; - +#endif friend class MutexTester; DISALLOW_COPY_AND_ASSIGN(ReaderWriterMutex); }; @@ -352,8 +248,8 @@ class ConditionVariable { void Broadcast(); void Signal(); - void Wait(Mutex& mutex); - void TimedWait(Mutex& mutex, const timespec& ts); + void Wait(Thread* self, Mutex& mutex); + void TimedWait(Thread* self, Mutex& mutex, const timespec& ts); private: pthread_cond_t cond_; @@ -365,15 +261,20 @@ class ConditionVariable { // upon destruction. class SCOPED_LOCKABLE MutexLock { public: - explicit MutexLock(Mutex& mu) EXCLUSIVE_LOCK_FUNCTION(mu) : mu_(mu) { - mu_.ExclusiveLock(); + explicit MutexLock(Thread* self, Mutex& mu) EXCLUSIVE_LOCK_FUNCTION(mu) : self_(self), mu_(mu) { + mu_.ExclusiveLock(self_); + } + + explicit MutexLock(Mutex& mu) EXCLUSIVE_LOCK_FUNCTION(mu) : self_(Thread::Current()), mu_(mu) { + mu_.ExclusiveLock(self_); } ~MutexLock() UNLOCK_FUNCTION() { - mu_.ExclusiveUnlock(); + mu_.ExclusiveUnlock(self_); } private: + Thread* const self_; Mutex& mu_; DISALLOW_COPY_AND_ASSIGN(MutexLock); }; @@ -384,15 +285,22 @@ class SCOPED_LOCKABLE MutexLock { // construction and releases it upon destruction. class SCOPED_LOCKABLE ReaderMutexLock { public: - explicit ReaderMutexLock(ReaderWriterMutex& mu) EXCLUSIVE_LOCK_FUNCTION(mu) : mu_(mu) { - mu_.SharedLock(); + explicit ReaderMutexLock(Thread* self, ReaderWriterMutex& mu) EXCLUSIVE_LOCK_FUNCTION(mu) : + self_(self), mu_(mu) { + mu_.SharedLock(self_); + } + + explicit ReaderMutexLock(ReaderWriterMutex& mu) EXCLUSIVE_LOCK_FUNCTION(mu) : + self_(Thread::Current()), mu_(mu) { + mu_.SharedLock(self_); } ~ReaderMutexLock() UNLOCK_FUNCTION() { - mu_.SharedUnlock(); + mu_.SharedUnlock(self_); } private: + Thread* const self_; ReaderWriterMutex& mu_; DISALLOW_COPY_AND_ASSIGN(ReaderMutexLock); }; @@ -404,15 +312,22 @@ class SCOPED_LOCKABLE ReaderMutexLock { // construction and releases it upon destruction. class SCOPED_LOCKABLE WriterMutexLock { public: - explicit WriterMutexLock(ReaderWriterMutex& mu) EXCLUSIVE_LOCK_FUNCTION(mu) : mu_(mu) { - mu_.ExclusiveLock(); + explicit WriterMutexLock(Thread* self, ReaderWriterMutex& mu) EXCLUSIVE_LOCK_FUNCTION(mu) : + self_(self), mu_(mu) { + mu_.ExclusiveLock(self_); + } + + explicit WriterMutexLock(ReaderWriterMutex& mu) EXCLUSIVE_LOCK_FUNCTION(mu) : + self_(Thread::Current()), mu_(mu) { + mu_.ExclusiveLock(self_); } ~WriterMutexLock() UNLOCK_FUNCTION() { - mu_.ExclusiveUnlock(); + mu_.ExclusiveUnlock(self_); } private: + Thread* self_; ReaderWriterMutex& mu_; DISALLOW_COPY_AND_ASSIGN(WriterMutexLock); }; @@ -420,27 +335,6 @@ class SCOPED_LOCKABLE WriterMutexLock { // "WriterMutexLock mu(lock)". #define WriterMutexLock(x) COMPILE_ASSERT(0, writer_mutex_lock_declaration_missing_variable_name) -// Scoped unlocker/locker for a ReaderWriterMutex that releases read access to mu upon -// construction and acquires it again upon destruction. -class ReaderMutexUnlock { - public: - explicit ReaderMutexUnlock(ReaderWriterMutex& mu) UNLOCK_FUNCTION(mu) : mu_(mu) { - mu_.SharedUnlock(); - } - - ~ReaderMutexUnlock() SHARED_LOCK_FUNCTION(mu_) { - mu_.SharedLock(); - } - - private: - ReaderWriterMutex& mu_; - DISALLOW_COPY_AND_ASSIGN(ReaderMutexUnlock); -}; -// Catch bug where variable name is omitted. "ReaderMutexUnlock (lock);" instead of -// "ReaderMutexUnlock mu(lock)". -#define ReaderMutexUnlock(x) \ - COMPILE_ASSERT(0, reader_mutex_unlock_declaration_missing_variable_name) - } // namespace art #endif // ART_SRC_MUTEX_H_ diff --git a/src/mutex_test.cc b/src/mutex_test.cc index a9989393ed..4dac3c606f 100644 --- a/src/mutex_test.cc +++ b/src/mutex_test.cc @@ -28,9 +28,9 @@ struct MutexTester { // This test is single-threaded, so we also know _who_ should hold the lock. if (expected_depth == 0) { - mu.AssertNotHeld(); + mu.AssertNotHeld(Thread::Current()); } else { - mu.AssertHeld(); + mu.AssertHeld(Thread::Current()); } } }; @@ -38,9 +38,9 @@ struct MutexTester { TEST_F(MutexTest, LockUnlock) { Mutex mu("test mutex"); MutexTester::AssertDepth(mu, 0U); - mu.Lock(); + mu.Lock(Thread::Current()); MutexTester::AssertDepth(mu, 1U); - mu.Unlock(); + mu.Unlock(Thread::Current()); MutexTester::AssertDepth(mu, 0U); } @@ -48,9 +48,9 @@ TEST_F(MutexTest, LockUnlock) { static void TryLockUnlockTest() NO_THREAD_SAFETY_ANALYSIS { Mutex mu("test mutex"); MutexTester::AssertDepth(mu, 0U); - ASSERT_TRUE(mu.TryLock()); + ASSERT_TRUE(mu.TryLock(Thread::Current())); MutexTester::AssertDepth(mu, 1U); - mu.Unlock(); + mu.Unlock(Thread::Current()); MutexTester::AssertDepth(mu, 0U); } @@ -62,13 +62,13 @@ TEST_F(MutexTest, TryLockUnlock) { static void RecursiveLockUnlockTest() NO_THREAD_SAFETY_ANALYSIS { Mutex mu("test mutex", kDefaultMutexLevel, true); MutexTester::AssertDepth(mu, 0U); - mu.Lock(); + mu.Lock(Thread::Current()); MutexTester::AssertDepth(mu, 1U); - mu.Lock(); + mu.Lock(Thread::Current()); MutexTester::AssertDepth(mu, 2U); - mu.Unlock(); + mu.Unlock(Thread::Current()); MutexTester::AssertDepth(mu, 1U); - mu.Unlock(); + mu.Unlock(Thread::Current()); MutexTester::AssertDepth(mu, 0U); } @@ -80,13 +80,13 @@ TEST_F(MutexTest, RecursiveLockUnlock) { static void RecursiveTryLockUnlockTest() NO_THREAD_SAFETY_ANALYSIS { Mutex mu("test mutex", kDefaultMutexLevel, true); MutexTester::AssertDepth(mu, 0U); - ASSERT_TRUE(mu.TryLock()); + ASSERT_TRUE(mu.TryLock(Thread::Current())); MutexTester::AssertDepth(mu, 1U); - ASSERT_TRUE(mu.TryLock()); + ASSERT_TRUE(mu.TryLock(Thread::Current())); MutexTester::AssertDepth(mu, 2U); - mu.Unlock(); + mu.Unlock(Thread::Current()); MutexTester::AssertDepth(mu, 1U); - mu.Unlock(); + mu.Unlock(Thread::Current()); MutexTester::AssertDepth(mu, 0U); } @@ -102,9 +102,9 @@ struct RecursiveLockWait { static void* Callback(void* arg) { RecursiveLockWait* state = reinterpret_cast<RecursiveLockWait*>(arg); - state->mu.Lock(); + state->mu.Lock(Thread::Current()); state->cv.Signal(); - state->mu.Unlock(); + state->mu.Unlock(Thread::Current()); return NULL; } @@ -115,17 +115,17 @@ struct RecursiveLockWait { // GCC has trouble with our mutex tests, so we have to turn off thread safety analysis. static void RecursiveLockWaitTest() NO_THREAD_SAFETY_ANALYSIS { RecursiveLockWait state; - state.mu.Lock(); - state.mu.Lock(); + state.mu.Lock(Thread::Current()); + state.mu.Lock(Thread::Current()); pthread_t pthread; int pthread_create_result = pthread_create(&pthread, NULL, RecursiveLockWait::Callback, &state); ASSERT_EQ(0, pthread_create_result); - state.cv.Wait(state.mu); + state.cv.Wait(Thread::Current(), state.mu); - state.mu.Unlock(); - state.mu.Unlock(); + state.mu.Unlock(Thread::Current()); + state.mu.Unlock(Thread::Current()); } // This ensures we don't hang when waiting on a recursively locked mutex, @@ -136,33 +136,33 @@ TEST_F(MutexTest, RecursiveLockWait) { TEST_F(MutexTest, SharedLockUnlock) { ReaderWriterMutex mu("test rwmutex"); - mu.AssertNotHeld(); - mu.AssertNotExclusiveHeld(); - mu.SharedLock(); - mu.AssertSharedHeld(); - mu.AssertNotExclusiveHeld(); - mu.SharedUnlock(); - mu.AssertNotHeld(); + mu.AssertNotHeld(Thread::Current()); + mu.AssertNotExclusiveHeld(Thread::Current()); + mu.SharedLock(Thread::Current()); + mu.AssertSharedHeld(Thread::Current()); + mu.AssertNotExclusiveHeld(Thread::Current()); + mu.SharedUnlock(Thread::Current()); + mu.AssertNotHeld(Thread::Current()); } TEST_F(MutexTest, ExclusiveLockUnlock) { ReaderWriterMutex mu("test rwmutex"); - mu.AssertNotHeld(); - mu.ExclusiveLock(); - mu.AssertSharedHeld(); - mu.AssertExclusiveHeld(); - mu.ExclusiveUnlock(); - mu.AssertNotHeld(); + mu.AssertNotHeld(Thread::Current()); + mu.ExclusiveLock(Thread::Current()); + mu.AssertSharedHeld(Thread::Current()); + mu.AssertExclusiveHeld(Thread::Current()); + mu.ExclusiveUnlock(Thread::Current()); + mu.AssertNotHeld(Thread::Current()); } // GCC has trouble with our mutex tests, so we have to turn off thread safety analysis. static void SharedTryLockUnlockTest() NO_THREAD_SAFETY_ANALYSIS { ReaderWriterMutex mu("test rwmutex"); - mu.AssertNotHeld(); - ASSERT_TRUE(mu.SharedTryLock()); - mu.AssertSharedHeld(); - mu.SharedUnlock(); - mu.AssertNotHeld(); + mu.AssertNotHeld(Thread::Current()); + ASSERT_TRUE(mu.SharedTryLock(Thread::Current())); + mu.AssertSharedHeld(Thread::Current()); + mu.SharedUnlock(Thread::Current()); + mu.AssertNotHeld(Thread::Current()); } TEST_F(MutexTest, SharedTryLockUnlock) { diff --git a/src/native/dalvik_system_VMRuntime.cc b/src/native/dalvik_system_VMRuntime.cc index fae06f68ca..f37b237342 100644 --- a/src/native/dalvik_system_VMRuntime.cc +++ b/src/native/dalvik_system_VMRuntime.cc @@ -154,14 +154,15 @@ static void VMRuntime_setTargetSdkVersion(JNIEnv*, jobject, jint targetSdkVersio } } -static void VMRuntime_trimHeap(JNIEnv*, jobject) { +static void VMRuntime_trimHeap(JNIEnv* env, jobject) { // Trim the managed heap. Heap* heap = Runtime::Current()->GetHeap(); uint64_t start_ns = NanoTime(); AllocSpace* alloc_space = heap->GetAllocSpace(); size_t alloc_space_size = alloc_space->Size(); float utilization = static_cast<float>(heap->GetBytesAllocated()) / alloc_space_size; - heap->Trim(); + Thread* self = static_cast<JNIEnvExt*>(env)->self; + heap->Trim(self); // Trim the native heap. dlmalloc_trim(0); dlmalloc_inspect_all(MspaceMadviseCallback, NULL); @@ -170,8 +171,9 @@ static void VMRuntime_trimHeap(JNIEnv*, jobject) { << " alloc space with " << static_cast<int>(100 * utilization) << "% utilization"; } -static void VMRuntime_concurrentGC(JNIEnv*, jobject) { - Runtime::Current()->GetHeap()->ConcurrentGC(); +static void VMRuntime_concurrentGC(JNIEnv* env, jobject) { + Thread* self = static_cast<JNIEnvExt*>(env)->self; + Runtime::Current()->GetHeap()->ConcurrentGC(self); } static JNINativeMethod gMethods[] = { diff --git a/src/native/java_lang_Thread.cc b/src/native/java_lang_Thread.cc index 2a6f177ce5..edf55c3c9e 100644 --- a/src/native/java_lang_Thread.cc +++ b/src/native/java_lang_Thread.cc @@ -98,7 +98,7 @@ static jboolean Thread_nativeHoldsLock(JNIEnv* env, jobject java_thread, jobject static void Thread_nativeInterrupt(JNIEnv* env, jobject java_thread) { ScopedObjectAccess soa(env); - MutexLock mu(*Locks::thread_list_lock_); + MutexLock mu(soa.Self(), *Locks::thread_list_lock_); Thread* thread = Thread::FromManagedThread(soa, java_thread); if (thread != NULL) { thread->Interrupt(); diff --git a/src/oat/jni/arm/jni_internal_arm.cc b/src/oat/jni/arm/jni_internal_arm.cc index 522066988d..61f29afed8 100644 --- a/src/oat/jni/arm/jni_internal_arm.cc +++ b/src/oat/jni/arm/jni_internal_arm.cc @@ -21,6 +21,7 @@ #include "asm_support.h" #include "compiled_method.h" #include "compiler.h" +#include "jni_internal.h" #include "oat/utils/arm/assembler_arm.h" #include "oat/utils/assembler.h" #include "object.h" diff --git a/src/oat/jni/mips/jni_internal_mips.cc b/src/oat/jni/mips/jni_internal_mips.cc index 6021cc10d2..a1fc0bf16c 100644 --- a/src/oat/jni/mips/jni_internal_mips.cc +++ b/src/oat/jni/mips/jni_internal_mips.cc @@ -21,6 +21,7 @@ #include "asm_support.h" #include "compiled_method.h" #include "compiler.h" +#include "jni_internal.h" #include "oat/utils/mips/assembler_mips.h" #include "oat/utils/assembler.h" #include "object.h" diff --git a/src/oat/jni/x86/jni_internal_x86.cc b/src/oat/jni/x86/jni_internal_x86.cc index a9d4004d3f..c34112bc2e 100644 --- a/src/oat/jni/x86/jni_internal_x86.cc +++ b/src/oat/jni/x86/jni_internal_x86.cc @@ -16,6 +16,7 @@ #include "compiled_method.h" #include "compiler.h" +#include "jni_internal.h" #include "oat/utils/assembler.h" #include "oat/utils/x86/assembler_x86.h" #include "object.h" diff --git a/src/oat/runtime/callee_save_frame.h b/src/oat/runtime/callee_save_frame.h index 28bcda6473..a8ebce8fa1 100644 --- a/src/oat/runtime/callee_save_frame.h +++ b/src/oat/runtime/callee_save_frame.h @@ -17,6 +17,7 @@ #ifndef ART_SRC_OAT_RUNTIME_CALLEE_SAVE_FRAME_H_ #define ART_SRC_OAT_RUNTIME_CALLEE_SAVE_FRAME_H_ +#include "../src/mutex.h" #include "thread.h" namespace art { @@ -24,10 +25,11 @@ namespace art { class AbstractMethod; // Place a special frame at the TOS that will save the callee saves for the given type. -static void FinishCalleeSaveFrameSetup(Thread* self, AbstractMethod** sp, Runtime::CalleeSaveType type) +static void FinishCalleeSaveFrameSetup(Thread* self, AbstractMethod** sp, + Runtime::CalleeSaveType type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // Be aware the store below may well stomp on an incoming argument. - Locks::mutator_lock_->AssertSharedHeld(); + Locks::mutator_lock_->AssertSharedHeld(self); *sp = Runtime::Current()->GetCalleeSaveMethod(type); self->SetTopOfStack(sp, 0); self->VerifyStack(); diff --git a/src/oat/runtime/support_jni.cc b/src/oat/runtime/support_jni.cc index 6116d563dc..60bcf08770 100644 --- a/src/oat/runtime/support_jni.cc +++ b/src/oat/runtime/support_jni.cc @@ -24,7 +24,7 @@ namespace art { // Used by the JNI dlsym stub to find the native method to invoke if none is registered. extern void* FindNativeMethod(Thread* self) LOCKS_EXCLUDED(Locks::mutator_lock_) { - Locks::mutator_lock_->AssertNotHeld(); // We come here as Native. + Locks::mutator_lock_->AssertNotHeld(self); // We come here as Native. DCHECK(Thread::Current() == self); ScopedObjectAccess soa(self); diff --git a/src/object.cc b/src/object.cc index eb11469341..284f221720 100644 --- a/src/object.cc +++ b/src/object.cc @@ -582,7 +582,6 @@ uint32_t AbstractMethod::FindCatchBlock(Class* exception_type, uint32_t dex_pc) void AbstractMethod::Invoke(Thread* self, Object* receiver, JValue* args, JValue* result) const { if (kIsDebugBuild) { self->AssertThreadSuspensionIsAllowable(); - MutexLock mu(*Locks::thread_suspend_count_lock_); CHECK_EQ(kRunnable, self->GetState()); } diff --git a/src/reference_table.cc b/src/reference_table.cc index 1f6cab7446..19b6d428fb 100644 --- a/src/reference_table.cc +++ b/src/reference_table.cc @@ -17,6 +17,7 @@ #include "reference_table.h" #include "indirect_reference_table.h" +#include "mutex.h" #include "object.h" @@ -63,7 +64,7 @@ struct ObjectComparator { bool operator()(const Object* obj1, const Object* obj2) // TODO: enable analysis when analysis can work with the STL. NO_THREAD_SAFETY_ANALYSIS { - Locks::mutator_lock_->AssertSharedHeld(); + Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); // Ensure null references and cleared jweaks appear at the end. if (obj1 == NULL) { return true; diff --git a/src/runtime.cc b/src/runtime.cc index b4dfcfe068..2b9a28d248 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -118,7 +118,7 @@ Runtime::~Runtime() { } // Make sure to let the GC complete if it is running. - heap_->WaitForConcurrentGcToComplete(); + heap_->WaitForConcurrentGcToComplete(Thread::Current()); // Make sure our internal threads are dead before we start tearing down things they're using. Dbg::StopJdwp(); diff --git a/src/runtime_linux.cc b/src/runtime_linux.cc index e5033585c1..85eeb8f0be 100644 --- a/src/runtime_linux.cc +++ b/src/runtime_linux.cc @@ -21,6 +21,7 @@ #include <sys/utsname.h> #include "logging.h" +#include "mutex.h" #include "stringprintf.h" #include "utils.h" diff --git a/src/runtime_support.h b/src/runtime_support.h index b4a23ff31d..eff50b3710 100644 --- a/src/runtime_support.h +++ b/src/runtime_support.h @@ -20,7 +20,9 @@ #include "class_linker.h" #include "common_throws.h" #include "dex_file.h" +#include "indirect_reference_table.h" #include "invoke_type.h" +#include "jni_internal.h" #include "object.h" #include "object_utils.h" #include "thread.h" diff --git a/src/scoped_thread_state_change.h b/src/scoped_thread_state_change.h index 14956e4624..b36922e10d 100644 --- a/src/scoped_thread_state_change.h +++ b/src/scoped_thread_state_change.h @@ -18,6 +18,7 @@ #define ART_SRC_SCOPED_THREAD_STATE_CHANGE_H_ #include "casts.h" +#include "jni_internal.h" #include "thread.h" namespace art { @@ -197,7 +198,7 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange { LOCKS_EXCLUDED(JavaVMExt::globals_lock, JavaVMExt::weak_globals_lock) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Locks::mutator_lock_->AssertSharedHeld(); + Locks::mutator_lock_->AssertSharedHeld(Self()); DCHECK_EQ(thread_state_, kRunnable); // Don't work with raw objects in non-runnable states. return down_cast<T>(Self()->DecodeJObject(obj)); } @@ -206,7 +207,7 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange { LOCKS_EXCLUDED(JavaVMExt::globals_lock, JavaVMExt::weak_globals_lock) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Locks::mutator_lock_->AssertSharedHeld(); + Locks::mutator_lock_->AssertSharedHeld(Self()); DCHECK_EQ(thread_state_, kRunnable); // Don't work with raw objects in non-runnable states. #ifdef MOVING_GARBAGE_COLLECTOR // TODO: we should make these unique weak globals if Field instances can ever move. @@ -219,7 +220,7 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange { LOCKS_EXCLUDED(JavaVMExt::globals_lock, JavaVMExt::weak_globals_lock) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Locks::mutator_lock_->AssertSharedHeld(); + Locks::mutator_lock_->AssertSharedHeld(Self()); DCHECK_EQ(thread_state_, kRunnable); // Don't work with raw objects in non-runnable states. #ifdef MOVING_GARBAGE_COLLECTOR UNIMPLEMENTED(WARNING); @@ -231,7 +232,7 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange { LOCKS_EXCLUDED(JavaVMExt::globals_lock, JavaVMExt::weak_globals_lock) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Locks::mutator_lock_->AssertSharedHeld(); + Locks::mutator_lock_->AssertSharedHeld(Self()); DCHECK_EQ(thread_state_, kRunnable); // Don't work with raw objects in non-runnable states. #ifdef MOVING_GARBAGE_COLLECTOR // TODO: we should make these unique weak globals if Method instances can ever move. @@ -242,7 +243,7 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange { jmethodID EncodeMethod(AbstractMethod* method) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Locks::mutator_lock_->AssertSharedHeld(); + Locks::mutator_lock_->AssertSharedHeld(Self()); DCHECK_EQ(thread_state_, kRunnable); // Don't work with raw objects in non-runnable states. #ifdef MOVING_GARBAGE_COLLECTOR UNIMPLEMENTED(WARNING); @@ -285,14 +286,14 @@ class ScopedObjectAccess : public ScopedObjectAccessUnchecked { LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) SHARED_LOCK_FUNCTION(Locks::mutator_lock_) : ScopedObjectAccessUnchecked(env) { - Locks::mutator_lock_->AssertSharedHeld(); + Locks::mutator_lock_->AssertSharedHeld(Self()); } explicit ScopedObjectAccess(Thread* self) LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) SHARED_LOCK_FUNCTION(Locks::mutator_lock_) : ScopedObjectAccessUnchecked(self) { - Locks::mutator_lock_->AssertSharedHeld(); + Locks::mutator_lock_->AssertSharedHeld(Self()); } ~ScopedObjectAccess() UNLOCK_FUNCTION(Locks::mutator_lock_) { diff --git a/src/signal_catcher.cc b/src/signal_catcher.cc index 72393745ff..57cae76659 100644 --- a/src/signal_catcher.cc +++ b/src/signal_catcher.cc @@ -69,9 +69,10 @@ SignalCatcher::SignalCatcher(const std::string& stack_trace_file) // Create a raw pthread; its start routine will attach to the runtime. CHECK_PTHREAD_CALL(pthread_create, (&pthread_, NULL, &Run, this), "signal catcher thread"); - MutexLock mu(lock_); + Thread* self = Thread::Current(); + MutexLock mu(self, lock_); while (thread_ == NULL) { - cond_.Wait(lock_); + cond_.Wait(self, lock_); } } @@ -122,12 +123,12 @@ void SignalCatcher::HandleSigQuit() { // We should exclusively hold the mutator lock, set state to Runnable without a pending // suspension to avoid giving away or trying to re-acquire the mutator lock. - Locks::mutator_lock_->AssertExclusiveHeld(); Thread* self = Thread::Current(); + Locks::mutator_lock_->AssertExclusiveHeld(self); ThreadState old_state; int suspend_count; { - MutexLock mu(*Locks::thread_suspend_count_lock_); + MutexLock mu(self, *Locks::thread_suspend_count_lock_); suspend_count = self->GetSuspendCount(); if (suspend_count != 0) { CHECK_EQ(suspend_count, 1); @@ -155,7 +156,7 @@ void SignalCatcher::HandleSigQuit() { os << "----- end " << getpid() << " -----\n"; { - MutexLock mu(*Locks::thread_suspend_count_lock_); + MutexLock mu(self, *Locks::thread_suspend_count_lock_); self->SetState(old_state); if (suspend_count != 0) { self->ModifySuspendCount(+1, false); @@ -201,7 +202,7 @@ void* SignalCatcher::Run(void* arg) { Thread* self = Thread::Current(); { - MutexLock mu(signal_catcher->lock_); + MutexLock mu(self, signal_catcher->lock_); signal_catcher->thread_ = self; signal_catcher->cond_.Broadcast(); } diff --git a/src/space.h b/src/space.h index c3c31a8a3d..d6c7f98a37 100644 --- a/src/space.h +++ b/src/space.h @@ -23,6 +23,7 @@ #include "globals.h" #include "image.h" #include "macros.h" +#include "mutex.h" #include "dlmalloc.h" #include "mem_map.h" diff --git a/src/stack.cc b/src/stack.cc index 2567c50568..7ec57b41b2 100644 --- a/src/stack.cc +++ b/src/stack.cc @@ -24,21 +24,6 @@ namespace art { -void ManagedStack::PushManagedStackFragment(ManagedStack* fragment) { - // Copy this top fragment into given fragment. - memcpy(fragment, this, sizeof(ManagedStack)); - // Clear this fragment, which has become the top. - memset(this, 0, sizeof(ManagedStack)); - // Link our top fragment onto the given fragment. - link_ = fragment; -} - -void ManagedStack::PopManagedStackFragment(const ManagedStack& fragment) { - DCHECK(&fragment == link_); - // Copy this given fragment back to the top. - memcpy(this, &fragment, sizeof(ManagedStack)); -} - size_t ManagedStack::NumShadowFrameReferences() const { size_t count = 0; for (const ManagedStack* current_fragment = this; current_fragment != NULL; diff --git a/src/stack.h b/src/stack.h index 4686c6be82..ca379d4497 100644 --- a/src/stack.h +++ b/src/stack.h @@ -147,8 +147,21 @@ class PACKED ManagedStack { public: ManagedStack() : link_(NULL), top_shadow_frame_(NULL), top_quick_frame_(NULL), top_quick_frame_pc_(0) {} - void PushManagedStackFragment(ManagedStack* fragment); - void PopManagedStackFragment(const ManagedStack& record); + + void PushManagedStackFragment(ManagedStack* fragment) { + // Copy this top fragment into given fragment. + memcpy(fragment, this, sizeof(ManagedStack)); + // Clear this fragment, which has become the top. + memset(this, 0, sizeof(ManagedStack)); + // Link our top fragment onto the given fragment. + link_ = fragment; + } + + void PopManagedStackFragment(const ManagedStack& fragment) { + DCHECK(&fragment == link_); + // Copy this given fragment back to the top. + memcpy(this, &fragment, sizeof(ManagedStack)); + } ManagedStack* GetLink() const { return link_; diff --git a/src/thread.cc b/src/thread.cc index f879ee2857..bc5b68e789 100644 --- a/src/thread.cc +++ b/src/thread.cc @@ -37,6 +37,7 @@ #include "heap.h" #include "jni_internal.h" #include "monitor.h" +#include "mutex.h" #include "oat/runtime/context.h" #include "object.h" #include "object_utils.h" @@ -139,9 +140,11 @@ Thread* Thread::FromManagedThread(const ScopedObjectAccessUnchecked& soa, Object Thread* result = reinterpret_cast<Thread*>(static_cast<uintptr_t>(f->GetInt(thread_peer))); // Sanity check that if we have a result it is either suspended or we hold the thread_list_lock_ // to stop it from going away. - MutexLock mu(*Locks::thread_suspend_count_lock_); - if (result != NULL && !result->IsSuspended()) { - Locks::thread_list_lock_->AssertHeld(); + if (kIsDebugBuild) { + MutexLock mu(soa.Self(), *Locks::thread_suspend_count_lock_); + if (result != NULL && !result->IsSuspended()) { + Locks::thread_list_lock_->AssertHeld(soa.Self()); + } } return result; } @@ -453,13 +456,13 @@ void Thread::GetThreadName(std::string& name) const { // Attempt to rectify locks so that we dump thread list with required locks before exiting. static void UnsafeLogFatalForSuspendCount(Thread* self) NO_THREAD_SAFETY_ANALYSIS { - Locks::thread_suspend_count_lock_->Unlock(); - Locks::mutator_lock_->SharedTryLock(); - if (!Locks::mutator_lock_->IsSharedHeld()) { + Locks::thread_suspend_count_lock_->Unlock(self); + Locks::mutator_lock_->SharedTryLock(self); + if (!Locks::mutator_lock_->IsSharedHeld(self)) { LOG(WARNING) << "Dumping thread list without holding mutator_lock_"; } - Locks::thread_list_lock_->TryLock(); - if (!Locks::thread_list_lock_->IsExclusiveHeld()) { + Locks::thread_list_lock_->TryLock(self); + if (!Locks::thread_list_lock_->IsExclusiveHeld(self)) { LOG(WARNING) << "Dumping thread list without holding thread_list_lock_"; } std::ostringstream ss; @@ -526,7 +529,7 @@ void Thread::TransitionFromRunnableToSuspended(ThreadState new_state) { DCHECK_EQ(GetState(), kRunnable); state_and_flags_.as_struct.state = new_state; // Release share on mutator_lock_. - Locks::mutator_lock_->SharedUnlock(); + Locks::mutator_lock_->SharedUnlock(this); } ThreadState Thread::TransitionFromSuspendedToRunnable() { @@ -534,33 +537,33 @@ ThreadState Thread::TransitionFromSuspendedToRunnable() { ThreadState old_state = GetState(); DCHECK_NE(old_state, kRunnable); do { - Locks::mutator_lock_->AssertNotHeld(); // Otherwise we starve GC.. + Locks::mutator_lock_->AssertNotHeld(this); // Otherwise we starve GC.. DCHECK_EQ(GetState(), old_state); if (ReadFlag(kSuspendRequest)) { // Wait while our suspend count is non-zero. - MutexLock mu(*Locks::thread_suspend_count_lock_); + MutexLock mu(this, *Locks::thread_suspend_count_lock_); DCHECK_EQ(GetState(), old_state); while (ReadFlag(kSuspendRequest)) { // Re-check when Thread::resume_cond_ is notified. - Thread::resume_cond_->Wait(*Locks::thread_suspend_count_lock_); + Thread::resume_cond_->Wait(this, *Locks::thread_suspend_count_lock_); DCHECK_EQ(GetState(), old_state); } DCHECK_EQ(GetSuspendCount(), 0); } // Re-acquire shared mutator_lock_ access. - Locks::mutator_lock_->SharedLock(); + Locks::mutator_lock_->SharedLock(this); // Atomically change from suspended to runnable if no suspend request pending. int16_t old_flags = state_and_flags_.as_struct.flags; if ((old_flags & kSuspendRequest) == 0) { int32_t old_state_and_flags = old_flags | (old_state << 16); int32_t new_state_and_flags = old_flags | (kRunnable << 16); done = android_atomic_cmpxchg(old_state_and_flags, new_state_and_flags, - reinterpret_cast<volatile int32_t*>(&state_and_flags_)) + &state_and_flags_.as_int) == 0; } if (!done) { // Failed to transition to Runnable. Release shared mutator_lock_ access and try again. - Locks::mutator_lock_->SharedUnlock(); + Locks::mutator_lock_->SharedUnlock(this); } } while (!done); return old_state; @@ -576,14 +579,14 @@ Thread* Thread::SuspendForDebugger(jobject peer, bool request_suspension, bool* Thread* thread; { ScopedObjectAccess soa(Thread::Current()); - MutexLock mu(*Locks::thread_list_lock_); + MutexLock mu(soa.Self(), *Locks::thread_list_lock_); thread = Thread::FromManagedThread(soa, peer); if (thread == NULL) { LOG(WARNING) << "No such thread for suspend: " << peer; return NULL; } { - MutexLock mu(*Locks::thread_suspend_count_lock_); + MutexLock mu(soa.Self(), *Locks::thread_suspend_count_lock_); if (request_suspension) { thread->ModifySuspendCount(+1, true /* for_debugger */); request_suspension = false; @@ -612,7 +615,7 @@ Thread* Thread::SuspendForDebugger(jobject peer, bool request_suspension, bool* // Release locks and come out of runnable state. } for (int i = kMaxMutexLevel; i >= 0; --i) { - BaseMutex* held_mutex = Thread::Current()->GetHeldMutex(static_cast<MutexLevel>(i)); + BaseMutex* held_mutex = Thread::Current()->GetHeldMutex(static_cast<LockLevel>(i)); if (held_mutex != NULL) { LOG(FATAL) << "Holding " << held_mutex->GetName() << " while sleeping for thread suspension"; @@ -640,9 +643,10 @@ void Thread::DumpState(std::ostream& os, const Thread* thread, pid_t tid) { std::string group_name; int priority; bool is_daemon = false; + Thread* self = Thread::Current(); if (thread != NULL && thread->peer_ != NULL) { - ScopedObjectAccess soa(Thread::Current()); + ScopedObjectAccess soa(self); Object* native_peer = soa.Decode<Object*>(thread->peer_); priority = soa.DecodeField(WellKnownClasses::java_lang_Thread_priority)->GetInt(native_peer); is_daemon = soa.DecodeField(WellKnownClasses::java_lang_Thread_daemon)->GetBoolean(native_peer); @@ -667,7 +671,7 @@ void Thread::DumpState(std::ostream& os, const Thread* thread, pid_t tid) { if (is_daemon) { os << " daemon"; } - MutexLock mu(*Locks::thread_suspend_count_lock_); + MutexLock mu(self, *Locks::thread_suspend_count_lock_); os << " prio=" << priority << " tid=" << thread->GetThinLockId() << " " << thread->GetState() << "\n"; @@ -678,7 +682,7 @@ void Thread::DumpState(std::ostream& os, const Thread* thread, pid_t tid) { } if (thread != NULL) { - MutexLock mu(*Locks::thread_suspend_count_lock_); + MutexLock mu(self, *Locks::thread_suspend_count_lock_); os << " | group=\"" << group_name << "\"" << " sCount=" << thread->suspend_count_ << " dsCount=" << thread->debug_suspend_count_ @@ -1059,7 +1063,7 @@ void Thread::SirtVisitRoots(Heap::RootVisitor* visitor, void* arg) { } Object* Thread::DecodeJObject(jobject obj) { - Locks::mutator_lock_->AssertSharedHeld(); + Locks::mutator_lock_->AssertSharedHeld(this); if (obj == NULL) { return NULL; } @@ -1077,7 +1081,7 @@ Object* Thread::DecodeJObject(jobject obj) { { JavaVMExt* vm = Runtime::Current()->GetJavaVM(); IndirectReferenceTable& globals = vm->globals; - MutexLock mu(vm->globals_lock); + MutexLock mu(this, vm->globals_lock); result = const_cast<Object*>(globals.Get(ref)); break; } @@ -1085,7 +1089,7 @@ Object* Thread::DecodeJObject(jobject obj) { { JavaVMExt* vm = Runtime::Current()->GetJavaVM(); IndirectReferenceTable& weak_globals = vm->weak_globals; - MutexLock mu(vm->weak_globals_lock); + MutexLock mu(this, vm->weak_globals_lock); result = const_cast<Object*>(weak_globals.Get(ref)); if (result == kClearedJniWeakGlobal) { // This is a special case where it's okay to return NULL. @@ -1117,6 +1121,40 @@ Object* Thread::DecodeJObject(jobject obj) { return result; } +// Implements java.lang.Thread.interrupted. +bool Thread::Interrupted() { + MutexLock mu(*wait_mutex_); + bool interrupted = interrupted_; + interrupted_ = false; + return interrupted; +} + +// Implements java.lang.Thread.isInterrupted. +bool Thread::IsInterrupted() { + MutexLock mu(*wait_mutex_); + return interrupted_; +} + +void Thread::Interrupt() { + MutexLock mu(*wait_mutex_); + if (interrupted_) { + return; + } + interrupted_ = true; + NotifyLocked(); +} + +void Thread::Notify() { + MutexLock mu(*wait_mutex_); + NotifyLocked(); +} + +void Thread::NotifyLocked() { + if (wait_monitor_ != NULL) { + wait_cond_->Signal(); + } +} + class CountStackDepthVisitor : public StackVisitor { public: CountStackDepthVisitor(const ManagedStack* stack, @@ -1874,7 +1912,7 @@ void Thread::AssertThreadSuspensionIsAllowable(bool check_locks) const { for (int i = kMaxMutexLevel; i >= 0; --i) { // We expect no locks except the mutator_lock_. if (i != kMutatorLock) { - BaseMutex* held_mutex = GetHeldMutex(static_cast<MutexLevel>(i)); + BaseMutex* held_mutex = GetHeldMutex(static_cast<LockLevel>(i)); if (held_mutex != NULL) { LOG(ERROR) << "holding \"" << held_mutex->GetName() << "\" at point where thread suspension is expected"; diff --git a/src/thread.h b/src/thread.h index 1b9bb74053..257dee4346 100644 --- a/src/thread.h +++ b/src/thread.h @@ -25,14 +25,10 @@ #include <string> #include <vector> -#include "dex_file.h" #include "globals.h" -#include "jni_internal.h" -#include "logging.h" #include "macros.h" -#include "mutex.h" -#include "mem_map.h" #include "oat/runtime/oat_support_entrypoints.h" +#include "locks.h" #include "offsets.h" #include "runtime_stats.h" #include "stack.h" @@ -44,13 +40,16 @@ namespace art { +class AbstractMethod; class Array; +class BaseMutex; class Class; class ClassLinker; class ClassLoader; class Context; struct DebugInvokeReq; -class AbstractMethod; +class DexFile; +struct JNIEnvExt; class Monitor; class Object; class Runtime; @@ -158,22 +157,16 @@ class PACKED Thread { ThreadState SetState(ThreadState new_state); - int GetSuspendCount() const - EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_suspend_count_lock_) { - Locks::thread_suspend_count_lock_->AssertHeld(); + int GetSuspendCount() const EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_suspend_count_lock_) { return suspend_count_; } - int GetDebugSuspendCount() const - EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_suspend_count_lock_) { - Locks::thread_suspend_count_lock_->AssertHeld(); + int GetDebugSuspendCount() const EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_suspend_count_lock_) { return debug_suspend_count_; } - bool IsSuspended() const - EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_suspend_count_lock_) { - int suspend_count = GetSuspendCount(); - return suspend_count != 0 && GetState() != kRunnable; + bool IsSuspended() const EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_suspend_count_lock_) { + return GetState() != kRunnable && ReadFlag(kSuspendRequest); } void ModifySuspendCount(int delta, bool for_debugger) @@ -386,38 +379,14 @@ class PACKED Thread { } // Convert a jobject into a Object* - Object* DecodeJObject(jobject obj) - LOCKS_EXCLUDED(JavaVMExt::globals_lock, - JavaVMExt::weak_globals_lock) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + Object* DecodeJObject(jobject obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Implements java.lang.Thread.interrupted. - bool Interrupted() { - MutexLock mu(*wait_mutex_); - bool interrupted = interrupted_; - interrupted_ = false; - return interrupted; - } - + bool Interrupted(); // Implements java.lang.Thread.isInterrupted. - bool IsInterrupted() { - MutexLock mu(*wait_mutex_); - return interrupted_; - } - - void Interrupt() { - MutexLock mu(*wait_mutex_); - if (interrupted_) { - return; - } - interrupted_ = true; - NotifyLocked(); - } - - void Notify() { - MutexLock mu(*wait_mutex_); - NotifyLocked(); - } + bool IsInterrupted(); + void Interrupt(); + void Notify(); ClassLoader* GetClassLoaderOverride() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return class_loader_override_; @@ -579,11 +548,11 @@ class PACKED Thread { return frame; } - BaseMutex* GetHeldMutex(MutexLevel level) const { + BaseMutex* GetHeldMutex(LockLevel level) const { return held_mutexes_[level]; } - void SetHeldMutex(MutexLevel level, BaseMutex* mutex) { + void SetHeldMutex(LockLevel level, BaseMutex* mutex) { held_mutexes_[level] = mutex; } @@ -634,11 +603,7 @@ class PACKED Thread { void InitPthreadKeySelf(); void InitStackHwm(); - void NotifyLocked() EXCLUSIVE_LOCKS_REQUIRED(wait_mutex_) { - if (wait_monitor_ != NULL) { - wait_cond_->Signal(); - } - } + void NotifyLocked() EXCLUSIVE_LOCKS_REQUIRED(wait_mutex_); bool ReadFlag(ThreadFlag flag) const { return (state_and_flags_.as_struct.flags & flag) != 0; diff --git a/src/thread_list.cc b/src/thread_list.cc index 550d5c7b6e..082d7af0da 100644 --- a/src/thread_list.cc +++ b/src/thread_list.cc @@ -128,18 +128,18 @@ void ThreadList::AssertThreadsAreSuspended() { #if HAVE_TIMED_RWLOCK // Attempt to rectify locks so that we dump thread list with required locks before exiting. -static void UnsafeLogFatalForThreadSuspendAllTimeout() NO_THREAD_SAFETY_ANALYSIS { +static void UnsafeLogFatalForThreadSuspendAllTimeout(Thread* self) NO_THREAD_SAFETY_ANALYSIS { Runtime* runtime = Runtime::Current(); std::ostringstream ss; ss << "Thread suspend timeout\n"; runtime->DumpLockHolders(ss); ss << "\n"; - Locks::mutator_lock_->SharedTryLock(); - if (!Locks::mutator_lock_->IsSharedHeld()) { + Locks::mutator_lock_->SharedTryLock(self); + if (!Locks::mutator_lock_->IsSharedHeld(self)) { LOG(WARNING) << "Dumping thread list without holding mutator_lock_"; } - Locks::thread_list_lock_->TryLock(); - if (!Locks::thread_list_lock_->IsExclusiveHeld()) { + Locks::thread_list_lock_->TryLock(self); + if (!Locks::thread_list_lock_->IsExclusiveHeld(self)) { LOG(WARNING) << "Dumping thread list without holding thread_list_lock_"; } runtime->GetThreadList()->DumpLocked(ss); @@ -153,16 +153,15 @@ void ThreadList::SuspendAll() { VLOG(threads) << *self << " SuspendAll starting..."; if (kIsDebugBuild) { - Locks::mutator_lock_->AssertNotHeld(); - Locks::thread_list_lock_->AssertNotHeld(); - Locks::thread_suspend_count_lock_->AssertNotHeld(); - MutexLock mu(*Locks::thread_suspend_count_lock_); + Locks::mutator_lock_->AssertNotHeld(self); + Locks::thread_list_lock_->AssertNotHeld(self); + Locks::thread_suspend_count_lock_->AssertNotHeld(self); CHECK_NE(self->GetState(), kRunnable); } { - MutexLock mu(*Locks::thread_list_lock_); + MutexLock mu(self, *Locks::thread_list_lock_); { - MutexLock mu2(*Locks::thread_suspend_count_lock_); + MutexLock mu2(self, *Locks::thread_suspend_count_lock_); // Update global suspend all state for attaching threads. ++suspend_all_count_; // Increment everybody's suspend count (except our own). @@ -183,11 +182,11 @@ void ThreadList::SuspendAll() { timespec timeout; clock_gettime(CLOCK_REALTIME, &timeout); timeout.tv_sec += 30; - if (UNLIKELY(!Locks::mutator_lock_->ExclusiveLockWithTimeout(timeout))) { - UnsafeLogFatalForThreadSuspendAllTimeout(); + if (UNLIKELY(!Locks::mutator_lock_->ExclusiveLockWithTimeout(self, timeout))) { + UnsafeLogFatalForThreadSuspendAllTimeout(self); } #else - Locks::mutator_lock_->ExclusiveLock(); + Locks::mutator_lock_->ExclusiveLock(self); #endif // Debug check that all threads are suspended. @@ -200,9 +199,10 @@ void ThreadList::ResumeAll() { Thread* self = Thread::Current(); VLOG(threads) << *self << " ResumeAll starting"; + Locks::mutator_lock_->ExclusiveUnlock(self); { - MutexLock mu(*Locks::thread_list_lock_); - MutexLock mu2(*Locks::thread_suspend_count_lock_); + MutexLock mu(self, *Locks::thread_list_lock_); + MutexLock mu2(self, *Locks::thread_suspend_count_lock_); // Update global suspend all state for attaching threads. --suspend_all_count_; // Decrement the suspend counts for all threads. @@ -219,20 +219,20 @@ void ThreadList::ResumeAll() { VLOG(threads) << *self << " ResumeAll waking others"; Thread::resume_cond_->Broadcast(); } - Locks::mutator_lock_->ExclusiveUnlock(); VLOG(threads) << *self << " ResumeAll complete"; } void ThreadList::Resume(Thread* thread, bool for_debugger) { - DCHECK(thread != Thread::Current()); + Thread* self = Thread::Current(); + DCHECK_NE(thread, self); VLOG(threads) << "Resume(" << *thread << ") starting..." << (for_debugger ? " (debugger)" : ""); { // To check Contains. - MutexLock mu(*Locks::thread_list_lock_); + MutexLock mu(self, *Locks::thread_list_lock_); // To check IsSuspended. - MutexLock mu2(*Locks::thread_suspend_count_lock_); - CHECK(thread->IsSuspended()); + MutexLock mu2(self, *Locks::thread_suspend_count_lock_); + DCHECK(thread->IsSuspended()); if (!Contains(thread)) { return; } @@ -241,7 +241,7 @@ void ThreadList::Resume(Thread* thread, bool for_debugger) { { VLOG(threads) << "Resume(" << *thread << ") waking others"; - MutexLock mu(*Locks::thread_suspend_count_lock_); + MutexLock mu(self, *Locks::thread_suspend_count_lock_); Thread::resume_cond_->Broadcast(); } @@ -255,9 +255,9 @@ void ThreadList::SuspendAllForDebugger() { VLOG(threads) << *self << " SuspendAllForDebugger starting..."; { - MutexLock mu(*Locks::thread_list_lock_); + MutexLock mu(self, *Locks::thread_list_lock_); { - MutexLock mu(*Locks::thread_suspend_count_lock_); + MutexLock mu(self, *Locks::thread_suspend_count_lock_); // Update global suspend all state for attaching threads. ++suspend_all_count_; ++debug_suspend_all_count_; @@ -280,14 +280,14 @@ void ThreadList::SuspendAllForDebugger() { timespec timeout; clock_gettime(CLOCK_REALTIME, &timeout); timeout.tv_sec += 30; - if (!Locks::mutator_lock_->ExclusiveLockWithTimeout(timeout)) { - UnsafeLogFatalForThreadSuspendAllTimeout(); + if (!Locks::mutator_lock_->ExclusiveLockWithTimeout(self, timeout)) { + UnsafeLogFatalForThreadSuspendAllTimeout(self); } else { - Locks::mutator_lock_->ExclusiveUnlock(); + Locks::mutator_lock_->ExclusiveUnlock(self); } #else - Locks::mutator_lock_->ExclusiveLock(); - Locks::mutator_lock_->ExclusiveUnlock(); + Locks::mutator_lock_->ExclusiveLock(self); + Locks::mutator_lock_->ExclusiveUnlock(self); #endif AssertThreadsAreSuspended(); @@ -305,7 +305,7 @@ void ThreadList::SuspendSelfForDebugger() { // Collisions with other suspends aren't really interesting. We want // to ensure that we're the only one fiddling with the suspend count // though. - MutexLock mu(*Locks::thread_suspend_count_lock_); + MutexLock mu(self, *Locks::thread_suspend_count_lock_); self->ModifySuspendCount(+1, true); // Suspend ourselves. @@ -319,7 +319,7 @@ void ThreadList::SuspendSelfForDebugger() { Dbg::ClearWaitForEventThread(); while (self->suspend_count_ != 0) { - Thread::resume_cond_->Wait(*Locks::thread_suspend_count_lock_); + Thread::resume_cond_->Wait(self, *Locks::thread_suspend_count_lock_); if (self->suspend_count_ != 0) { // The condition was signaled but we're still suspended. This // can happen if the debugger lets go while a SIGQUIT thread @@ -340,8 +340,8 @@ void ThreadList::UndoDebuggerSuspensions() { VLOG(threads) << *self << " UndoDebuggerSuspensions starting"; { - MutexLock mu(*Locks::thread_list_lock_); - MutexLock mu2(*Locks::thread_suspend_count_lock_); + MutexLock mu(self, *Locks::thread_list_lock_); + MutexLock mu2(self, *Locks::thread_suspend_count_lock_); // Update global suspend all state for attaching threads. suspend_all_count_ -= debug_suspend_all_count_; debug_suspend_all_count_ = 0; @@ -356,7 +356,7 @@ void ThreadList::UndoDebuggerSuspensions() { } { - MutexLock mu(*Locks::thread_suspend_count_lock_); + MutexLock mu(self, *Locks::thread_suspend_count_lock_); Thread::resume_cond_->Broadcast(); } @@ -364,8 +364,9 @@ void ThreadList::UndoDebuggerSuspensions() { } void ThreadList::WaitForOtherNonDaemonThreadsToExit() { - Locks::mutator_lock_->AssertNotHeld(); - MutexLock mu(*Locks::thread_list_lock_); + Thread* self = Thread::Current(); + Locks::mutator_lock_->AssertNotHeld(self); + MutexLock mu(self, *Locks::thread_list_lock_); bool all_threads_are_daemons; do { all_threads_are_daemons = true; @@ -373,28 +374,29 @@ void ThreadList::WaitForOtherNonDaemonThreadsToExit() { // TODO: there's a race here with thread exit that's being worked around by checking if the // thread has a peer. Thread* thread = *it; - if (thread != Thread::Current() && thread->HasPeer() && !thread->IsDaemon()) { + if (thread != self && thread->HasPeer() && !thread->IsDaemon()) { all_threads_are_daemons = false; break; } } if (!all_threads_are_daemons) { // Wait for another thread to exit before re-checking. - thread_exit_cond_.Wait(*Locks::thread_list_lock_); + thread_exit_cond_.Wait(self, *Locks::thread_list_lock_); } } while(!all_threads_are_daemons); } void ThreadList::SuspendAllDaemonThreads() { - MutexLock mu(*Locks::thread_list_lock_); + Thread* self = Thread::Current(); + MutexLock mu(self, *Locks::thread_list_lock_); { // Tell all the daemons it's time to suspend. - MutexLock mu2(*Locks::thread_suspend_count_lock_); + MutexLock mu2(self, *Locks::thread_suspend_count_lock_); for (It it = list_.begin(), end = list_.end(); it != end; ++it) { Thread* thread = *it; // This is only run after all non-daemon threads have exited, so the remainder should all be // daemons. CHECK(thread->IsDaemon()); - if (thread != Thread::Current()) { + if (thread != self) { thread->ModifySuspendCount(+1, false); } } @@ -406,8 +408,8 @@ void ThreadList::SuspendAllDaemonThreads() { bool all_suspended = true; for (It it = list_.begin(), end = list_.end(); it != end; ++it) { Thread* thread = *it; - MutexLock mu2(*Locks::thread_suspend_count_lock_); - if (thread != Thread::Current() && thread->GetState() == kRunnable) { + MutexLock mu2(self, *Locks::thread_suspend_count_lock_); + if (thread != self && thread->GetState() == kRunnable) { if (!have_complained) { LOG(WARNING) << "daemon thread not yet suspended: " << *thread; have_complained = true; @@ -432,8 +434,8 @@ void ThreadList::Register(Thread* self) { // Atomically add self to the thread list and make its thread_suspend_count_ reflect ongoing // SuspendAll requests. - MutexLock mu(*Locks::thread_list_lock_); - MutexLock mu2(*Locks::thread_suspend_count_lock_); + MutexLock mu(self, *Locks::thread_list_lock_); + MutexLock mu2(self, *Locks::thread_suspend_count_lock_); self->suspend_count_ = suspend_all_count_; self->debug_suspend_count_ = debug_suspend_all_count_; CHECK(!Contains(self)); @@ -451,7 +453,7 @@ void ThreadList::Unregister(Thread* self) { { // Remove this thread from the list. - MutexLock mu(*Locks::thread_list_lock_); + MutexLock mu(self, *Locks::thread_list_lock_); CHECK(Contains(self)); list_.remove(self); } @@ -466,7 +468,7 @@ void ThreadList::Unregister(Thread* self) { CHECK_PTHREAD_CALL(pthread_setspecific, (Thread::pthread_key_self_, NULL), "detach self"); // Signal that a thread just detached. - MutexLock mu(*Locks::thread_list_lock_); + MutexLock mu(NULL, *Locks::thread_list_lock_); thread_exit_cond_.Signal(); } @@ -477,14 +479,14 @@ void ThreadList::ForEach(void (*callback)(Thread*, void*), void* context) { } void ThreadList::VisitRoots(Heap::RootVisitor* visitor, void* arg) const { - MutexLock mu(*Locks::thread_list_lock_); + MutexLock mu(Thread::Current(), *Locks::thread_list_lock_); for (It it = list_.begin(), end = list_.end(); it != end; ++it) { (*it)->VisitRoots(visitor, arg); } } uint32_t ThreadList::AllocThreadId() { - MutexLock mu(allocated_ids_lock_); + MutexLock mu(Thread::Current(), allocated_ids_lock_); for (size_t i = 0; i < allocated_ids_.size(); ++i) { if (!allocated_ids_[i]) { allocated_ids_.set(i); @@ -496,7 +498,7 @@ uint32_t ThreadList::AllocThreadId() { } void ThreadList::ReleaseThreadId(uint32_t id) { - MutexLock mu(allocated_ids_lock_); + MutexLock mu(Thread::Current(), allocated_ids_lock_); --id; // Zero is reserved to mean "invalid". DCHECK(allocated_ids_[id]) << id; allocated_ids_.reset(id); diff --git a/src/trace.cc b/src/trace.cc index d1f3f50e95..d0132e1f4e 100644 --- a/src/trace.cc +++ b/src/trace.cc @@ -489,8 +489,9 @@ static void DumpThread(Thread* t, void* arg) { } void Trace::DumpThreadList(std::ostream& os) { - Locks::thread_list_lock_->AssertNotHeld(); - MutexLock mu(*Locks::thread_list_lock_); + Thread* self = Thread::Current(); + Locks::thread_list_lock_->AssertNotHeld(self); + MutexLock mu(self, *Locks::thread_list_lock_); Runtime::Current()->GetThreadList()->ForEach(DumpThread, &os); } @@ -499,9 +500,10 @@ void Trace::InstallStubs() { } void Trace::UninstallStubs() { - Locks::thread_list_lock_->AssertNotHeld(); + Thread* self = Thread::Current(); + Locks::thread_list_lock_->AssertNotHeld(self); Runtime::Current()->GetClassLinker()->VisitClasses(UninstallStubsClassVisitor, NULL); - MutexLock mu(*Locks::thread_list_lock_); + MutexLock mu(self, *Locks::thread_list_lock_); Runtime::Current()->GetThreadList()->ForEach(TraceRestoreStack, NULL); } |