mirror of
https://github.com/RT-Thread/rt-thread.git
synced 2026-09-21 10:36:04 +08:00
🎯 Sync smart & scheduler codes (#8537)
Signed-off-by: Shell <smokewood@qq.com> Co-authored-by: xqyjlj <xqyjlj@126.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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 <rtthread.h>
|
||||
#include <stdlib.h>
|
||||
#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);
|
||||
}
|
||||
|
||||
@@ -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 <rtthread.h>
|
||||
#include <stdlib.h>
|
||||
#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);
|
||||
@@ -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 <rtthread.h>
|
||||
#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);
|
||||
@@ -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 <rtthread.h>
|
||||
#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);
|
||||
@@ -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 <rtthread.h>
|
||||
#include <stdlib.h>
|
||||
#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);
|
||||
@@ -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 <rtthread.h>
|
||||
#include <stdlib.h>
|
||||
#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);
|
||||
@@ -22,7 +22,8 @@
|
||||
#include <rtthread.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <rtthread.h>
|
||||
#include <stdlib.h>
|
||||
#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;
|
||||
|
||||
Reference in New Issue
Block a user