xref: /llvm-project/libc/test/integration/src/pthread/pthread_mutex_test.cpp (revision 0a537a1299b03126b23938c36edbfd2edbea44c1)
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 
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 
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 
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 
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 
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 
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 
188 int main() {
189   relay_counter();
190   wait_and_step();
191   multiple_waiters();
192   return 0;
193 }
194