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));
+  }
+}