/* * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2021-09.01 yangjie the firet version * 2021-10.11 mazhiyuan add idle, yield, suspend, control, priority, delay_until */ #include #include #include "utest.h" #define THREAD_STACK_SIZE 512 #define THREAD_TIMESLICE 10 rt_align(RT_ALIGN_SIZE) static char thread2_stack[1024]; static struct rt_thread thread2; #ifdef RT_USING_HEAP static rt_thread_t tid1 = RT_NULL; static rt_thread_t tid3 = RT_NULL; static rt_thread_t tid4 = RT_NULL; static rt_thread_t tid5 = RT_NULL; static rt_thread_t tid6 = RT_NULL; static rt_thread_t tid7 = RT_NULL; #endif /* RT_USING_HEAP */ static volatile rt_uint32_t tid3_delay_pass_flag = 0; static volatile rt_uint32_t tid3_finish_flag = 0; static volatile rt_uint32_t tid4_finish_flag = 0; static volatile rt_uint32_t tid6_finish_flag = 0; static volatile rt_uint32_t thread5_source = 0; #ifndef RT_USING_SMP static rt_uint32_t thread_yield_flag = 0; #endif static rt_uint32_t entry_idle_hook_times = 0; static rt_thread_t __current_thread; static rt_uint8_t change_priority; static rt_uint32_t count = 0; void thread1_entry(void *param) { while (1); } static void test_dynamic_thread(void) { rt_err_t ret_startup = -RT_ERROR; rt_err_t ret_delete = -RT_ERROR; tid1 = rt_thread_create("thread1", thread1_entry, (void *)1, THREAD_STACK_SIZE, __current_thread->current_priority + 1, THREAD_TIMESLICE - 5); if (tid1 == RT_NULL) { uassert_false(tid1 == RT_NULL); goto __exit; } ret_startup = rt_thread_startup(tid1); if (ret_startup != RT_EOK) { uassert_false(ret_startup != RT_EOK); goto __exit; } ret_delete = rt_thread_delete(tid1); if (ret_delete != RT_EOK) { uassert_false(ret_delete != RT_EOK); goto __exit; } uassert_true(tid1 != RT_NULL && ret_startup == RT_EOK && ret_delete == RT_EOK); __exit: if (tid1 != RT_NULL && ret_delete != RT_EOK) { rt_thread_delete(tid1); } return; } void thread2_entry(void *param) { while (1); } static void test_static_thread(void) { rt_err_t ret_init = -RT_ERROR; rt_err_t ret_startup = -RT_ERROR; rt_err_t ret_detach = -RT_ERROR; ret_init = rt_thread_init(&thread2, "thread2", thread2_entry, (void *)2, &thread2_stack[0], sizeof(thread2_stack), __current_thread->current_priority + 1, THREAD_TIMESLICE); if (ret_init != RT_EOK) { uassert_false(ret_init != RT_EOK); goto __exit; } ret_startup = rt_thread_startup(&thread2); if (ret_startup != RT_EOK) { uassert_false(ret_startup != RT_EOK); goto __exit; } ret_detach = rt_thread_detach(&thread2); if (ret_detach != RT_EOK) { uassert_false(ret_detach != RT_EOK); goto __exit; } uassert_true(ret_init == RT_EOK && ret_startup == RT_EOK && ret_detach == RT_EOK); __exit: if (ret_init == RT_EOK && ret_detach != RT_EOK) { rt_thread_detach(&thread2); } return; } static void thread3_entry(void *parameter) { rt_tick_t tick; tick = rt_tick_get(); rt_thread_delay(15); if (rt_tick_get() - tick > 16) { tid3_finish_flag = 1; tid3_delay_pass_flag = 0; return; } tid3_delay_pass_flag = 1; tid3_finish_flag = 1; } static void test_thread_delay(void) { rt_err_t ret_startup = -RT_ERROR; tid3 = rt_thread_create("thread3", thread3_entry, RT_NULL, THREAD_STACK_SIZE, __current_thread->current_priority - 1, THREAD_TIMESLICE); if (tid3 == RT_NULL) { LOG_E("rt_thread_create failed!"); uassert_false(tid3 == RT_NULL); goto __exit; } ret_startup = rt_thread_startup(tid3); if (ret_startup != RT_EOK) { LOG_E("rt_thread_startup failed!"); uassert_false(1); goto __exit; } while (tid3_finish_flag != 1); uassert_true(tid3_delay_pass_flag == 1); __exit: return; } static void idle_hook(void) { entry_idle_hook_times ++; } static void thread4_entry(void *parameter) { rt_uint32_t delay_times = 5; while (delay_times --) { rt_thread_mdelay(300); } rt_thread_idle_delhook(idle_hook); tid4_finish_flag = 1; } static void test_idle_hook(void) { rt_err_t ret_startup = -RT_ERROR; rt_thread_idle_sethook(idle_hook); tid4 = rt_thread_create("thread4", thread4_entry, RT_NULL, THREAD_STACK_SIZE, __current_thread->current_priority - 1, THREAD_TIMESLICE); if (tid4 == RT_NULL) { LOG_E("rt_thread_create failed!"); uassert_false(tid4 == RT_NULL); goto __exit; } ret_startup = rt_thread_startup(tid4); if (ret_startup != RT_EOK) { LOG_E("rt_thread_startup failed!"); uassert_false(1); goto __exit; } while (tid4_finish_flag != 1) { rt_thread_mdelay(200); } uassert_true(entry_idle_hook_times > 0); __exit: return; } static void thread5_entry(void *parameter) { while (1) { thread5_source ++; rt_thread_delay(5); if (thread5_source == 5) { rt_thread_yield(); } } } static void thread6_entry(void *parameter) { while (++ thread5_source <= 9); tid6_finish_flag = 1; } static void test_thread_yield(void) { rt_err_t ret_startup = -RT_ERROR; thread5_source = 0; tid5 = rt_thread_create("thread5", thread5_entry, RT_NULL, THREAD_STACK_SIZE, __current_thread->current_priority - 1, THREAD_TIMESLICE); if (tid5 == RT_NULL) { LOG_E("rt_thread_create failed!"); uassert_false(tid5 == RT_NULL); goto __exit; } ret_startup = rt_thread_startup(tid5); if (ret_startup != RT_EOK) { LOG_E("rt_thread_startup failed!"); uassert_false(1); goto __exit; } tid6 = rt_thread_create("thread6", thread6_entry, RT_NULL, THREAD_STACK_SIZE, __current_thread->current_priority - 1, THREAD_TIMESLICE); if (tid6 == RT_NULL) { LOG_E("rt_thread_create failed!"); uassert_false(tid6 == RT_NULL); goto __exit; } ret_startup = rt_thread_startup(tid6); if (ret_startup != RT_EOK) { LOG_E("rt_thread_startup failed!"); uassert_false(1); goto __exit; } while (tid6_finish_flag != 1); uassert_true(thread5_source == 10); __exit: if (tid5 != RT_NULL) { rt_thread_delete(tid5); } return; } static void thread7_entry(void *parameter) { while (1); } static void test_thread_control(void) { rt_err_t ret_control = -RT_ERROR; rt_err_t rst_delete = -RT_ERROR; tid7 = rt_thread_create("thread7", thread7_entry, RT_NULL, THREAD_STACK_SIZE, __current_thread->current_priority + 1, THREAD_TIMESLICE); if (tid7 == RT_NULL) { LOG_E("rt_thread_create failed!"); uassert_false(tid7 == RT_NULL); goto __exit; } ret_control = rt_thread_control(tid7, RT_THREAD_CTRL_STARTUP, RT_NULL); if (ret_control != RT_EOK) { LOG_E("rt_thread_control failed!"); uassert_false(1); goto __exit; } rt_thread_mdelay(200); rt_thread_control(tid7, RT_THREAD_CTRL_CHANGE_PRIORITY, &change_priority); if (tid7->current_priority != change_priority) { LOG_E("rt_thread_control failed!"); uassert_false(1); goto __exit; } rst_delete = rt_thread_control(tid7, RT_THREAD_CTRL_CLOSE, RT_NULL); if (rst_delete != RT_EOK) { LOG_E("rt_thread_control failed!"); uassert_false(rst_delete != RT_EOK); goto __exit; } uassert_true(1); __exit: if (tid7 != RT_NULL && rst_delete != RT_EOK) { rt_thread_delete(tid7); } return; } static void thread8_entry(void *parameter) { for (; count < 10; count ++); } static void test_thread_priority(void) { rt_err_t ret_startup = -RT_ERROR; rt_thread_t tid8 = RT_NULL; tid8 = rt_thread_create("thread8", thread8_entry, RT_NULL, THREAD_STACK_SIZE, __current_thread->current_priority - 1, THREAD_TIMESLICE); if (tid8 == RT_NULL) { LOG_E("rt_thread_create failed!"); uassert_false(tid8 == RT_NULL); return; } count = 0; ret_startup = rt_thread_startup(tid8); if (ret_startup != RT_EOK) { uassert_false(ret_startup != RT_EOK); return ; } uassert_true(count == 10); return; } static void test_delay_until(void) { rt_tick_t tick; rt_tick_t check_tick = 0; rt_tick_t delta = 0; tick = rt_tick_get(); check_tick = tick; rt_thread_delay_until(&tick, 100); delta = rt_tick_get() - check_tick; rt_kprintf("delta[100] -> %d\n", delta); uassert_int_equal(delta, 100); check_tick = tick; rt_thread_delay(2); rt_thread_delay_until(&tick, 200); delta = rt_tick_get() - check_tick; rt_kprintf("delta[200] -> %d\n", delta); uassert_int_equal(delta, 200); check_tick = tick; rt_thread_delay(2); rt_thread_delay_until(&tick, 300); delta = rt_tick_get() - check_tick; rt_kprintf("delta[300] -> %d\n", delta); uassert_int_equal(delta, 300); check_tick = tick; rt_thread_delay(2); rt_thread_delay_until(&tick, 100); delta = rt_tick_get() - check_tick; uassert_int_equal(delta, 100); check_tick = tick; rt_thread_delay(2); rt_thread_delay_until(&tick, 50); delta = rt_tick_get() - check_tick; rt_kprintf("delta[50] -> %d\n", delta); uassert_int_equal(delta, 50); check_tick = tick; rt_thread_delay(2); rt_thread_delay_until(&tick, 20); delta = rt_tick_get() - check_tick; rt_kprintf("delta[20] -> %d\n", delta); uassert_int_equal(delta, 20); check_tick = tick; rt_thread_delay(2); rt_thread_delay_until(&tick, 10); delta = rt_tick_get() - check_tick; rt_kprintf("delta[10] -> %d\n", delta); uassert_int_equal(delta, 10); } static rt_thread_t tidA, tidB1, tidB2; static uint32_t timeslice_cntA, timeslice_cntB1, timeslice_cntB2; static void test_timeslice_threadA_entry(void *parameter) { while (1) { rt_thread_delay(2); timeslice_cntA++; if (timeslice_cntA > 10) return; } } static void test_timeslice_threadB1_entry(void *parameter) { while (1) { timeslice_cntB1++; if (timeslice_cntA > 10) return; } } static void test_timeslice_threadB2_entry(void *parameter) { while (1) { timeslice_cntB2++; if (timeslice_cntA > 10) return; } } void test_timeslice(void) { rt_err_t ret_startup = -RT_ERROR; uint32_t diff; timeslice_cntA = 0; timeslice_cntB1 = 0; timeslice_cntB2 = 0; tidA = rt_thread_create("timeslice", test_timeslice_threadA_entry, RT_NULL, 2048, __current_thread->current_priority + 1, 10); if (!tidA) { LOG_E("rt_thread_create failed!"); return; } rt_thread_control(tidA, RT_THREAD_CTRL_BIND_CPU, (void *)1); ret_startup = rt_thread_startup(tidA); if (ret_startup != RT_EOK) { LOG_E("rt_thread_startup failed!"); uassert_false(1); return ; } tidB1 = rt_thread_create("timeslice", test_timeslice_threadB1_entry, RT_NULL, 2048, __current_thread->current_priority + 2, 2); if (!tidB1) { LOG_E("rt_thread_create failed!"); return; } rt_thread_control(tidB1, RT_THREAD_CTRL_BIND_CPU, (void *)1); ret_startup = rt_thread_startup(tidB1); if (ret_startup != RT_EOK) { LOG_E("rt_thread_startup failed!"); uassert_false(1); return ; } tidB2 = rt_thread_create("timeslice", test_timeslice_threadB2_entry, RT_NULL, 2048, __current_thread->current_priority + 2, 2); if (!tidB2) { LOG_E("rt_thread_create failed!"); return; } rt_thread_control(tidB2, RT_THREAD_CTRL_BIND_CPU, (void *)1); ret_startup = rt_thread_startup(tidB2); if (ret_startup != RT_EOK) { LOG_E("rt_thread_startup failed!"); uassert_false(1); return ; } do{ rt_thread_delay(2 * 20); }while(timeslice_cntA <= 10); rt_kprintf("A:%d,B1:%d,B2:%d\n", timeslice_cntA, timeslice_cntB1, timeslice_cntB2); diff = abs(timeslice_cntB1 - timeslice_cntB2); uassert_true(diff * 100 / timeslice_cntB1 < 30); uassert_true(timeslice_cntA == 11); } #ifndef RT_USING_SMP static volatile rt_uint32_t yield_count; static void test_thread_yield_inc_entry(void *parameter) { rt_uint32_t loop = 0; while (1) { if (loop++ > 10001) break; yield_count++; rt_thread_yield(); } } static void test_thread_yield_entry(void *parameter) { rt_err_t ret_startup = -RT_ERROR; rt_thread_t tid; rt_uint32_t loop = 0; rt_uint32_t count_before; tid = rt_thread_create("inc", test_thread_yield_inc_entry, RT_NULL, 2048, 1, 10); if (!tid) { LOG_E("rt_thread_create failed!"); return; } ret_startup = rt_thread_startup(tid); if (ret_startup != RT_EOK) { LOG_E("rt_thread_startup failed!"); uassert_false(1); return ; } while (1) { if (loop++ > 10000) break; count_before = yield_count; rt_thread_yield(); if (yield_count == count_before) { LOG_E("yield error!"); return; } } thread_yield_flag = 1; } void test_thread_yield_nosmp(void) { rt_err_t ret_startup = -RT_ERROR; rt_thread_t tid; yield_count = 0; tid = rt_thread_create("chkcnt", test_thread_yield_entry, RT_NULL, 2048, 1, 10); if (!tid) { LOG_E("rt_thread_create failed!"); return; } ret_startup = rt_thread_startup(tid); if (ret_startup != RT_EOK) { LOG_E("rt_thread_startup failed!"); uassert_false(1); return ; } uassert_true(thread_yield_flag == 1); } // static rt_uint32_t thread9_count = 0; // static void thread9_entry(void *parameter) // { // while (1) // { // thread9_count ++; // } // } // static void test_thread_suspend(void) // { // static rt_thread_t tid; // rt_err_t ret_startup = -RT_ERROR; // uint32_t count_before_suspend, count_before_resume, count_after_resume; // tid = rt_thread_create("thread9", // thread9_entry, // RT_NULL, // THREAD_STACK_SIZE, // __current_thread->current_priority + 1, // THREAD_TIMESLICE); // if (tid == RT_NULL) // { // LOG_E("rt_thread_create failed!"); // uassert_false(tid4 == RT_NULL); // goto __exit; // } // ret_startup = rt_thread_startup(tid); // if (ret_startup != RT_EOK) // { // LOG_E("rt_thread_startup failed!"); // uassert_false(1); // goto __exit; // } // rt_thread_delay(5); // rt_thread_suspend(tid); // count_before_suspend = thread9_count; // uassert_true(count_before_suspend != 0); // rt_thread_delay(5); // count_before_resume = thread9_count; // uassert_true(count_before_suspend == count_before_resume); // rt_thread_resume(tid); // rt_thread_delay(5); // count_after_resume = thread9_count; // uassert_true(count_after_resume != count_before_resume); // __exit: // if (tid != RT_NULL) // { // rt_thread_delete(tid); // } // return; // } #endif static rt_err_t utest_tc_init(void) { __current_thread = rt_thread_self(); change_priority = __current_thread->current_priority + 5; tid3_delay_pass_flag = 0; tid3_finish_flag = 0; tid4_finish_flag = 0; tid6_finish_flag = 0; entry_idle_hook_times = 0; count = 0; return RT_EOK; } static rt_err_t utest_tc_cleanup(void) { return RT_EOK; } static void testcase(void) { /* init, detach */ UTEST_UNIT_RUN(test_static_thread); /* create, delete */ UTEST_UNIT_RUN(test_dynamic_thread); /* delay */ UTEST_UNIT_RUN(test_thread_delay); /* idle_sethook, idle_delhook */ UTEST_UNIT_RUN(test_idle_hook); /* yield */ UTEST_UNIT_RUN(test_thread_yield); #ifndef RT_USING_SMP /* yield_nosmp */ UTEST_UNIT_RUN(test_thread_yield_nosmp); /* suspend, resume */ // UTEST_UNIT_RUN(test_thread_suspend); #endif /* control */ UTEST_UNIT_RUN(test_thread_control); UTEST_UNIT_RUN(test_thread_priority); /* delay_until */ UTEST_UNIT_RUN(test_delay_until); /* timeslice */ // UTEST_UNIT_RUN(test_timeslice); /* Can not running in Github Action QEMU */ } UTEST_TC_EXPORT(testcase, "testcases.kernel.thread_tc", utest_tc_init, utest_tc_cleanup, 1000); /********************* end of file ************************/