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