1#include "sync/tests/test_internal.h"
2
3#define MUTEX_REPORT_PROBLEMS() \
4 test_info("Mutex tests are encountering problems and will be skipped"); \
5 return TEST_SKIP(TEST_SKIP_NONE);
6
7static struct mutex pi_mutex = MUTEX_INIT;
8static struct thread *pi_ts, *pi_rt, *pi_dum;
9static atomic_bool pi_ts_got = false;
10static atomic_uint pi_done = 0;
11
12static void pi_dummy(void *nothing) {
13 (void) nothing;
14 test_info("dummy");
15 while (atomic_load(&pi_done) < 1)
16 scheduler_yield();
17
18 atomic_fetch_add(&pi_done, 1);
19 test_info("exiting");
20}
21
22static void pi_rt_thread(void *nothing) {
23 (void) nothing;
24 mutex_lock(&pi_mutex);
25 test_info("lock");
26 kassert(mutex_get_owner(&pi_mutex) == thread_get_current());
27 mutex_unlock(&pi_mutex);
28 test_info("unlock");
29 atomic_fetch_add(&pi_done, 1);
30 test_info("exiting");
31}
32
33static void pi_ts_thread(void *nothing) {
34 (void) nothing;
35 mutex_lock(&pi_mutex);
36 test_info("lock");
37 atomic_store(&pi_ts_got, true);
38
39 while (thread_get_current()->perceived_prio_class != THREAD_PRIO_CLASS_RT)
40 cpu_relax();
41
42 kassert(mutex_get_owner(&pi_mutex) == thread_get_current());
43 test_info("boosted");
44
45 test_info("unlock");
46 mutex_unlock(&pi_mutex);
47
48 atomic_fetch_add(&pi_done, 1);
49 test_info("exiting");
50}
51
52TEST_DECLARE_INTEGRATION(mutex, pi_boost) {
53 if (global.core_count == 1) {
54 return TEST_SKIP(TEST_SKIP_NONE);
55 }
56
57 atomic_store(&pi_ts_got, false);
58 atomic_store(&pi_done, 0);
59
60 cpu_id_t cpu = 1;
61 pi_ts = thread_create(name: "pi_ts", entry_point: pi_ts_thread, NULL);
62 pi_rt = thread_create(name: "pi_rt", entry_point: pi_rt_thread, NULL);
63 pi_dum = thread_create(name: "pi_dum", entry_point: pi_dummy, NULL);
64 pi_rt->perceived_prio_class = THREAD_PRIO_CLASS_RT;
65 pi_dum->perceived_prio_class = THREAD_PRIO_CLASS_RT;
66
67 thread_pin(t: pi_dum);
68 thread_pin(t: pi_ts);
69 thread_pin(t: pi_rt);
70
71 thread_set_joinable(t: pi_ts);
72 thread_set_joinable(t: pi_rt);
73 thread_set_joinable(t: pi_dum);
74
75 thread_enqueue_on_core(t: pi_ts, core_id: cpu);
76
77 /* Wait for them to get the mutex, then RTs run */
78 while (!atomic_load(&pi_ts_got))
79 scheduler_yield();
80
81 thread_enqueue_on_core(t: pi_dum, core_id: cpu);
82 thread_enqueue_on_core(t: pi_rt, core_id: cpu);
83
84 thread_join(t: pi_ts);
85 thread_join(t: pi_rt);
86 thread_join(t: pi_dum);
87
88 TEST_ASSERT_EQ(atomic_load(&pi_done), 3);
89
90 return TEST_SUCCESS;
91}
92
93static struct mutex pi_mtx_a = MUTEX_INIT;
94static struct mutex pi_mtx_b = MUTEX_INIT;
95
96static struct thread *pi_ts1, *pi_ts2, *pi_rt2;
97static atomic_uint pi_chain_done = 0;
98static atomic_bool ts1_grabbed_a = false;
99static atomic_bool ts2_grabbed_b = false;
100
101static void pi_chain_ts2(void *arg) {
102 (void) arg;
103 mutex_lock(&pi_mtx_b);
104 test_info("ts2 lock b");
105 atomic_store(&ts2_grabbed_b, true);
106
107 while (thread_get_current()->perceived_prio_class != THREAD_PRIO_CLASS_RT)
108 cpu_relax();
109
110 test_info("ts2 boosted");
111 mutex_unlock(&pi_mtx_b);
112 atomic_fetch_add(&pi_chain_done, 1);
113}
114
115static void pi_chain_ts1(void *arg) {
116 (void) arg;
117 mutex_lock(&pi_mtx_a);
118 test_info("ts1 lock a");
119 atomic_store(&ts1_grabbed_a, true);
120
121 while (thread_get_current()->perceived_prio_class != THREAD_PRIO_CLASS_RT)
122 cpu_relax();
123
124 mutex_lock(&pi_mtx_b);
125 test_info("ts1 lock b");
126
127 mutex_unlock(&pi_mtx_b);
128 mutex_unlock(&pi_mtx_a);
129 atomic_fetch_add(&pi_chain_done, 1);
130}
131
132static void pi_chain_rt(void *arg) {
133 (void) arg;
134 test_info("rt lock");
135 mutex_lock(&pi_mtx_a);
136 test_info("rt lock got");
137
138 mutex_unlock(&pi_mtx_a);
139 atomic_fetch_add(&pi_chain_done, 1);
140}
141
142TEST_DECLARE_INTEGRATION(mutex, pi_chain) {
143 if (global.core_count < 2) {
144 return TEST_SKIP(TEST_SKIP_NONE);
145 }
146
147 atomic_store(&pi_chain_done, 0);
148 atomic_store(&ts1_grabbed_a, false);
149 atomic_store(&ts2_grabbed_b, false);
150
151 cpu_id_t cpu = 1;
152
153 pi_ts2 = thread_create(name: "pi_ts2", entry_point: pi_chain_ts2, NULL);
154 pi_ts1 = thread_create(name: "pi_ts1", entry_point: pi_chain_ts1, NULL);
155 pi_rt2 = thread_create(name: "pi_rt2", entry_point: pi_chain_rt, NULL);
156
157 pi_rt2->perceived_prio_class = THREAD_PRIO_CLASS_RT;
158
159 thread_pin(t: pi_ts1);
160 thread_pin(t: pi_ts2);
161 thread_pin(t: pi_rt2);
162
163 thread_set_joinable(t: pi_ts2);
164 thread_set_joinable(t: pi_ts1);
165 thread_set_joinable(t: pi_rt2);
166
167 thread_enqueue_on_core(t: pi_ts2, core_id: cpu);
168 while (!atomic_load(&ts2_grabbed_b))
169 scheduler_yield();
170
171 thread_enqueue_on_core(t: pi_ts1, core_id: cpu);
172
173 while (!atomic_load(&ts1_grabbed_a))
174 scheduler_yield();
175
176 thread_enqueue_on_core(t: pi_rt2, core_id: cpu);
177
178 thread_join(t: pi_ts2);
179 thread_join(t: pi_ts1);
180 thread_join(t: pi_rt2);
181
182 TEST_ASSERT_EQ(atomic_load(&pi_chain_done), 3);
183
184 return TEST_SUCCESS;
185}
186
187static struct mutex pi_multi_mtx = MUTEX_INIT;
188static atomic_uint pi_multi_done = 0;
189static atomic_bool ts_got = false;
190
191static void pi_multi_ts(void *arg) {
192 (void) arg;
193 mutex_lock(&pi_multi_mtx);
194 test_info("multi_ts running");
195 atomic_store(&ts_got, true);
196
197 while (thread_get_current()->perceived_prio_class != THREAD_PRIO_CLASS_RT)
198 cpu_relax();
199
200 test_info("ts boosted");
201 mutex_unlock(&pi_multi_mtx);
202 atomic_fetch_add(&pi_multi_done, 1);
203}
204
205static void pi_multi_rt(void *arg) {
206 (void) arg;
207 test_info("multi_rt running");
208 mutex_lock(&pi_multi_mtx);
209 mutex_unlock(&pi_multi_mtx);
210 atomic_fetch_add(&pi_multi_done, 1);
211}
212
213TEST_DECLARE_INTEGRATION(mutex, pi_multi_waiters,
214 TEST_INTENSITY_LINEAR(2, 2, 8, "rt_waiters")) {
215 if (global.core_count < 2) {
216 return TEST_SKIP(TEST_SKIP_NONE);
217 }
218
219 size_t num_rt = ctx->intensity_val ? ctx->intensity_val : 2;
220 if (num_rt < 1)
221 num_rt = 1;
222 if (num_rt > 8)
223 num_rt = 8;
224
225 atomic_store(&pi_multi_done, 0);
226 atomic_store(&ts_got, false);
227
228 cpu_id_t cpu = 1;
229
230 struct thread *ts = thread_create(name: "pi_ts", entry_point: pi_multi_ts, NULL);
231 struct thread *rt[8];
232 for (size_t i = 0; i < num_rt; i++) {
233 rt[i] = thread_create(name: "pi_rt", entry_point: pi_multi_rt, NULL);
234 rt[i]->perceived_prio_class = THREAD_PRIO_CLASS_RT;
235 thread_pin(t: rt[i]);
236 thread_set_joinable(t: rt[i]);
237 }
238
239 thread_pin(t: ts);
240 thread_set_joinable(t: ts);
241
242 thread_enqueue_on_core(t: ts, core_id: cpu);
243 while (!atomic_load(&ts_got))
244 scheduler_yield();
245
246 for (size_t i = 0; i < num_rt; i++)
247 thread_enqueue_on_core(t: rt[i], core_id: cpu);
248
249 thread_join(t: ts);
250 for (size_t i = 0; i < num_rt; i++)
251 thread_join(t: rt[i]);
252
253 TEST_ASSERT_EQ(atomic_load(&pi_multi_done), (unsigned) (num_rt + 1));
254
255 return TEST_SUCCESS;
256}
257
258static struct mutex pi_revert_mtx = MUTEX_INIT;
259static atomic_bool pi_reverted = false;
260static atomic_bool pi_revert_got = false;
261static atomic_uint pi_reverted_done = 0;
262
263static void pi_revert_ts(void *arg) {
264 (void) arg;
265 mutex_lock(&pi_revert_mtx);
266
267 atomic_store(&pi_revert_got, true);
268
269 while (thread_get_current()->perceived_prio_class != THREAD_PRIO_CLASS_RT)
270 cpu_relax();
271
272 mutex_unlock(&pi_revert_mtx);
273
274 while (thread_get_current()->perceived_prio_class == THREAD_PRIO_CLASS_RT)
275 cpu_relax();
276
277 atomic_store(&pi_reverted, true);
278 atomic_fetch_add(&pi_reverted_done, 1);
279}
280
281static void pi_revert_rt(void *arg) {
282 (void) arg;
283 mutex_lock(&pi_revert_mtx);
284 mutex_unlock(&pi_revert_mtx);
285 atomic_fetch_add(&pi_reverted_done, 1);
286}
287
288TEST_DECLARE_INTEGRATION(mutex, pi_revert) {
289 if (global.core_count < 2) {
290 return TEST_SKIP(TEST_SKIP_NONE);
291 }
292
293 atomic_store(&pi_reverted, false);
294 atomic_store(&pi_revert_got, false);
295 atomic_store(&pi_reverted_done, 0);
296
297 cpu_id_t cpu = 1;
298
299 struct thread *ts = thread_create(name: "pi_ts", entry_point: pi_revert_ts, NULL);
300 struct thread *rt = thread_create(name: "pi_rt", entry_point: pi_revert_rt, NULL);
301
302 rt->perceived_prio_class = THREAD_PRIO_CLASS_RT;
303
304 thread_pin(t: ts);
305 thread_pin(t: rt);
306
307 thread_set_joinable(t: ts);
308 thread_set_joinable(t: rt);
309
310 thread_enqueue_on_core(t: ts, core_id: cpu);
311 while (!atomic_load(&pi_revert_got))
312 scheduler_yield();
313
314 thread_enqueue_on_core(t: rt, core_id: cpu);
315
316 thread_join(t: ts);
317 thread_join(t: rt);
318
319 TEST_ASSERT(atomic_load(&pi_reverted));
320
321 return TEST_SUCCESS;
322}
323