1 /*
2 * Copyright (c) 2016 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include "test_sched.h"
8 #define THREADS_NUM 3
9 #define DURATION K_MSEC(1)
10
11 BUILD_ASSERT(THREADS_NUM <= MAX_NUM_THREAD);
12
13 static struct thread_data tdata[THREADS_NUM];
14 static struct k_thread tthread[THREADS_NUM];
15 static int old_prio, init_prio;
16
17 struct k_thread t;
18
19 K_SEM_DEFINE(pend_sema, 0, 1);
20 K_SEM_DEFINE(timer_sema, 0, 1);
21 struct k_timer th_wakeup_timer;
22
thread_entry(void * p1,void * p2,void * p3)23 static void thread_entry(void *p1, void *p2, void *p3)
24 {
25 int sleep_ms = POINTER_TO_INT(p2);
26
27 if (sleep_ms > 0) {
28 k_msleep(sleep_ms);
29 }
30
31 int tnum = POINTER_TO_INT(p1);
32
33 tdata[tnum].executed = 1;
34 }
35
setup_threads(void)36 static void setup_threads(void)
37 {
38 old_prio = k_thread_priority_get(k_current_get());
39 for (int i = 0; i < THREADS_NUM; i++) {
40 if (i == 0) {
41 /* spawn thread with higher priority */
42 tdata[i].priority = init_prio - 1;
43 } else if (i == 1) {
44 /* spawn thread with same priority */
45 tdata[i].priority = init_prio;
46 } else {
47 /* spawn thread with lower priority */
48 tdata[i].priority = init_prio + 1;
49 }
50 tdata[i].executed = 0;
51 }
52 k_thread_priority_set(k_current_get(), init_prio);
53 }
54
spawn_threads(int sleep_sec)55 static void spawn_threads(int sleep_sec)
56 {
57 for (int i = 0; i < THREADS_NUM; i++) {
58 tdata[i].tid = k_thread_create(&tthread[i], tstacks[i],
59 STACK_SIZE, thread_entry,
60 INT_TO_POINTER(i),
61 INT_TO_POINTER(sleep_sec),
62 NULL, tdata[i].priority, 0,
63 K_NO_WAIT);
64 }
65 }
66
teardown_threads(void)67 static void teardown_threads(void)
68 {
69 for (int i = 0; i < THREADS_NUM; i++) {
70 k_thread_abort(tdata[i].tid);
71 }
72 k_thread_priority_set(k_current_get(), old_prio);
73 }
74
timer_handler(struct k_timer * timer)75 static void timer_handler(struct k_timer *timer)
76 {
77 ARG_UNUSED(timer);
78 k_sem_give(&timer_sema);
79 }
80
thread_handler(void * p1,void * p2,void * p3)81 static void thread_handler(void *p1, void *p2, void *p3)
82 {
83 k_timer_init(&th_wakeup_timer, timer_handler, NULL);
84 k_timer_start(&th_wakeup_timer, DURATION, K_NO_WAIT);
85 }
86
87 /* test cases */
88
89 /**
90 * @brief Validate the behavior of cooperative thread
91 * when it yields
92 *
93 * @ingroup kernel_sched_tests
94 *
95 * @details Create 3 threads of priority -2, -1 and 0.
96 * Yield the main thread which is cooperative. Check
97 * if all the threads gets executed.
98 */
ZTEST(threads_scheduling,test_yield_cooperative)99 ZTEST(threads_scheduling, test_yield_cooperative)
100 {
101
102 /* set current thread to a cooperative priority */
103 init_prio = -1;
104 setup_threads();
105
106 spawn_threads(0);
107 /* checkpoint: only higher priority thread get executed when yield */
108 k_yield();
109 zassert_true(tdata[0].executed == 1);
110 zassert_true(tdata[1].executed == 1);
111 for (int i = 2; i < THREADS_NUM; i++) {
112 zassert_true(tdata[i].executed == 0);
113 }
114 /* restore environment */
115 teardown_threads();
116 }
117 /**
118 * @brief Validate the behavior of cooperative thread when it sleeps
119 *
120 * @details Create 3 threads of priority -2, -1 and 0. Put the main
121 * thread in timeout queue by calling k_sleep() which is cooperative.
122 * Check if all the threads gets executed.
123 *
124 * @ingroup kernel_sched_tests
125 */
ZTEST(threads_scheduling,test_sleep_cooperative)126 ZTEST(threads_scheduling, test_sleep_cooperative)
127 {
128 /* set current thread to a cooperative priority */
129 init_prio = -1;
130 setup_threads();
131
132 spawn_threads(0);
133 /* checkpoint: all ready threads get executed when k_sleep */
134 k_sleep(K_MSEC(100));
135 for (int i = 0; i < THREADS_NUM; i++) {
136 zassert_true(tdata[i].executed == 1);
137 }
138
139 /* restore environment */
140 teardown_threads();
141 }
142
ZTEST(threads_scheduling,test_busy_wait_cooperative)143 ZTEST(threads_scheduling, test_busy_wait_cooperative)
144 {
145 /* set current thread to a cooperative priority */
146 init_prio = -1;
147 setup_threads();
148
149 spawn_threads(0);
150 k_busy_wait(100000); /* 100 ms */
151 /* checkpoint: No other threads get executed */
152 for (int i = 0; i < THREADS_NUM; i++) {
153 zassert_true(tdata[i].executed == 0);
154 }
155 /* restore environment */
156 teardown_threads();
157 }
158
159 /**
160 * @brief Validate k_wakeup()
161 *
162 * @details Create 3 threads with main thread with priority 0
163 * and other threads with -1, 0 ,+1 priority. Now -1 priority
164 * thread gets executed and it is made to sleep for 10 sec.
165 * Now, wake up the -1 priority thread and check if it starts
166 * executing.
167 *
168 * @see k_wakeup()
169 *
170 * @ingroup kernel_sched_tests
171 */
ZTEST(threads_scheduling,test_sleep_wakeup_preemptible)172 ZTEST(threads_scheduling, test_sleep_wakeup_preemptible)
173 {
174 /* set current thread to a preemptible priority */
175 init_prio = 0;
176 setup_threads();
177
178 spawn_threads(10 * 1000); /* 10 second */
179 /* checkpoint: lower threads not executed, high threads are in sleep */
180 for (int i = 0; i < THREADS_NUM; i++) {
181 zassert_true(tdata[i].executed == 0);
182 }
183 k_wakeup(tdata[0].tid);
184 zassert_true(tdata[0].executed == 1);
185 /* restore environment */
186 teardown_threads();
187 }
188
189 static int executed;
coop_thread(void * p1,void * p2,void * p3)190 static void coop_thread(void *p1, void *p2, void *p3)
191 {
192 ARG_UNUSED(p1);
193 ARG_UNUSED(p2);
194 ARG_UNUSED(p3);
195
196 k_sem_take(&pend_sema, K_MSEC(100));
197 executed = 1;
198 }
199
200 /**
201 * @brief Verify k_wakeup() behavior on pending thread
202 *
203 * @details The test creates a cooperative thread and let
204 * it wait for semaphore. Then calls k_wakeup(). The k_wakeup()
205 * call should return gracefully without waking up the thread
206 *
207 * @see k_wakeup()
208 *
209 * @ingroup kernel_sched_tests
210 */
ZTEST(threads_scheduling,test_pending_thread_wakeup)211 ZTEST(threads_scheduling, test_pending_thread_wakeup)
212 {
213 /* Make current thread preemptible */
214 k_thread_priority_set(k_current_get(), K_PRIO_PREEMPT(1));
215
216 /* Create a thread which waits for semaphore */
217 k_tid_t tid = k_thread_create(&t, tstack, STACK_SIZE,
218 coop_thread,
219 NULL, NULL, NULL,
220 K_PRIO_COOP(1), 0, K_NO_WAIT);
221
222 zassert_false(executed == 1, "The thread didn't wait"
223 " for semaphore acquisition");
224
225 /* Call wakeup on pending thread */
226 k_wakeup(tid);
227
228 /* TESTPOINT: k_wakeup() shouldn't resume
229 * execution of pending thread
230 */
231 zassert_true(executed != 1, "k_wakeup woke up a"
232 " pending thread!");
233
234 k_thread_abort(tid);
235 }
236
237 /**
238 * @brief Validate preemptive thread behavior with time slice
239 *
240 * @details Create 3 threads with -1, 0, and 1 as priority, setup
241 * time slice for threads with priority 0. Make sure the threads
242 * with equal priorities are executed in time slice.
243 *
244 * @ingroup kernel_sched_tests
245 */
ZTEST(threads_scheduling,test_time_slicing_preemptible)246 ZTEST(threads_scheduling, test_time_slicing_preemptible)
247 {
248 #ifdef CONFIG_TIMESLICING
249 /* set current thread to a preemptible priority */
250 init_prio = 0;
251 setup_threads();
252
253 k_sched_time_slice_set(200, 0); /* 200 ms */
254 spawn_threads(0);
255 /* checkpoint: higher priority threads get executed immediately */
256 zassert_true(tdata[0].executed == 1);
257 k_busy_wait(500000); /* 500 ms */
258 /* checkpoint: equal priority threads get executed every time slice */
259 zassert_true(tdata[1].executed == 1);
260 for (int i = 2; i < THREADS_NUM; i++) {
261 zassert_true(tdata[i].executed == 0);
262 }
263
264 /* restore environment */
265 k_sched_time_slice_set(0, 0); /* disable time slice */
266 teardown_threads();
267 #else /* CONFIG_TIMESLICING */
268 ztest_test_skip();
269 #endif /* CONFIG_TIMESLICING */
270 }
271
272 /**
273 * @brief Check the behavior of preemptive thread with k_busy_wait()
274 *
275 * @details Create 3 threads with -1, 0, and 1 as priority,
276 * setup time slice for threads with priority 0. Make sure the
277 * threads with equal priorities are executed in time slice.
278 * Also run k_busy_wait() for 5 secs and check if other threads
279 * are not executed at that time.
280 *
281 * @see k_busy_wait()
282 *
283 * @ingroup kernel_sched_tests
284 */
ZTEST(threads_scheduling,test_time_slicing_disable_preemptible)285 ZTEST(threads_scheduling, test_time_slicing_disable_preemptible)
286 {
287 #ifdef CONFIG_TIMESLICING
288 /* set current thread to a preemptible priority */
289 init_prio = 0;
290 setup_threads();
291
292 spawn_threads(0);
293 /* checkpoint: higher priority threads get executed immediately */
294 zassert_true(tdata[0].executed == 1);
295 k_busy_wait(500000); /* 500 ms */
296 /* checkpoint: equal priority threads get executed every time slice */
297 zassert_true(tdata[1].executed == 0);
298 for (int i = 2; i < THREADS_NUM; i++) {
299 zassert_true(tdata[i].executed == 0);
300 }
301 /* restore environment */
302 teardown_threads();
303 #else /* CONFIG_TIMESLICING */
304 ztest_test_skip();
305 #endif /* CONFIG_TIMESLICING */
306 }
307
308 /**
309 * @brief Lock the scheduler when preemptive threads are running
310 *
311 * @details Create 3 threads and lock the scheduler. Make sure that the
312 * threads are not executed. Call k_sleep() and check if the threads
313 * have executed.
314 *
315 * @ingroup kernel_sched_tests
316 */
ZTEST(threads_scheduling,test_lock_preemptible)317 ZTEST(threads_scheduling, test_lock_preemptible)
318 {
319 /* set current thread to a preemptible priority */
320 init_prio = 0;
321 setup_threads();
322
323 k_sched_lock();
324 spawn_threads(0);
325 /* do critical thing */
326 k_busy_wait(100000);
327 /* checkpoint: all other threads not been executed */
328 for (int i = 0; i < THREADS_NUM; i++) {
329 zassert_true(tdata[i].executed == 0);
330 }
331 /* make current thread unready */
332 k_sleep(K_MSEC(100));
333 /* checkpoint: all other threads get executed */
334 for (int i = 0; i < THREADS_NUM; i++) {
335 zassert_true(tdata[i].executed == 1);
336 }
337 /* restore environment */
338 teardown_threads();
339 }
340
341 /**
342 * @brief Validate k_sched_lock() and k_sched_unlock()
343 *
344 * @details Lock the scheduler and create 3 threads. Check
345 * that the threads are not executed. Call k_sched_unlock()
346 * and check if the threads have executed.
347 *
348 * @see k_sched_lock(), k_sched_unlock()
349 *
350 * @ingroup kernel_sched_tests
351 */
ZTEST(threads_scheduling,test_unlock_preemptible)352 ZTEST(threads_scheduling, test_unlock_preemptible)
353 {
354 /* set current thread to a preemptible priority */
355 init_prio = 0;
356 setup_threads();
357
358 k_sched_lock();
359 spawn_threads(0);
360 /* do critical thing */
361 k_busy_wait(100000);
362
363 k_sched_unlock();
364
365 /* ensure threads of equal priority can run */
366 k_yield();
367
368 /* checkpoint: higher and equal threads get executed */
369 zassert_true(tdata[0].executed == 1);
370 zassert_true(tdata[1].executed == 1);
371 zassert_true(tdata[2].executed == 0);
372
373 /* restore environment */
374 teardown_threads();
375 }
376
377 /**
378 * @brief Validate nested k_sched_lock() and k_sched_unlock()
379 *
380 * @details In a preemptive thread, lock the scheduler twice and
381 * create a cooperative thread. Call k_sched_unlock() and check the
382 * cooperative thread haven't executed. Unlock it again to see the
383 * thread have executed this time.
384 *
385 * @see k_sched_lock(), k_sched_unlock()
386 *
387 * @ingroup kernel_sched_tests
388 */
ZTEST(threads_scheduling,test_unlock_nested_sched_lock)389 ZTEST(threads_scheduling, test_unlock_nested_sched_lock)
390 {
391 /* set current thread to a preemptible priority */
392 init_prio = 0;
393 setup_threads();
394
395 /* take the scheduler lock twice */
396 k_sched_lock();
397 k_sched_lock();
398
399 /* spawn threads without wait */
400 spawn_threads(0);
401
402 /* do critical thing */
403 k_busy_wait(100000);
404
405 /* unlock once; this shouldn't let other threads to run */
406 k_sched_unlock();
407
408 /* checkpoint: no threads get executed */
409 for (int i = 0; i < THREADS_NUM; i++) {
410 zassert_true(tdata[i].executed == 0);
411 }
412
413 /* unlock another; this let the higher thread to run */
414 k_sched_unlock();
415
416 /* Ensure threads of equal priority run */
417 k_yield();
418
419 /* checkpoint: higher threads NOT get executed */
420 zassert_true(tdata[0].executed == 1);
421 zassert_true(tdata[1].executed == 1);
422 zassert_true(tdata[2].executed == 0);
423
424 /* restore environment */
425 teardown_threads();
426 }
427
428 /**
429 * @brief validate k_wakeup() in some corner scenario
430 * @details trigger a timer and after expiration of timer
431 * call k_wakeup(), even the thread is not in sleep state neither
432 * in pending state
433 *
434 * @see k_wakeup()
435 *
436 * @ingroup kernel_sched_tests
437 */
ZTEST(threads_scheduling,test_wakeup_expired_timer_thread)438 ZTEST(threads_scheduling, test_wakeup_expired_timer_thread)
439 {
440 k_tid_t tid = k_thread_create(&tthread[0], tstack, STACK_SIZE,
441 thread_handler, NULL, NULL, NULL,
442 K_PRIO_PREEMPT(0), 0, K_NO_WAIT);
443 k_sem_take(&timer_sema, K_FOREVER);
444 /* wakeup a thread if the timer is expired */
445 k_wakeup(tid);
446 k_thread_abort(tid);
447 }
448