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