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