1#include "sync/tests/test_internal.h"
2
3#include <test/fleet.h>
4
5#define MUTEX_REPORT_PROBLEMS() \
6 test_info("Mutex tests are encountering problems and will be skipped"); \
7 return TEST_SKIP(TEST_SKIP_NONE);
8
9#define MUTEX_MANY_WAITER_MAX 64
10#define MUTEX_MANY_WAITER_LOOP_COUNT 500
11
12struct many_waiter_fix {
13 struct mutex lock;
14 atomic_uint32_t completed;
15};
16
17static bool many_worker(struct test_fleet *f, struct test_conc_worker *w) {
18 struct many_waiter_fix *fix = w->arg;
19 cc_ignore(f);
20
21 for (int i = 0; i < MUTEX_MANY_WAITER_LOOP_COUNT; i++) {
22 mutex_lock(&fix->lock);
23 scheduler_yield();
24 mutex_unlock(&fix->lock);
25 }
26
27 atomic_inc(&fix->completed);
28 return true;
29}
30
31TEST_DEFINE_INTEGRATION(mutex, mt_many_waiters, TEST_INTENSITY(2, 10, 32)) {
32 struct test_fleet *fleet = test_fleet_init(ctx, .pin = true);
33
34 struct many_waiter_fix *fix = test_new(fix);
35 mutex_init(&fix->lock);
36
37 size_t num_waiters = ctx->intensity_val ? ctx->intensity_val : 10;
38 if (num_waiters > MUTEX_MANY_WAITER_MAX)
39 num_waiters = MUTEX_MANY_WAITER_MAX;
40
41 for (size_t i = 0; i < num_waiters; i++)
42 test_fleet_spawn(fleet, "mw", many_worker, .arg = fix);
43
44 test_fleet_start_all(f: fleet);
45
46 struct test_verdict v = test_fleet_join(f: fleet);
47 if (v.result != TEST_RESULT_OK)
48 return v;
49
50 TEST_ASSERT_EQ(atomic_load(&fix->completed), num_waiters);
51 return TEST_SUCCESS;
52}
53
54#define CHAOS_THREAD_MAX 64
55#define CHAOS_LOOPS 500
56
57struct chaos_fix {
58 struct mutex lock;
59 atomic_uint32_t completed;
60};
61
62static bool chaos(struct test_fleet *f, struct test_conc_worker *w) {
63 struct chaos_fix *fix = w->arg;
64 cc_ignore(f);
65
66 for (int i = 0; i < CHAOS_LOOPS; i++) {
67 mutex_lock(&fix->lock);
68
69 for (volatile size_t j = 0; j < (test_rng_next(rng: &w->rng) & 0x1F); j++)
70 cpu_pause();
71
72 mutex_unlock(&fix->lock);
73
74 if (test_rng_next(rng: &w->rng) & 1)
75 scheduler_yield();
76 }
77
78 atomic_inc(&fix->completed);
79 return true;
80}
81
82static struct test_verdict mt_chaos_run(struct test_context *ctx,
83 const char *const *noise) {
84 struct test_fleet *fleet = test_fleet_init(ctx);
85
86 struct chaos_fix *fix = test_new(fix);
87 mutex_init(&fix->lock);
88
89 size_t num_threads = ctx->intensity_val ? ctx->intensity_val : 24;
90 if (num_threads > CHAOS_THREAD_MAX)
91 num_threads = CHAOS_THREAD_MAX;
92
93 for (size_t i = 0; i < num_threads; i++)
94 test_fleet_spawn(fleet, "ch", chaos, .arg = fix);
95
96 if (noise)
97 test_fleet_spawn_perturbers(f: fleet, names: noise, TEST_PERTURB_VICTIM_ANY);
98
99 test_fleet_start_all(f: fleet);
100
101 struct test_verdict v = test_fleet_join(f: fleet);
102 if (v.result != TEST_RESULT_OK)
103 return v;
104
105 TEST_ASSERT_EQ(atomic_load(&fix->completed), num_threads);
106 return TEST_SUCCESS;
107}
108
109TEST_DEFINE_INTEGRATION(mutex, mt_chaos, TEST_INTENSITY(20, 50, 100)) {
110 return mt_chaos_run(ctx, NULL);
111}
112
113/* Noise */
114static const char *const mt_chaos_noise[] = {
115 "migrator",
116 "ipi_storm",
117 "dpc_storm",
118 NULL,
119};
120
121TEST_DEFINE_INTEGRATION(mutex, mt_chaos_noisy, TEST_INTENSITY(20, 50, 100)) {
122 return mt_chaos_run(ctx, noise: mt_chaos_noise);
123}
124