summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorShell <[email protected]>2024-02-23 17:49:15 +0800
committerGitHub <[email protected]>2024-02-23 17:49:15 +0800
commit71560bafb5735220696ca24a42ba4a668b55949b (patch)
tree236df5973a1fd5b6f0cc9f6c6b6a2d71fd069203 /examples
parent6fe69d7431ed54fcea863ed68d7c4f9746e771b0 (diff)
🎯 Sync smart & scheduler codes (#8537)
Signed-off-by: Shell <[email protected]> Co-authored-by: xqyjlj <[email protected]>
Diffstat (limited to 'examples')
-rw-r--r--examples/utest/testcases/kernel/Kconfig4
-rw-r--r--examples/utest/testcases/kernel/SConscript7
-rw-r--r--examples/utest/testcases/kernel/mutex_tc.c9
-rw-r--r--examples/utest/testcases/kernel/sched_mtx_tc.c107
-rw-r--r--examples/utest/testcases/kernel/sched_sem_tc.c196
-rw-r--r--examples/utest/testcases/kernel/sched_thread_tc.c121
-rw-r--r--examples/utest/testcases/kernel/sched_timed_mtx_tc.c232
-rw-r--r--examples/utest/testcases/kernel/sched_timed_sem_tc.c149
-rw-r--r--examples/utest/testcases/kernel/signal_tc.c16
-rw-r--r--examples/utest/testcases/kernel/thread_tc.c45
10 files changed, 862 insertions, 24 deletions
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 <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);
}
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 <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);
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 <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);
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 <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);
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 <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);
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 <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);
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 <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;
}
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 <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;