1#include <crypto/prng.h>
2#include <mem/alloc.h>
3#include <sch/sched.h>
4#include <smp/core.h>
5#include <stdatomic.h>
6#include <test.h>
7#include <thread/apc.h>
8#include <thread/thread.h>
9
10#define CHAOS_THREADS 16
11#define CHAOS_ITERS 50000
12
13struct chaos_thread_state {
14 struct thread *t;
15 atomic_bool alive;
16 atomic_bool ready;
17 _Atomic uintptr_t last_cookie;
18 enum thread_wake_reason last_reason;
19};
20
21static struct chaos_thread_state states[CHAOS_THREADS];
22static atomic_bool chaos_stop = false;
23static atomic_bool starter_ok = false;
24static atomic_uint sync_chaos_left = CHAOS_THREADS;
25
26/* ------------------------------------
27 * Rate-limited logging helpers
28 * ------------------------------------ */
29
30#define CHAOS_LOG_INTERVAL_NS (50ULL * 1000 * 1000) /* 50ms */
31#define CHAOS_LOG_BURST 3
32
33static inline bool chaos_log_allow(uint64_t *last_ns, uint32_t *burst) {
34 uint64_t now = time_get_ns();
35
36 if (now - *last_ns > CHAOS_LOG_INTERVAL_NS) {
37 *last_ns = now;
38 *burst = 0;
39 return true;
40 }
41
42 if (*burst < CHAOS_LOG_BURST) {
43 (*burst)++;
44 return true;
45 }
46
47 return false;
48}
49
50#define CHAOS_LOG(fmt, ...) \
51 do { \
52 static uint64_t _last_ns; \
53 static uint32_t _burst; \
54 if (chaos_log_allow(&_last_ns, &_burst)) \
55 printf("[chaos %llu ms] " fmt "\n", time_get_ms(), ##__VA_ARGS__); \
56 } while (0)
57
58/* ------------------------------------
59 * APC spammer callback
60 * ------------------------------------ */
61static void chaos_apc_fn(void *apc) {
62 (void) apc;
63 /* No signal needed; the wake logic handles APC ordering. */
64}
65
66/* ------------------------------------
67 * Thread: Sleeper
68 * Random interruptible sleeps
69 * ------------------------------------ */
70static void chaos_sleeper(void *arg) {
71 size_t id = (size_t) arg;
72 struct chaos_thread_state *s = &states[id];
73
74 CHAOS_LOG("sleeper[%zu] start on core %u", id, smp_core_id());
75
76 while (!atomic_load(&starter_ok))
77 cpu_relax();
78
79 for (int i = 0; i < CHAOS_ITERS && !atomic_load(&chaos_stop); i++) {
80 uintptr_t cookie = prng_next();
81 atomic_store(&s->last_cookie, cookie);
82 atomic_store(&s->ready, false);
83
84 CHAOS_LOG("sleeper[%zu] sleep iter=%d cookie=%p", id, i,
85 (void *) cookie);
86 thread_prepare_to_sleep(t: thread_get_current(),
87 r: THREAD_SLEEP_REASON_MANUAL,
88 wait_type: THREAD_WAIT_INTERRUPTIBLE, expect_wake_src: (void *) cookie);
89
90 thread_yield_until_wake_match();
91
92 atomic_store(&s->ready, true);
93
94 if (prng_next() % 5 == 0)
95 scheduler_yield();
96 }
97
98 CHAOS_LOG("sleeper[%zu] exit", id);
99
100 atomic_fetch_sub(&sync_chaos_left, 1);
101 atomic_store(&s->alive, false);
102}
103
104/* ------------------------------------
105 * Thread: Waker
106 * Randomly wakes sleeper threads
107 * ------------------------------------ */
108static void chaos_waker(void *a) {
109 (void) a;
110 CHAOS_LOG("waker start");
111
112 while (!atomic_load(&chaos_stop)) {
113 int id = prng_next() % CHAOS_THREADS;
114 struct chaos_thread_state *s = &states[id];
115
116 if (!atomic_load(&s->alive))
117 continue;
118
119 if (!thread_get(obj: s->t))
120 continue;
121
122 bool correct = (prng_next() % 3 == 0);
123 uintptr_t cookie = correct ? atomic_load(&s->last_cookie) : prng_next();
124
125 CHAOS_LOG("waker wake %p", s->t);
126 thread_wake(t: s->t, reason: THREAD_WAKE_REASON_SLEEP_MANUAL,
127 prio: s->t->perceived_prio_class, wake_src: (void *) cookie);
128 CHAOS_LOG("waker wake done");
129
130 thread_put(t: s->t);
131
132 if (prng_next() % 2)
133 scheduler_yield();
134 }
135
136 CHAOS_LOG("waker exit");
137}
138
139/* ------------------------------------
140 * Thread: APC Spammer
141 * ------------------------------------ */
142static void chaos_apc_spammer(void *arg) {
143 (void) arg;
144 static atomic_uint apc_count = 0;
145
146 CHAOS_LOG("apc spammer start");
147
148 while (!atomic_load(&chaos_stop)) {
149
150 int id = prng_next() % CHAOS_THREADS;
151 struct chaos_thread_state *s = &states[id];
152
153 if (!atomic_load(&s->alive))
154 continue;
155
156 if (!thread_get(obj: s->t))
157 continue;
158
159 struct apc *apc = apc_create();
160 apc_init(a: apc, fn: chaos_apc_fn, NULL);
161 apc_enqueue(t: s->t, a: apc, type: APC_TYPE_KERNEL);
162
163 uint32_t n = atomic_fetch_add(&apc_count, 1) + 1;
164 if ((n & 0xfff) == 0) {
165 CHAOS_LOG("apc spammer queued %u APCs", n);
166 }
167
168 thread_put(t: s->t);
169 scheduler_yield();
170 }
171
172 CHAOS_LOG("apc spammer exit");
173}
174
175/* ------------------------------------
176 * Thread: Migrator
177 * Moves threads across cores
178 * ------------------------------------ */
179static void chaos_migrator() {
180 uint32_t cores = global.core_count;
181
182 CHAOS_LOG("migrator start (%u cores)", cores);
183
184 while (!atomic_load(&chaos_stop)) {
185 int id = prng_next() % CHAOS_THREADS;
186 uint32_t core = prng_next() % cores;
187
188 if (!atomic_load(&states[id].alive))
189 continue;
190
191 if (!thread_get(obj: states[id].t))
192 continue;
193
194 CHAOS_LOG("migrate %p to %u", states[id].t, core);
195 thread_migrate(t: states[id].t, dest_core: core);
196 CHAOS_LOG("migrate %p ok", states[id].t);
197 thread_put(t: states[id].t);
198
199 scheduler_yield();
200 }
201
202 CHAOS_LOG("migrator exit");
203}
204
205/* ------------------------------------
206 * Main Test
207 * ------------------------------------ */
208TEST_DECLARE(thread_interruptible_chaos_fuzz, .tier = TEST_TIER_INTEGRATION) {
209 test_info("this test is long. comment me out to run it.");
210 return TEST_SKIP(TEST_SKIP_NONE);
211
212 CHAOS_LOG("chaos test start");
213
214 if (global.core_count < 6) {
215 test_info("needs 6+ cores for chaos fuzz");
216 return TEST_SKIP(TEST_SKIP_NONE);
217 }
218
219 enum irql irql = irql_raise(new_level: IRQL_DISPATCH_LEVEL);
220 for (size_t i = 0; i < CHAOS_THREADS; i++) {
221 atomic_store(&states[i].alive, true);
222 states[i].t = thread_spawn_on_core(name: "chaos_sleeper", entry: chaos_sleeper,
223 arg: (void *) i, core_id: i % global.core_count);
224 }
225
226 atomic_store(&starter_ok, true);
227
228 thread_spawn(name: "chaos_wake", entry: chaos_waker, NULL);
229 thread_spawn(name: "chaos_migrate", entry: chaos_migrator, NULL);
230 thread_spawn(name: "chaos_apc", entry: chaos_apc_spammer, NULL);
231 irql_lower(old_level: irql);
232
233 uint64_t last_report = time_get_ms();
234
235 while (atomic_load(&sync_chaos_left)) {
236 if (time_get_ms() - last_report > 1000) {
237 last_report = time_get_ms();
238 CHAOS_LOG("waiting: %u sleepers left",
239 atomic_load(&sync_chaos_left));
240 }
241 scheduler_yield();
242 }
243
244 atomic_store(&chaos_stop, true);
245
246 CHAOS_LOG("chaos test complete");
247 return TEST_SUCCESS;
248}
249