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