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

source code of libc/test/integration/src/pthread/pthread_mutex_test.cpp