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 cc_ignore(nothing);
14 test_info("dummy");
15 while (atomic_load(&pi_done) < 1)
16 scheduler_yield();
17
18 atomic_inc(&pi_done);
19 test_info("exiting");
20}
21
22static void pi_rt_thread(void *nothing) {
23 cc_ignore(nothing);
24 mutex_lock(&pi_mutex);
25 test_info("lock");
26 enforce(mutex_read_owner(&pi_mutex) == thread_get_current());
27 mutex_unlock(&pi_mutex);
28 test_info("unlock");
29 atomic_inc(&pi_done);
30 test_info("exiting");
31}
32
33static void pi_ts_thread(void *nothing) {
34 cc_ignore(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 !=
40 THREAD_PRIO_CLASS_REALTIME)
41 cpu_pause();
42
43 enforce(mutex_read_owner(&pi_mutex) == thread_get_current());
44 test_info("boosted");
45
46 test_info("unlock");
47 mutex_unlock(&pi_mutex);
48
49 atomic_inc(&pi_done);
50 test_info("exiting");
51}
52
53TEST_DEFINE_INTEGRATION(mutex, pi_boost) {
54 if (global.core_count == 1) {
55 return TEST_SKIP(TEST_SKIP_NONE);
56 }
57
58 atomic_store(&pi_ts_got, false);
59 atomic_store(&pi_done, 0);
60
61 cpu_id_t cpu = 1;
62 pi_ts =
63 thread_create("pi_ts", pi_ts_thread, .joinable = true, .pinned = true);
64 pi_rt =
65 thread_create("pi_rt", pi_rt_thread, .joinable = true, .pinned = true);
66 pi_dum =
67 thread_create("pi_dum", pi_dummy, .joinable = true, .pinned = true);
68 pi_rt->base_prio_class = THREAD_PRIO_CLASS_REALTIME;
69 pi_rt->perceived_prio_class = THREAD_PRIO_CLASS_REALTIME;
70 pi_dum->base_prio_class = THREAD_PRIO_CLASS_REALTIME;
71 pi_dum->perceived_prio_class = THREAD_PRIO_CLASS_REALTIME;
72
73 thread_enqueue_on_core(t: pi_ts, core_id: cpu);
74
75 /* Wait for them to get the mutex, then RTs run */
76 while (!atomic_load(&pi_ts_got))
77 scheduler_yield();
78
79 thread_enqueue_on_core(t: pi_dum, core_id: cpu);
80 thread_enqueue_on_core(t: pi_rt, core_id: cpu);
81
82 thread_join(t: pi_ts);
83 thread_join(t: pi_rt);
84 thread_join(t: pi_dum);
85
86 TEST_ASSERT_EQ(atomic_load(&pi_done), 3);
87
88 return TEST_SUCCESS;
89}
90
91static struct mutex pi_mtx_a = MUTEX_INIT;
92static struct mutex pi_mtx_b = MUTEX_INIT;
93
94static struct thread *pi_ts1, *pi_ts2, *pi_rt2;
95static atomic_uint pi_chain_done = 0;
96static atomic_bool ts1_grabbed_a = false;
97static atomic_bool ts2_grabbed_b = false;
98
99static void pi_chain_ts2(void *arg) {
100 cc_ignore(arg);
101 mutex_lock(&pi_mtx_b);
102 test_info("ts2 lock b");
103 atomic_store(&ts2_grabbed_b, true);
104
105 while (thread_get_current()->perceived_prio_class !=
106 THREAD_PRIO_CLASS_REALTIME)
107 cpu_pause();
108
109 test_info("ts2 boosted");
110 mutex_unlock(&pi_mtx_b);
111 atomic_inc(&pi_chain_done);
112}
113
114static void pi_chain_ts1(void *arg) {
115 cc_ignore(arg);
116 mutex_lock(&pi_mtx_a);
117 test_info("ts1 lock a");
118 atomic_store(&ts1_grabbed_a, true);
119
120 while (thread_get_current()->perceived_prio_class !=
121 THREAD_PRIO_CLASS_REALTIME)
122 cpu_pause();
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_inc(&pi_chain_done);
130}
131
132static void pi_chain_rt(void *arg) {
133 cc_ignore(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_inc(&pi_chain_done);
140}
141
142TEST_DEFINE_INTEGRATION(mutex, pi_chain, .min_cores = 2) {
143 atomic_store(&pi_chain_done, 0);
144 atomic_store(&ts1_grabbed_a, false);
145 atomic_store(&ts2_grabbed_b, false);
146
147 cpu_id_t cpu = 1;
148
149 pi_ts2 =
150 thread_create("pi_ts2", pi_chain_ts2, .joinable = true, .pinned = true);
151 pi_ts1 =
152 thread_create("pi_ts1", pi_chain_ts1, .joinable = true, .pinned = true);
153 pi_rt2 =
154 thread_create("pi_rt2", pi_chain_rt, .joinable = true, .pinned = true);
155
156 pi_rt2->base_prio_class = THREAD_PRIO_CLASS_REALTIME;
157 pi_rt2->perceived_prio_class = THREAD_PRIO_CLASS_REALTIME;
158
159 thread_enqueue_on_core(t: pi_ts2, core_id: cpu);
160 while (!atomic_load(&ts2_grabbed_b))
161 scheduler_yield();
162
163 thread_enqueue_on_core(t: pi_ts1, core_id: cpu);
164
165 while (!atomic_load(&ts1_grabbed_a))
166 scheduler_yield();
167
168 thread_enqueue_on_core(t: pi_rt2, core_id: cpu);
169
170 thread_join(t: pi_ts2);
171 thread_join(t: pi_ts1);
172 thread_join(t: pi_rt2);
173
174 TEST_ASSERT_EQ(atomic_load(&pi_chain_done), 3);
175
176 return TEST_SUCCESS;
177}
178
179static struct mutex pi_multi_mtx = MUTEX_INIT;
180static atomic_uint pi_multi_done = 0;
181static atomic_bool ts_got = false;
182
183static void pi_multi_ts(void *arg) {
184 cc_ignore(arg);
185 mutex_lock(&pi_multi_mtx);
186 test_info("multi_ts running");
187 atomic_store(&ts_got, true);
188
189 while (thread_get_current()->perceived_prio_class !=
190 THREAD_PRIO_CLASS_REALTIME)
191 cpu_pause();
192
193 test_info("ts boosted");
194 mutex_unlock(&pi_multi_mtx);
195 atomic_inc(&pi_multi_done);
196}
197
198static void pi_multi_rt(void *arg) {
199 cc_ignore(arg);
200 test_info("multi_rt running");
201 mutex_lock(&pi_multi_mtx);
202 mutex_unlock(&pi_multi_mtx);
203 atomic_inc(&pi_multi_done);
204}
205
206TEST_DEFINE_INTEGRATION(mutex, pi_multi_waiters,
207 TEST_INTENSITY_LINEAR(2, 2, 8, "rt_waiters"),
208 .min_cores = 2) {
209 size_t num_rt = ctx->intensity_val ? ctx->intensity_val : 2;
210 if (num_rt < 1)
211 num_rt = 1;
212 if (num_rt > 8)
213 num_rt = 8;
214
215 atomic_store(&pi_multi_done, 0);
216 atomic_store(&ts_got, false);
217
218 cpu_id_t cpu = 1;
219
220 struct thread *ts =
221 thread_create("pi_ts", pi_multi_ts, .joinable = true, .pinned = true);
222 struct thread *rt[8];
223 for (size_t i = 0; i < num_rt; i++) {
224 rt[i] = thread_create("pi_rt", pi_multi_rt, .joinable = true,
225 .pinned = true);
226 rt[i]->base_prio_class = THREAD_PRIO_CLASS_REALTIME;
227 rt[i]->perceived_prio_class = THREAD_PRIO_CLASS_REALTIME;
228 }
229
230 thread_enqueue_on_core(t: ts, core_id: cpu);
231 while (!atomic_load(&ts_got))
232 scheduler_yield();
233
234 for (size_t i = 0; i < num_rt; i++)
235 thread_enqueue_on_core(t: rt[i], core_id: cpu);
236
237 thread_join(t: ts);
238 for (size_t i = 0; i < num_rt; i++)
239 thread_join(t: rt[i]);
240
241 TEST_ASSERT_EQ(atomic_load(&pi_multi_done), (unsigned) (num_rt + 1));
242
243 return TEST_SUCCESS;
244}
245
246static struct mutex pi_revert_mtx = MUTEX_INIT;
247static atomic_bool pi_reverted = false;
248static atomic_bool pi_revert_got = false;
249static atomic_uint pi_reverted_done = 0;
250
251static void pi_revert_ts(void *arg) {
252 cc_ignore(arg);
253 mutex_lock(&pi_revert_mtx);
254
255 atomic_store(&pi_revert_got, true);
256
257 while (thread_get_current()->perceived_prio_class !=
258 THREAD_PRIO_CLASS_REALTIME)
259 cpu_pause();
260
261 mutex_unlock(&pi_revert_mtx);
262
263 while (thread_get_current()->perceived_prio_class ==
264 THREAD_PRIO_CLASS_REALTIME)
265 cpu_pause();
266
267 atomic_store(&pi_reverted, true);
268 atomic_inc(&pi_reverted_done);
269}
270
271static void pi_revert_rt(void *arg) {
272 cc_ignore(arg);
273 mutex_lock(&pi_revert_mtx);
274 mutex_unlock(&pi_revert_mtx);
275 atomic_inc(&pi_reverted_done);
276}
277
278TEST_DEFINE_INTEGRATION(mutex, pi_revert, .min_cores = 2) {
279 atomic_store(&pi_reverted, false);
280 atomic_store(&pi_revert_got, false);
281 atomic_store(&pi_reverted_done, 0);
282
283 cpu_id_t cpu = 1;
284
285 struct thread *ts =
286 thread_create("pi_ts", pi_revert_ts, .joinable = true, .pinned = true);
287 struct thread *rt =
288 thread_create("pi_rt", pi_revert_rt, .joinable = true, .pinned = true);
289
290 rt->base_prio_class = THREAD_PRIO_CLASS_REALTIME;
291 rt->perceived_prio_class = THREAD_PRIO_CLASS_REALTIME;
292 enforce(thread_getref(ts));
293
294 thread_enqueue_on_core(t: ts, core_id: cpu);
295 while (!atomic_load(&pi_revert_got))
296 scheduler_yield();
297
298 thread_enqueue_on_core(t: rt, core_id: cpu);
299
300 thread_join(t: ts);
301 thread_join(t: rt);
302
303#ifdef DEBUG_THREAD
304 uint32_t boosts = ts->boost_count;
305#endif
306
307 thread_putref(t: ts);
308
309#ifdef DEBUG_THREAD
310 TEST_ASSERT_EQ(boosts, 1);
311#endif
312
313 TEST_ASSERT(atomic_load(&pi_reverted));
314
315 return TEST_SUCCESS;
316}
317
318static struct mutex pi_ooo_l1 = MUTEX_INIT;
319static struct mutex pi_ooo_l2 = MUTEX_INIT;
320static atomic_bool pi_ooo_held_both = false;
321static atomic_bool pi_ooo_rt_ready = false;
322static atomic_bool pi_ooo_held_urgent_after_l1 = false;
323static atomic_bool pi_ooo_reverted_after_l2 = false;
324
325static void pi_ooo_owner(void *arg) {
326 cc_ignore(arg);
327 struct thread *curr = thread_get_current();
328 enum thread_prio_class base = curr->base_prio_class;
329
330 mutex_lock(&pi_ooo_l1);
331 mutex_lock(&pi_ooo_l2);
332 atomic_store(&pi_ooo_held_both, true);
333
334 while (curr->perceived_prio_class != THREAD_PRIO_CLASS_REALTIME)
335 cpu_pause();
336
337 atomic_store(&pi_ooo_rt_ready, true);
338
339 while (curr->perceived_prio_class != THREAD_PRIO_CLASS_URGENT)
340 cpu_pause();
341
342 mutex_unlock(&pi_ooo_l1);
343 atomic_store(&pi_ooo_held_urgent_after_l1,
344 curr->perceived_prio_class == THREAD_PRIO_CLASS_URGENT);
345
346 mutex_unlock(&pi_ooo_l2);
347 atomic_store(&pi_ooo_reverted_after_l2, curr->perceived_prio_class == base);
348}
349
350static void pi_ooo_waiter_l1(void *arg) {
351 cc_ignore(arg);
352 mutex_lock(&pi_ooo_l1);
353 mutex_unlock(&pi_ooo_l1);
354}
355
356static void pi_ooo_waiter_l2(void *arg) {
357 cc_ignore(arg);
358 mutex_lock(&pi_ooo_l2);
359 mutex_unlock(&pi_ooo_l2);
360}
361
362TEST_DEFINE_INTEGRATION(mutex, pi_out_of_order_unlock, .min_cores = 2) {
363 atomic_store(&pi_ooo_held_both, false);
364 atomic_store(&pi_ooo_rt_ready, false);
365 atomic_store(&pi_ooo_held_urgent_after_l1, false);
366 atomic_store(&pi_ooo_reverted_after_l2, false);
367
368 cpu_id_t cpu = 1;
369
370 struct thread *ts = thread_create("pi_ooo_ts", pi_ooo_owner,
371 .joinable = true, .pinned = true);
372 struct thread *rt = thread_create("pi_ooo_rt", pi_ooo_waiter_l1,
373 .joinable = true, .pinned = true);
374 struct thread *urg = thread_create("pi_ooo_urg", pi_ooo_waiter_l2,
375 .joinable = true, .pinned = true);
376
377 rt->base_prio_class = THREAD_PRIO_CLASS_REALTIME;
378 rt->perceived_prio_class = THREAD_PRIO_CLASS_REALTIME;
379 urg->base_prio_class = THREAD_PRIO_CLASS_URGENT;
380 urg->perceived_prio_class = THREAD_PRIO_CLASS_URGENT;
381
382 thread_enqueue_on_core(t: ts, core_id: cpu);
383 while (!atomic_load(&pi_ooo_held_both))
384 scheduler_yield();
385
386 thread_enqueue_on_core(t: rt, core_id: cpu);
387 while (!atomic_load(&pi_ooo_rt_ready))
388 scheduler_yield();
389
390 thread_enqueue_on_core(t: urg, core_id: cpu);
391
392 thread_join(t: ts);
393 thread_join(t: rt);
394 thread_join(t: urg);
395
396 TEST_ASSERT(atomic_load(&pi_ooo_held_urgent_after_l1));
397 TEST_ASSERT(atomic_load(&pi_ooo_reverted_after_l2));
398
399 return TEST_SUCCESS;
400}
401