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(struct apc_context *ac, void *arg) {
11 cc_ignore(ac);
12 cc_ignore(arg);
13 atomic_store(&yd_nesting_at_delivery,
14 scheduler_yield_nesting(thread_get_current()));
15 atomic_store(&yd_apc_ran, true);
16}
17
18static void yd_subject_main(void *arg) {
19 cc_ignore(arg);
20 for (size_t i = 0; i < YIELD_APC_SPIN_LIMIT; i++) {
21 if (atomic_load(&yd_apc_ran))
22 return;
23 scheduler_yield();
24 }
25 atomic_store(&yd_gave_up, true);
26}
27
28static void yd_enqueue_main(void *arg) {
29 struct thread *target = arg;
30 struct apc *apc = apc_create();
31 if (!apc)
32 return;
33
34 apc_init(a: apc, fn: yd_apc, NULL, destroy: apc_destroy_free);
35 rcu_read_lock();
36 bool got = thread_getref_rcu(t: target);
37 rcu_read_unlock();
38
39 if (got) {
40 apc_enqueue(t: target, a: apc, type: APC_TYPE_KERNEL);
41 thread_putref(t: target);
42 }
43 apc_putref(a: apc);
44}
45
46TEST_DEFINE_INTEGRATION(sched, yield_defers_kernel_apcs, .min_cores = 3) {
47 atomic_store(&yd_apc_ran, false);
48 atomic_store(&yd_gave_up, false);
49 atomic_store(&yd_nesting_at_delivery, YIELD_APC_NO_READING);
50
51 struct thread *subject = thread_spawn("yd_subject", yd_subject_main,
52 .on_cpu = 1, .joinable = true);
53 TEST_ASSERT_NONNULL(subject);
54
55 struct thread *enq = thread_spawn("yd_enq", yd_enqueue_main, .arg = subject,
56 .on_cpu = 2, .joinable = true);
57 TEST_ASSERT_NONNULL(enq);
58
59 thread_join(t: subject);
60 thread_join(t: enq);
61
62 TEST_ASSERT(!atomic_load(&yd_gave_up));
63 TEST_ASSERT(atomic_load(&yd_apc_ran));
64
65 TEST_ASSERT_EQ_S((int) atomic_load(&yd_nesting_at_delivery), 0);
66
67 return TEST_SUCCESS;
68}
69