1 //===-- Unittests for mtx_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 "include/threads.h"
10 #include "src/threads/mtx_init.h"
11 #include "src/threads/mtx_lock.h"
12 #include "src/threads/mtx_unlock.h"
13 #include "src/threads/thrd_create.h"
14 #include "src/threads/thrd_join.h"
15 #include "utils/UnitTest/Test.h"
16 
17 constexpr int START = 0;
18 constexpr int MAX = 10000;
19 
20 mtx_t mutex;
21 static int shared_int = START;
22 
counter(void * arg)23 int counter(void *arg) {
24   int last_count = START;
25   while (true) {
26     __llvm_libc::mtx_lock(&mutex);
27     if (shared_int == last_count + 1) {
28       shared_int++;
29       last_count = shared_int;
30     }
31     __llvm_libc::mtx_unlock(&mutex);
32     if (last_count >= MAX)
33       break;
34   }
35   return 0;
36 }
37 
TEST(MutexTest,RelayCounter)38 TEST(MutexTest, RelayCounter) {
39   ASSERT_EQ(__llvm_libc::mtx_init(&mutex, mtx_plain),
40             static_cast<int>(thrd_success));
41 
42   // The idea of this test is that two competing threads will update
43   // a counter only if the other thread has updated it.
44   thrd_t thread;
45   __llvm_libc::thrd_create(&thread, counter, nullptr);
46 
47   int last_count = START;
48   while (true) {
49     ASSERT_EQ(__llvm_libc::mtx_lock(&mutex), static_cast<int>(thrd_success));
50     if (shared_int == START) {
51       ++shared_int;
52       last_count = shared_int;
53     } else if (shared_int != last_count) {
54       ASSERT_EQ(shared_int, last_count + 1);
55       ++shared_int;
56       last_count = shared_int;
57     }
58     ASSERT_EQ(__llvm_libc::mtx_unlock(&mutex), static_cast<int>(thrd_success));
59     if (last_count > MAX)
60       break;
61   }
62 
63   int retval = 123;
64   __llvm_libc::thrd_join(&thread, &retval);
65   ASSERT_EQ(retval, 0);
66 }
67 
68 mtx_t start_lock, step_lock;
69 bool start, step;
70 
stepper(void * arg)71 int stepper(void *arg) {
72   __llvm_libc::mtx_lock(&start_lock);
73   start = true;
74   __llvm_libc::mtx_unlock(&start_lock);
75 
76   __llvm_libc::mtx_lock(&step_lock);
77   step = true;
78   __llvm_libc::mtx_unlock(&step_lock);
79   return 0;
80 }
81 
TEST(MutexTest,WaitAndStep)82 TEST(MutexTest, WaitAndStep) {
83   ASSERT_EQ(__llvm_libc::mtx_init(&start_lock, mtx_plain),
84             static_cast<int>(thrd_success));
85   ASSERT_EQ(__llvm_libc::mtx_init(&step_lock, mtx_plain),
86             static_cast<int>(thrd_success));
87 
88   // In this test, we start a new thread but block it before it can make a
89   // step. Once we ensure that the thread is blocked, we unblock it.
90   // After unblocking, we then verify that the thread was indeed unblocked.
91   step = false;
92   start = false;
93   ASSERT_EQ(__llvm_libc::mtx_lock(&step_lock), static_cast<int>(thrd_success));
94 
95   thrd_t thread;
96   __llvm_libc::thrd_create(&thread, stepper, nullptr);
97 
98   while (true) {
99     // Make sure the thread actually started.
100     ASSERT_EQ(__llvm_libc::mtx_lock(&start_lock),
101               static_cast<int>(thrd_success));
102     bool s = start;
103     ASSERT_EQ(__llvm_libc::mtx_unlock(&start_lock),
104               static_cast<int>(thrd_success));
105     if (s)
106       break;
107   }
108 
109   // Since |step_lock| is still locked, |step| should be false.
110   ASSERT_FALSE(step);
111 
112   // Unlock the step lock and wait until the step is made.
113   ASSERT_EQ(__llvm_libc::mtx_unlock(&step_lock),
114             static_cast<int>(thrd_success));
115 
116   while (true) {
117     ASSERT_EQ(__llvm_libc::mtx_lock(&step_lock),
118               static_cast<int>(thrd_success));
119     bool current_step_value = step;
120     ASSERT_EQ(__llvm_libc::mtx_unlock(&step_lock),
121               static_cast<int>(thrd_success));
122     if (current_step_value)
123       break;
124   }
125 
126   int retval = 123;
127   __llvm_libc::thrd_join(&thread, &retval);
128   ASSERT_EQ(retval, 0);
129 }
130