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 | |
28 | void *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 | |
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: &retval); |
69 | ASSERT_EQ(uintptr_t(retval), uintptr_t(nullptr)); |
70 | |
71 | LIBC_NAMESPACE::pthread_mutex_destroy(mutex: &mutex); |
72 | } |
73 | |
74 | pthread_mutex_t start_lock, step_lock; |
75 | bool started, step; |
76 | |
77 | void *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 | |
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: &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 | |
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 | |
138 | void *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 | |
155 | void 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 | |
189 | TEST_MAIN() { |
190 | relay_counter(); |
191 | wait_and_step(); |
192 | multiple_waiters(); |
193 | return 0; |
194 | } |
195 | |