xref: /llvm-project/libc/test/integration/src/threads/mtx_test.cpp (revision b6bc9d72f65a5086f310f321e969d96e9a559e75)
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