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