| /* |
| * Copyright (C) 2011 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 "mutex.h" |
| |
| #include <errno.h> |
| |
| #include "logging.h" |
| #include "runtime.h" |
| #include "thread.h" |
| #include "utils.h" |
| |
| #if defined(__APPLE__) |
| #include "AvailabilityMacros.h" // For MAC_OS_X_VERSION_MAX_ALLOWED |
| #endif |
| |
| #define CHECK_MUTEX_CALL(call, args) CHECK_PTHREAD_CALL(call, args, name_) |
| |
| extern int pthread_mutex_lock(pthread_mutex_t* mutex) EXCLUSIVE_LOCK_FUNCTION(mutex); |
| 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); |
| |
| namespace art { |
| |
| // This works on Mac OS 10.6 but hasn't been tested on older releases. |
| struct __attribute__((__may_alias__)) darwin_pthread_mutex_t { |
| long padding0; |
| int padding1; |
| uint32_t padding2; |
| int16_t padding3; |
| int16_t padding4; |
| uint32_t padding5; |
| pthread_t darwin_pthread_mutex_owner; |
| // ...other stuff we don't care about. |
| }; |
| |
| struct __attribute__((__may_alias__)) darwin_pthread_rwlock_t { |
| long padding0; |
| pthread_mutex_t padding1; |
| int padding2; |
| pthread_cond_t padding3; |
| pthread_cond_t padding4; |
| int padding5; |
| int padding6; |
| pthread_t darwin_pthread_rwlock_owner; |
| // ...other stuff we don't care about. |
| }; |
| |
| struct __attribute__((__may_alias__)) glibc_pthread_mutex_t { |
| int32_t padding0[2]; |
| int owner; |
| // ...other stuff we don't care about. |
| }; |
| |
| struct __attribute__((__may_alias__)) glibc_pthread_rwlock_t { |
| #ifdef __LP64__ |
| int32_t padding0[6]; |
| #else |
| int32_t padding0[7]; |
| #endif |
| int writer; |
| // ...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, MutexLevel level) : level_(level), name_(name) {} |
| |
| static void CheckUnattachedThread(MutexLevel 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) { |
| Runtime* runtime = Runtime::Current(); |
| CHECK(runtime == NULL || !runtime->IsStarted() || runtime->IsShuttingDown() || |
| level == kDefaultMutexLevel || level == kThreadListLock || |
| level == kLoggingLock || level == kAbortLock); |
| } |
| } |
| |
| void BaseMutex::RegisterAsLockedWithCurrentThread() { |
| Thread* self = Thread::Current(); |
| if (self == NULL) { |
| CheckUnattachedThread(level_); |
| return; |
| } |
| if (kDebugLocking) { |
| // 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)); |
| if (UNLIKELY(held_mutex != NULL)) { |
| LOG(ERROR) << "Lock level violation: holding \"" << held_mutex->name_ << "\" (level " << i |
| << ") while locking \"" << name_ << "\" (level " << static_cast<int>(level_) << ")"; |
| if (i > kAbortLock) { |
| // Only abort in the check below if this is more than abort level lock. |
| bad_mutexes_held = true; |
| } |
| } |
| } |
| CHECK(!bad_mutexes_held); |
| } |
| // Don't record monitors as they are outside the scope of analysis. They may be inspected off of |
| // the monitor list. |
| if (level_ != kMonitorLock) { |
| self->SetHeldMutex(level_, this); |
| } |
| } |
| |
| void BaseMutex::RegisterAsUnlockedWithCurrentThread() { |
| Thread* self = Thread::Current(); |
| if (self == NULL) { |
| CheckUnattachedThread(level_); |
| return; |
| } |
| if (level_ != kMonitorLock) { |
| if (kDebugLocking) { |
| CHECK(self->GetHeldMutex(level_) == this) << "Unlocking on unacquired mutex: " << name_; |
| } |
| self->SetHeldMutex(level_, NULL); |
| } |
| } |
| |
| void BaseMutex::CheckSafeToWait() { |
| Thread* self = Thread::Current(); |
| if (self == NULL) { |
| CheckUnattachedThread(level_); |
| return; |
| } |
| if (kDebugLocking) { |
| CHECK(self->GetHeldMutex(level_) == this) << "Waiting on unacquired mutex: " << name_; |
| bool bad_mutexes_held = false; |
| for (int i = kMaxMutexLevel; i >= 0; --i) { |
| if (i != level_) { |
| BaseMutex* held_mutex = self->GetHeldMutex(static_cast<MutexLevel>(i)); |
| if (held_mutex != NULL) { |
| LOG(ERROR) << "Holding " << held_mutex->name_ << " (level " << i |
| << ") while performing wait on: " |
| << name_ << " (level " << static_cast<int>(level_) << ")"; |
| bad_mutexes_held = true; |
| } |
| } |
| } |
| CHECK(!bad_mutexes_held); |
| } |
| } |
| |
| Mutex::Mutex(const char* name, MutexLevel 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 |
| // non-recursive mutexes don't have TIDs to check lock ownership of. |
| pthread_mutexattr_t attributes; |
| CHECK_MUTEX_CALL(pthread_mutexattr_init, (&attributes)); |
| CHECK_MUTEX_CALL(pthread_mutexattr_settype, (&attributes, PTHREAD_MUTEX_RECURSIVE)); |
| CHECK_MUTEX_CALL(pthread_mutex_init, (&mutex_, &attributes)); |
| CHECK_MUTEX_CALL(pthread_mutexattr_destroy, (&attributes)); |
| #else |
| CHECK_MUTEX_CALL(pthread_mutex_init, (&mutex_, NULL)); |
| #endif |
| } |
| |
| Mutex::~Mutex() { |
| // We can't use CHECK_MUTEX_CALL here because on shutdown a suspended daemon thread |
| // may still be using locks. |
| int rc = pthread_mutex_destroy(&mutex_); |
| if (rc != 0) { |
| errno = rc; |
| // TODO: should we just not log at all if shutting down? this could be the logging mutex! |
| bool shutting_down = Runtime::Current()->IsShuttingDown(); |
| PLOG(shutting_down ? WARNING : FATAL) << "pthread_mutex_destroy failed for " << name_; |
| } |
| } |
| |
| void Mutex::ExclusiveLock() { |
| if (kDebugLocking && !recursive_) { |
| AssertNotHeld(); |
| } |
| if (!recursive_ || !IsExclusiveHeld()) { |
| CHECK_MUTEX_CALL(pthread_mutex_lock, (&mutex_)); |
| RegisterAsLockedWithCurrentThread(); |
| } |
| recursion_count_++; |
| if (kDebugLocking) { |
| CHECK(recursion_count_ == 1 || recursive_) << "Unexpected recursion count on mutex: " |
| << name_ << " " << recursion_count_; |
| AssertHeld(); |
| } |
| } |
| |
| bool Mutex::ExclusiveTryLock() { |
| if (kDebugLocking && !recursive_) { |
| AssertNotHeld(); |
| } |
| if (!recursive_ || !IsExclusiveHeld()) { |
| int result = pthread_mutex_trylock(&mutex_); |
| if (result == EBUSY) { |
| return false; |
| } |
| if (result != 0) { |
| errno = result; |
| PLOG(FATAL) << "pthread_mutex_trylock failed for " << name_; |
| } |
| RegisterAsLockedWithCurrentThread(); |
| } |
| recursion_count_++; |
| if (kDebugLocking) { |
| CHECK(recursion_count_ == 1 || recursive_) << "Unexpected recursion count on mutex: " |
| << name_ << " " << recursion_count_; |
| AssertHeld(); |
| } |
| return true; |
| } |
| |
| void Mutex::ExclusiveUnlock() { |
| AssertHeld(); |
| recursion_count_--; |
| if (!recursive_ || recursion_count_ == 0) { |
| if (kDebugLocking) { |
| CHECK(recursion_count_ == 0 || recursive_) << "Unexpected recursion count on mutex: " |
| << name_ << " " << recursion_count_; |
| } |
| RegisterAsUnlockedWithCurrentThread(); |
| CHECK_MUTEX_CALL(pthread_mutex_unlock, (&mutex_)); |
| } |
| } |
| |
| bool Mutex::IsExclusiveHeld() const { |
| Thread* self = Thread::Current(); |
| bool result; |
| if (self == NULL || level_ == kMonitorLock) { // Handle unattached threads and monitors. |
| result = (GetExclusiveOwnerTid() == static_cast<uint64_t>(GetTid())); |
| } else { |
| result = (self->GetHeldMutex(level_) == this); |
| // Sanity debug check that if we think it is locked, so does the pthread. |
| if (kDebugLocking) { |
| CHECK(result == (GetExclusiveOwnerTid() == static_cast<uint64_t>(GetTid()))); |
| } |
| } |
| return result; |
| } |
| |
| uint64_t Mutex::GetExclusiveOwnerTid() const { |
| #if defined(__BIONIC__) |
| return static_cast<uint64_t>((mutex_.value >> 16) & 0xffff); |
| #elif defined(__GLIBC__) |
| return reinterpret_cast<const glibc_pthread_mutex_t*>(&mutex_)->owner; |
| #elif defined(__APPLE__) |
| const darwin_pthread_mutex_t* dpmutex = reinterpret_cast<const darwin_pthread_mutex_t*>(&mutex_); |
| pthread_t owner = dpmutex->darwin_pthread_mutex_owner; |
| // 0 for unowned, -1 for PTHREAD_MTX_TID_SWITCHING |
| // TODO: should we make darwin_pthread_mutex_owner volatile and recheck until not -1? |
| if ((owner == (pthread_t)0) || (owner == (pthread_t)-1)) { |
| return 0; |
| } |
| uint64_t tid; |
| CHECK_PTHREAD_CALL(pthread_threadid_np, (owner, &tid), __FUNCTION__); // Requires Mac OS 10.6 |
| return tid; |
| #else |
| #error unsupported C library |
| #endif |
| } |
| |
| ReaderWriterMutex::ReaderWriterMutex(const char* name, MutexLevel level) : BaseMutex(name, level) { |
| CHECK_MUTEX_CALL(pthread_rwlock_init, (&rwlock_, NULL)); |
| } |
| |
| ReaderWriterMutex::~ReaderWriterMutex() { |
| // 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_); |
| if (rc != 0) { |
| errno = rc; |
| // TODO: should we just not log at all if shutting down? this could be the logging mutex! |
| bool shutting_down = Runtime::Current()->IsShuttingDown(); |
| PLOG(shutting_down ? WARNING : FATAL) << "pthread_mutex_destroy failed for " << name_; |
| } |
| } |
| |
| void ReaderWriterMutex::ExclusiveLock() { |
| AssertNotExclusiveHeld(); |
| CHECK_MUTEX_CALL(pthread_rwlock_wrlock, (&rwlock_)); |
| RegisterAsLockedWithCurrentThread(); |
| AssertExclusiveHeld(); |
| } |
| |
| void ReaderWriterMutex::ExclusiveUnlock() { |
| AssertExclusiveHeld(); |
| RegisterAsUnlockedWithCurrentThread(); |
| CHECK_MUTEX_CALL(pthread_rwlock_unlock, (&rwlock_)); |
| } |
| |
| #if HAVE_TIMED_RWLOCK |
| bool ReaderWriterMutex::ExclusiveLockWithTimeout(const timespec& abs_timeout) { |
| int result = pthread_rwlock_timedwrlock(&rwlock_, &abs_timeout); |
| if (result == ETIMEDOUT) { |
| return false; |
| } |
| if (result != 0) { |
| errno = result; |
| PLOG(FATAL) << "pthread_rwlock_timedwrlock failed for " << name_; |
| } |
| RegisterAsLockedWithCurrentThread(); |
| AssertSharedHeld(); |
| return true; |
| } |
| #endif |
| |
| void ReaderWriterMutex::SharedLock() { |
| CHECK_MUTEX_CALL(pthread_rwlock_rdlock, (&rwlock_)); |
| RegisterAsLockedWithCurrentThread(); |
| AssertSharedHeld(); |
| } |
| |
| bool ReaderWriterMutex::SharedTryLock() { |
| int result = pthread_rwlock_tryrdlock(&rwlock_); |
| if (result == EBUSY) { |
| return false; |
| } |
| if (result != 0) { |
| errno = result; |
| PLOG(FATAL) << "pthread_mutex_trylock failed for " << name_; |
| } |
| RegisterAsLockedWithCurrentThread(); |
| AssertSharedHeld(); |
| return true; |
| } |
| |
| void ReaderWriterMutex::SharedUnlock() { |
| AssertSharedHeld(); |
| RegisterAsUnlockedWithCurrentThread(); |
| CHECK_MUTEX_CALL(pthread_rwlock_unlock, (&rwlock_)); |
| } |
| |
| bool ReaderWriterMutex::IsExclusiveHeld() 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 result; |
| if (UNLIKELY(self == NULL)) { // Handle unattached threads. |
| result = IsExclusiveHeld(); // TODO: a better best effort here. |
| } else { |
| result = (self->GetHeldMutex(level_) == this); |
| } |
| return result; |
| } |
| |
| uint64_t ReaderWriterMutex::GetExclusiveOwnerTid() const { |
| #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_); |
| pthread_t owner = dprwlock->darwin_pthread_rwlock_owner; |
| if (owner == (pthread_t)0) { |
| return 0; |
| } |
| uint64_t tid; |
| CHECK_PTHREAD_CALL(pthread_threadid_np, (owner, &tid), __FUNCTION__); // Requires Mac OS 10.6 |
| return tid; |
| #else |
| #error unsupported C library |
| #endif |
| } |
| |
| ConditionVariable::ConditionVariable(const std::string& name) : name_(name) { |
| CHECK_MUTEX_CALL(pthread_cond_init, (&cond_, NULL)); |
| } |
| |
| ConditionVariable::~ConditionVariable() { |
| // We can't use CHECK_MUTEX_CALL here because on shutdown a suspended daemon thread |
| // may still be using condition variables. |
| int rc = pthread_cond_destroy(&cond_); |
| if (rc != 0) { |
| errno = rc; |
| bool shutting_down = Runtime::Current()->IsShuttingDown(); |
| PLOG(shutting_down ? WARNING : FATAL) << "pthread_cond_destroy failed for " << name_; |
| } |
| } |
| |
| void ConditionVariable::Broadcast() { |
| CHECK_MUTEX_CALL(pthread_cond_broadcast, (&cond_)); |
| } |
| |
| void ConditionVariable::Signal() { |
| CHECK_MUTEX_CALL(pthread_cond_signal, (&cond_)); |
| } |
| |
| void ConditionVariable::Wait(Mutex& mutex) { |
| mutex.CheckSafeToWait(); |
| 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) { |
| #ifdef HAVE_TIMEDWAIT_MONOTONIC |
| #define TIMEDWAIT pthread_cond_timedwait_monotonic |
| #else |
| #define TIMEDWAIT pthread_cond_timedwait |
| #endif |
| mutex.CheckSafeToWait(); |
| unsigned int old_recursion_count = mutex.recursion_count_; |
| mutex.recursion_count_ = 0; |
| int rc = TIMEDWAIT(&cond_, &mutex.mutex_, &ts); |
| mutex.recursion_count_ = old_recursion_count; |
| if (rc != 0 && rc != ETIMEDOUT) { |
| errno = rc; |
| PLOG(FATAL) << "TimedWait failed for " << name_; |
| } |
| } |
| |
| } // namespace art |