xref: /aosp_15_r20/external/llvm-libc/test/integration/src/pthread/pthread_mutex_test.cpp (revision 71db0c75aadcf003ffe3238005f61d7618a3fead)
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 
22 constexpr int START = 0;
23 constexpr int MAX = 10000;
24 
25 pthread_mutex_t mutex;
26 static int shared_int = START;
27 
counter(void * arg)28 void *counter(void *arg) {
29   int last_count = START;
30   while (true) {
31     LIBC_NAMESPACE::pthread_mutex_lock(&mutex);
32     if (shared_int == last_count + 1) {
33       shared_int++;
34       last_count = shared_int;
35     }
36     LIBC_NAMESPACE::pthread_mutex_unlock(&mutex);
37     if (last_count >= MAX)
38       break;
39   }
40   return nullptr;
41 }
42 
relay_counter()43 void 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);
69   ASSERT_EQ(uintptr_t(retval), uintptr_t(nullptr));
70 
71   LIBC_NAMESPACE::pthread_mutex_destroy(&mutex);
72 }
73 
74 pthread_mutex_t start_lock, step_lock;
75 bool started, step;
76 
stepper(void * arg)77 void *stepper(void *arg) {
78   LIBC_NAMESPACE::pthread_mutex_lock(&start_lock);
79   started = true;
80   LIBC_NAMESPACE::pthread_mutex_unlock(&start_lock);
81 
82   LIBC_NAMESPACE::pthread_mutex_lock(&step_lock);
83   step = true;
84   LIBC_NAMESPACE::pthread_mutex_unlock(&step_lock);
85   return nullptr;
86 }
87 
wait_and_step()88 void 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);
127   ASSERT_EQ(uintptr_t(retval), uintptr_t(nullptr));
128 
129   LIBC_NAMESPACE::pthread_mutex_destroy(&start_lock);
130   LIBC_NAMESPACE::pthread_mutex_destroy(&step_lock);
131 }
132 
133 static constexpr int THREAD_COUNT = 10;
134 static pthread_mutex_t multiple_waiter_lock;
135 static pthread_mutex_t counter_lock;
136 static int wait_count = 0;
137 
waiter_func(void *)138 void *waiter_func(void *) {
139   LIBC_NAMESPACE::pthread_mutex_lock(&counter_lock);
140   ++wait_count;
141   LIBC_NAMESPACE::pthread_mutex_unlock(&counter_lock);
142 
143   // Block on the waiter lock until the main
144   // thread unblocks.
145   LIBC_NAMESPACE::pthread_mutex_lock(&multiple_waiter_lock);
146   LIBC_NAMESPACE::pthread_mutex_unlock(&multiple_waiter_lock);
147 
148   LIBC_NAMESPACE::pthread_mutex_lock(&counter_lock);
149   --wait_count;
150   LIBC_NAMESPACE::pthread_mutex_unlock(&counter_lock);
151 
152   return nullptr;
153 }
154 
multiple_waiters()155 void multiple_waiters() {
156   LIBC_NAMESPACE::pthread_mutex_init(&multiple_waiter_lock, nullptr);
157   LIBC_NAMESPACE::pthread_mutex_init(&counter_lock, nullptr);
158 
159   LIBC_NAMESPACE::pthread_mutex_lock(&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(&counter_lock);
169     if (wait_count == THREAD_COUNT) {
170       LIBC_NAMESPACE::pthread_mutex_unlock(&counter_lock);
171       break;
172     }
173     LIBC_NAMESPACE::pthread_mutex_unlock(&counter_lock);
174   }
175 
176   LIBC_NAMESPACE::pthread_mutex_unlock(&multiple_waiter_lock);
177 
178   void *retval;
179   for (int i = 0; i < THREAD_COUNT; ++i) {
180     LIBC_NAMESPACE::pthread_join(waiters[i], &retval);
181   }
182 
183   ASSERT_EQ(wait_count, 0);
184 
185   LIBC_NAMESPACE::pthread_mutex_destroy(&multiple_waiter_lock);
186   LIBC_NAMESPACE::pthread_mutex_destroy(&counter_lock);
187 }
188 
TEST_MAIN()189 TEST_MAIN() {
190   relay_counter();
191   wait_and_step();
192   multiple_waiters();
193   return 0;
194 }
195