xref: /aosp_15_r20/external/llvm-libc/test/integration/src/threads/mtx_test.cpp (revision 71db0c75aadcf003ffe3238005f61d7618a3fead)
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 "test/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     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 
relay_counter()41 void 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 update
46   // 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 
74 mtx_t start_lock, step_lock;
75 bool started, step;
76 
stepper(void * arg)77 int stepper(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 
wait_and_step()88 void 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 a
95   // 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 
141 static constexpr int THREAD_COUNT = 10;
142 static mtx_t multiple_waiter_lock;
143 static mtx_t counter_lock;
144 static int wait_count = 0;
145 
waiter_func(void *)146 int 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 main
152   // 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 
multiple_waiters()163 void 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 desired
174   // 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 
TEST_MAIN()197 TEST_MAIN() {
198   relay_counter();
199   wait_and_step();
200   multiple_waiters();
201   return 0;
202 }
203