1#include "sch/tests/test_internal.h"
2
3#define YIELD_APC_SPIN_LIMIT 2000000
4#define YIELD_APC_NO_READING 999
5
6static atomic_bool yd_apc_ran = false;
7static atomic_uint yd_nesting_at_delivery = YIELD_APC_NO_READING;
8static atomic_bool yd_gave_up = false;
9
10static void yd_apc(void *arg) {
11 (void) arg;
12 atomic_store(&yd_nesting_at_delivery,
13 scheduler_yield_nesting(thread_get_current()));
14 atomic_store(&yd_apc_ran, true);
15}
16
17static void yd_subject_main(void *) {
18 for (size_t i = 0; i < YIELD_APC_SPIN_LIMIT; i++) {
19 if (atomic_load(&yd_apc_ran))
20 return;
21 scheduler_yield();
22 }
23 atomic_store(&yd_gave_up, true);
24}
25
26static void yd_enqueue_main(void *arg) {
27 struct thread *target = arg;
28 struct apc *apc = apc_create();
29 if (!apc)
30 return;
31
32 apc_init(a: apc, fn: yd_apc, NULL, destroy: apc_destroy_free);
33 if (thread_get(obj: target)) {
34 apc_enqueue(t: target, a: apc, type: APC_TYPE_KERNEL);
35 thread_put(t: target);
36 }
37 apc_put(a: apc);
38}
39
40TEST_DECLARE_INTEGRATION(sched, yield_defers_kernel_apcs) {
41 if (global.core_count < 3) {
42 test_info("too few cores");
43 return TEST_SKIP(TEST_SKIP_NONE);
44 }
45
46 atomic_store(&yd_apc_ran, false);
47 atomic_store(&yd_gave_up, false);
48 atomic_store(&yd_nesting_at_delivery, YIELD_APC_NO_READING);
49
50 struct thread *subject =
51 thread_spawn_joinable_on_core(name: "yd_subject", entry: yd_subject_main, NULL, core_id: 1);
52 TEST_ASSERT_NONNULL(subject);
53
54 struct thread *enq =
55 thread_spawn_joinable_on_core(name: "yd_enq", entry: yd_enqueue_main, arg: subject, core_id: 2);
56 TEST_ASSERT_NONNULL(enq);
57
58 thread_join(t: subject);
59 thread_join(t: enq);
60
61 TEST_ASSERT(!atomic_load(&yd_gave_up));
62 TEST_ASSERT(atomic_load(&yd_apc_ran));
63
64 TEST_ASSERT_EQ_S((int) atomic_load(&yd_nesting_at_delivery), 0);
65
66 return TEST_SUCCESS;
67}
68