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
|
/*
* 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
* 2025-12-12 lhxj Add standardized utest documentation block
*/
/**
* Test Case Name: Scheduler Mutex Stress Test (core.scheduler_mutex)
*
* Test Objectives:
* - Validate the stability of the Mutex subsystem under high contention (Stress Test).
* - Ensure priority inheritance (if supported) or basic blocking/waking works correctly.
* - In multi-core systems, verify data consistency and spinlock mechanisms when multiple cores contend for a single kernel object simultaneously.
* - List specific functions or APIs to be tested:
* - rt_mutex_take()
* - rt_mutex_release()
* - rt_thread_create()
*
* Test Scenarios:
* - **Stress Test (mutex_stress_tc):**
* 1. Create `RT_CPUS_NR` threads (e.g., 4 threads in a Quad-Core setup).
* 2. Assign **staggered priorities** to these threads (`priority_base + i % range`) to simulate contention between high and low priority tasks.
* 3. All tester threads execute a tight loop: attempting to take and immediately release the *same* global mutex (`_racing_lock`).
* - In SMP, this simulates true parallel contention.
* 4. The main test thread sleeps for `TEST_SECONDS` (30s), periodically printing progress.
* 5. After time is up, signal threads to exit (`_exit_flag`) and wait for them using a semaphore (`_thr_exit_sem`).
*
* Verification Metrics:
* - **Pass:** The system must remain responsive (no deadlocks, hard faults, or RCU stalls) during the 30-second run.
* - **Pass:** The main thread must successfully wait for all tester threads to exit (`rt_sem_take` returns `RT_EOK`).
* - **Pass:** `uassert_true(1)` is executed periodically, confirming the main loop is alive.
*
* Dependencies:
* - Hardware requirements (e.g., specific peripherals)
* - No specific peripherals required, but Multi-core CPU recommended for SMP testing.
* (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
* - Requires sufficient heap memory to allocate stacks for `RT_CPUS_NR` threads.
* - Run the test case from the msh prompt:
* `utest_run core.scheduler_mutex`
*
* Expected Results:
* - The test continues for approximately 30 seconds.
* - The console logs periodic success assertions.
* - Final Output: `[ PASSED ] [ result ] testcase (core.scheduler_mutex)`
*/
#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, "core.scheduler_mutex", utest_tc_init, utest_tc_cleanup, TEST_SECONDS);
|