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