From 71560bafb5735220696ca24a42ba4a668b55949b Mon Sep 17 00:00:00 2001 From: Shell Date: Fri, 23 Feb 2024 17:49:15 +0800 Subject: 🎯 Sync smart & scheduler codes (#8537) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Shell Co-authored-by: xqyjlj --- examples/utest/testcases/kernel/Kconfig | 4 + examples/utest/testcases/kernel/SConscript | 7 + examples/utest/testcases/kernel/mutex_tc.c | 9 +- examples/utest/testcases/kernel/sched_mtx_tc.c | 107 ++++++++++ examples/utest/testcases/kernel/sched_sem_tc.c | 196 +++++++++++++++++ examples/utest/testcases/kernel/sched_thread_tc.c | 121 +++++++++++ .../utest/testcases/kernel/sched_timed_mtx_tc.c | 232 +++++++++++++++++++++ .../utest/testcases/kernel/sched_timed_sem_tc.c | 149 +++++++++++++ examples/utest/testcases/kernel/signal_tc.c | 16 +- examples/utest/testcases/kernel/thread_tc.c | 45 ++-- 10 files changed, 862 insertions(+), 24 deletions(-) create mode 100644 examples/utest/testcases/kernel/sched_mtx_tc.c create mode 100644 examples/utest/testcases/kernel/sched_sem_tc.c create mode 100644 examples/utest/testcases/kernel/sched_thread_tc.c create mode 100644 examples/utest/testcases/kernel/sched_timed_mtx_tc.c create mode 100644 examples/utest/testcases/kernel/sched_timed_sem_tc.c (limited to 'examples') diff --git a/examples/utest/testcases/kernel/Kconfig b/examples/utest/testcases/kernel/Kconfig index 988f13f43f..072e1876a3 100644 --- a/examples/utest/testcases/kernel/Kconfig +++ b/examples/utest/testcases/kernel/Kconfig @@ -69,4 +69,8 @@ config UTEST_MTSAFE_KPRINT_TC bool "mtsafe kprint test" default n +config UTEST_SCHEDULER_TC + bool "scheduler test" + default n + endmenu diff --git a/examples/utest/testcases/kernel/SConscript b/examples/utest/testcases/kernel/SConscript index 598b92251a..67ad659d1c 100644 --- a/examples/utest/testcases/kernel/SConscript +++ b/examples/utest/testcases/kernel/SConscript @@ -50,6 +50,13 @@ if GetDepend(['UTEST_HOOKLIST_TC']): if GetDepend(['UTEST_MTSAFE_KPRINT_TC']): src += ['mtsafe_kprint_tc.c'] +# Stressful testcase for scheduler (MP/UP) +if GetDepend(['UTEST_SCHEDULER_TC']): + src += ['sched_timed_sem_tc.c'] + src += ['sched_timed_mtx_tc.c'] + src += ['sched_mtx_tc.c'] + src += ['sched_sem_tc.c', 'sched_thread_tc.c'] + group = DefineGroup('utestcases', src, depend = ['RT_USING_UTESTCASES'], CPPPATH = CPPPATH) Return('group') diff --git a/examples/utest/testcases/kernel/mutex_tc.c b/examples/utest/testcases/kernel/mutex_tc.c index 8e714610b1..d5ec9095e4 100644 --- a/examples/utest/testcases/kernel/mutex_tc.c +++ b/examples/utest/testcases/kernel/mutex_tc.c @@ -8,15 +8,16 @@ * 2021-09.01 luckyzjq the first version * 2023-09-15 xqyjlj change stack size in cpu64 */ +#define __RT_IPC_SOURCE__ #include #include #include "utest.h" #ifdef ARCH_CPU_64BIT -#define THREAD_STACKSIZE 4096 +#define THREAD_STACKSIZE 8192 #else -#define THREAD_STACKSIZE 1024 +#define THREAD_STACKSIZE 4096 #endif static struct rt_mutex static_mutex; @@ -241,7 +242,7 @@ static void static_thread1_entry(void *param) /* thread3 hode mutex thread2 take mutex */ /* check thread2 and thread3 priority */ - if (tid2->current_priority != tid3->current_priority) + if (RT_SCHED_PRIV(tid2).current_priority != RT_SCHED_PRIV(tid3).current_priority) { uassert_true(RT_FALSE); } @@ -550,7 +551,7 @@ static void dynamic_thread1_entry(void *param) /* thread3 hode mutex thread2 take mutex */ /* check thread2 and thread3 priority */ - if (tid2->current_priority != tid3->current_priority) + if (RT_SCHED_PRIV(tid2).current_priority != RT_SCHED_PRIV(tid3).current_priority) { uassert_true(RT_FALSE); } diff --git a/examples/utest/testcases/kernel/sched_mtx_tc.c b/examples/utest/testcases/kernel/sched_mtx_tc.c new file mode 100644 index 0000000000..488485a39f --- /dev/null +++ b/examples/utest/testcases/kernel/sched_mtx_tc.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-01-17 Shell the first version + */ +#include +#include +#include "utest.h" + +/** + * Stressful Test for Mutex + */ + +#define TEST_SECONDS 30 +#define TEST_LOOP_TICKS (TEST_SECONDS * RT_TICK_PER_SECOND) +#define TEST_THREAD_COUNTS (RT_CPUS_NR) +#define TEST_PROGRESS_COUNTS (36) +#define TEST_PROGRESS_ON (TEST_LOOP_TICKS/TEST_PROGRESS_COUNTS) +#define TEST_PRIORITY_HIGHEST (UTEST_THR_PRIORITY+1) +#define TEST_RANDOM_LATENCY_MAX (1000 * 1000) + +static struct rt_semaphore _thr_exit_sem; +static rt_atomic_t _progress_counter; +static rt_atomic_t _exit_flag; +static struct rt_mutex _racing_lock; + +static void test_thread_entry(void *param) +{ + while (1) + { + rt_mutex_take(&_racing_lock, RT_WAITING_FOREVER); + rt_mutex_release(&_racing_lock); + + if (rt_atomic_load(&_exit_flag)) + { + break; + } + } + + rt_sem_release(&_thr_exit_sem); +} + +static void mutex_stress_tc(void) +{ + rt_err_t error; + rt_thread_t tester; + const rt_base_t priority_base = TEST_PRIORITY_HIGHEST; + + for (size_t i = 0; i < TEST_THREAD_COUNTS; i++) + { + tester = rt_thread_create( + "tester", + test_thread_entry, + (void *)0, + UTEST_THR_STACK_SIZE, + priority_base + (i % (RT_THREAD_PRIORITY_MAX - TEST_PRIORITY_HIGHEST)), + 1); + + rt_thread_startup(tester); + } + + for (size_t i = 0; i < TEST_LOOP_TICKS; i++) + { + rt_thread_delay(1); + + if (rt_atomic_add(&_progress_counter, 1) % TEST_PROGRESS_ON == 0) + uassert_true(1); + } + + /* trigger exit request for all sub-threads */ + rt_atomic_store(&_exit_flag, 1); + + /* waiting for sub-threads to exit */ + for (size_t i = 0; i < TEST_THREAD_COUNTS; i++) + { + error = rt_sem_take(&_thr_exit_sem, RT_WAITING_FOREVER); + uassert_int_equal(error, RT_EOK); + } +} + +static rt_err_t utest_tc_init(void) +{ + int *pseed = rt_malloc(sizeof(int)); + srand(*(int *)pseed); + rt_free(pseed); + + rt_sem_init(&_thr_exit_sem, "test", 0, RT_IPC_FLAG_PRIO); + rt_mutex_init(&_racing_lock, "ipc", RT_IPC_FLAG_PRIO); + return RT_EOK; +} + +static rt_err_t utest_tc_cleanup(void) +{ + rt_sem_detach(&_thr_exit_sem); + rt_mutex_detach(&_racing_lock); + return RT_EOK; +} + +static void testcase(void) +{ + UTEST_UNIT_RUN(mutex_stress_tc); +} +UTEST_TC_EXPORT(testcase, "testcases.kernel.scheduler.mutex", utest_tc_init, utest_tc_cleanup, TEST_SECONDS); diff --git a/examples/utest/testcases/kernel/sched_sem_tc.c b/examples/utest/testcases/kernel/sched_sem_tc.c new file mode 100644 index 0000000000..a6302daba2 --- /dev/null +++ b/examples/utest/testcases/kernel/sched_sem_tc.c @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-01-17 Shell the first version + */ +#define __RT_IPC_SOURCE__ + +#include +#include "rthw.h" +#include "utest.h" + +#define KERN_TEST_CONFIG_LOOP_TIMES 160 +#define KERN_TEST_CONCURRENT_THREADS (RT_CPUS_NR * 2) +#define KERN_TEST_CONFIG_HIGHEST_PRIO 3 +#define KERN_TEST_CONFIG_LOWEST_PRIO (RT_THREAD_PRIORITY_MAX - 2) + +#define TEST_LEVEL_COUNTS (KERN_TEST_CONFIG_LOWEST_PRIO - KERN_TEST_CONFIG_HIGHEST_PRIO + 1) +#if TEST_LEVEL_COUNTS <= RT_CPUS_NR +#warning for the best of this test, TEST_LEVEL_COUNTS should greater than RT_CPUS_NR +#endif +#if KERN_TEST_CONCURRENT_THREADS < RT_CPUS_NR +#warning for the best of this test, KERN_TEST_CONCURRENT_THREADS should greater than RT_CPUS_NR +#endif +#if KERN_TEST_CONFIG_LOWEST_PRIO >= RT_THREAD_PRIORITY_MAX - 1 +#error the thread priority should at least be greater than idle +#endif + +static rt_atomic_t _star_counter = 1; +static struct rt_semaphore _thr_exit_sem; +static struct rt_semaphore _level_waiting[TEST_LEVEL_COUNTS]; +static rt_thread_t _thread_matrix[TEST_LEVEL_COUNTS][KERN_TEST_CONCURRENT_THREADS]; +static rt_atomic_t _load_average[RT_CPUS_NR]; + +static void _print_char(rt_thread_t thr_self, int character) +{ + rt_base_t current_counter; + +#ifdef RT_USING_SMP + rt_kprintf("%c%d", character, RT_SCHED_CTX(thr_self).oncpu); +#else + rt_kprintf("%c0", character); +#endif /* RT_USING_SMP */ + + current_counter = rt_atomic_add(&_star_counter, 1); + if (current_counter % 30 == 0) + { + rt_kprintf("\n"); + } +} + +static void _stats_load_avg_inc(void) +{ + int cpuid; + + cpuid = rt_hw_cpu_id(); + rt_atomic_add(&_load_average[cpuid], 1); +} + +static void _stats_load_avg_print(void) +{ + rt_base_t counts = 0; + const rt_base_t total_test_counts = KERN_TEST_CONFIG_LOOP_TIMES * TEST_LEVEL_COUNTS * KERN_TEST_CONCURRENT_THREADS; + + for (size_t i = 0; i < RT_CPUS_NR; i++) + { + rt_kprintf("%ld ", _load_average[i]); + counts += _load_average[i]; + } + + rt_kprintf("\n"); + uassert_int_equal(counts, total_test_counts); +} + +static void _thread_entry(void *param) +{ + int level = (rt_ubase_t)param; + rt_thread_t thr_self = rt_thread_self(); + + if (level == 0) + { + /* always the first to execute among other working threads */ + for (size_t i = 0; i < KERN_TEST_CONFIG_LOOP_TIMES; i++) + { + /* notify our consumer */ + rt_sem_release(&_level_waiting[level + 1]); + + _stats_load_avg_inc(); + + /* waiting for resource of ours */ + rt_sem_take(&_level_waiting[level], RT_WAITING_FOREVER); + } + } + else if (level == TEST_LEVEL_COUNTS - 1) + { + + for (size_t i = 0; i < KERN_TEST_CONFIG_LOOP_TIMES; i++) + { + /* waiting for our resource first */ + rt_sem_take(&_level_waiting[level], RT_WAITING_FOREVER); + + _stats_load_avg_inc(); + + _print_char(thr_self, '*'); + + rt_thread_delay(1); + + /* produce for level 0 worker */ + rt_sem_release(&_level_waiting[0]); + } + } + else + { + for (size_t i = 0; i < KERN_TEST_CONFIG_LOOP_TIMES; i++) + { + /* waiting for resource of ours */ + rt_sem_take(&_level_waiting[level], RT_WAITING_FOREVER); + + _stats_load_avg_inc(); + + /* notify our consumer */ + rt_sem_release(&_level_waiting[level + 1]); + } + } + + uassert_true(1); + rt_sem_release(&_thr_exit_sem); + + return; +} + +static void scheduler_tc(void) +{ + LOG_I("Test starts..."); + for (size_t i = 0; i < TEST_LEVEL_COUNTS; i++) + { + for (size_t j = 0; j < KERN_TEST_CONCURRENT_THREADS; j++) + { + rt_thread_startup(_thread_matrix[i][j]); + } + } + LOG_I("%d threads startup...", TEST_LEVEL_COUNTS * KERN_TEST_CONCURRENT_THREADS); + + /* waiting for sub-threads to exit */ + for (size_t i = 0; i < TEST_LEVEL_COUNTS * KERN_TEST_CONCURRENT_THREADS; i++) + { + rt_sem_take(&_thr_exit_sem, RT_WAITING_FOREVER); + } + + /* print load average */ + _stats_load_avg_print(); +} + +static rt_err_t utest_tc_init(void) +{ + LOG_I("Setup environment..."); + rt_sem_init(&_thr_exit_sem, "test", 0, RT_IPC_FLAG_PRIO); + + for (size_t i = 0; i < TEST_LEVEL_COUNTS; i++) + { + rt_sem_init(&_level_waiting[i], "test", 0, RT_IPC_FLAG_PRIO); + + for (size_t j = 0; j < KERN_TEST_CONCURRENT_THREADS; j++) + { + _thread_matrix[i][j] = + rt_thread_create("test", + _thread_entry, + (void *)i, + UTEST_THR_STACK_SIZE, + KERN_TEST_CONFIG_HIGHEST_PRIO+i, + 5); + if (!_thread_matrix[i][j]) + uassert_not_null(_thread_matrix[i][j]); + } + } + return RT_EOK; +} + +static rt_err_t utest_tc_cleanup(void) +{ + rt_sem_detach(&_thr_exit_sem); + for (size_t i = 0; i < TEST_LEVEL_COUNTS; i++) + { + rt_sem_detach(&_level_waiting[i]); + } + return RT_EOK; +} + +static void testcase(void) +{ + UTEST_UNIT_RUN(scheduler_tc); +} +UTEST_TC_EXPORT(testcase, "testcases.kernel.scheduler.sem", utest_tc_init, utest_tc_cleanup, 10); diff --git a/examples/utest/testcases/kernel/sched_thread_tc.c b/examples/utest/testcases/kernel/sched_thread_tc.c new file mode 100644 index 0000000000..d978e51ecc --- /dev/null +++ b/examples/utest/testcases/kernel/sched_thread_tc.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-01-25 Shell init ver. + */ +#define __RT_KERNEL_SOURCE__ +#include +#include "utest.h" + +#define TEST_LOOP_TIMES (100 * 1000) +#define TEST_PROGRESS_COUNTS (36) +#define TEST_THREAD_COUNT (RT_CPUS_NR * 1) +#define TEST_PROGRESS_ON (TEST_LOOP_TIMES*TEST_THREAD_COUNT/TEST_PROGRESS_COUNTS) + +static struct rt_semaphore _thr_exit_sem; +static rt_atomic_t _progress_counter; + +static volatile rt_thread_t threads_group[TEST_THREAD_COUNT][2]; + +static void _thread_entry1(void *param) +{ + rt_base_t critical_level; + size_t idx = (size_t)param; + + for (size_t i = 0; i < TEST_LOOP_TIMES; i++) + { + critical_level = rt_enter_critical(); + + rt_thread_suspend(rt_thread_self()); + rt_thread_resume(threads_group[idx][1]); + + rt_exit_critical_safe(critical_level); + + if (rt_atomic_add(&_progress_counter, 1) % TEST_PROGRESS_ON == 0) + uassert_true(1); + } + + rt_sem_release(&_thr_exit_sem); + return; +} + +static void _thread_entry2(void *param) +{ + rt_base_t critical_level; + size_t idx = (size_t)param; + + for (size_t i = 0; i < TEST_LOOP_TIMES; i++) + { + critical_level = rt_enter_critical(); + + rt_thread_suspend(rt_thread_self()); + rt_thread_resume(threads_group[idx][0]); + + rt_exit_critical_safe(critical_level); + + if (rt_atomic_add(&_progress_counter, 1) % TEST_PROGRESS_ON == 0) + uassert_true(1); + } + + rt_sem_release(&_thr_exit_sem); + return; +} + +static void scheduler_tc(void) +{ + for (size_t i = 0; i < TEST_THREAD_COUNT; i++) + { + rt_thread_t t1 = + rt_thread_create( + "t1", + _thread_entry1, + (void *)i, + UTEST_THR_STACK_SIZE, + UTEST_THR_PRIORITY + 1, + 100); + rt_thread_t t2 = + rt_thread_create( + "t2", + _thread_entry2, + (void *)i, + UTEST_THR_STACK_SIZE, + UTEST_THR_PRIORITY + 1, + 100); + + threads_group[i][0] = t1; + threads_group[i][1] = t2; + } + + for (size_t i = 0; i < TEST_THREAD_COUNT; i++) + { + rt_thread_startup(threads_group[i][0]); + rt_thread_startup(threads_group[i][1]); + } + + for (size_t i = 0; i < TEST_THREAD_COUNT; i++) + { + rt_sem_take(&_thr_exit_sem, RT_WAITING_FOREVER); + } +} + +static rt_err_t utest_tc_init(void) +{ + rt_sem_init(&_thr_exit_sem, "test", 0, RT_IPC_FLAG_PRIO); + return RT_EOK; +} + +static rt_err_t utest_tc_cleanup(void) +{ + rt_sem_detach(&_thr_exit_sem); + return RT_EOK; +} + +static void testcase(void) +{ + UTEST_UNIT_RUN(scheduler_tc); +} +UTEST_TC_EXPORT(testcase, "testcases.kernel.scheduler.thread", utest_tc_init, utest_tc_cleanup, 10); diff --git a/examples/utest/testcases/kernel/sched_timed_mtx_tc.c b/examples/utest/testcases/kernel/sched_timed_mtx_tc.c new file mode 100644 index 0000000000..f03202a2dc --- /dev/null +++ b/examples/utest/testcases/kernel/sched_timed_mtx_tc.c @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-01-25 Shell init ver. + */ +#define __RT_KERNEL_SOURCE__ +#include +#include +#include "utest.h" + +#define TEST_SECONDS 10 +#define TEST_LOOP_TICKS (TEST_SECONDS * RT_TICK_PER_SECOND) +#define TEST_PROGRESS_COUNTS (36) +#define TEST_PROGRESS_ON (TEST_LOOP_TICKS*2/TEST_PROGRESS_COUNTS) + +static struct rt_semaphore _thr_exit_sem; +static struct rt_mutex _ipc_primitive; +static struct rt_semaphore _cons_can_take_mtx; +static struct rt_semaphore _prod_can_take_mtx; +static rt_atomic_t _progress_counter; +#define CONSUMER_MAGIC 0x11223344 +#define PRODUCER_MAGIC 0x44332211 +static rt_atomic_t _last_holder_flag = CONSUMER_MAGIC; +static rt_base_t _timedout_failed_times = 0; + +/** + * Test on timedout IPC with racing condition where timedout routine and producer + * thread may race to wakeup sleeper. + * + * This test will fork 2 thread, one producer and one consumer. The producer will + * looping and trigger the IPC on the edge of new tick arrives. The consumer will + * wait on IPC with a timedout of 1 tick. + */ + +static void _wait_until_edge(void) +{ + rt_tick_t entry_level, current; + rt_base_t random_latency; + + entry_level = rt_tick_get(); + do + { + current = rt_tick_get(); + } + while (current == entry_level); + + /* give a random latency for test */ + random_latency = rand() % 1000 * 1000; + entry_level = current; + for (size_t i = 0; i < random_latency; i++) + { + current = rt_tick_get(); + if (current != entry_level) + break; + } +} + +static void _producer_entry(void *param) +{ + rt_err_t error; + for (size_t i = 0; i < TEST_LOOP_TICKS; i++) + { + /** + * only try to take mutex after consumer have taken it after last + * release from us. + */ + error = rt_sem_take(&_prod_can_take_mtx, RT_WAITING_FOREVER); + if (error) + { + uassert_true(0); + break; + } + + error = rt_mutex_take(&_ipc_primitive, RT_WAITING_FOREVER); + if (error) + { + uassert_true(0); + break; + } + + /* ensure that mutex should be held in round-robin method */ + if (rt_atomic_load(&_last_holder_flag) != CONSUMER_MAGIC) + { + uassert_true(0); + break; + } + else + { + rt_atomic_store(&_last_holder_flag, PRODUCER_MAGIC); + rt_sem_release(&_cons_can_take_mtx); + } + + _wait_until_edge(); + + rt_mutex_release(&_ipc_primitive); + + if (rt_atomic_add(&_progress_counter, 1) % TEST_PROGRESS_ON == 0) + uassert_true(1); + } + + rt_sem_release(&_thr_exit_sem); + return; +} + +static void _consumer_entry(void *param) +{ + rt_err_t error; + for (size_t i = 0; i < TEST_LOOP_TICKS; i++) + { + /** + * only try to take mutex after producer have taken it after last + * release from us. + */ + error = rt_sem_take(&_cons_can_take_mtx, RT_WAITING_FOREVER); + if (error) + { + uassert_true(0); + break; + } + + while (1) + { + error = rt_mutex_take_interruptible(&_ipc_primitive, 1); + if (error == -RT_ETIMEOUT) + { + _timedout_failed_times++; + if (rt_mutex_get_owner(&_ipc_primitive) == rt_thread_self()) + { + uassert_true(0); + break; + } + } + else + { + break; + } + } + + if (error != RT_EOK) + { + uassert_true(0); + break; + } + + /* ensure that mutex should be held in round-robin method */ + if (rt_atomic_load(&_last_holder_flag) != PRODUCER_MAGIC) + { + uassert_true(0); + break; + } + else + { + rt_atomic_store(&_last_holder_flag, CONSUMER_MAGIC); + rt_sem_release(&_prod_can_take_mtx); + } + + rt_mutex_release(&_ipc_primitive); + if (rt_mutex_get_owner(&_ipc_primitive) == rt_thread_self()) + { + uassert_true(0); + break; + } + + if (rt_atomic_add(&_progress_counter, 1) % TEST_PROGRESS_ON == 0) + uassert_true(1); + } + + rt_sem_release(&_thr_exit_sem); + return; +} + +static void timed_mtx_tc(void) +{ + rt_thread_t prod = rt_thread_create( + "prod", + _producer_entry, + (void *)0, + UTEST_THR_STACK_SIZE, + UTEST_THR_PRIORITY + 1, + 4); + + rt_thread_t cons = rt_thread_create( + "cons", + _consumer_entry, + (void *)0, + UTEST_THR_STACK_SIZE, + UTEST_THR_PRIORITY + 1, + 100); + + rt_thread_startup(prod); + rt_thread_startup(cons); + + for (size_t i = 0; i < 2; i++) + { + uassert_int_equal( + rt_sem_take(&_thr_exit_sem, 2 * TEST_LOOP_TICKS), + RT_EOK); + } + + /* Summary */ + LOG_I("Total failed times: %ld(in %d)\n", _timedout_failed_times, TEST_LOOP_TICKS); +} + +static rt_err_t utest_tc_init(void) +{ + _timedout_failed_times = 0; + + rt_mutex_init(&_ipc_primitive, "ipc", RT_IPC_FLAG_PRIO); + rt_sem_init(&_cons_can_take_mtx, "test", 0, RT_IPC_FLAG_PRIO); + rt_sem_init(&_prod_can_take_mtx, "test", 1, RT_IPC_FLAG_PRIO); + rt_sem_init(&_thr_exit_sem, "test", 0, RT_IPC_FLAG_PRIO); + return RT_EOK; +} + +static rt_err_t utest_tc_cleanup(void) +{ + rt_mutex_detach(&_ipc_primitive); + rt_sem_detach(&_cons_can_take_mtx); + rt_sem_detach(&_prod_can_take_mtx); + rt_sem_detach(&_thr_exit_sem); + return RT_EOK; +} + +static void testcase(void) +{ + UTEST_UNIT_RUN(timed_mtx_tc); +} +UTEST_TC_EXPORT(testcase, "testcases.kernel.scheduler.timed_mtx", utest_tc_init, utest_tc_cleanup, TEST_SECONDS * 2); diff --git a/examples/utest/testcases/kernel/sched_timed_sem_tc.c b/examples/utest/testcases/kernel/sched_timed_sem_tc.c new file mode 100644 index 0000000000..6dbe3f51cc --- /dev/null +++ b/examples/utest/testcases/kernel/sched_timed_sem_tc.c @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-01-25 Shell init ver. + */ +#define __RT_KERNEL_SOURCE__ +#include +#include +#include "utest.h" + +#define TEST_SECONDS 10 +#define TEST_LOOP_TICKS (TEST_SECONDS * RT_TICK_PER_SECOND) +#define TEST_PROGRESS_COUNTS (36) +#define TEST_PROGRESS_ON (TEST_LOOP_TICKS*2/TEST_PROGRESS_COUNTS) + +static struct rt_semaphore _thr_exit_sem; +static struct rt_semaphore _ipc_sem; +static rt_atomic_t _progress_counter; +static rt_base_t _timedout_failed_times = 0; + +/** + * Test on timedout IPC with racing condition where timedout routine and producer + * thread may race to wakeup sleeper. + * + * This test will fork 2 thread, one producer and one consumer. The producer will + * looping and trigger the IPC on the edge of new tick arrives. The consumer will + * wait on IPC with a timedout of 1 tick. + */ + +static void _wait_until_edge(void) +{ + rt_tick_t entry_level, current; + rt_base_t random_latency; + + entry_level = rt_tick_get(); + do + { + current = rt_tick_get(); + } + while (current == entry_level); + + /* give a random latency for test */ + random_latency = rand(); + entry_level = current; + for (size_t i = 0; i < random_latency; i++) + { + current = rt_tick_get(); + if (current != entry_level) + break; + } +} + +static void _producer_entry(void *param) +{ + for (size_t i = 0; i < TEST_LOOP_TICKS; i++) + { + _wait_until_edge(); + + rt_sem_release(&_ipc_sem); + + if (rt_atomic_add(&_progress_counter, 1) % TEST_PROGRESS_ON == 0) + uassert_true(1); + } + + rt_sem_release(&_thr_exit_sem); + return; +} + +static void _consumer_entry(void *param) +{ + int error; + for (size_t i = 0; i < TEST_LOOP_TICKS; i++) + { + error = rt_sem_take_interruptible(&_ipc_sem, 1); + if (error == -RT_ETIMEOUT) + { + _timedout_failed_times++; + } + else + { + if (error != RT_EOK) + uassert_true(0); + } + + if (rt_atomic_add(&_progress_counter, 1) % TEST_PROGRESS_ON == 0) + uassert_true(1); + } + + rt_sem_release(&_thr_exit_sem); + return; +} + +static void timed_sem_tc(void) +{ + rt_thread_t prod = rt_thread_create( + "prod", + _producer_entry, + (void *)0, + UTEST_THR_STACK_SIZE, + UTEST_THR_PRIORITY + 1, + 4); + + rt_thread_t cons = rt_thread_create( + "cons", + _consumer_entry, + (void *)0, + UTEST_THR_STACK_SIZE, + UTEST_THR_PRIORITY + 1, + 100); + + rt_thread_startup(prod); + rt_thread_startup(cons); + + for (size_t i = 0; i < 2; i++) + { + rt_sem_take(&_thr_exit_sem, RT_WAITING_FOREVER); + } + + /* Summary */ + LOG_I("Total failed times: %ld(in %d)\n", _timedout_failed_times, TEST_LOOP_TICKS); +} + +static rt_err_t utest_tc_init(void) +{ + int *pseed = rt_malloc(sizeof(int)); + srand(*(int *)pseed); + rt_free(pseed); + + rt_sem_init(&_ipc_sem, "ipc", 0, RT_IPC_FLAG_PRIO); + rt_sem_init(&_thr_exit_sem, "test", 0, RT_IPC_FLAG_PRIO); + return RT_EOK; +} + +static rt_err_t utest_tc_cleanup(void) +{ + rt_sem_detach(&_ipc_sem); + rt_sem_detach(&_thr_exit_sem); + return RT_EOK; +} + +static void testcase(void) +{ + UTEST_UNIT_RUN(timed_sem_tc); +} +UTEST_TC_EXPORT(testcase, "testcases.kernel.scheduler.timed_sem", utest_tc_init, utest_tc_cleanup, TEST_SECONDS * 2); diff --git a/examples/utest/testcases/kernel/signal_tc.c b/examples/utest/testcases/kernel/signal_tc.c index f23aa09124..b19739d65f 100644 --- a/examples/utest/testcases/kernel/signal_tc.c +++ b/examples/utest/testcases/kernel/signal_tc.c @@ -22,7 +22,8 @@ #include #include "utest.h" -int recive_sig = 0; +static volatile int recive_sig = 0; +static struct rt_semaphore _received_signal; void sig_handle_default(int signo) { @@ -125,12 +126,15 @@ void rt_signal_wait_thread(void *parm) (void)sigaddset(&selectset, SIGUSR1); /* case 5:rt_signal_wait, two thread, thread1: install and unmask, then wait 1s; thread2: kill, should received. */ - if (rt_signal_wait(&selectset, &recive_si, RT_TICK_PER_SECOND) != RT_EOK) + if (rt_signal_wait((void *)&selectset, &recive_si, RT_TICK_PER_SECOND) != RT_EOK) { return; } recive_sig = recive_si.si_signo; + + LOG_I("received signal %d", recive_sig); + rt_sem_release(&_received_signal); } static void rt_signal_wait_test(void) @@ -147,7 +151,7 @@ static void rt_signal_wait_test(void) rt_thread_mdelay(1); /* case 5:rt_signal_wait, two thread, thread1: install and unmask, then wait 1s; thread2: kill, should received. */ uassert_int_equal(rt_thread_kill(t1, SIGUSR1), RT_EOK); - rt_thread_mdelay(1); + rt_sem_take(&_received_signal, RT_WAITING_FOREVER); uassert_int_equal(recive_sig, SIGUSR1); return; @@ -167,7 +171,9 @@ static void rt_signal_wait_test2(void) /* case 6:rt_signal_wait, two thread, thread1: install and unmask, then wait 1s; thread2: sleep 2s then kill, should can't received. */ rt_thread_mdelay(2000); uassert_int_equal(rt_thread_kill(t1, SIGUSR1), RT_EOK); - rt_thread_mdelay(1); + uassert_int_not_equal( + rt_sem_take(&_received_signal, 1), + RT_EOK); uassert_int_not_equal(recive_sig, SIGUSR1); return; @@ -175,11 +181,13 @@ static void rt_signal_wait_test2(void) static rt_err_t utest_tc_init(void) { + rt_sem_init(&_received_signal, "utest", 0, RT_IPC_FLAG_PRIO); return RT_EOK; } static rt_err_t utest_tc_cleanup(void) { + rt_sem_detach(&_received_signal); return RT_EOK; } diff --git a/examples/utest/testcases/kernel/thread_tc.c b/examples/utest/testcases/kernel/thread_tc.c index 03e1bb928f..7bea397b03 100644 --- a/examples/utest/testcases/kernel/thread_tc.c +++ b/examples/utest/testcases/kernel/thread_tc.c @@ -9,6 +9,8 @@ * 2021-10.11 mazhiyuan add idle, yield, suspend, control, priority, delay_until */ +#define __RT_IPC_SOURCE__ /* include internal API for utest */ + #include #include #include "utest.h" @@ -56,7 +58,7 @@ static void test_dynamic_thread(void) thread1_entry, (void *)1, THREAD_STACK_SIZE, - __current_thread->current_priority + 1, + UTEST_THR_PRIORITY + 1, THREAD_TIMESLICE - 5); if (tid1 == RT_NULL) { @@ -105,7 +107,7 @@ static void test_static_thread(void) (void *)2, &thread2_stack[0], sizeof(thread2_stack), - __current_thread->current_priority + 1, + UTEST_THR_PRIORITY + 1, THREAD_TIMESLICE); if (ret_init != RT_EOK) { @@ -139,10 +141,11 @@ __exit: static void thread3_entry(void *parameter) { - rt_tick_t tick; + rt_tick_t tick, latency_tick; tick = rt_tick_get(); rt_thread_delay(15); - if (rt_tick_get() - tick > 16) + latency_tick = rt_tick_get() - tick; + if (latency_tick > 16 || latency_tick < 15) { tid3_finish_flag = 1; tid3_delay_pass_flag = 0; @@ -160,7 +163,7 @@ static void test_thread_delay(void) thread3_entry, RT_NULL, THREAD_STACK_SIZE, - __current_thread->current_priority - 1, + UTEST_THR_PRIORITY - 1, THREAD_TIMESLICE); if (tid3 == RT_NULL) { @@ -210,7 +213,7 @@ static void test_idle_hook(void) thread4_entry, RT_NULL, THREAD_STACK_SIZE, - __current_thread->current_priority - 1, + UTEST_THR_PRIORITY - 1, THREAD_TIMESLICE); if (tid4 == RT_NULL) { @@ -264,7 +267,7 @@ static void test_thread_yield(void) thread5_entry, RT_NULL, THREAD_STACK_SIZE, - __current_thread->current_priority - 1, + UTEST_THR_PRIORITY - 1, THREAD_TIMESLICE); if (tid5 == RT_NULL) { @@ -283,7 +286,7 @@ static void test_thread_yield(void) thread6_entry, RT_NULL, THREAD_STACK_SIZE, - __current_thread->current_priority - 1, + UTEST_THR_PRIORITY - 1, THREAD_TIMESLICE); if (tid6 == RT_NULL) { @@ -319,12 +322,13 @@ static void test_thread_control(void) { rt_err_t ret_control = -RT_ERROR; rt_err_t rst_delete = -RT_ERROR; + rt_sched_lock_level_t slvl; tid7 = rt_thread_create("thread7", thread7_entry, RT_NULL, THREAD_STACK_SIZE, - __current_thread->current_priority + 1, + UTEST_THR_PRIORITY + 1, THREAD_TIMESLICE); if (tid7 == RT_NULL) { @@ -342,12 +346,17 @@ static void test_thread_control(void) } rt_thread_mdelay(200); rt_thread_control(tid7, RT_THREAD_CTRL_CHANGE_PRIORITY, &change_priority); - if (tid7->current_priority != change_priority) + + rt_sched_lock(&slvl); + if (rt_sched_thread_get_curr_prio(tid7) != change_priority) { LOG_E("rt_thread_control failed!"); uassert_false(1); + rt_sched_unlock(slvl); goto __exit; } + rt_sched_unlock(slvl); + rst_delete = rt_thread_control(tid7, RT_THREAD_CTRL_CLOSE, RT_NULL); if (rst_delete != RT_EOK) { @@ -380,7 +389,7 @@ static void test_thread_priority(void) thread8_entry, RT_NULL, THREAD_STACK_SIZE, - __current_thread->current_priority - 1, + UTEST_THR_PRIORITY - 1, THREAD_TIMESLICE); if (tid8 == RT_NULL) { @@ -448,6 +457,10 @@ static void test_delay_until(void) rt_kprintf("delta[20] -> %d\n", delta); uassert_int_equal(delta, 20); + /** + * the rt_kprints above can take few ticks to complete, maybe more than 10 + */ + tick = rt_tick_get(); check_tick = tick; rt_thread_delay(2); rt_thread_delay_until(&tick, 10); @@ -495,7 +508,7 @@ void test_timeslice(void) timeslice_cntB2 = 0; tidA = rt_thread_create("timeslice", test_timeslice_threadA_entry, RT_NULL, - 2048, __current_thread->current_priority + 1, 10); + 2048, UTEST_THR_PRIORITY + 1, 10); if (!tidA) { LOG_E("rt_thread_create failed!"); @@ -512,7 +525,7 @@ void test_timeslice(void) } tidB1 = rt_thread_create("timeslice", test_timeslice_threadB1_entry, RT_NULL, - 2048, __current_thread->current_priority + 2, 2); + 2048, UTEST_THR_PRIORITY + 2, 2); if (!tidB1) { LOG_E("rt_thread_create failed!"); @@ -529,7 +542,7 @@ void test_timeslice(void) } tidB2 = rt_thread_create("timeslice", test_timeslice_threadB2_entry, RT_NULL, - 2048, __current_thread->current_priority + 2, 2); + 2048, UTEST_THR_PRIORITY + 2, 2); if (!tidB2) { LOG_E("rt_thread_create failed!"); @@ -655,7 +668,7 @@ void test_thread_yield_nosmp(void) // thread9_entry, // RT_NULL, // THREAD_STACK_SIZE, -// __current_thread->current_priority + 1, +// UTEST_THR_PRIORITY + 1, // THREAD_TIMESLICE); // if (tid == RT_NULL) // { @@ -695,7 +708,7 @@ void test_thread_yield_nosmp(void) static rt_err_t utest_tc_init(void) { __current_thread = rt_thread_self(); - change_priority = __current_thread->current_priority + 5; + change_priority = UTEST_THR_PRIORITY + 5; tid3_delay_pass_flag = 0; tid3_finish_flag = 0; tid4_finish_flag = 0; -- cgit v1.3.1