Implement pthread barrier.
Bug: 24341262
Change-Id: I5472549e5d7545c1c3f0bef78235f545557b9630
diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp
index 9f887e3..88b8a5c 100644
--- a/tests/pthread_test.cpp
+++ b/tests/pthread_test.cpp
@@ -721,11 +721,11 @@
ASSERT_EQ(0, pthread_rwlock_destroy(&l));
}
-static void WaitUntilThreadSleep(std::atomic<pid_t>& pid) {
- while (pid == 0) {
+static void WaitUntilThreadSleep(std::atomic<pid_t>& tid) {
+ while (tid == 0) {
usleep(1000);
}
- std::string filename = android::base::StringPrintf("/proc/%d/stat", pid.load());
+ std::string filename = android::base::StringPrintf("/proc/%d/stat", tid.load());
std::regex regex {R"(\s+S\s+)"};
while (true) {
@@ -1653,3 +1653,117 @@
kill(getpid(), SIGUSR1);
ASSERT_TRUE(signal_handler_on_altstack_done);
}
+
+TEST(pthread, pthread_barrierattr_smoke) {
+ pthread_barrierattr_t attr;
+ ASSERT_EQ(0, pthread_barrierattr_init(&attr));
+ int pshared;
+ ASSERT_EQ(0, pthread_barrierattr_getpshared(&attr, &pshared));
+ ASSERT_EQ(PTHREAD_PROCESS_PRIVATE, pshared);
+ ASSERT_EQ(0, pthread_barrierattr_setpshared(&attr, PTHREAD_PROCESS_SHARED));
+ ASSERT_EQ(0, pthread_barrierattr_getpshared(&attr, &pshared));
+ ASSERT_EQ(PTHREAD_PROCESS_SHARED, pshared);
+ ASSERT_EQ(0, pthread_barrierattr_destroy(&attr));
+}
+
+struct BarrierTestHelperArg {
+ std::atomic<pid_t> tid;
+ pthread_barrier_t* barrier;
+ size_t iteration_count;
+};
+
+static void BarrierTestHelper(BarrierTestHelperArg* arg) {
+ arg->tid = gettid();
+ for (size_t i = 0; i < arg->iteration_count; ++i) {
+ ASSERT_EQ(0, pthread_barrier_wait(arg->barrier));
+ }
+}
+
+TEST(pthread, pthread_barrier_smoke) {
+ const size_t BARRIER_ITERATION_COUNT = 10;
+ const size_t BARRIER_THREAD_COUNT = 10;
+ pthread_barrier_t barrier;
+ ASSERT_EQ(0, pthread_barrier_init(&barrier, nullptr, BARRIER_THREAD_COUNT + 1));
+ std::vector<pthread_t> threads(BARRIER_THREAD_COUNT);
+ std::vector<BarrierTestHelperArg> args(threads.size());
+ for (size_t i = 0; i < threads.size(); ++i) {
+ args[i].tid = 0;
+ args[i].barrier = &barrier;
+ args[i].iteration_count = BARRIER_ITERATION_COUNT;
+ ASSERT_EQ(0, pthread_create(&threads[i], nullptr,
+ reinterpret_cast<void* (*)(void*)>(BarrierTestHelper), &args[i]));
+ }
+ for (size_t iteration = 0; iteration < BARRIER_ITERATION_COUNT; ++iteration) {
+ for (size_t i = 0; i < threads.size(); ++i) {
+ WaitUntilThreadSleep(args[i].tid);
+ }
+ ASSERT_EQ(PTHREAD_BARRIER_SERIAL_THREAD, pthread_barrier_wait(&barrier));
+ }
+ for (size_t i = 0; i < threads.size(); ++i) {
+ ASSERT_EQ(0, pthread_join(threads[i], nullptr));
+ }
+ ASSERT_EQ(0, pthread_barrier_destroy(&barrier));
+}
+
+TEST(pthread, pthread_barrier_destroy) {
+ pthread_barrier_t barrier;
+ ASSERT_EQ(0, pthread_barrier_init(&barrier, nullptr, 2));
+ pthread_t thread;
+ BarrierTestHelperArg arg;
+ arg.tid = 0;
+ arg.barrier = &barrier;
+ arg.iteration_count = 1;
+ ASSERT_EQ(0, pthread_create(&thread, nullptr,
+ reinterpret_cast<void* (*)(void*)>(BarrierTestHelper), &arg));
+ WaitUntilThreadSleep(arg.tid);
+ ASSERT_EQ(EBUSY, pthread_barrier_destroy(&barrier));
+ ASSERT_EQ(PTHREAD_BARRIER_SERIAL_THREAD, pthread_barrier_wait(&barrier));
+ // Verify if the barrier can be destroyed directly after pthread_barrier_wait().
+ ASSERT_EQ(0, pthread_barrier_destroy(&barrier));
+ ASSERT_EQ(0, pthread_join(thread, nullptr));
+#if defined(__BIONIC__)
+ ASSERT_EQ(EINVAL, pthread_barrier_destroy(&barrier));
+#endif
+}
+
+struct BarrierOrderingTestHelperArg {
+ pthread_barrier_t* barrier;
+ size_t* array;
+ size_t array_length;
+ size_t id;
+};
+
+void BarrierOrderingTestHelper(BarrierOrderingTestHelperArg* arg) {
+ const size_t ITERATION_COUNT = 10000;
+ for (size_t i = 1; i <= ITERATION_COUNT; ++i) {
+ arg->array[arg->id] = i;
+ int ret = pthread_barrier_wait(arg->barrier);
+ ASSERT_TRUE(ret == 0 || ret == PTHREAD_BARRIER_SERIAL_THREAD);
+ for (size_t j = 0; j < arg->array_length; ++j) {
+ ASSERT_EQ(i, arg->array[j]);
+ }
+ ret = pthread_barrier_wait(arg->barrier);
+ ASSERT_TRUE(ret == 0 || ret == PTHREAD_BARRIER_SERIAL_THREAD);
+ }
+}
+
+TEST(pthread, pthread_barrier_check_ordering) {
+ const size_t THREAD_COUNT = 4;
+ pthread_barrier_t barrier;
+ ASSERT_EQ(0, pthread_barrier_init(&barrier, nullptr, THREAD_COUNT));
+ size_t array[THREAD_COUNT];
+ std::vector<pthread_t> threads(THREAD_COUNT);
+ std::vector<BarrierOrderingTestHelperArg> args(THREAD_COUNT);
+ for (size_t i = 0; i < THREAD_COUNT; ++i) {
+ args[i].barrier = &barrier;
+ args[i].array = array;
+ args[i].array_length = THREAD_COUNT;
+ args[i].id = i;
+ ASSERT_EQ(0, pthread_create(&threads[i], nullptr,
+ reinterpret_cast<void* (*)(void*)>(BarrierOrderingTestHelper),
+ &args[i]));
+ }
+ for (size_t i = 0; i < THREAD_COUNT; ++i) {
+ ASSERT_EQ(0, pthread_join(threads[i], nullptr));
+ }
+}