Support priority inheritance mutex in 64bit programs.
Bug: http://b/29177606
Test: run bionic-unit-tests on walleye.
Test: run bionic-unit-tests-glibc on host.
Change-Id: Iac349284aa73515f384e7509445f87434757f59e
diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp
index 183312d..9ecb10c 100644
--- a/tests/pthread_test.cpp
+++ b/tests/pthread_test.cpp
@@ -33,7 +33,9 @@
#include <atomic>
#include <vector>
+#include <android-base/parseint.h>
#include <android-base/scopeguard.h>
+#include <android-base/strings.h>
#include "private/bionic_constants.h"
#include "private/bionic_macros.h"
@@ -1631,11 +1633,27 @@
ASSERT_EQ(0, pthread_mutexattr_destroy(&attr));
}
+TEST(pthread, pthread_mutexattr_protocol) {
+ pthread_mutexattr_t attr;
+ ASSERT_EQ(0, pthread_mutexattr_init(&attr));
+
+ int protocol;
+ ASSERT_EQ(0, pthread_mutexattr_getprotocol(&attr, &protocol));
+ ASSERT_EQ(PTHREAD_PRIO_NONE, protocol);
+ for (size_t repeat = 0; repeat < 2; ++repeat) {
+ for (int set_protocol : {PTHREAD_PRIO_NONE, PTHREAD_PRIO_INHERIT}) {
+ ASSERT_EQ(0, pthread_mutexattr_setprotocol(&attr, set_protocol));
+ ASSERT_EQ(0, pthread_mutexattr_getprotocol(&attr, &protocol));
+ ASSERT_EQ(protocol, set_protocol);
+ }
+ }
+}
+
struct PthreadMutex {
pthread_mutex_t lock;
- explicit PthreadMutex(int mutex_type) {
- init(mutex_type);
+ explicit PthreadMutex(int mutex_type, int protocol = PTHREAD_PRIO_NONE) {
+ init(mutex_type, protocol);
}
~PthreadMutex() {
@@ -1643,10 +1661,11 @@
}
private:
- void init(int mutex_type) {
+ void init(int mutex_type, int protocol) {
pthread_mutexattr_t attr;
ASSERT_EQ(0, pthread_mutexattr_init(&attr));
ASSERT_EQ(0, pthread_mutexattr_settype(&attr, mutex_type));
+ ASSERT_EQ(0, pthread_mutexattr_setprotocol(&attr, protocol));
ASSERT_EQ(0, pthread_mutex_init(&lock, &attr));
ASSERT_EQ(0, pthread_mutexattr_destroy(&attr));
}
@@ -1658,8 +1677,8 @@
DISALLOW_COPY_AND_ASSIGN(PthreadMutex);
};
-TEST(pthread, pthread_mutex_lock_NORMAL) {
- PthreadMutex m(PTHREAD_MUTEX_NORMAL);
+static void TestPthreadMutexLockNormal(int protocol) {
+ PthreadMutex m(PTHREAD_MUTEX_NORMAL, protocol);
ASSERT_EQ(0, pthread_mutex_lock(&m.lock));
ASSERT_EQ(0, pthread_mutex_unlock(&m.lock));
@@ -1668,20 +1687,24 @@
ASSERT_EQ(0, pthread_mutex_unlock(&m.lock));
}
-TEST(pthread, pthread_mutex_lock_ERRORCHECK) {
- PthreadMutex m(PTHREAD_MUTEX_ERRORCHECK);
+static void TestPthreadMutexLockErrorCheck(int protocol) {
+ PthreadMutex m(PTHREAD_MUTEX_ERRORCHECK, protocol);
ASSERT_EQ(0, pthread_mutex_lock(&m.lock));
ASSERT_EQ(EDEADLK, pthread_mutex_lock(&m.lock));
ASSERT_EQ(0, pthread_mutex_unlock(&m.lock));
ASSERT_EQ(0, pthread_mutex_trylock(&m.lock));
- ASSERT_EQ(EBUSY, pthread_mutex_trylock(&m.lock));
+ if (protocol == PTHREAD_PRIO_NONE) {
+ ASSERT_EQ(EBUSY, pthread_mutex_trylock(&m.lock));
+ } else {
+ ASSERT_EQ(EDEADLK, pthread_mutex_trylock(&m.lock));
+ }
ASSERT_EQ(0, pthread_mutex_unlock(&m.lock));
ASSERT_EQ(EPERM, pthread_mutex_unlock(&m.lock));
}
-TEST(pthread, pthread_mutex_lock_RECURSIVE) {
- PthreadMutex m(PTHREAD_MUTEX_RECURSIVE);
+static void TestPthreadMutexLockRecursive(int protocol) {
+ PthreadMutex m(PTHREAD_MUTEX_RECURSIVE, protocol);
ASSERT_EQ(0, pthread_mutex_lock(&m.lock));
ASSERT_EQ(0, pthread_mutex_lock(&m.lock));
@@ -1694,6 +1717,28 @@
ASSERT_EQ(EPERM, pthread_mutex_unlock(&m.lock));
}
+TEST(pthread, pthread_mutex_lock_NORMAL) {
+ TestPthreadMutexLockNormal(PTHREAD_PRIO_NONE);
+}
+
+TEST(pthread, pthread_mutex_lock_ERRORCHECK) {
+ TestPthreadMutexLockErrorCheck(PTHREAD_PRIO_NONE);
+}
+
+TEST(pthread, pthread_mutex_lock_RECURSIVE) {
+ TestPthreadMutexLockRecursive(PTHREAD_PRIO_NONE);
+}
+
+TEST(pthread, pthread_mutex_lock_pi) {
+#if defined(__BIONIC__) && !defined(__LP64__)
+ GTEST_LOG_(INFO) << "PTHREAD_PRIO_INHERIT isn't supported in 32bit programs, skipping test";
+ return;
+#endif
+ TestPthreadMutexLockNormal(PTHREAD_PRIO_INHERIT);
+ TestPthreadMutexLockErrorCheck(PTHREAD_PRIO_INHERIT);
+ TestPthreadMutexLockRecursive(PTHREAD_PRIO_INHERIT);
+}
+
TEST(pthread, pthread_mutex_init_same_as_static_initializers) {
pthread_mutex_t lock_normal = PTHREAD_MUTEX_INITIALIZER;
PthreadMutex m1(PTHREAD_MUTEX_NORMAL);
@@ -1773,6 +1818,103 @@
helper.test();
}
+static int GetThreadPriority(pid_t tid) {
+ // sched_getparam() returns the static priority of a thread, which can't reflect a thread's
+ // priority after priority inheritance. So read /proc/<pid>/stat to get the dynamic priority.
+ std::string filename = android::base::StringPrintf("/proc/%d/stat", tid);
+ std::string content;
+ int result = INT_MAX;
+ if (!android::base::ReadFileToString(filename, &content)) {
+ return result;
+ }
+ std::vector<std::string> strs = android::base::Split(content, " ");
+ if (strs.size() < 18) {
+ return result;
+ }
+ if (!android::base::ParseInt(strs[17], &result)) {
+ return INT_MAX;
+ }
+ return result;
+}
+
+class PIMutexWakeupHelper {
+private:
+ PthreadMutex m;
+ int protocol;
+ enum Progress {
+ LOCK_INITIALIZED,
+ LOCK_CHILD_READY,
+ LOCK_WAITING,
+ LOCK_RELEASED,
+ };
+ std::atomic<Progress> progress;
+ std::atomic<pid_t> main_tid;
+ std::atomic<pid_t> child_tid;
+ PthreadMutex start_thread_m;
+
+ static void thread_fn(PIMutexWakeupHelper* helper) {
+ helper->child_tid = gettid();
+ ASSERT_EQ(LOCK_INITIALIZED, helper->progress);
+ ASSERT_EQ(0, setpriority(PRIO_PROCESS, gettid(), 1));
+ ASSERT_EQ(21, GetThreadPriority(gettid()));
+ ASSERT_EQ(0, pthread_mutex_lock(&helper->m.lock));
+ helper->progress = LOCK_CHILD_READY;
+ ASSERT_EQ(0, pthread_mutex_lock(&helper->start_thread_m.lock));
+
+ ASSERT_EQ(0, pthread_mutex_unlock(&helper->start_thread_m.lock));
+ WaitUntilThreadSleep(helper->main_tid);
+ ASSERT_EQ(LOCK_WAITING, helper->progress);
+
+ if (helper->protocol == PTHREAD_PRIO_INHERIT) {
+ ASSERT_EQ(20, GetThreadPriority(gettid()));
+ } else {
+ ASSERT_EQ(21, GetThreadPriority(gettid()));
+ }
+ helper->progress = LOCK_RELEASED;
+ ASSERT_EQ(0, pthread_mutex_unlock(&helper->m.lock));
+ }
+
+public:
+ explicit PIMutexWakeupHelper(int mutex_type, int protocol)
+ : m(mutex_type, protocol), protocol(protocol), start_thread_m(PTHREAD_MUTEX_NORMAL) {
+ }
+
+ void test() {
+ ASSERT_EQ(0, pthread_mutex_lock(&start_thread_m.lock));
+ main_tid = gettid();
+ ASSERT_EQ(20, GetThreadPriority(main_tid));
+ progress = LOCK_INITIALIZED;
+ child_tid = 0;
+
+ pthread_t thread;
+ ASSERT_EQ(0, pthread_create(&thread, NULL,
+ reinterpret_cast<void* (*)(void*)>(PIMutexWakeupHelper::thread_fn), this));
+
+ WaitUntilThreadSleep(child_tid);
+ ASSERT_EQ(LOCK_CHILD_READY, progress);
+ ASSERT_EQ(0, pthread_mutex_unlock(&start_thread_m.lock));
+ progress = LOCK_WAITING;
+ ASSERT_EQ(0, pthread_mutex_lock(&m.lock));
+
+ ASSERT_EQ(LOCK_RELEASED, progress);
+ ASSERT_EQ(0, pthread_mutex_unlock(&m.lock));
+ ASSERT_EQ(0, pthread_join(thread, nullptr));
+ }
+};
+
+TEST(pthread, pthread_mutex_pi_wakeup) {
+#if defined(__BIONIC__) && !defined(__LP64__)
+ GTEST_LOG_(INFO) << "PTHREAD_PRIO_INHERIT isn't supported in 32bit programs, skipping test";
+ return;
+#endif
+ for (int type : {PTHREAD_MUTEX_NORMAL, PTHREAD_MUTEX_RECURSIVE, PTHREAD_MUTEX_ERRORCHECK}) {
+ for (int protocol : {PTHREAD_PRIO_INHERIT}) {
+ PIMutexWakeupHelper helper(type, protocol);
+ helper.test();
+ }
+ }
+}
+
TEST(pthread, pthread_mutex_owner_tid_limit) {
#if defined(__BIONIC__) && !defined(__LP64__)
FILE* fp = fopen("/proc/sys/kernel/pid_max", "r");
@@ -1816,6 +1958,34 @@
ASSERT_EQ(0, pthread_mutex_destroy(&m));
}
+TEST(pthread, pthread_mutex_timedlock_pi) {
+#if defined(__BIONIC__) && !defined(__LP64__)
+ GTEST_LOG_(INFO) << "PTHREAD_PRIO_INHERIT isn't supported in 32bit programs, skipping test";
+ return;
+#endif
+ PthreadMutex m(PTHREAD_MUTEX_NORMAL, PTHREAD_PRIO_INHERIT);
+ timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_sec += 1;
+ ASSERT_EQ(0, pthread_mutex_timedlock(&m.lock, &ts));
+
+ auto ThreadFn = [](void* arg) -> void* {
+ PthreadMutex& m = *static_cast<PthreadMutex*>(arg);
+ timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_sec += 1;
+ intptr_t result = pthread_mutex_timedlock(&m.lock, &ts);
+ return reinterpret_cast<void*>(result);
+ };
+
+ pthread_t thread;
+ ASSERT_EQ(0, pthread_create(&thread, NULL, ThreadFn, &m));
+ void* result;
+ ASSERT_EQ(0, pthread_join(thread, &result));
+ ASSERT_EQ(ETIMEDOUT, reinterpret_cast<intptr_t>(result));
+ ASSERT_EQ(0, pthread_mutex_unlock(&m.lock));
+}
+
class StrictAlignmentAllocator {
public:
void* allocate(size_t size, size_t alignment) {