1#include "sch/tests/test_internal.h"
2
3TEST_GROUP_DECLARE(sched, .intensity_desc = {
4 .curve = SCALE_PIECEWISE_LOG,
5 .unit = "iters",
6 });
7
8static void sleepy_entry(void *) {
9 thread_sleep_for_ms(ms: 50);
10}
11
12TEST_DECLARE_INTEGRATION(sched, sleep_ms) {
13 struct thread *t =
14 thread_spawn_joinable(name: "sched_sleepy_test", entry: sleepy_entry, NULL);
15 TEST_ASSERT_NONNULL(t);
16 thread_join(t);
17 return TEST_SUCCESS;
18}
19
20static atomic_bool slept_for_us = false;
21
22static void micro_sleep_entry(void *arg) {
23 (void) arg;
24 thread_sleep_for_us(us: 200);
25 atomic_store(&slept_for_us, true);
26}
27
28TEST_DECLARE_INTEGRATION(sched, sleep_us) {
29 atomic_store(&slept_for_us, false);
30 struct thread *t = thread_spawn_joinable(name: "sched_micro_sleep_test",
31 entry: micro_sleep_entry, NULL);
32 TEST_ASSERT_NONNULL(t);
33 thread_join(t);
34 TEST_ASSERT(atomic_load(&slept_for_us));
35 return TEST_SUCCESS;
36}
37
38static atomic_bool short_sleep_stop = false;
39static atomic_size_t short_sleep_count = 0;
40
41static void short_sleep_entry(void *arg) {
42 (void) arg;
43 for (size_t i = 0; i < 200 && !atomic_load(&short_sleep_stop); i++) {
44 thread_sleep_for_us(us: 1);
45 atomic_fetch_add(&short_sleep_count, 1);
46 }
47}
48
49TEST_DECLARE_INTEGRATION(sched, short_sleep_lost_wake) {
50 atomic_store(&short_sleep_stop, false);
51 atomic_store(&short_sleep_count, 0);
52
53 struct thread *t = thread_spawn_joinable(name: "sched_short_sleep_lost_wake_test",
54 entry: short_sleep_entry, NULL);
55 TEST_ASSERT_NONNULL(t);
56
57 bool joined = thread_join_timeout(t, timeout_ms: 1000, NULL);
58 if (!joined) {
59 atomic_store(&short_sleep_stop, true);
60 thread_wake(t, reason: THREAD_WAKE_REASON_SLEEP_TIMEOUT,
61 prio: t->perceived_prio_class, wake_src: t);
62 thread_join(t);
63 }
64
65 if (!joined)
66 test_info("short timed sleep lost its wake after %zu cycles",
67 atomic_load(&short_sleep_count));
68 TEST_ASSERT(joined);
69 TEST_ASSERT_EQ(200, atomic_load(&short_sleep_count));
70 return TEST_SUCCESS;
71}
72
73static atomic_bool si_apc_ran = false;
74static struct thread *si_t;
75static atomic_bool si_ok = false;
76static atomic_bool si_started = false;
77
78static void apc_si(void *apc) {
79 (void) apc;
80 atomic_store(&si_apc_ran, true);
81}
82
83static void apc_enqueue_thread(void *) {
84 struct apc *apc = apc_create();
85 apc_init(a: apc, fn: apc_si, NULL, destroy: apc_destroy_free);
86
87 while (!atomic_load(&si_started))
88 cpu_relax();
89
90 if (thread_get(obj: si_t)) {
91 apc_enqueue(t: si_t, a: apc, type: APC_TYPE_KERNEL);
92 thread_put(t: si_t);
93 }
94 apc_put(a: apc);
95}
96
97static void sleeping_thread(void *) {
98 atomic_store(&si_started, true);
99
100 thread_prepare_to_sleep(t: thread_get_current(), r: THREAD_SLEEP_REASON_MANUAL,
101 wait_type: THREAD_WAIT_INTERRUPTIBLE, expect_wake_src: (void *) 4);
102
103 thread_yield_until_wake_match();
104
105 atomic_store(&si_ok, true);
106}
107
108static void waking_thread(void *) {
109 while (!atomic_load(&si_apc_ran))
110 scheduler_yield();
111
112 thread_wake(t: si_t, reason: THREAD_WAKE_REASON_SLEEP_MANUAL,
113 prio: si_t->perceived_prio_class, wake_src: (void *) 4);
114}
115
116TEST_DECLARE_INTEGRATION(sched, sleep_interruptible_apc) {
117 if (global.core_count < 4) {
118 test_info("too few cores");
119 return TEST_SKIP(TEST_SKIP_NONE);
120 }
121
122 atomic_store(&si_apc_ran, false);
123 atomic_store(&si_ok, false);
124 atomic_store(&si_started, false);
125
126 si_t = thread_spawn_joinable_on_core(name: "si_thread", entry: sleeping_thread, NULL, core_id: 1);
127 struct thread *waker =
128 thread_spawn_joinable_on_core(name: "si_wake", entry: waking_thread, NULL, core_id: 2);
129 struct thread *enq =
130 thread_spawn_joinable_on_core(name: "si_apc_e", entry: apc_enqueue_thread, NULL, core_id: 3);
131
132 TEST_ASSERT_NONNULL(si_t);
133 TEST_ASSERT_NONNULL(waker);
134 TEST_ASSERT_NONNULL(enq);
135
136 thread_join(t: si_t);
137 thread_join(t: waker);
138 thread_join(t: enq);
139
140 TEST_ASSERT(atomic_load(&si_ok));
141
142 return TEST_SUCCESS;
143}
144
145static atomic_bool sub_apc_ran = false;
146static struct thread *sub_t;
147static atomic_bool sub_interrupted = false;
148static atomic_bool sub_started = false;
149
150static void apc_sub(void *apc) {
151 (void) apc;
152 atomic_store(&sub_apc_ran, true);
153}
154
155static void apc_sub_enq_thread(void *) {
156 struct apc *apc = apc_create();
157 apc_init(a: apc, fn: apc_sub, NULL, destroy: apc_destroy_free);
158
159 while (!atomic_load(&sub_started))
160 cpu_relax();
161
162 if (thread_get(obj: sub_t)) {
163 apc_enqueue(t: sub_t, a: apc, type: APC_TYPE_KERNEL);
164 thread_put(t: sub_t);
165 }
166 apc_put(a: apc);
167}
168
169static void sleeping_sub_thread(void *) {
170 atomic_store(&sub_started, true);
171
172 thread_prepare_to_sleep(t: thread_get_current(), r: THREAD_SLEEP_REASON_MANUAL,
173 wait_type: THREAD_WAIT_INTERRUPTIBLE, expect_wake_src: (void *) 0xdeadbeef);
174
175 enum thread_wait_status st = thread_yield_interruptible();
176 if (st == THREAD_WAIT_INTERRUPTED)
177 atomic_store(&sub_interrupted, true);
178}
179
180TEST_DECLARE_INTEGRATION(sched, wait_interruptible_substrate) {
181 if (global.core_count < 3) {
182 test_info("too few cores");
183 return TEST_SKIP(TEST_SKIP_NONE);
184 }
185
186 atomic_store(&sub_apc_ran, false);
187 atomic_store(&sub_interrupted, false);
188 atomic_store(&sub_started, false);
189
190 sub_t =
191 thread_spawn_joinable_on_core(name: "sub_th", entry: sleeping_sub_thread, NULL, core_id: 1);
192 struct thread *enq =
193 thread_spawn_joinable_on_core(name: "sub_enq", entry: apc_sub_enq_thread, NULL, core_id: 2);
194
195 TEST_ASSERT_NONNULL(sub_t);
196 TEST_ASSERT_NONNULL(enq);
197
198 thread_join(t: sub_t);
199 thread_join(t: enq);
200
201 TEST_ASSERT(atomic_load(&sub_apc_ran));
202 TEST_ASSERT(atomic_load(&sub_interrupted));
203
204 return TEST_SUCCESS;
205}
206
207static struct thread *arb_t;
208static atomic_bool arb_started = false;
209static atomic_bool arb_matched = false;
210
211static void arbitrary_sleeping_thread(void *) {
212 atomic_store(&arb_started, true);
213
214 enum thread_wait_status st =
215 thread_yield_arbitrary(type: THREAD_WAIT_UNINTERRUPTIBLE);
216 if (st == THREAD_WAIT_MATCHED)
217 atomic_store(&arb_matched, true);
218}
219
220static void arbitrary_waking_thread(void *) {
221 while (!atomic_load(&arb_started))
222 cpu_relax();
223
224 thread_sleep_for_ms(ms: 5);
225
226 thread_wake(t: arb_t, reason: THREAD_WAKE_REASON_SLEEP_MANUAL,
227 prio: arb_t->perceived_prio_class, wake_src: (void *) 0xcafe);
228}
229
230TEST_DECLARE_INTEGRATION(sched, wait_arbitrary_any_src) {
231 if (global.core_count < 3) {
232 test_info("too few cores");
233 return TEST_SKIP(TEST_SKIP_NONE);
234 }
235
236 atomic_store(&arb_started, false);
237 atomic_store(&arb_matched, false);
238
239 arb_t = thread_spawn_joinable_on_core(name: "arb_th", entry: arbitrary_sleeping_thread,
240 NULL, core_id: 1);
241 struct thread *waker = thread_spawn_joinable_on_core(
242 name: "arb_waker", entry: arbitrary_waking_thread, NULL, core_id: 2);
243
244 TEST_ASSERT_NONNULL(arb_t);
245 TEST_ASSERT_NONNULL(waker);
246
247 thread_join(t: arb_t);
248 thread_join(t: waker);
249
250 TEST_ASSERT(atomic_load(&arb_matched));
251
252 return TEST_SUCCESS;
253}
254