1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
/*
* 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.
* 2025-12-12 lhxj Add standardized utest documentation block
*/
/**
* Test Case Name: Scheduler Thread Stability Test (core.scheduler_thread)
*
* Test Objectives:
* - Verify the stability of the scheduler during intensive context switching.
* - Test the interaction between `rt_thread_suspend` and `rt_thread_resume` within critical sections.
* - Verify scheduler robustness in multi-core environments (using `RT_CPUS_NR`) ensuring no deadlocks or race conditions occur during thread state transitions.
* - List specific functions or APIs to be tested:
* - rt_thread_create
* - rt_thread_startup
* - rt_thread_suspend
* - rt_thread_resume
* - rt_enter_critical / rt_exit_critical_safe
* - rt_atomic_add
*
* Test Scenarios:
* - **Multi-threaded Ping-Pong Context Switching:**
* 1. Initialize a semaphore `_thr_exit_sem` for completion synchronization.
* 2. Create `TEST_THREAD_COUNT` pairs of threads (based on `RT_CPUS_NR`).
* 3. Each thread pair performs a "ping-pong" operation in a loop (100,000 iterations):
* - Thread A enters critical section, suspends self, resumes Thread B, exits critical section.
* - Thread B enters critical section, suspends self, resumes Thread A, exits critical section.
* 4. An atomic counter `_progress_counter` tracks execution progress, triggering `uassert_true` at intervals.
* 5. The main test thread waits for all worker threads to signal completion via the semaphore.
*
* Verification Metrics:
* - **Pass:** All created threads complete their execution loops without system hangs or crashes.
* - **Pass:** The progress counter increments as expected, validating thread execution flow.
*
* Dependencies:
* - Hardware requirements (e.g., specific peripherals)
* - No specific peripherals required.
* (This is met by the qemu-virt64-riscv BSP).
* - Software configuration (e.g., kernel options, driver initialization)
* - `RT_USING_UTEST` must be enabled (`RT-Thread Utestcases`).
* - `Scheduler Test` must be enabled (`RT-Thread Utestcases` -> `Kernel Core` -> 'Scheduler Test').
* - (Optional) Enable SMP for parallel testing:
* - Go to `RT-Thread Kernel` -> `Enable SMP (Symmetric multiprocessing)`.
* - Set `Number of CPUs` to > 1 (e.g., 4).
* - Environmental assumptions
* - `UTEST_THR_STACK_SIZE` is sufficient for the test threads.
* - Run the test case from the msh prompt:
* `utest_run core.scheduler_thread`
*
* Expected Results:
* - The test proceeds through multiple loops of thread suspension and resumption.
* - Final Output: `[ PASSED ] [ result ] testcase (core.scheduler_thread)`
*/
#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, "core.scheduler_thread", utest_tc_init, utest_tc_cleanup, 10);
|