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