Skip to content

Commit fa0e6e8

Browse files
committed
[add][thread]Add api:rt_thread_suspend_any allow suspend other threads.
1 parent 98a12f1 commit fa0e6e8

File tree

4 files changed

+389
-14
lines changed

4 files changed

+389
-14
lines changed

examples/utest/testcases/kernel/SConscript

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ if GetDepend(['UTEST_MAILBOX_TC']):
4444
if GetDepend(['UTEST_THREAD_TC']):
4545
src += ['thread_tc.c']
4646
src += ['thread_overflow_tc.c']
47+
src += ['thread_suspend_tc.c']
4748

4849
if GetDepend(['UTEST_DEVICE_TC']):
4950
src += ['device_tc.c']
Lines changed: 345 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
1+
/*
2+
* Copyright (c) 2025, RT-Thread Development Team
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
* Change Logs:
7+
* Date Author Notes
8+
* 2025-09-02 Rbb666 utest case for rt_thread_suspend_any comprehensive tests
9+
*/
10+
11+
#include <rtthread.h>
12+
#include <rtdevice.h>
13+
#include "utest.h"
14+
15+
#define THREAD_STACK_SIZE 1024
16+
#define THREAD_TIMESLICE 5
17+
#define TEST_THREAD_PRIORITY 25
18+
19+
/* Global variables for normal usage test */
20+
static rt_thread_t target_thread = RT_NULL;
21+
static rt_thread_t monitor_thread = RT_NULL;
22+
static rt_sem_t sync_sem = RT_NULL;
23+
static volatile rt_uint32_t work_counter = 0;
24+
static volatile rt_bool_t suspend_test_done = RT_FALSE;
25+
static volatile rt_bool_t suspend_success = RT_FALSE;
26+
static volatile rt_bool_t resume_success = RT_FALSE;
27+
28+
/* Global variables for deadlock test */
29+
static rt_mutex_t test_mutex = RT_NULL;
30+
static rt_thread_t holder_thread = RT_NULL;
31+
static rt_thread_t waiter_thread = RT_NULL;
32+
static volatile rt_uint32_t shared_counter = 0;
33+
static volatile rt_bool_t holder_got_mutex = RT_FALSE;
34+
static volatile rt_bool_t deadlock_detected = RT_FALSE;
35+
static volatile rt_bool_t test_completed = RT_FALSE;
36+
static volatile rt_bool_t thread_started = RT_FALSE;
37+
static volatile rt_bool_t thread_should_exit = RT_FALSE;
38+
39+
/* Target work thread - the thread to be suspended */
40+
static void target_work_thread(void *parameter)
41+
{
42+
while (!suspend_test_done)
43+
{
44+
work_counter++;
45+
/* Yield CPU appropriately to simulate normal work */
46+
rt_thread_mdelay(10);
47+
}
48+
}
49+
50+
/* Monitor thread - responsible for suspending and resuming target thread */
51+
static void monitor_control_thread(void *parameter)
52+
{
53+
rt_uint32_t counter_before, counter_after;
54+
55+
/* Wait for target thread to start working */
56+
rt_thread_mdelay(300);
57+
58+
/* Record counter value before suspend */
59+
counter_before = work_counter;
60+
61+
/* Use rt_thread_suspend_any to suspend target thread */
62+
if (rt_thread_suspend_any(target_thread, RT_UNINTERRUPTIBLE) == RT_EOK)
63+
{
64+
suspend_success = RT_TRUE;
65+
66+
/* Trigger scheduling to ensure thread is suspended */
67+
rt_schedule();
68+
69+
/* Wait for a while to verify thread is indeed suspended */
70+
rt_thread_mdelay(500);
71+
72+
counter_after = work_counter;
73+
74+
/* Verify thread is indeed suspended (counter should stop changing) */
75+
if (counter_after == counter_before)
76+
{
77+
/* Resume target thread */
78+
if (rt_thread_resume(target_thread) == RT_EOK)
79+
{
80+
resume_success = RT_TRUE;
81+
/* Wait for a while to verify thread resumes work */
82+
rt_thread_mdelay(200);
83+
}
84+
}
85+
}
86+
87+
/* End test */
88+
suspend_test_done = RT_TRUE;
89+
90+
/* Send semaphore to notify test completion */
91+
rt_sem_release(sync_sem);
92+
}
93+
94+
/* Thread that holds the mutex */
95+
static void mutex_holder_thread(void *parameter)
96+
{
97+
if (rt_mutex_take(test_mutex, RT_WAITING_FOREVER) == RT_EOK)
98+
{
99+
holder_got_mutex = RT_TRUE;
100+
101+
/* Simulate critical section work */
102+
for (int i = 0; i < 1000 && !test_completed; i++)
103+
{
104+
shared_counter++;
105+
if (i % 200 == 0)
106+
{
107+
rt_thread_mdelay(10);
108+
}
109+
}
110+
111+
if (!test_completed)
112+
{
113+
rt_mutex_release(test_mutex);
114+
}
115+
}
116+
rt_kprintf("Holder thread exiting\n");
117+
}
118+
119+
/* Thread that waits for the mutex */
120+
static void mutex_waiter_thread(void *parameter)
121+
{
122+
/* Wait a bit to ensure holder gets mutex first */
123+
rt_thread_mdelay(50);
124+
125+
rt_err_t result = rt_mutex_take(test_mutex, rt_tick_from_millisecond(1500));
126+
if (result == RT_EOK)
127+
{
128+
shared_counter += 1000;
129+
rt_mutex_release(test_mutex);
130+
}
131+
else
132+
{
133+
/* Timeout indicates deadlock - holder is suspended and cannot release lock */
134+
deadlock_detected = RT_TRUE;
135+
rt_kprintf("Deadlock detected: waiter timeout (holder suspended with mutex)\n");
136+
}
137+
}
138+
139+
void simple_thread_entry(void *param)
140+
{
141+
volatile rt_bool_t *flag = (volatile rt_bool_t *)param;
142+
*flag = RT_TRUE;
143+
144+
/* Wait for exit signal */
145+
while (!thread_should_exit)
146+
{
147+
rt_thread_mdelay(10);
148+
}
149+
}
150+
151+
/* Test normal usage of rt_thread_suspend_any function */
152+
static void test_suspend_any_normal_usage(void)
153+
{
154+
/* Reset global variables */
155+
work_counter = 0;
156+
suspend_test_done = RT_FALSE;
157+
suspend_success = RT_FALSE;
158+
resume_success = RT_FALSE;
159+
160+
/* Create synchronization semaphore */
161+
sync_sem = rt_sem_create("sync", 0, RT_IPC_FLAG_FIFO);
162+
uassert_not_null(sync_sem);
163+
164+
/* Create target work thread */
165+
target_thread = rt_thread_create("target",
166+
target_work_thread,
167+
RT_NULL,
168+
THREAD_STACK_SIZE,
169+
TEST_THREAD_PRIORITY,
170+
THREAD_TIMESLICE);
171+
172+
uassert_not_null(target_thread);
173+
174+
/* Create monitor thread */
175+
monitor_thread = rt_thread_create("monitor",
176+
monitor_control_thread,
177+
RT_NULL,
178+
THREAD_STACK_SIZE,
179+
UTEST_THR_PRIORITY,
180+
THREAD_TIMESLICE);
181+
182+
uassert_not_null(monitor_thread);
183+
184+
/* Start threads */
185+
rt_thread_startup(target_thread);
186+
rt_thread_startup(monitor_thread);
187+
188+
/* Wait for test completion */
189+
rt_sem_take(sync_sem, RT_WAITING_FOREVER);
190+
191+
/* Wait for a while to ensure threads exit normally */
192+
rt_thread_mdelay(100);
193+
194+
/* Verify test results */
195+
uassert_true(suspend_success);
196+
uassert_true(resume_success);
197+
uassert_true(work_counter > 0);
198+
199+
/* Clean up resources */
200+
if (sync_sem)
201+
{
202+
rt_sem_delete(sync_sem);
203+
sync_sem = RT_NULL;
204+
}
205+
206+
target_thread = RT_NULL;
207+
monitor_thread = RT_NULL;
208+
}
209+
210+
/* Basic API test */
211+
static void test_suspend_any_api_basic(void)
212+
{
213+
rt_thread_t api_thread;
214+
215+
/* Create a simple test thread */
216+
api_thread = rt_thread_create("api_test",
217+
simple_thread_entry,
218+
(void *)&thread_started,
219+
THREAD_STACK_SIZE,
220+
UTEST_THR_PRIORITY,
221+
THREAD_TIMESLICE);
222+
223+
uassert_not_null(api_thread);
224+
225+
rt_thread_startup(api_thread);
226+
rt_thread_mdelay(50); /* Wait for thread to start */
227+
228+
uassert_true(thread_started);
229+
230+
/* Test basic suspend functionality */
231+
rt_err_t result = rt_thread_suspend_any(api_thread, RT_UNINTERRUPTIBLE);
232+
uassert_true(result == RT_EOK);
233+
234+
rt_schedule();
235+
rt_thread_mdelay(100);
236+
237+
/* Resume thread */
238+
result = rt_thread_resume(api_thread);
239+
uassert_true(result == RT_EOK);
240+
241+
rt_thread_mdelay(50);
242+
243+
/* Notify thread to exit */
244+
thread_should_exit = RT_TRUE;
245+
246+
/* Wait for thread to exit naturally */
247+
rt_thread_mdelay(100);
248+
}
249+
250+
/* Test deadlock risk */
251+
static void test_suspend_any_deadlock_risk(void)
252+
{
253+
/* Reset global variables */
254+
shared_counter = 0;
255+
holder_got_mutex = RT_FALSE;
256+
deadlock_detected = RT_FALSE;
257+
test_completed = RT_FALSE;
258+
259+
/* Create mutex */
260+
test_mutex = rt_mutex_create("test_mutex", RT_IPC_FLAG_PRIO);
261+
uassert_not_null(test_mutex);
262+
263+
/* Create and start holder thread */
264+
holder_thread = rt_thread_create("holder", mutex_holder_thread, RT_NULL,
265+
THREAD_STACK_SIZE, UTEST_THR_PRIORITY, THREAD_TIMESLICE);
266+
uassert_not_null(holder_thread);
267+
rt_thread_startup(holder_thread);
268+
269+
/* Create and start waiter thread */
270+
waiter_thread = rt_thread_create("waiter", mutex_waiter_thread, RT_NULL,
271+
THREAD_STACK_SIZE, UTEST_THR_PRIORITY + 1, THREAD_TIMESLICE);
272+
uassert_not_null(waiter_thread);
273+
/* Now start waiter thread, it will try to acquire mutex held by suspended thread */
274+
rt_thread_startup(waiter_thread);
275+
276+
/* Wait for holder to get mutex */
277+
int timeout = 100; /* 1 second timeout */
278+
while (!holder_got_mutex && timeout-- > 0)
279+
{
280+
rt_thread_mdelay(10);
281+
}
282+
283+
uassert_true(holder_got_mutex);
284+
285+
/* This is the critical test! Suspend thread that holds the mutex */
286+
rt_err_t suspend_result = rt_thread_suspend_any(holder_thread, RT_UNINTERRUPTIBLE);
287+
uassert_true(suspend_result == RT_EOK);
288+
rt_kprintf("Suspended holder thread (which holds the mutex)\n");
289+
290+
rt_schedule();
291+
292+
/* Wait for waiter thread to try acquiring lock */
293+
rt_thread_mdelay(2000);
294+
295+
uassert_true(deadlock_detected == RT_TRUE);
296+
297+
/* Resume thread */
298+
rt_err_t resume_result = rt_thread_resume(holder_thread);
299+
uassert_true(resume_result == RT_EOK);
300+
rt_kprintf("Resumed holder thread\n");
301+
302+
test_completed = RT_TRUE;
303+
304+
/* Wait for threads to complete */
305+
rt_thread_mdelay(1000);
306+
307+
/* Verify rt_thread_suspend_any and rt_thread_resume executed successfully */
308+
uassert_true(suspend_result == RT_EOK);
309+
uassert_true(resume_result == RT_EOK);
310+
311+
/* Verify system didn't crash, threads can work normally */
312+
uassert_true(shared_counter > 0);
313+
314+
/* Clean up resources */
315+
if (test_mutex)
316+
{
317+
rt_mutex_delete(test_mutex);
318+
test_mutex = RT_NULL;
319+
}
320+
321+
/* Wait again to ensure threads exit naturally */
322+
rt_thread_mdelay(200);
323+
324+
holder_thread = RT_NULL;
325+
waiter_thread = RT_NULL;
326+
}
327+
328+
static rt_err_t utest_tc_init(void)
329+
{
330+
return RT_EOK;
331+
}
332+
333+
static rt_err_t utest_tc_cleanup(void)
334+
{
335+
return RT_EOK;
336+
}
337+
338+
static void testcase(void)
339+
{
340+
UTEST_UNIT_RUN(test_suspend_any_api_basic);
341+
UTEST_UNIT_RUN(test_suspend_any_normal_usage);
342+
UTEST_UNIT_RUN(test_suspend_any_deadlock_risk);
343+
}
344+
UTEST_TC_EXPORT(testcase, "testcases.kernel.thread_suspend", utest_tc_init, utest_tc_cleanup, 30);
345+

include/rtthread.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ rt_err_t rt_thread_mdelay(rt_int32_t ms);
175175
rt_err_t rt_thread_control(rt_thread_t thread, int cmd, void *arg);
176176
rt_err_t rt_thread_suspend(rt_thread_t thread);
177177
rt_err_t rt_thread_suspend_with_flag(rt_thread_t thread, int suspend_flag);
178+
rt_err_t rt_thread_suspend_any(rt_thread_t thread, int suspend_flag);
178179
rt_err_t rt_thread_resume(rt_thread_t thread);
179180
#ifdef RT_USING_SMART
180181
rt_err_t rt_thread_wakeup(rt_thread_t thread);

0 commit comments

Comments
 (0)