| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <errno.h> |
| #include <pthread.h> |
| |
| #include <cstdlib> |
| |
| #include <zxtest/zxtest.h> |
| |
| namespace { |
| |
| struct ThreadArgs { |
| pthread_barrier_t* barrier; |
| std::atomic<int> result; |
| }; |
| |
| void* BarrierWait(void* arg) { |
| auto args = reinterpret_cast<ThreadArgs*>(arg); |
| args->result = pthread_barrier_wait(args->barrier); |
| |
| return nullptr; |
| } |
| |
| constexpr int kNumThreads = 16; |
| constexpr int kNumIterations = 128; |
| constexpr pthread_barrierattr_t* kDefaultBarrierAttrs = nullptr; |
| constexpr pthread_attr_t* kDefaultPthreadAttrs = nullptr; |
| constexpr void** kNoRetValue = nullptr; |
| |
| TEST(PThreadBarrierTest, SingleThreadWinsBarrierObject) { |
| pthread_barrier_t barrier; |
| ASSERT_EQ(pthread_barrier_init(&barrier, kDefaultBarrierAttrs, kNumThreads), 0); |
| |
| pthread_t threads[kNumThreads]; |
| ThreadArgs args[kNumThreads]; |
| |
| for (int idx = 0; idx < kNumThreads; ++idx) { |
| args[idx].barrier = &barrier; |
| ASSERT_EQ(pthread_create(&threads[idx], kDefaultPthreadAttrs, &BarrierWait, |
| static_cast<void*>(&args[idx])), |
| 0); |
| } |
| |
| for (int idx = 0; idx < kNumThreads; ++idx) { |
| ASSERT_EQ(pthread_join(threads[idx], kNoRetValue), 0); |
| } |
| |
| int num_wins = 0; |
| int num_zeros = 0; |
| for (int idx = 0; idx < kNumThreads; ++idx) { |
| int result = args[idx].result; |
| if (result == PTHREAD_BARRIER_SERIAL_THREAD) { |
| num_wins++; |
| } else if (result == 0) { |
| num_zeros++; |
| } else { |
| ASSERT_EQ(result, 0, "bad result for thread: %d result: %d\n", idx, result); |
| } |
| } |
| ASSERT_EQ(num_wins, 1); |
| ASSERT_EQ(num_zeros, kNumThreads - 1); |
| ASSERT_EQ(pthread_barrier_destroy(&barrier), 0); |
| } |
| |
| TEST(PThreadBarrierTest, SingleThreadWinsBarrierObjectResetsBetweenIterations) { |
| pthread_barrier_t barrier; |
| ASSERT_EQ(pthread_barrier_init(&barrier, kDefaultBarrierAttrs, kNumThreads), 0); |
| |
| for (int count = 0; count < kNumIterations; ++count) { |
| pthread_t threads[kNumThreads]; |
| ThreadArgs args[kNumThreads]; |
| |
| for (int idx = 0; idx < kNumThreads; ++idx) { |
| args[idx].barrier = &barrier; |
| ASSERT_EQ(pthread_create(&threads[idx], kDefaultPthreadAttrs, &BarrierWait, |
| static_cast<void*>(&args[idx])), |
| 0); |
| } |
| |
| for (int idx = 0; idx < kNumThreads; ++idx) { |
| ASSERT_EQ(pthread_join(threads[idx], kNoRetValue), 0); |
| } |
| |
| int num_wins = 0; |
| int num_zeros = 0; |
| for (int idx = 0; idx < kNumThreads; ++idx) { |
| int result = args[idx].result; |
| if (result == PTHREAD_BARRIER_SERIAL_THREAD) { |
| num_wins++; |
| } else if (result == 0) { |
| num_zeros++; |
| } else { |
| ASSERT_EQ(result, 0, "bad result for thread: %d result: %d\n", idx, result); |
| } |
| } |
| ASSERT_EQ(num_wins, 1); |
| ASSERT_EQ(num_zeros, kNumThreads - 1); |
| } |
| ASSERT_EQ(pthread_barrier_destroy(&barrier), 0); |
| } |
| |
| TEST(PThreadBarrierTest, InitWithNoThreadsReturnsInval) { |
| pthread_barrier_t barrier; |
| constexpr int kThreadCount = 0; |
| ASSERT_EQ(pthread_barrier_init(&barrier, kDefaultBarrierAttrs, kThreadCount), EINVAL, |
| "zero thread count should fail"); |
| } |
| |
| } // namespace |