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