1
0

thread_suspend_tc.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  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 comprehensive tests
  9. */
  10. #include <rtthread.h>
  11. #include <rtdevice.h>
  12. #include "utest.h"
  13. #define THREAD_STACK_SIZE 1024
  14. #define THREAD_TIMESLICE 5
  15. #define TEST_THREAD_PRIORITY 25
  16. /* Global variables for normal usage test */
  17. static rt_thread_t target_thread = RT_NULL;
  18. static rt_thread_t monitor_thread = RT_NULL;
  19. static rt_sem_t sync_sem = RT_NULL;
  20. static volatile rt_uint32_t work_counter = 0;
  21. static volatile rt_bool_t suspend_test_done = RT_FALSE;
  22. static volatile rt_bool_t suspend_success = RT_FALSE;
  23. static volatile rt_bool_t resume_success = RT_FALSE;
  24. /* Global variables for deadlock test */
  25. static rt_mutex_t test_mutex = RT_NULL;
  26. static rt_thread_t holder_thread = RT_NULL;
  27. static rt_thread_t waiter_thread = RT_NULL;
  28. static volatile rt_uint32_t shared_counter = 0;
  29. static volatile rt_bool_t holder_got_mutex = RT_FALSE;
  30. static volatile rt_bool_t deadlock_detected = RT_FALSE;
  31. static volatile rt_bool_t test_completed = RT_FALSE;
  32. static volatile rt_bool_t thread_started = RT_FALSE;
  33. static volatile rt_bool_t thread_should_exit = RT_FALSE;
  34. /* Target work thread - the thread to be suspended */
  35. static void target_work_thread(void *parameter)
  36. {
  37. while (1)
  38. {
  39. if (!suspend_test_done)
  40. {
  41. work_counter++;
  42. }
  43. /* Yield CPU appropriately to simulate normal work */
  44. rt_thread_mdelay(10);
  45. }
  46. }
  47. /* Monitor thread - responsible for suspending and resuming target thread */
  48. static void monitor_control_thread(void *parameter)
  49. {
  50. rt_uint32_t counter_before, counter_after;
  51. /* Wait for target thread to start working */
  52. rt_thread_mdelay(300);
  53. /* Record counter value before suspend */
  54. counter_before = work_counter;
  55. /* Use rt_thread_suspend to suspend target thread */
  56. if (rt_thread_suspend(target_thread) == RT_EOK)
  57. {
  58. suspend_success = RT_TRUE;
  59. /* Trigger scheduling to ensure thread is suspended */
  60. rt_schedule();
  61. /* Wait for a while to verify thread is indeed suspended */
  62. rt_thread_mdelay(500);
  63. counter_after = work_counter;
  64. /* Verify thread is indeed suspended (counter should stop changing) */
  65. if (counter_after == counter_before)
  66. {
  67. /* Resume target thread */
  68. if (rt_thread_resume(target_thread) == RT_EOK)
  69. {
  70. resume_success = RT_TRUE;
  71. /* Wait for a while to verify thread resumes work */
  72. rt_thread_mdelay(200);
  73. }
  74. }
  75. }
  76. /* End test */
  77. suspend_test_done = RT_TRUE;
  78. /* Send semaphore to notify test completion */
  79. rt_sem_release(sync_sem);
  80. /* Keep running until deleted */
  81. while (1)
  82. {
  83. rt_thread_mdelay(100);
  84. }
  85. }
  86. /* Thread that holds the mutex */
  87. static void mutex_holder_thread(void *parameter)
  88. {
  89. if (rt_mutex_take(test_mutex, RT_WAITING_FOREVER) == RT_EOK)
  90. {
  91. holder_got_mutex = RT_TRUE;
  92. /* Simulate critical section work */
  93. for (int i = 0; i < 1000 && !test_completed; i++)
  94. {
  95. shared_counter++;
  96. if (i % 200 == 0)
  97. {
  98. rt_thread_mdelay(10);
  99. }
  100. }
  101. if (!test_completed)
  102. {
  103. rt_mutex_release(test_mutex);
  104. }
  105. }
  106. rt_kprintf("Holder thread exiting\n");
  107. /* Keep running until deleted */
  108. while (1)
  109. {
  110. rt_thread_mdelay(100);
  111. }
  112. }
  113. /* Thread that waits for the mutex */
  114. static void mutex_waiter_thread(void *parameter)
  115. {
  116. /* Wait a bit to ensure holder gets mutex first */
  117. rt_thread_mdelay(50);
  118. rt_err_t result = rt_mutex_take(test_mutex, rt_tick_from_millisecond(1500));
  119. if (result == RT_EOK)
  120. {
  121. shared_counter += 1000;
  122. rt_mutex_release(test_mutex);
  123. }
  124. else
  125. {
  126. /* Timeout indicates deadlock - holder is suspended and cannot release lock */
  127. deadlock_detected = RT_TRUE;
  128. rt_kprintf("Deadlock detected: waiter timeout (holder suspended with mutex)\n");
  129. }
  130. /* Keep running until deleted */
  131. while (1)
  132. {
  133. rt_thread_mdelay(100);
  134. }
  135. }
  136. void simple_thread_entry(void *param)
  137. {
  138. volatile rt_bool_t *flag = (volatile rt_bool_t *)param;
  139. *flag = RT_TRUE;
  140. /* Keep the thread running until it's suspended and deleted */
  141. while (1)
  142. {
  143. rt_thread_mdelay(100);
  144. }
  145. }
  146. /* Test normal usage of rt_thread_suspend function */
  147. static void test_suspend_force_normal_usage(void)
  148. {
  149. /* Reset global variables */
  150. work_counter = 0;
  151. suspend_test_done = RT_FALSE;
  152. suspend_success = RT_FALSE;
  153. resume_success = RT_FALSE;
  154. /* Create synchronization semaphore */
  155. sync_sem = rt_sem_create("sync", 0, RT_IPC_FLAG_FIFO);
  156. uassert_not_null(sync_sem);
  157. /* Create target work thread */
  158. target_thread = rt_thread_create("target",
  159. target_work_thread,
  160. RT_NULL,
  161. THREAD_STACK_SIZE,
  162. TEST_THREAD_PRIORITY,
  163. THREAD_TIMESLICE);
  164. uassert_not_null(target_thread);
  165. /* Create monitor thread */
  166. monitor_thread = rt_thread_create("monitor",
  167. monitor_control_thread,
  168. RT_NULL,
  169. THREAD_STACK_SIZE,
  170. UTEST_THR_PRIORITY,
  171. THREAD_TIMESLICE);
  172. uassert_not_null(monitor_thread);
  173. /* Start threads */
  174. rt_thread_startup(target_thread);
  175. rt_thread_startup(monitor_thread);
  176. /* Wait for test completion */
  177. rt_sem_take(sync_sem, RT_WAITING_FOREVER);
  178. /* Wait for a while to ensure threads exit normally */
  179. rt_thread_mdelay(100);
  180. /* Verify test results */
  181. uassert_true(suspend_success);
  182. uassert_true(resume_success);
  183. uassert_true(work_counter > 0);
  184. /* Clean up resources */
  185. if (sync_sem)
  186. {
  187. rt_sem_delete(sync_sem);
  188. sync_sem = RT_NULL;
  189. }
  190. /* Delete threads */
  191. if (target_thread != RT_NULL)
  192. {
  193. rt_thread_delete(target_thread);
  194. target_thread = RT_NULL;
  195. }
  196. if (monitor_thread != RT_NULL)
  197. {
  198. rt_thread_delete(monitor_thread);
  199. monitor_thread = RT_NULL;
  200. }
  201. }
  202. /* Basic API test */
  203. static void test_suspend_force_api_basic(void)
  204. {
  205. rt_thread_t api_thread;
  206. /* Reset global variables */
  207. thread_started = RT_FALSE;
  208. thread_should_exit = RT_FALSE;
  209. /* Create a simple test thread */
  210. api_thread = rt_thread_create("api_test",
  211. simple_thread_entry,
  212. (void *)&thread_started,
  213. THREAD_STACK_SIZE,
  214. UTEST_THR_PRIORITY,
  215. THREAD_TIMESLICE);
  216. uassert_not_null(api_thread);
  217. rt_thread_startup(api_thread);
  218. rt_thread_mdelay(50); /* Wait for thread to start */
  219. uassert_true(thread_started);
  220. /* Test basic suspend functionality */
  221. rt_err_t result = rt_thread_suspend(api_thread);
  222. uassert_true(result == RT_EOK);
  223. rt_schedule();
  224. rt_thread_mdelay(100);
  225. /* Resume thread */
  226. result = rt_thread_resume(api_thread);
  227. uassert_true(result == RT_EOK);
  228. rt_thread_mdelay(50);
  229. /* Clean up - delete the thread directly */
  230. if (api_thread != RT_NULL)
  231. {
  232. rt_thread_delete(api_thread);
  233. api_thread = RT_NULL;
  234. }
  235. /* Reset global variables for next test */
  236. thread_started = RT_FALSE;
  237. thread_should_exit = RT_FALSE;
  238. }
  239. /* Test suspend on thread that is created but not started */
  240. static void test_suspend_force_not_started_thread(void)
  241. {
  242. rt_thread_t not_started_thread;
  243. /* Create a thread but don't start it */
  244. not_started_thread = rt_thread_create("not_started",
  245. simple_thread_entry,
  246. (void *)&thread_started,
  247. THREAD_STACK_SIZE,
  248. UTEST_THR_PRIORITY,
  249. THREAD_TIMESLICE);
  250. uassert_not_null(not_started_thread);
  251. /* Verify thread is in INIT state */
  252. uassert_true((RT_SCHED_CTX(not_started_thread).stat & RT_THREAD_STAT_MASK) == RT_THREAD_INIT);
  253. /* Try to suspend a thread that hasn't been started yet */
  254. rt_err_t suspend_result = rt_thread_suspend(not_started_thread);
  255. rt_schedule();
  256. uassert_true(suspend_result == -RT_ERROR);
  257. /* Try to resume the not-started thread */
  258. rt_err_t resume_result = rt_thread_resume(not_started_thread);
  259. uassert_true(resume_result == -RT_EINVAL);
  260. /* Now start the thread to see if it works normally */
  261. rt_err_t startup_result = rt_thread_startup(not_started_thread);
  262. uassert_true(startup_result == RT_EOK);
  263. /* Wait a bit to see if thread starts normally */
  264. rt_thread_mdelay(100);
  265. /* The thread should have started successfully despite previous suspend/resume calls */
  266. uassert_true(thread_started == RT_TRUE);
  267. /* Clean up */
  268. if (not_started_thread != RT_NULL)
  269. {
  270. rt_thread_delete(not_started_thread);
  271. not_started_thread = RT_NULL;
  272. }
  273. /* Reset flag for next test */
  274. thread_started = RT_FALSE;
  275. }
  276. /* Test deadlock risk */
  277. static void test_suspend_force_deadlock_risk(void)
  278. {
  279. /* Reset global variables */
  280. shared_counter = 0;
  281. holder_got_mutex = RT_FALSE;
  282. deadlock_detected = RT_FALSE;
  283. test_completed = RT_FALSE;
  284. /* Create mutex */
  285. test_mutex = rt_mutex_create("test_mutex", RT_IPC_FLAG_PRIO);
  286. uassert_not_null(test_mutex);
  287. /* Create and start holder thread */
  288. holder_thread = rt_thread_create("holder", mutex_holder_thread, RT_NULL,
  289. THREAD_STACK_SIZE, UTEST_THR_PRIORITY, THREAD_TIMESLICE);
  290. uassert_not_null(holder_thread);
  291. rt_thread_startup(holder_thread);
  292. /* Create and start waiter thread */
  293. waiter_thread = rt_thread_create("waiter", mutex_waiter_thread, RT_NULL,
  294. THREAD_STACK_SIZE, UTEST_THR_PRIORITY + 1, THREAD_TIMESLICE);
  295. uassert_not_null(waiter_thread);
  296. /* Now start waiter thread, it will try to acquire mutex held by suspended thread */
  297. rt_thread_startup(waiter_thread);
  298. /* Wait for holder to get mutex */
  299. int timeout = 100; /* 1 second timeout */
  300. while (!holder_got_mutex && timeout-- > 0)
  301. {
  302. rt_thread_mdelay(10);
  303. }
  304. uassert_true(holder_got_mutex);
  305. /* This is the critical test! Suspend thread that holds the mutex */
  306. rt_err_t suspend_result = rt_thread_suspend(holder_thread);
  307. uassert_true(suspend_result == RT_EOK);
  308. rt_kprintf("Suspended holder thread (which holds the mutex)\n");
  309. rt_schedule();
  310. /* Wait for waiter thread to try acquiring lock */
  311. rt_thread_mdelay(2000);
  312. uassert_true(deadlock_detected == RT_TRUE);
  313. /* Resume thread */
  314. rt_err_t resume_result = rt_thread_resume(holder_thread);
  315. uassert_true(resume_result == RT_EOK);
  316. rt_kprintf("Resumed holder thread\n");
  317. test_completed = RT_TRUE;
  318. /* Wait for threads to complete */
  319. rt_thread_mdelay(1000);
  320. /* Verify rt_thread_suspend and rt_thread_resume executed successfully */
  321. uassert_true(suspend_result == RT_EOK);
  322. uassert_true(resume_result == RT_EOK);
  323. /* Verify system didn't crash, threads can work normally */
  324. uassert_true(shared_counter > 0);
  325. /* Clean up resources */
  326. if (test_mutex)
  327. {
  328. rt_mutex_delete(test_mutex);
  329. test_mutex = RT_NULL;
  330. }
  331. /* Delete threads */
  332. if (holder_thread != RT_NULL)
  333. {
  334. rt_thread_delete(holder_thread);
  335. holder_thread = RT_NULL;
  336. }
  337. if (waiter_thread != RT_NULL)
  338. {
  339. rt_thread_delete(waiter_thread);
  340. waiter_thread = RT_NULL;
  341. }
  342. /* Wait again to ensure threads are cleaned up */
  343. rt_thread_mdelay(200);
  344. }
  345. static rt_err_t utest_tc_init(void)
  346. {
  347. return RT_EOK;
  348. }
  349. static rt_err_t utest_tc_cleanup(void)
  350. {
  351. /* Reset all global variables to ensure clean state between tests */
  352. work_counter = 0;
  353. suspend_test_done = RT_FALSE;
  354. suspend_success = RT_FALSE;
  355. resume_success = RT_FALSE;
  356. shared_counter = 0;
  357. holder_got_mutex = RT_FALSE;
  358. deadlock_detected = RT_FALSE;
  359. test_completed = RT_FALSE;
  360. thread_started = RT_FALSE;
  361. thread_should_exit = RT_FALSE;
  362. /* Clean up any remaining resources - safety check */
  363. if (sync_sem != RT_NULL)
  364. {
  365. rt_sem_delete(sync_sem);
  366. sync_sem = RT_NULL;
  367. }
  368. if (test_mutex != RT_NULL)
  369. {
  370. rt_mutex_delete(test_mutex);
  371. test_mutex = RT_NULL;
  372. }
  373. /* Ensure all thread pointers are NULL */
  374. target_thread = RT_NULL;
  375. monitor_thread = RT_NULL;
  376. holder_thread = RT_NULL;
  377. waiter_thread = RT_NULL;
  378. /* Give system time to complete cleanup */
  379. rt_thread_mdelay(50);
  380. return RT_EOK;
  381. }
  382. static void testcase(void)
  383. {
  384. UTEST_UNIT_RUN(test_suspend_force_api_basic);
  385. UTEST_UNIT_RUN(test_suspend_force_not_started_thread);
  386. UTEST_UNIT_RUN(test_suspend_force_normal_usage);
  387. UTEST_UNIT_RUN(test_suspend_force_deadlock_risk);
  388. }
  389. UTEST_TC_EXPORT(testcase, "testcases.kernel.thread_suspend", utest_tc_init, utest_tc_cleanup, 30);