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