1d5475af2SSiva Chandra Reddy //===-- Tests for mtx_t operations ----------------------------------------===//
2d5475af2SSiva Chandra Reddy //
3d5475af2SSiva Chandra Reddy // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4d5475af2SSiva Chandra Reddy // See https://llvm.org/LICENSE.txt for license information.
5d5475af2SSiva Chandra Reddy // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6d5475af2SSiva Chandra Reddy //
7d5475af2SSiva Chandra Reddy //===----------------------------------------------------------------------===//
8d5475af2SSiva Chandra Reddy
9d5475af2SSiva Chandra Reddy #include "src/threads/mtx_destroy.h"
10d5475af2SSiva Chandra Reddy #include "src/threads/mtx_init.h"
11d5475af2SSiva Chandra Reddy #include "src/threads/mtx_lock.h"
12d5475af2SSiva Chandra Reddy #include "src/threads/mtx_unlock.h"
13d5475af2SSiva Chandra Reddy #include "src/threads/thrd_create.h"
14d5475af2SSiva Chandra Reddy #include "src/threads/thrd_join.h"
15d5475af2SSiva Chandra Reddy
16af1315c2SSiva Chandra Reddy #include "test/IntegrationTest/test.h"
17d5475af2SSiva Chandra Reddy
18d5475af2SSiva Chandra Reddy #include <threads.h>
19d5475af2SSiva Chandra Reddy
20d5475af2SSiva Chandra Reddy constexpr int START = 0;
21d5475af2SSiva Chandra Reddy constexpr int MAX = 10000;
22d5475af2SSiva Chandra Reddy
23d5475af2SSiva Chandra Reddy mtx_t mutex;
24d5475af2SSiva Chandra Reddy static int shared_int = START;
25d5475af2SSiva Chandra Reddy
counter(void * arg)26d5475af2SSiva Chandra Reddy int counter(void *arg) {
27d5475af2SSiva Chandra Reddy int last_count = START;
28d5475af2SSiva Chandra Reddy while (true) {
29*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::mtx_lock(&mutex);
30d5475af2SSiva Chandra Reddy if (shared_int == last_count + 1) {
31d5475af2SSiva Chandra Reddy shared_int++;
32d5475af2SSiva Chandra Reddy last_count = shared_int;
33d5475af2SSiva Chandra Reddy }
34*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::mtx_unlock(&mutex);
35d5475af2SSiva Chandra Reddy if (last_count >= MAX)
36d5475af2SSiva Chandra Reddy break;
37d5475af2SSiva Chandra Reddy }
38d5475af2SSiva Chandra Reddy return 0;
39d5475af2SSiva Chandra Reddy }
40d5475af2SSiva Chandra Reddy
relay_counter()41d5475af2SSiva Chandra Reddy void relay_counter() {
42*b6bc9d72SGuillaume Chatelet ASSERT_EQ(LIBC_NAMESPACE::mtx_init(&mutex, mtx_plain),
43d5475af2SSiva Chandra Reddy static_cast<int>(thrd_success));
44d5475af2SSiva Chandra Reddy
45d5475af2SSiva Chandra Reddy // The idea of this test is that two competing threads will update
46d5475af2SSiva Chandra Reddy // a counter only if the other thread has updated it.
47d5475af2SSiva Chandra Reddy thrd_t thread;
48*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::thrd_create(&thread, counter, nullptr);
49d5475af2SSiva Chandra Reddy
50d5475af2SSiva Chandra Reddy int last_count = START;
51d5475af2SSiva Chandra Reddy while (true) {
52*b6bc9d72SGuillaume Chatelet ASSERT_EQ(LIBC_NAMESPACE::mtx_lock(&mutex), static_cast<int>(thrd_success));
53d5475af2SSiva Chandra Reddy if (shared_int == START) {
54d5475af2SSiva Chandra Reddy ++shared_int;
55d5475af2SSiva Chandra Reddy last_count = shared_int;
56d5475af2SSiva Chandra Reddy } else if (shared_int != last_count) {
57d5475af2SSiva Chandra Reddy ASSERT_EQ(shared_int, last_count + 1);
58d5475af2SSiva Chandra Reddy ++shared_int;
59d5475af2SSiva Chandra Reddy last_count = shared_int;
60d5475af2SSiva Chandra Reddy }
61*b6bc9d72SGuillaume Chatelet ASSERT_EQ(LIBC_NAMESPACE::mtx_unlock(&mutex),
62*b6bc9d72SGuillaume Chatelet static_cast<int>(thrd_success));
63d5475af2SSiva Chandra Reddy if (last_count > MAX)
64d5475af2SSiva Chandra Reddy break;
65d5475af2SSiva Chandra Reddy }
66d5475af2SSiva Chandra Reddy
67d5475af2SSiva Chandra Reddy int retval = 123;
68*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::thrd_join(thread, &retval);
69d5475af2SSiva Chandra Reddy ASSERT_EQ(retval, 0);
70d5475af2SSiva Chandra Reddy
71*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::mtx_destroy(&mutex);
72d5475af2SSiva Chandra Reddy }
73d5475af2SSiva Chandra Reddy
74d5475af2SSiva Chandra Reddy mtx_t start_lock, step_lock;
75d5475af2SSiva Chandra Reddy bool started, step;
76d5475af2SSiva Chandra Reddy
stepper(void * arg)77d5475af2SSiva Chandra Reddy int stepper(void *arg) {
78*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::mtx_lock(&start_lock);
79d5475af2SSiva Chandra Reddy started = true;
80*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::mtx_unlock(&start_lock);
81d5475af2SSiva Chandra Reddy
82*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::mtx_lock(&step_lock);
83d5475af2SSiva Chandra Reddy step = true;
84*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::mtx_unlock(&step_lock);
85d5475af2SSiva Chandra Reddy return 0;
86d5475af2SSiva Chandra Reddy }
87d5475af2SSiva Chandra Reddy
wait_and_step()88d5475af2SSiva Chandra Reddy void wait_and_step() {
89*b6bc9d72SGuillaume Chatelet ASSERT_EQ(LIBC_NAMESPACE::mtx_init(&start_lock, mtx_plain),
90d5475af2SSiva Chandra Reddy static_cast<int>(thrd_success));
91*b6bc9d72SGuillaume Chatelet ASSERT_EQ(LIBC_NAMESPACE::mtx_init(&step_lock, mtx_plain),
92d5475af2SSiva Chandra Reddy static_cast<int>(thrd_success));
93d5475af2SSiva Chandra Reddy
94d5475af2SSiva Chandra Reddy // In this test, we start a new thread but block it before it can make a
95d5475af2SSiva Chandra Reddy // step. Once we ensure that the thread is blocked, we unblock it.
96d5475af2SSiva Chandra Reddy // After unblocking, we then verify that the thread was indeed unblocked.
97d5475af2SSiva Chandra Reddy step = false;
98d5475af2SSiva Chandra Reddy started = false;
99*b6bc9d72SGuillaume Chatelet ASSERT_EQ(LIBC_NAMESPACE::mtx_lock(&step_lock),
100*b6bc9d72SGuillaume Chatelet static_cast<int>(thrd_success));
101d5475af2SSiva Chandra Reddy
102d5475af2SSiva Chandra Reddy thrd_t thread;
103*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::thrd_create(&thread, stepper, nullptr);
104d5475af2SSiva Chandra Reddy
105d5475af2SSiva Chandra Reddy while (true) {
106d5475af2SSiva Chandra Reddy // Make sure the thread actually started.
107*b6bc9d72SGuillaume Chatelet ASSERT_EQ(LIBC_NAMESPACE::mtx_lock(&start_lock),
108d5475af2SSiva Chandra Reddy static_cast<int>(thrd_success));
109d5475af2SSiva Chandra Reddy bool s = started;
110*b6bc9d72SGuillaume Chatelet ASSERT_EQ(LIBC_NAMESPACE::mtx_unlock(&start_lock),
111d5475af2SSiva Chandra Reddy static_cast<int>(thrd_success));
112d5475af2SSiva Chandra Reddy if (s)
113d5475af2SSiva Chandra Reddy break;
114d5475af2SSiva Chandra Reddy }
115d5475af2SSiva Chandra Reddy
116d5475af2SSiva Chandra Reddy // Since |step_lock| is still locked, |step| should be false.
117d5475af2SSiva Chandra Reddy ASSERT_FALSE(step);
118d5475af2SSiva Chandra Reddy
119d5475af2SSiva Chandra Reddy // Unlock the step lock and wait until the step is made.
120*b6bc9d72SGuillaume Chatelet ASSERT_EQ(LIBC_NAMESPACE::mtx_unlock(&step_lock),
121d5475af2SSiva Chandra Reddy static_cast<int>(thrd_success));
122d5475af2SSiva Chandra Reddy
123d5475af2SSiva Chandra Reddy while (true) {
124*b6bc9d72SGuillaume Chatelet ASSERT_EQ(LIBC_NAMESPACE::mtx_lock(&step_lock),
125d5475af2SSiva Chandra Reddy static_cast<int>(thrd_success));
126d5475af2SSiva Chandra Reddy bool current_step_value = step;
127*b6bc9d72SGuillaume Chatelet ASSERT_EQ(LIBC_NAMESPACE::mtx_unlock(&step_lock),
128d5475af2SSiva Chandra Reddy static_cast<int>(thrd_success));
129d5475af2SSiva Chandra Reddy if (current_step_value)
130d5475af2SSiva Chandra Reddy break;
131d5475af2SSiva Chandra Reddy }
132d5475af2SSiva Chandra Reddy
133d5475af2SSiva Chandra Reddy int retval = 123;
134*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::thrd_join(thread, &retval);
135d5475af2SSiva Chandra Reddy ASSERT_EQ(retval, 0);
136d5475af2SSiva Chandra Reddy
137*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::mtx_destroy(&start_lock);
138*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::mtx_destroy(&step_lock);
139d5475af2SSiva Chandra Reddy }
140d5475af2SSiva Chandra Reddy
141d5475af2SSiva Chandra Reddy static constexpr int THREAD_COUNT = 10;
142d5475af2SSiva Chandra Reddy static mtx_t multiple_waiter_lock;
143d5475af2SSiva Chandra Reddy static mtx_t counter_lock;
144d5475af2SSiva Chandra Reddy static int wait_count = 0;
145d5475af2SSiva Chandra Reddy
waiter_func(void *)146d5475af2SSiva Chandra Reddy int waiter_func(void *) {
147*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::mtx_lock(&counter_lock);
148d5475af2SSiva Chandra Reddy ++wait_count;
149*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::mtx_unlock(&counter_lock);
150d5475af2SSiva Chandra Reddy
151d5475af2SSiva Chandra Reddy // Block on the waiter lock until the main
152d5475af2SSiva Chandra Reddy // thread unblocks.
153*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::mtx_lock(&multiple_waiter_lock);
154*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::mtx_unlock(&multiple_waiter_lock);
155d5475af2SSiva Chandra Reddy
156*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::mtx_lock(&counter_lock);
157d5475af2SSiva Chandra Reddy --wait_count;
158*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::mtx_unlock(&counter_lock);
159d5475af2SSiva Chandra Reddy
160d5475af2SSiva Chandra Reddy return 0;
161d5475af2SSiva Chandra Reddy }
162d5475af2SSiva Chandra Reddy
multiple_waiters()163d5475af2SSiva Chandra Reddy void multiple_waiters() {
164*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::mtx_init(&multiple_waiter_lock, mtx_plain);
165*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::mtx_init(&counter_lock, mtx_plain);
166d5475af2SSiva Chandra Reddy
167*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::mtx_lock(&multiple_waiter_lock);
168d5475af2SSiva Chandra Reddy thrd_t waiters[THREAD_COUNT];
169d5475af2SSiva Chandra Reddy for (int i = 0; i < THREAD_COUNT; ++i) {
170*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::thrd_create(waiters + i, waiter_func, nullptr);
171d5475af2SSiva Chandra Reddy }
172d5475af2SSiva Chandra Reddy
173d5475af2SSiva Chandra Reddy // Spin until the counter is incremented to the desired
174d5475af2SSiva Chandra Reddy // value.
175d5475af2SSiva Chandra Reddy while (true) {
176*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::mtx_lock(&counter_lock);
177d5475af2SSiva Chandra Reddy if (wait_count == THREAD_COUNT) {
178*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::mtx_unlock(&counter_lock);
179d5475af2SSiva Chandra Reddy break;
180d5475af2SSiva Chandra Reddy }
181*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::mtx_unlock(&counter_lock);
182d5475af2SSiva Chandra Reddy }
183d5475af2SSiva Chandra Reddy
184*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::mtx_unlock(&multiple_waiter_lock);
185d5475af2SSiva Chandra Reddy
186d5475af2SSiva Chandra Reddy int retval;
187d5475af2SSiva Chandra Reddy for (int i = 0; i < THREAD_COUNT; ++i) {
188*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::thrd_join(waiters[i], &retval);
189d5475af2SSiva Chandra Reddy }
190d5475af2SSiva Chandra Reddy
191d5475af2SSiva Chandra Reddy ASSERT_EQ(wait_count, 0);
192d5475af2SSiva Chandra Reddy
193*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::mtx_destroy(&multiple_waiter_lock);
194*b6bc9d72SGuillaume Chatelet LIBC_NAMESPACE::mtx_destroy(&counter_lock);
195d5475af2SSiva Chandra Reddy }
196d5475af2SSiva Chandra Reddy
TEST_MAIN()19712df3080SSiva Chandra Reddy TEST_MAIN() {
198d5475af2SSiva Chandra Reddy relay_counter();
199d5475af2SSiva Chandra Reddy wait_and_step();
200d5475af2SSiva Chandra Reddy multiple_waiters();
201d5475af2SSiva Chandra Reddy return 0;
202d5475af2SSiva Chandra Reddy }
203