1 /*
2  * Copyright (c) 2016 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "test_sched.h"
8 #include <ksched.h>
9 
10 #define THREAD_NUM 4
11 
12 static struct k_thread tdata_prio[THREAD_NUM];
13 static struct k_thread tdata;
14 static int last_prio;
15 static uint8_t tid_num[4];
16 static struct k_sem sync_sema;
17 
18 
thread_entry(void * p1,void * p2,void * p3)19 static void thread_entry(void *p1, void *p2, void *p3)
20 {
21 	last_prio = k_thread_priority_get(k_current_get());
22 }
23 
thread_entry_prio(void * p1,void * p2,void * p3)24 static void thread_entry_prio(void *p1, void *p2, void *p3)
25 {
26 	static int i;
27 
28 	k_sem_take(&sync_sema, K_MSEC(100));
29 
30 	tid_num[i++] = POINTER_TO_INT(p1);
31 }
32 
33 
34 /* test cases */
35 
36 /**
37  * @brief Validate that the cooperative thread will
38  * not be preempted
39  *
40  * @details Create a cooperative thread with priority higher
41  * than the current cooperative thread. Make sure that the higher
42  * priority thread will not preempt the lower priority cooperative
43  * thread.
44  *
45  * @ingroup kernel_sched_tests
46  */
ZTEST(threads_scheduling,test_priority_cooperative)47 ZTEST(threads_scheduling, test_priority_cooperative)
48 {
49 	int old_prio = k_thread_priority_get(k_current_get());
50 
51 	/* set current thread to a negative priority */
52 	last_prio = -1;
53 	k_thread_priority_set(k_current_get(), last_prio);
54 
55 	/* spawn thread with higher priority */
56 	int spawn_prio = last_prio - 1;
57 
58 	k_tid_t tid = k_thread_create(&tdata, tstack, STACK_SIZE,
59 				      thread_entry, NULL, NULL, NULL,
60 				      spawn_prio, 0, K_NO_WAIT);
61 	/* checkpoint: current thread shouldn't preempted by higher thread */
62 	zassert_true(last_prio == k_thread_priority_get(k_current_get()));
63 	k_sleep(K_MSEC(100));
64 	/* checkpoint: spawned thread get executed */
65 	zassert_true(last_prio == spawn_prio);
66 	k_thread_abort(tid);
67 
68 	/* restore environment */
69 	k_thread_priority_set(k_current_get(), old_prio);
70 }
71 
72 /**
73  * @brief Validate preemptiveness of preemptive thread
74  *
75  * @details Create a preemptive thread which is of priority
76  * lower than current thread. Current thread is made has preemptive.
77  * Make sure newly created thread is not preempted. Now create a
78  * preemptive thread which is of priority higher than current
79  * thread. Make sure newly created thread is preempted
80  *
81  * @ingroup kernel_sched_tests
82  */
ZTEST(threads_scheduling,test_priority_preemptible)83 ZTEST(threads_scheduling, test_priority_preemptible)
84 {
85 	int old_prio = k_thread_priority_get(k_current_get());
86 
87 	/* set current thread to a non-negative priority */
88 	last_prio = 2;
89 	k_thread_priority_set(k_current_get(), last_prio);
90 
91 	int spawn_prio = last_prio - 1;
92 
93 	k_tid_t tid = k_thread_create(&tdata, tstack, STACK_SIZE,
94 				      thread_entry, NULL, NULL, NULL,
95 				      spawn_prio, 0, K_NO_WAIT);
96 	/* checkpoint: thread is preempted by higher thread */
97 	zassert_true(last_prio == spawn_prio);
98 
99 	k_sleep(K_MSEC(100));
100 	k_thread_abort(tid);
101 
102 	spawn_prio = last_prio + 1;
103 	tid = k_thread_create(&tdata, tstack, STACK_SIZE,
104 			      thread_entry, NULL, NULL, NULL,
105 			      spawn_prio, 0, K_NO_WAIT);
106 	/* checkpoint: thread is not preempted by lower thread */
107 	zassert_false(last_prio == spawn_prio);
108 	k_thread_abort(tid);
109 
110 	/* restore environment */
111 	k_thread_priority_set(k_current_get(), old_prio);
112 }
113 
114 /**
115  * @brief Validate scheduling sequence of preemptive threads with start delay
116  *
117  * @details Create four preemptive threads which are of priority
118  * higher than current thread. Make sure that the highest priority
119  * and longest waiting thread is scheduled first.
120  *
121  * @ingroup kernel_sched_tests
122  */
ZTEST(threads_scheduling_1cpu,test_priority_preemptible_wait_prio)123 ZTEST(threads_scheduling_1cpu, test_priority_preemptible_wait_prio)
124 {
125 	int old_prio = k_thread_priority_get(k_current_get());
126 	k_tid_t tid[THREAD_NUM];
127 	uint8_t tid_chk[4] =  { 0, 1, 2, 3 };
128 
129 	k_sem_init(&sync_sema, 0, THREAD_NUM);
130 
131 	/* Ensure that this code starts running at the start of a system tick */
132 	k_usleep(1);
133 
134 	/* set current thread to a non-negative priority */
135 	last_prio = K_PRIO_PREEMPT(2);
136 	k_thread_priority_set(k_current_get(), last_prio);
137 
138 	/* the highest-priority thread that has waited the longest */
139 	tid[0] = k_thread_create(&tdata_prio[0], tstacks[0], STACK_SIZE,
140 			thread_entry_prio, INT_TO_POINTER(0), NULL, NULL,
141 			K_PRIO_PREEMPT(0), 0, K_MSEC(10));
142 	/* the highest-priority thread that has waited the shorter */
143 	tid[1] = k_thread_create(&tdata_prio[1], tstacks[1], STACK_SIZE,
144 			thread_entry_prio, INT_TO_POINTER(1), NULL, NULL,
145 			K_PRIO_PREEMPT(0), 0, K_MSEC(20));
146 	/* the lowest-priority thread that has waited longest */
147 	tid[2] = k_thread_create(&tdata_prio[2], tstacks[2], STACK_SIZE,
148 			thread_entry_prio, INT_TO_POINTER(2), NULL, NULL,
149 			K_PRIO_PREEMPT(1), 0, K_MSEC(10));
150 	/* the lowest-priority thread that has waited shorter */
151 	tid[3] = k_thread_create(&tdata_prio[3], tstacks[3], STACK_SIZE,
152 			thread_entry_prio, INT_TO_POINTER(3), NULL, NULL,
153 			K_PRIO_PREEMPT(1), 0, K_MSEC(20));
154 
155 	/* relinquish CPU for above threads to start */
156 	k_sleep(K_MSEC(30));
157 
158 	for (int i = 0; i < THREAD_NUM; i++) {
159 		k_sem_give(&sync_sema);
160 	}
161 
162 	zassert_true((memcmp(tid_num, tid_chk, 4) == 0),
163 		     "scheduling priority failed");
164 
165 	/* test case tear down */
166 	for (int i = 0; i < THREAD_NUM; i++) {
167 		k_thread_abort(tid[i]);
168 	}
169 
170 	/* restore environment */
171 	k_thread_priority_set(k_current_get(), old_prio);
172 }
173 
174 extern void idle(void *p1, void *p2, void *p3);
175 
176 /**
177  * Validate checking priority values
178  *
179  * Our test cases don't cover every outcome of whether a priority is valid,
180  * do so here.
181  *
182  * @ingroup kernel_sched_tests
183  */
ZTEST(threads_scheduling,test_bad_priorities)184 ZTEST(threads_scheduling, test_bad_priorities)
185 {
186 	struct prio_test {
187 		int prio;
188 		void *entry;
189 		bool result;
190 	} testcases[] = {
191 		{ K_IDLE_PRIO, idle, true },
192 		{ K_IDLE_PRIO, NULL, false },
193 		{ K_HIGHEST_APPLICATION_THREAD_PRIO - 1, NULL, false },
194 		{ K_LOWEST_APPLICATION_THREAD_PRIO + 1, NULL, false },
195 		{ K_HIGHEST_APPLICATION_THREAD_PRIO, NULL, true },
196 		{ K_LOWEST_APPLICATION_THREAD_PRIO, NULL, true },
197 		{ CONFIG_MAIN_THREAD_PRIORITY, NULL, true }
198 	};
199 
200 	for (int i = 0; i < ARRAY_SIZE(testcases); i++) {
201 		zassert_equal(_is_valid_prio(testcases[i].prio,
202 					     testcases[i].entry),
203 			      testcases[i].result, "failed check %d", i);
204 		/* XXX why are these even separate APIs? */
205 		zassert_equal(Z_VALID_PRIO(testcases[i].prio,
206 					   testcases[i].entry),
207 			      testcases[i].result, "failed check %d", i);
208 	}
209 }
210