197 lines · cpp
1//===-- Tests for pthread_mutex_t -----------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "hdr/stdint_proxy.h" // uintptr_t10#include "src/pthread/pthread_create.h"11#include "src/pthread/pthread_join.h"12#include "src/pthread/pthread_mutex_destroy.h"13#include "src/pthread/pthread_mutex_init.h"14#include "src/pthread/pthread_mutex_lock.h"15#include "src/pthread/pthread_mutex_unlock.h"16#include "test/IntegrationTest/test.h"17 18#include <pthread.h>19 20constexpr int START = 0;21constexpr int MAX = 10000;22 23pthread_mutex_t mutex;24static int shared_int = START;25 26void *counter([[maybe_unused]] void *arg) {27 int last_count = START;28 while (true) {29 LIBC_NAMESPACE::pthread_mutex_lock(&mutex);30 if (shared_int == last_count + 1) {31 shared_int++;32 last_count = shared_int;33 }34 LIBC_NAMESPACE::pthread_mutex_unlock(&mutex);35 if (last_count >= MAX)36 break;37 }38 return nullptr;39}40 41void relay_counter() {42 ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_init(&mutex, nullptr), 0);43 44 // The idea of this test is that two competing threads will update45 // a counter only if the other thread has updated it.46 pthread_t thread;47 LIBC_NAMESPACE::pthread_create(&thread, nullptr, counter, nullptr);48 49 int last_count = START;50 while (true) {51 ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_lock(&mutex), 0);52 if (shared_int == START) {53 ++shared_int;54 last_count = shared_int;55 } else if (shared_int != last_count) {56 ASSERT_EQ(shared_int, last_count + 1);57 ++shared_int;58 last_count = shared_int;59 }60 ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_unlock(&mutex), 0);61 if (last_count > MAX)62 break;63 }64 65 void *retval = reinterpret_cast<void *>(123);66 LIBC_NAMESPACE::pthread_join(thread, &retval);67 ASSERT_EQ(uintptr_t(retval), uintptr_t(nullptr));68 69 LIBC_NAMESPACE::pthread_mutex_destroy(&mutex);70}71 72pthread_mutex_t start_lock, step_lock;73bool started, step;74 75void *stepper([[maybe_unused]] void *arg) {76 LIBC_NAMESPACE::pthread_mutex_lock(&start_lock);77 started = true;78 LIBC_NAMESPACE::pthread_mutex_unlock(&start_lock);79 80 LIBC_NAMESPACE::pthread_mutex_lock(&step_lock);81 step = true;82 LIBC_NAMESPACE::pthread_mutex_unlock(&step_lock);83 return nullptr;84}85 86void wait_and_step() {87 ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_init(&start_lock, nullptr), 0);88 ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_init(&step_lock, nullptr), 0);89 90 // In this test, we start a new thread but block it before it can make a91 // step. Once we ensure that the thread is blocked, we unblock it.92 // After unblocking, we then verify that the thread was indeed unblocked.93 step = false;94 started = false;95 ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_lock(&step_lock), 0);96 97 pthread_t thread;98 LIBC_NAMESPACE::pthread_create(&thread, nullptr, stepper, nullptr);99 100 while (true) {101 // Make sure the thread actually started.102 ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_lock(&start_lock), 0);103 bool s = started;104 ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_unlock(&start_lock), 0);105 if (s)106 break;107 }108 109 // Since |step_lock| is still locked, |step| should be false.110 ASSERT_FALSE(step);111 112 // Unlock the step lock and wait until the step is made.113 ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_unlock(&step_lock), 0);114 115 while (true) {116 ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_lock(&step_lock), 0);117 bool current_step_value = step;118 ASSERT_EQ(LIBC_NAMESPACE::pthread_mutex_unlock(&step_lock), 0);119 if (current_step_value)120 break;121 }122 123 void *retval = reinterpret_cast<void *>(123);124 LIBC_NAMESPACE::pthread_join(thread, &retval);125 ASSERT_EQ(uintptr_t(retval), uintptr_t(nullptr));126 127 LIBC_NAMESPACE::pthread_mutex_destroy(&start_lock);128 LIBC_NAMESPACE::pthread_mutex_destroy(&step_lock);129}130 131static constexpr int THREAD_COUNT = 10;132static pthread_mutex_t multiple_waiter_lock;133static pthread_mutex_t counter_lock;134static int wait_count = 0;135 136void *waiter_func(void *) {137 LIBC_NAMESPACE::pthread_mutex_lock(&counter_lock);138 ++wait_count;139 LIBC_NAMESPACE::pthread_mutex_unlock(&counter_lock);140 141 // Block on the waiter lock until the main142 // thread unblocks.143 LIBC_NAMESPACE::pthread_mutex_lock(&multiple_waiter_lock);144 LIBC_NAMESPACE::pthread_mutex_unlock(&multiple_waiter_lock);145 146 LIBC_NAMESPACE::pthread_mutex_lock(&counter_lock);147 --wait_count;148 LIBC_NAMESPACE::pthread_mutex_unlock(&counter_lock);149 150 return nullptr;151}152 153void multiple_waiters() {154 LIBC_NAMESPACE::pthread_mutex_init(&multiple_waiter_lock, nullptr);155 LIBC_NAMESPACE::pthread_mutex_init(&counter_lock, nullptr);156 157 LIBC_NAMESPACE::pthread_mutex_lock(&multiple_waiter_lock);158 pthread_t waiters[THREAD_COUNT];159 for (int i = 0; i < THREAD_COUNT; ++i) {160 LIBC_NAMESPACE::pthread_create(waiters + i, nullptr, waiter_func, nullptr);161 }162 163 // Spin until the counter is incremented to the desired164 // value.165 while (true) {166 LIBC_NAMESPACE::pthread_mutex_lock(&counter_lock);167 if (wait_count == THREAD_COUNT) {168 LIBC_NAMESPACE::pthread_mutex_unlock(&counter_lock);169 break;170 }171 LIBC_NAMESPACE::pthread_mutex_unlock(&counter_lock);172 }173 174 LIBC_NAMESPACE::pthread_mutex_unlock(&multiple_waiter_lock);175 176 void *retval;177 for (int i = 0; i < THREAD_COUNT; ++i) {178 LIBC_NAMESPACE::pthread_join(waiters[i], &retval);179 }180 181 ASSERT_EQ(wait_count, 0);182 183 LIBC_NAMESPACE::pthread_mutex_destroy(&multiple_waiter_lock);184 LIBC_NAMESPACE::pthread_mutex_destroy(&counter_lock);185}186 187// Test the initializer188[[maybe_unused]]189static pthread_mutex_t test_initializer = PTHREAD_MUTEX_INITIALIZER;190 191TEST_MAIN() {192 relay_counter();193 wait_and_step();194 multiple_waiters();195 return 0;196}197