Clean up Mutex a little and add the missing pieces for Mac OS.
This -- as you'd expect, given that we're fine on bionic and glibc --
didn't find any bugs. But it's another step towards completeness and
lets me rule out Mac pthread_mutex_t weirdness as a potential cause of
our Mac dex2oat crashes.
Change-Id: If3f4aacf8dbc7c7b9fd6b8932bc01616ccf86b47
diff --git a/src/mutex.cc b/src/mutex.cc
index 5a9df17..71b0ba8 100644
--- a/src/mutex.cc
+++ b/src/mutex.cc
@@ -27,6 +27,22 @@
namespace art {
+// This works on Mac OS 10.7, but hasn't been tested on older releases.
+struct __attribute__((__may_alias__)) darwin_pthread_mutex_t {
+ uint32_t padding0[2];
+ uint32_t value;
+ uint32_t padding1[5];
+ uint64_t owner_tid;
+ // ...other stuff we don't care about.
+};
+
+struct __attribute__((__may_alias__)) glibc_pthread_mutex_t {
+ int lock;
+ unsigned int count;
+ int owner;
+ // ...other stuff we don't care about.
+};
+
static inline void CheckSafeToLockOrUnlock(MutexRank rank, bool is_locking) {
if (!kIsDebugBuild) {
return;
@@ -95,28 +111,30 @@
CHECK_MUTEX_CALL(pthread_mutex_unlock, (&mutex_));
}
-pid_t Mutex::GetOwner() {
+#if !defined(NDEBUG)
+void Mutex::AssertHeld() {
+ DCHECK_EQ(GetOwner(), static_cast<uint64_t>(GetTid()));
+}
+
+void Mutex::AssertNotHeld() {
+ DCHECK_NE(GetOwner(), static_cast<uint64_t>(GetTid()));
+}
+#endif
+
+uint64_t Mutex::GetOwner() {
#if defined(__BIONIC__)
- return static_cast<pid_t>((mutex_.value >> 16) & 0xffff);
+ return static_cast<uint64_t>((mutex_.value >> 16) & 0xffff);
#elif defined(__GLIBC__)
- struct __attribute__((__may_alias__)) glibc_pthread_t {
- int lock;
- unsigned int count;
- int owner;
- // ...other stuff we don't care about.
- };
- return reinterpret_cast<glibc_pthread_t*>(&mutex_)->owner;
+ return reinterpret_cast<glibc_pthread_mutex_t*>(&mutex_)->owner;
#elif defined(__APPLE__)
- // We don't know a way to implement this for Mac OS.
- return 0;
+ return reinterpret_cast<darwin_pthread_mutex_t*>(&mutex_)->owner_tid;
#else
- UNIMPLEMENTED(FATAL);
- return 0;
+#error unsupported C library
#endif
}
uint32_t Mutex::GetDepth() {
- bool held = (GetOwner() == GetTid());
+ bool held = (GetOwner() == static_cast<uint64_t>(GetTid()));
if (!held) {
return 0;
}
@@ -124,28 +142,17 @@
#if defined(__BIONIC__)
depth = static_cast<uint32_t>((mutex_.value >> 2) & 0x7ff) + 1;
#elif defined(__GLIBC__)
- struct __attribute__((__may_alias__)) glibc_pthread_t {
- int lock;
- unsigned int count;
- int owner;
- // ...other stuff we don't care about.
- };
- depth = reinterpret_cast<glibc_pthread_t*>(&mutex_)->count;
+ depth = reinterpret_cast<glibc_pthread_mutex_t*>(&mutex_)->count;
#elif defined(__APPLE__)
- // We don't know a way to implement this for Mac OS.
- return 0;
+ darwin_pthread_mutex_t* darwin_mutex = reinterpret_cast<darwin_pthread_mutex_t*>(&mutex_);
+ depth = ((darwin_mutex->value >> 16) & 0xffff);
#else
- UNIMPLEMENTED(FATAL);
- return 0;
+#error unsupported C library
#endif
- CHECK_NE(0U, depth) << "owner=" << GetOwner() << " tid=" << GetTid();
+ CHECK_NE(depth, 0U) << "owner=" << GetOwner() << " tid=" << GetTid();
return depth;
}
-pid_t Mutex::GetTid() {
- return ::art::GetTid();
-}
-
ConditionVariable::ConditionVariable(const std::string& name) : name_(name) {
CHECK_MUTEX_CALL(pthread_cond_init, (&cond_, NULL));
}
@@ -163,8 +170,8 @@
}
void ConditionVariable::Wait(Mutex& mutex) {
- CheckSafeToWait(mutex.GetRank());
- CHECK_MUTEX_CALL(pthread_cond_wait, (&cond_, mutex.GetImpl()));
+ CheckSafeToWait(mutex.rank_);
+ CHECK_MUTEX_CALL(pthread_cond_wait, (&cond_, &mutex.mutex_));
}
void ConditionVariable::TimedWait(Mutex& mutex, const timespec& ts) {
@@ -173,8 +180,8 @@
#else
#define TIMEDWAIT pthread_cond_timedwait
#endif
- CheckSafeToWait(mutex.GetRank());
- int rc = TIMEDWAIT(&cond_, mutex.GetImpl(), &ts);
+ CheckSafeToWait(mutex.rank_);
+ int rc = TIMEDWAIT(&cond_, &mutex.mutex_, &ts);
if (rc != 0 && rc != ETIMEDOUT) {
errno = rc;
PLOG(FATAL) << "TimedWait failed for " << name_;
diff --git a/src/mutex.h b/src/mutex.h
index 02d127f..b019f68 100644
--- a/src/mutex.h
+++ b/src/mutex.h
@@ -49,41 +49,24 @@
void Unlock();
- const char* GetName() {
- return name_.c_str();
- }
-
- pthread_mutex_t* GetImpl() {
- return &mutex_;
- }
-
- MutexRank GetRank() const {
- return rank_;
- }
-
- void AssertHeld() {
-#if !defined(__APPLE__)
- DCHECK_EQ(GetOwner(), GetTid());
+#if !defined(NDEBUG)
+ void AssertHeld();
+ void AssertNotHeld();
+#else
+ void AssertHeld() {}
+ void AssertNotHeld() {}
#endif
- }
- void AssertNotHeld() {
-#if !defined(__APPLE__)
- DCHECK_NE(GetOwner(), GetTid());
-#endif
- }
-
- pid_t GetOwner();
+ uint64_t GetOwner();
private:
- static pid_t GetTid();
-
uint32_t GetDepth();
pthread_mutex_t mutex_;
std::string name_;
MutexRank rank_;
+ friend class ConditionVariable;
friend class MutexTester;
DISALLOW_COPY_AND_ASSIGN(Mutex);
};
diff --git a/src/mutex_test.cc b/src/mutex_test.cc
index b59b305..7c7189b 100644
--- a/src/mutex_test.cc
+++ b/src/mutex_test.cc
@@ -20,10 +20,16 @@
namespace art {
-#if !defined(__APPLE__)
struct MutexTester {
static void AssertDepth(Mutex& mu, uint32_t expected_depth) {
ASSERT_EQ(expected_depth, mu.GetDepth());
+
+ // This test is single-threaded, so we also know _who_ should hold the lock.
+ if (expected_depth == 0) {
+ mu.AssertNotHeld();
+ } else {
+ mu.AssertHeld();
+ }
}
};
@@ -70,6 +76,5 @@
mu.Unlock();
MutexTester::AssertDepth(mu, 0U);
}
-#endif
} // namespace art
diff --git a/src/utils.cc b/src/utils.cc
index 190cf4a..01fdac2 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -40,6 +40,7 @@
#if defined(__APPLE__)
#include "AvailabilityMacros.h"
+#include <sys/syscall.h>
#endif
#if defined(__linux__)
@@ -50,8 +51,9 @@
pid_t GetTid() {
#if defined(__APPLE__)
- // Mac OS doesn't have gettid(2).
- return getpid();
+ // gettid(2) returns -1 on Darwin, but thread_selfid(2) works, and seems to be what their
+ // pthreads implementation uses.
+ return syscall(SYS_thread_selfid);
#else
// Neither bionic nor glibc exposes gettid(2).
return syscall(__NR_gettid);