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
counter(void * arg)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
relay_counter()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
stepper(void * arg)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
wait_and_step()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
waiter_func(void *)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
multiple_waiters()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
main()195 int main() {
196 relay_counter();
197 wait_and_step();
198 multiple_waiters();
199 return 0;
200 }
201