1 /*
2 * Copyright (c) 2006-2021, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2021-09.01 yangjie the firet version
9 * 2021-10.11 mazhiyuan add idle, yield, suspend, control, priority, delay_until
10 */
11
12 #define __RT_IPC_SOURCE__ /* include internal API for utest */
13
14 #include <rtthread.h>
15 #include <stdlib.h>
16 #include "utest.h"
17
18 #define THREAD_STACK_SIZE UTEST_THR_STACK_SIZE
19 #define THREAD_TIMESLICE 10
20
21 rt_align(RT_ALIGN_SIZE)
22 static char thread2_stack[UTEST_THR_STACK_SIZE];
23 static struct rt_thread thread2;
24 #ifdef RT_USING_HEAP
25 static rt_thread_t tid1 = RT_NULL;
26 static rt_thread_t tid3 = RT_NULL;
27 static rt_thread_t tid4 = RT_NULL;
28 static rt_thread_t tid5 = RT_NULL;
29 static rt_thread_t tid6 = RT_NULL;
30 static rt_thread_t tid7 = RT_NULL;
31 #endif /* RT_USING_HEAP */
32
33 static volatile rt_uint32_t tid3_delay_pass_flag = 0;
34 static volatile rt_uint32_t tid3_finish_flag = 0;
35 static volatile rt_uint32_t tid4_finish_flag = 0;
36 static volatile rt_uint32_t tid6_finish_flag = 0;
37 static volatile rt_uint32_t thread5_source = 0;
38
39 #ifndef RT_USING_SMP
40 static volatile rt_uint32_t thread_yield_flag = 0;
41 #endif
42 static volatile rt_uint32_t entry_idle_hook_times = 0;
43 static rt_thread_t __current_thread;
44 static rt_uint8_t change_priority;
45 static volatile rt_uint32_t count = 0;
46
thread1_entry(void * param)47 void thread1_entry(void *param)
48 {
49 while (1);
50 }
51
test_dynamic_thread(void)52 static void test_dynamic_thread(void)
53 {
54 rt_err_t ret_startup = -RT_ERROR;
55 rt_err_t ret_delete = -RT_ERROR;
56
57 tid1 = rt_thread_create("thread1",
58 thread1_entry,
59 (void *)1,
60 THREAD_STACK_SIZE,
61 UTEST_THR_PRIORITY + 1,
62 THREAD_TIMESLICE - 5);
63 if (tid1 == RT_NULL)
64 {
65 uassert_false(tid1 == RT_NULL);
66 goto __exit;
67 }
68
69 ret_startup = rt_thread_startup(tid1);
70 if (ret_startup != RT_EOK)
71 {
72 uassert_false(ret_startup != RT_EOK);
73 goto __exit;
74 }
75
76 ret_delete = rt_thread_delete(tid1);
77 if (ret_delete != RT_EOK)
78 {
79 uassert_false(ret_delete != RT_EOK);
80 goto __exit;
81 }
82
83 uassert_true(tid1 != RT_NULL && ret_startup == RT_EOK && ret_delete == RT_EOK);
84
85 __exit:
86 if (tid1 != RT_NULL && ret_delete != RT_EOK)
87 {
88 rt_thread_delete(tid1);
89 }
90 return;
91 }
92
thread2_entry(void * param)93 void thread2_entry(void *param)
94 {
95 while (1);
96 }
97
test_static_thread(void)98 static void test_static_thread(void)
99 {
100 rt_err_t ret_init = -RT_ERROR;
101 rt_err_t ret_startup = -RT_ERROR;
102 rt_err_t ret_detach = -RT_ERROR;
103
104 ret_init = rt_thread_init(&thread2,
105 "thread2",
106 thread2_entry,
107 (void *)2,
108 &thread2_stack[0],
109 sizeof(thread2_stack),
110 UTEST_THR_PRIORITY + 1,
111 THREAD_TIMESLICE);
112 if (ret_init != RT_EOK)
113 {
114 uassert_false(ret_init != RT_EOK);
115 goto __exit;
116 }
117
118 ret_startup = rt_thread_startup(&thread2);
119 if (ret_startup != RT_EOK)
120 {
121 uassert_false(ret_startup != RT_EOK);
122 goto __exit;
123 }
124
125 ret_detach = rt_thread_detach(&thread2);
126 if (ret_detach != RT_EOK)
127 {
128 uassert_false(ret_detach != RT_EOK);
129 goto __exit;
130 }
131
132 uassert_true(ret_init == RT_EOK && ret_startup == RT_EOK && ret_detach == RT_EOK);
133
134 __exit:
135 if (ret_init == RT_EOK && ret_detach != RT_EOK)
136 {
137 rt_thread_detach(&thread2);
138 }
139 return;
140 }
141
thread3_entry(void * parameter)142 static void thread3_entry(void *parameter)
143 {
144 rt_tick_t tick, latency_tick;
145 tick = rt_tick_get();
146 rt_thread_delay(15);
147 latency_tick = rt_tick_get() - tick;
148 if (latency_tick > 16 || latency_tick < 15)
149 {
150 tid3_finish_flag = 1;
151 tid3_delay_pass_flag = 0;
152 return;
153 }
154 tid3_delay_pass_flag = 1;
155 tid3_finish_flag = 1;
156 }
157
test_thread_delay(void)158 static void test_thread_delay(void)
159 {
160 rt_err_t ret_startup = -RT_ERROR;
161
162 tid3 = rt_thread_create("thread3",
163 thread3_entry,
164 RT_NULL,
165 THREAD_STACK_SIZE,
166 UTEST_THR_PRIORITY - 1,
167 THREAD_TIMESLICE);
168 if (tid3 == RT_NULL)
169 {
170 LOG_E("rt_thread_create failed!");
171 uassert_false(tid3 == RT_NULL);
172 goto __exit;
173 }
174
175 ret_startup = rt_thread_startup(tid3);
176 if (ret_startup != RT_EOK)
177 {
178 LOG_E("rt_thread_startup failed!");
179 uassert_false(1);
180 goto __exit;
181 }
182
183 while (tid3_finish_flag != 1);
184 uassert_true(tid3_delay_pass_flag == 1);
185
186 __exit:
187 return;
188 }
189
idle_hook(void)190 static void idle_hook(void)
191 {
192 entry_idle_hook_times ++;
193 }
194
thread4_entry(void * parameter)195 static void thread4_entry(void *parameter)
196 {
197 rt_uint32_t delay_times = 5;
198 while (delay_times --)
199 {
200 rt_thread_mdelay(300);
201 }
202 rt_thread_idle_delhook(idle_hook);
203 tid4_finish_flag = 1;
204 }
205
test_idle_hook(void)206 static void test_idle_hook(void)
207 {
208 rt_err_t ret_startup = -RT_ERROR;
209
210 rt_thread_idle_sethook(idle_hook);
211
212 tid4 = rt_thread_create("thread4",
213 thread4_entry,
214 RT_NULL,
215 THREAD_STACK_SIZE,
216 UTEST_THR_PRIORITY - 1,
217 THREAD_TIMESLICE);
218 if (tid4 == RT_NULL)
219 {
220 LOG_E("rt_thread_create failed!");
221 uassert_false(tid4 == RT_NULL);
222 goto __exit;
223 }
224
225 ret_startup = rt_thread_startup(tid4);
226 if (ret_startup != RT_EOK)
227 {
228 LOG_E("rt_thread_startup failed!");
229 uassert_false(1);
230 goto __exit;
231 }
232
233 while (tid4_finish_flag != 1)
234 {
235 rt_thread_mdelay(200);
236 }
237 uassert_true(entry_idle_hook_times > 0);
238
239 __exit:
240 return;
241 }
242
thread5_entry(void * parameter)243 static void thread5_entry(void *parameter)
244 {
245 while (1)
246 {
247 thread5_source ++;
248 rt_thread_delay(5);
249 if (thread5_source == 5)
250 {
251 rt_thread_yield();
252 }
253 }
254 }
255
thread6_entry(void * parameter)256 static void thread6_entry(void *parameter)
257 {
258 while (++ thread5_source <= 9);
259 tid6_finish_flag = 1;
260 }
261
test_thread_yield(void)262 static void test_thread_yield(void)
263 {
264 rt_err_t ret_startup = -RT_ERROR;
265 thread5_source = 0;
266 tid5 = rt_thread_create("thread5",
267 thread5_entry,
268 RT_NULL,
269 THREAD_STACK_SIZE,
270 UTEST_THR_PRIORITY - 1,
271 THREAD_TIMESLICE);
272 if (tid5 == RT_NULL)
273 {
274 LOG_E("rt_thread_create failed!");
275 uassert_false(tid5 == RT_NULL);
276 goto __exit;
277 }
278 ret_startup = rt_thread_startup(tid5);
279 if (ret_startup != RT_EOK)
280 {
281 LOG_E("rt_thread_startup failed!");
282 uassert_false(1);
283 goto __exit;
284 }
285 tid6 = rt_thread_create("thread6",
286 thread6_entry,
287 RT_NULL,
288 THREAD_STACK_SIZE,
289 UTEST_THR_PRIORITY - 1,
290 THREAD_TIMESLICE);
291 if (tid6 == RT_NULL)
292 {
293 LOG_E("rt_thread_create failed!");
294 uassert_false(tid6 == RT_NULL);
295 goto __exit;
296 }
297 ret_startup = rt_thread_startup(tid6);
298 if (ret_startup != RT_EOK)
299 {
300 LOG_E("rt_thread_startup failed!");
301 uassert_false(1);
302 goto __exit;
303 }
304
305 while (tid6_finish_flag != 1);
306 uassert_true(thread5_source == 10);
307
308 __exit:
309 if (tid5 != RT_NULL)
310 {
311 rt_thread_delete(tid5);
312 }
313 return;
314 }
315
thread7_entry(void * parameter)316 static void thread7_entry(void *parameter)
317 {
318 while (1);
319 }
320
test_thread_control(void)321 static void test_thread_control(void)
322 {
323 rt_err_t ret_control = -RT_ERROR;
324 rt_err_t rst_delete = -RT_ERROR;
325 rt_sched_lock_level_t slvl;
326
327 tid7 = rt_thread_create("thread7",
328 thread7_entry,
329 RT_NULL,
330 THREAD_STACK_SIZE,
331 UTEST_THR_PRIORITY + 1,
332 THREAD_TIMESLICE);
333 if (tid7 == RT_NULL)
334 {
335 LOG_E("rt_thread_create failed!");
336 uassert_false(tid7 == RT_NULL);
337 goto __exit;
338 }
339
340 ret_control = rt_thread_control(tid7, RT_THREAD_CTRL_STARTUP, RT_NULL);
341 if (ret_control != RT_EOK)
342 {
343 LOG_E("rt_thread_control failed!");
344 uassert_false(1);
345 goto __exit;
346 }
347 rt_thread_mdelay(200);
348 rt_thread_control(tid7, RT_THREAD_CTRL_CHANGE_PRIORITY, &change_priority);
349
350 rt_sched_lock(&slvl);
351 if (rt_sched_thread_get_curr_prio(tid7) != change_priority)
352 {
353 LOG_E("rt_thread_control failed!");
354 uassert_false(1);
355 rt_sched_unlock(slvl);
356 goto __exit;
357 }
358 rt_sched_unlock(slvl);
359
360 rst_delete = rt_thread_control(tid7, RT_THREAD_CTRL_CLOSE, RT_NULL);
361 if (rst_delete != RT_EOK)
362 {
363 LOG_E("rt_thread_control failed!");
364 uassert_false(rst_delete != RT_EOK);
365 goto __exit;
366 }
367
368 uassert_true(1);
369
370 __exit:
371 if (tid7 != RT_NULL && rst_delete != RT_EOK)
372 {
373 rt_thread_delete(tid7);
374 }
375 return;
376 }
377
thread8_entry(void * parameter)378 static void thread8_entry(void *parameter)
379 {
380 for (; count < 10; count ++);
381 }
382
test_thread_priority(void)383 static void test_thread_priority(void)
384 {
385 rt_err_t ret_startup = -RT_ERROR;
386 rt_thread_t tid8 = RT_NULL;
387
388 tid8 = rt_thread_create("thread8",
389 thread8_entry,
390 RT_NULL,
391 THREAD_STACK_SIZE,
392 UTEST_THR_PRIORITY - 1,
393 THREAD_TIMESLICE);
394 if (tid8 == RT_NULL)
395 {
396 LOG_E("rt_thread_create failed!");
397 uassert_false(tid8 == RT_NULL);
398 return;
399 }
400 count = 0;
401 ret_startup = rt_thread_startup(tid8);
402 if (ret_startup != RT_EOK)
403 {
404 uassert_false(ret_startup != RT_EOK);
405 return ;
406 }
407 uassert_true(count == 10);
408
409 return;
410 }
411
test_delay_until(void)412 static void test_delay_until(void)
413 {
414 rt_tick_t tick;
415 rt_tick_t check_tick = 0;
416 rt_tick_t delta = 0;
417
418 tick = rt_tick_get();
419
420 check_tick = tick;
421 rt_thread_delay_until(&tick, 100);
422 delta = rt_tick_get() - check_tick;
423 rt_kprintf("delta[100] -> %d\n", delta);
424 uassert_int_equal(delta, 100);
425
426 check_tick = tick;
427 rt_thread_delay(2);
428 rt_thread_delay_until(&tick, 200);
429 delta = rt_tick_get() - check_tick;
430 rt_kprintf("delta[200] -> %d\n", delta);
431 uassert_int_equal(delta, 200);
432
433 check_tick = tick;
434 rt_thread_delay(2);
435 rt_thread_delay_until(&tick, 300);
436 delta = rt_tick_get() - check_tick;
437 rt_kprintf("delta[300] -> %d\n", delta);
438 uassert_int_equal(delta, 300);
439
440 check_tick = tick;
441 rt_thread_delay(2);
442 rt_thread_delay_until(&tick, 100);
443 delta = rt_tick_get() - check_tick;
444 uassert_int_equal(delta, 100);
445
446 check_tick = tick;
447 rt_thread_delay(2);
448 rt_thread_delay_until(&tick, 50);
449 delta = rt_tick_get() - check_tick;
450 rt_kprintf("delta[50] -> %d\n", delta);
451 uassert_int_equal(delta, 50);
452
453 check_tick = tick;
454 rt_thread_delay(2);
455 rt_thread_delay_until(&tick, 20);
456 delta = rt_tick_get() - check_tick;
457 rt_kprintf("delta[20] -> %d\n", delta);
458 uassert_int_equal(delta, 20);
459
460 /**
461 * the rt_kprints above can take few ticks to complete, maybe more than 10
462 */
463 tick = rt_tick_get();
464 check_tick = tick;
465 rt_thread_delay(2);
466 rt_thread_delay_until(&tick, 10);
467 delta = rt_tick_get() - check_tick;
468 rt_kprintf("delta[10] -> %d\n", delta);
469 uassert_int_equal(delta, 10);
470 }
471
472 static rt_thread_t tidA, tidB1, tidB2;
473 static uint32_t timeslice_cntA, timeslice_cntB1, timeslice_cntB2;
474
test_timeslice_threadA_entry(void * parameter)475 static void test_timeslice_threadA_entry(void *parameter)
476 {
477 while (1)
478 {
479 rt_thread_delay(2);
480 timeslice_cntA++;
481 if (timeslice_cntA > 10) return;
482 }
483 }
test_timeslice_threadB1_entry(void * parameter)484 static void test_timeslice_threadB1_entry(void *parameter)
485 {
486 while (1)
487 {
488 timeslice_cntB1++;
489 if (timeslice_cntA > 10) return;
490 }
491 }
test_timeslice_threadB2_entry(void * parameter)492 static void test_timeslice_threadB2_entry(void *parameter)
493 {
494 while (1)
495 {
496 timeslice_cntB2++;
497 if (timeslice_cntA > 10) return;
498 }
499 }
500
test_timeslice(void)501 void test_timeslice(void)
502 {
503 rt_err_t ret_startup = -RT_ERROR;
504 uint32_t diff;
505
506 timeslice_cntA = 0;
507 timeslice_cntB1 = 0;
508 timeslice_cntB2 = 0;
509
510 tidA = rt_thread_create("timeslice", test_timeslice_threadA_entry, RT_NULL,
511 2048, UTEST_THR_PRIORITY + 1, 10);
512 if (!tidA)
513 {
514 LOG_E("rt_thread_create failed!");
515 return;
516 }
517
518 rt_thread_control(tidA, RT_THREAD_CTRL_BIND_CPU, (void *)1);
519 ret_startup = rt_thread_startup(tidA);
520 if (ret_startup != RT_EOK)
521 {
522 LOG_E("rt_thread_startup failed!");
523 uassert_false(1);
524 return ;
525 }
526
527 tidB1 = rt_thread_create("timeslice", test_timeslice_threadB1_entry, RT_NULL,
528 2048, UTEST_THR_PRIORITY + 2, 2);
529 if (!tidB1)
530 {
531 LOG_E("rt_thread_create failed!");
532 return;
533 }
534
535 rt_thread_control(tidB1, RT_THREAD_CTRL_BIND_CPU, (void *)1);
536 ret_startup = rt_thread_startup(tidB1);
537 if (ret_startup != RT_EOK)
538 {
539 LOG_E("rt_thread_startup failed!");
540 uassert_false(1);
541 return ;
542 }
543
544 tidB2 = rt_thread_create("timeslice", test_timeslice_threadB2_entry, RT_NULL,
545 2048, UTEST_THR_PRIORITY + 2, 2);
546 if (!tidB2)
547 {
548 LOG_E("rt_thread_create failed!");
549 return;
550 }
551
552 rt_thread_control(tidB2, RT_THREAD_CTRL_BIND_CPU, (void *)1);
553 ret_startup = rt_thread_startup(tidB2);
554 if (ret_startup != RT_EOK)
555 {
556 LOG_E("rt_thread_startup failed!");
557 uassert_false(1);
558 return ;
559 }
560 do{
561 rt_thread_delay(2 * 20);
562 }while(timeslice_cntA <= 10);
563
564 rt_kprintf("A:%d,B1:%d,B2:%d\n", timeslice_cntA, timeslice_cntB1, timeslice_cntB2);
565 diff = abs(timeslice_cntB1 - timeslice_cntB2);
566 uassert_true(diff * 100 / timeslice_cntB1 < 30);
567 uassert_true(timeslice_cntA == 11);
568 }
569
570 #ifndef RT_USING_SMP
571 static volatile rt_uint32_t yield_count;
572
test_thread_yield_inc_entry(void * parameter)573 static void test_thread_yield_inc_entry(void *parameter)
574 {
575 rt_uint32_t loop = 0;
576
577 while (1)
578 {
579 if (loop++ > 10001)
580 break;
581 yield_count++;
582 rt_thread_yield();
583 }
584 }
585
test_thread_yield_entry(void * parameter)586 static void test_thread_yield_entry(void *parameter)
587 {
588 rt_err_t ret_startup = -RT_ERROR;
589
590 rt_thread_t tid;
591 rt_uint32_t loop = 0;
592 rt_uint32_t count_before;
593
594 tid = rt_thread_create("inc", test_thread_yield_inc_entry, RT_NULL,
595 2048, 1, 10);
596 if (!tid)
597 {
598 LOG_E("rt_thread_create failed!");
599 return;
600 }
601
602 ret_startup = rt_thread_startup(tid);
603 if (ret_startup != RT_EOK)
604 {
605 LOG_E("rt_thread_startup failed!");
606 uassert_false(1);
607 return ;
608 }
609
610 while (1)
611 {
612 if (loop++ > 10000)
613 break;
614
615 count_before = yield_count;
616 rt_thread_yield();
617 if (yield_count == count_before)
618 {
619 LOG_E("yield error!");
620 return;
621 }
622 }
623 thread_yield_flag = 1;
624 }
625
test_thread_yield_nosmp(void)626 void test_thread_yield_nosmp(void)
627 {
628 rt_err_t ret_startup = -RT_ERROR;
629
630 rt_thread_t tid;
631
632 yield_count = 0;
633
634 tid = rt_thread_create("chkcnt", test_thread_yield_entry, RT_NULL,
635 2048, 1, 10);
636 if (!tid)
637 {
638 LOG_E("rt_thread_create failed!");
639 return;
640 }
641
642 ret_startup = rt_thread_startup(tid);
643 if (ret_startup != RT_EOK)
644 {
645 LOG_E("rt_thread_startup failed!");
646 uassert_false(1);
647 return ;
648 }
649
650 uassert_true(thread_yield_flag == 1);
651 }
652
653 // static rt_uint32_t thread9_count = 0;
654 // static void thread9_entry(void *parameter)
655 // {
656 // while (1)
657 // {
658 // thread9_count ++;
659 // }
660
661 // }
662 // static void test_thread_suspend(void)
663 // {
664 // static rt_thread_t tid;
665 // rt_err_t ret_startup = -RT_ERROR;
666 // uint32_t count_before_suspend, count_before_resume, count_after_resume;
667 // tid = rt_thread_create("thread9",
668 // thread9_entry,
669 // RT_NULL,
670 // THREAD_STACK_SIZE,
671 // UTEST_THR_PRIORITY + 1,
672 // THREAD_TIMESLICE);
673 // if (tid == RT_NULL)
674 // {
675 // LOG_E("rt_thread_create failed!");
676 // uassert_false(tid4 == RT_NULL);
677 // goto __exit;
678 // }
679
680 // ret_startup = rt_thread_startup(tid);
681 // if (ret_startup != RT_EOK)
682 // {
683 // LOG_E("rt_thread_startup failed!");
684 // uassert_false(1);
685 // goto __exit;
686 // }
687 // rt_thread_delay(5);
688 // rt_thread_suspend(tid);
689 // count_before_suspend = thread9_count;
690 // uassert_true(count_before_suspend != 0);
691 // rt_thread_delay(5);
692 // count_before_resume = thread9_count;
693 // uassert_true(count_before_suspend == count_before_resume);
694 // rt_thread_resume(tid);
695 // rt_thread_delay(5);
696 // count_after_resume = thread9_count;
697 // uassert_true(count_after_resume != count_before_resume);
698
699 // __exit:
700 // if (tid != RT_NULL)
701 // {
702 // rt_thread_delete(tid);
703 // }
704 // return;
705 // }
706 #endif
707
utest_tc_init(void)708 static rt_err_t utest_tc_init(void)
709 {
710 __current_thread = rt_thread_self();
711 change_priority = UTEST_THR_PRIORITY + 5;
712 tid3_delay_pass_flag = 0;
713 tid3_finish_flag = 0;
714 tid4_finish_flag = 0;
715 tid6_finish_flag = 0;
716 entry_idle_hook_times = 0;
717 count = 0;
718 return RT_EOK;
719 }
720
utest_tc_cleanup(void)721 static rt_err_t utest_tc_cleanup(void)
722 {
723 return RT_EOK;
724 }
725
testcase(void)726 static void testcase(void)
727 {
728 /* init, detach */
729 UTEST_UNIT_RUN(test_static_thread);
730 /* create, delete */
731 UTEST_UNIT_RUN(test_dynamic_thread);
732 /* delay */
733 UTEST_UNIT_RUN(test_thread_delay);
734 /* idle_sethook, idle_delhook */
735 UTEST_UNIT_RUN(test_idle_hook);
736 /* yield */
737 UTEST_UNIT_RUN(test_thread_yield);
738 #ifndef RT_USING_SMP
739 /* yield_nosmp */
740 UTEST_UNIT_RUN(test_thread_yield_nosmp);
741 /* suspend, resume */
742 // UTEST_UNIT_RUN(test_thread_suspend);
743 #endif
744 /* control */
745 UTEST_UNIT_RUN(test_thread_control);
746 UTEST_UNIT_RUN(test_thread_priority);
747 /* delay_until */
748 UTEST_UNIT_RUN(test_delay_until);
749 /* timeslice */
750 // UTEST_UNIT_RUN(test_timeslice); /* Can not running in Github Action QEMU */
751 }
752
753
754 UTEST_TC_EXPORT(testcase, "testcases.kernel.thread_tc", utest_tc_init, utest_tc_cleanup, 1000);
755
756 /********************* end of file ************************/
757