test_vTaskSuspendAll_xTaskResumeAll.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. /*
  2. * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include "sdkconfig.h"
  7. #include <stdbool.h>
  8. #include "freertos/FreeRTOS.h"
  9. #include "freertos/task.h"
  10. #include "freertos/semphr.h"
  11. #include "driver/gptimer.h"
  12. #include "esp_rom_sys.h"
  13. #include "unity.h"
  14. #include "test_utils.h"
  15. /*
  16. Scheduler suspension behavior differs significantly in SMP FreeRTOS, thus none of these tests apply to SMP FreeRTOS
  17. */
  18. #if !CONFIG_FREERTOS_SMP
  19. /*
  20. GP timer is used to trigger an interrupt. Test cases will register an interrupt callback called from the timer's
  21. interrupt callback. The functions below simply the interrupt registration/trigger/deregistration process.
  22. */
  23. static gptimer_handle_t gptimer = NULL;
  24. static bool (*registered_intr_callback)(void *) = NULL;
  25. static bool on_timer_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx)
  26. {
  27. bool yield;
  28. if (registered_intr_callback) {
  29. yield = registered_intr_callback(user_ctx);
  30. } else {
  31. yield = false;
  32. }
  33. return yield;
  34. }
  35. static void register_intr_cb(bool (*callback)(void *), void *callback_arg)
  36. {
  37. gptimer_handle_t gptimer_temp;
  38. // Initialize a GP timer used to trigger an interrupt
  39. gptimer_config_t timer_config = {
  40. .clk_src = GPTIMER_CLK_SRC_DEFAULT,
  41. .direction = GPTIMER_COUNT_UP,
  42. .resolution_hz = 1000000, // 1MHz, 1 tick=1us
  43. };
  44. TEST_ESP_OK(gptimer_new_timer(&timer_config, &gptimer_temp));
  45. // Configure an alarm (of 1ms) and callback for the timer
  46. gptimer_alarm_config_t alarm_config = {
  47. .reload_count = 0,
  48. .alarm_count = 1000, // alarm period 1ms
  49. .flags.auto_reload_on_alarm = true,
  50. };
  51. gptimer_event_callbacks_t cbs = {
  52. .on_alarm = on_timer_alarm_cb,
  53. };
  54. TEST_ESP_OK(gptimer_register_event_callbacks(gptimer_temp, &cbs, callback_arg));
  55. TEST_ESP_OK(gptimer_enable(gptimer_temp));
  56. TEST_ESP_OK(gptimer_set_alarm_action(gptimer_temp, &alarm_config));
  57. gptimer = gptimer_temp;
  58. registered_intr_callback = callback;
  59. }
  60. static void trigger_intr_cb(void)
  61. {
  62. // Interrupt should be triggered in 1ms
  63. TEST_ESP_OK(gptimer_start(gptimer));
  64. }
  65. static void deregister_intr_cb(void)
  66. {
  67. gptimer_handle_t gptimer_temp = gptimer;
  68. gptimer = NULL;
  69. registered_intr_callback = NULL;
  70. TEST_ESP_OK(gptimer_stop(gptimer_temp));
  71. TEST_ESP_OK(gptimer_disable(gptimer_temp));
  72. TEST_ESP_OK(gptimer_del_timer(gptimer_temp));
  73. }
  74. /* ---------------------------------------------------------------------------------------------------------------------
  75. Test vTaskSuspendAll() and xTaskResumeAll() basic
  76. Purpose:
  77. - Test that vTaskSuspendAll() will suspends the scheduler for the calling core
  78. - Test that xTaskResumeAll() will resumes scheduling for the calling core
  79. Procedure:
  80. - Call vTaskSuspendAll() to suspend the scheduler
  81. - Call xTaskResumeAll() to resume the scheduler
  82. Expected:
  83. - xTaskGetSchedulerState() should return the correct state
  84. --------------------------------------------------------------------------------------------------------------------- */
  85. TEST_CASE("Test vTaskSuspendAll and xTaskResumeAll basic", "[freertos]")
  86. {
  87. // Check scheduler is running on the current core
  88. TEST_ASSERT_EQUAL(taskSCHEDULER_RUNNING, xTaskGetSchedulerState());
  89. vTaskSuspendAll();
  90. TEST_ASSERT_EQUAL(taskSCHEDULER_SUSPENDED, xTaskGetSchedulerState());
  91. xTaskResumeAll();
  92. TEST_ASSERT_EQUAL(taskSCHEDULER_RUNNING, xTaskGetSchedulerState());
  93. }
  94. /* ---------------------------------------------------------------------------------------------------------------------
  95. Test vTaskSuspendAll() and xTaskResumeAll() multicore
  96. Only runs on !CONFIG_FREERTOS_UNICORE
  97. Purpose:
  98. - Test that vTaskSuspendAll() will only suspends scheduling only for the calling core
  99. - Test that xTaskResumeAll() will only resume scheduling for the calling core
  100. Procedure:
  101. Each core gets tested in the role of core A
  102. - Create a taskA pinned to one core (e.g., core A) that will disable the scheduler
  103. - Created a "taskB" to another core (e.g., core B) that will not disable the scheduler
  104. - taskA calls vTaskSuspendAll() to suspend the scheduler on core A
  105. - taskA calls xTaskResumeAll() to resume the scheduler on core A
  106. Expected:
  107. - vTaskSuspendAll() should only disable the scheduler for the suspended core A
  108. - xTaskResumeAll() should resume the scheduler for the suspended core A
  109. - Scheduler on core B should remain enabled
  110. --------------------------------------------------------------------------------------------------------------------- */
  111. #if !CONFIG_FREERTOS_UNICORE
  112. #define TEST_BASIC_BUSY_DELAY_US 10000
  113. static volatile int taskA_sync;
  114. static volatile int taskB_sync;
  115. static void test_multicore_taskA(void *arg)
  116. {
  117. // Wait to be started
  118. ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
  119. // Check scheduler on core A is enabled
  120. TEST_ASSERT_EQUAL(taskSCHEDULER_RUNNING, xTaskGetSchedulerState());
  121. taskA_sync++;
  122. while (taskB_sync != 1) {
  123. ; // Wait for task B to complete its check
  124. }
  125. // Suspend the scheduler on core A
  126. vTaskSuspendAll();
  127. // Check scheduler is suspended on core A
  128. TEST_ASSERT_EQUAL(taskSCHEDULER_SUSPENDED, xTaskGetSchedulerState());
  129. taskA_sync++;
  130. while (taskB_sync != 2) {
  131. ; // Wait for task B to complete its check
  132. }
  133. // Busy spin for a while to simulate work done while scheduler is suspended
  134. esp_rom_delay_us(TEST_BASIC_BUSY_DELAY_US);
  135. // Check scheduler is still suspended on core A
  136. TEST_ASSERT_EQUAL(taskSCHEDULER_SUSPENDED, xTaskGetSchedulerState());
  137. taskA_sync++;
  138. while (taskB_sync != 3) {
  139. ; // Wait for task B to complete its check
  140. }
  141. // Resume the scheduler on core A
  142. xTaskResumeAll();
  143. // Check that scheduler has resumed resumed on core A
  144. TEST_ASSERT_EQUAL(taskSCHEDULER_RUNNING, xTaskGetSchedulerState());
  145. taskA_sync++;
  146. while (taskB_sync != 4) {
  147. ; // Wait for task B to complete its check
  148. }
  149. // Indicate done and wait to be deleted
  150. SemaphoreHandle_t done_sem = (SemaphoreHandle_t)arg;
  151. xSemaphoreGive(done_sem);
  152. vTaskSuspend(NULL);
  153. }
  154. static void test_multicore_taskB(void *arg)
  155. {
  156. // Wait to be started
  157. ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
  158. for (int i = 1; i <= 4; i++) {
  159. // Wait for suspended trigger from task A
  160. while (taskA_sync != i) {
  161. ;
  162. }
  163. // Check that scheduler is still running on core B
  164. TEST_ASSERT_EQUAL(taskSCHEDULER_RUNNING, xTaskGetSchedulerState());
  165. taskB_sync++;
  166. }
  167. // Indicate done and wait to be deleted
  168. SemaphoreHandle_t done_sem = (SemaphoreHandle_t)arg;
  169. xSemaphoreGive(done_sem);
  170. vTaskSuspend(NULL);
  171. }
  172. TEST_CASE("Test vTaskSuspendAll() and xTaskResumeAll() multicore", "[freertos]")
  173. {
  174. SemaphoreHandle_t done_sem = xSemaphoreCreateCounting(portNUM_PROCESSORS, 0);
  175. TEST_ASSERT_NOT_EQUAL(NULL, done_sem);
  176. for (int i = 0; i < portNUM_PROCESSORS; i++) {
  177. // Create tasks on core A and core B
  178. TaskHandle_t taskA_hdl;
  179. TaskHandle_t taskB_hdl;
  180. TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(test_multicore_taskA, "taskA", 2048, (void *)done_sem, UNITY_FREERTOS_PRIORITY - 1, &taskA_hdl, i));
  181. TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(test_multicore_taskB, "taskB", 2048, (void *)done_sem, UNITY_FREERTOS_PRIORITY - 1, &taskB_hdl, !i));
  182. // Start the tasks and wait for their completion
  183. taskA_sync = 0;
  184. taskB_sync = 0;
  185. xTaskNotifyGive(taskA_hdl);
  186. xTaskNotifyGive(taskB_hdl);
  187. for (int j = 0; j < 2; j++) {
  188. xSemaphoreTake(done_sem, portMAX_DELAY);
  189. }
  190. // Cleanup the tasks
  191. vTaskDelete(taskA_hdl);
  192. vTaskDelete(taskB_hdl);
  193. }
  194. vSemaphoreDelete(done_sem);
  195. }
  196. #endif // !CONFIG_FREERTOS_UNICORE
  197. /* ---------------------------------------------------------------------------------------------------------------------
  198. Test vTaskSuspendAll allows scheduling on other cores
  199. Only runs on !CONFIG_FREERTOS_UNICORE
  200. Purpose:
  201. - Test that disabling a scheduler on one core (e.g., core B) does not disable scheduling on the other core (e.g., core A)
  202. - While the scheduler on core B is disabled, test that...
  203. - A task on Core A can be unblocked by another task also on core A
  204. - A task on Core A can be unblocked by an interrupt on core A
  205. Procedure:
  206. Each core gets tested in the role of core A
  207. - Create task B1 pinned to core B that will suspend scheduling on core B
  208. - Create task A2 pinned to core A that will test unblocking on core A
  209. - Create task A1 pinned to core A that will unblock task A2
  210. - Register an interrupt on core A that will unblock task A2
  211. - Have A2 block
  212. - Have B1 disable scheduling on core B. A1 checks that scheduling is still enabled on core A
  213. - Have A1 unblock A2
  214. - Have the core A ISR unblock A2
  215. - Cleanup the tasks
  216. Expected:
  217. When B1 disables scheduling on core B...
  218. - Scheduling on core A should still be enabled
  219. - A2 should be unblocked by A1 and run without issue
  220. - A2 should be unblocked by core A ISR and run without issue
  221. --------------------------------------------------------------------------------------------------------------------- */
  222. #if !CONFIG_FREERTOS_UNICORE
  223. static volatile int test_unblk_sync;
  224. static SemaphoreHandle_t test_unblk_done_sem;
  225. static bool test_unblk_coreA_isr(void *arg)
  226. {
  227. TaskHandle_t a2_task_hdl = (TaskHandle_t)arg;
  228. BaseType_t task_woken = pdFALSE;
  229. // Unblock task b2
  230. vTaskNotifyGiveFromISR(a2_task_hdl, &task_woken);
  231. return (task_woken == pdTRUE);
  232. }
  233. static void test_unblk_a2_task(void *arg)
  234. {
  235. volatile int *a2_task_run_count = (volatile int *)arg;
  236. // Wait to be unblocked by A1
  237. ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
  238. (*a2_task_run_count)++;
  239. // Wait to be unblocked by Core A ISR
  240. ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
  241. (*a2_task_run_count)++;
  242. // Wait to be deleted
  243. vTaskSuspend(NULL);
  244. }
  245. static void test_unblk_a1_task(void *arg)
  246. {
  247. volatile int a2_task_run_count = 0;
  248. // Create task A2 to block on this core (i.e., core A)
  249. TaskHandle_t a2_task_hdl;
  250. TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(test_unblk_a2_task, "A2", 8192, (void *)&a2_task_run_count, UNITY_FREERTOS_PRIORITY + 2, &a2_task_hdl, xPortGetCoreID()));
  251. // Install an interrupt on core A
  252. register_intr_cb(test_unblk_coreA_isr, (void *)a2_task_hdl);
  253. // Wait to be started by the main task
  254. ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
  255. // Start B1
  256. xTaskNotifyGive((TaskHandle_t)arg);
  257. while (test_unblk_sync != 1) {
  258. ; // Wait for confirmation from B1 that scheduler has been suspended on Core B
  259. }
  260. // Verify that the scheduler is still enabled on core A
  261. TEST_ASSERT_EQUAL(taskSCHEDULER_RUNNING, xTaskGetSchedulerState());
  262. // Unblock A2, it should preempt immediately due to its higher priority
  263. xTaskNotifyGive(a2_task_hdl);
  264. // Verify that task A2 has run
  265. TEST_ASSERT_EQUAL(1, a2_task_run_count);
  266. // Trigger an ISR on this core A to unblock task A2. A2 should preempt immediately due to its higher priority
  267. trigger_intr_cb();
  268. esp_rom_delay_us(2000); // Short busy delay to ensure interrupt has triggered
  269. // Verify that task A2 has run
  270. TEST_ASSERT_EQUAL(2, a2_task_run_count);
  271. // Trigger B1 to resume scheduling on core B
  272. test_unblk_sync = 2;
  273. while (test_unblk_sync != 3) {
  274. ; // Wait for confirmation from B1 that scheduler has been resumed
  275. }
  276. // Verify that the scheduler is still enabled on core A
  277. TEST_ASSERT_EQUAL(taskSCHEDULER_RUNNING, xTaskGetSchedulerState());
  278. // Cleanup A2 and interrupt
  279. deregister_intr_cb();
  280. vTaskDelete(a2_task_hdl);
  281. // Indicate done and wait to be deleted
  282. xSemaphoreGive(test_unblk_done_sem);
  283. vTaskSuspend(NULL);
  284. }
  285. static void test_unblk_b1_task(void *arg)
  286. {
  287. // Wait to be started by A1
  288. ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
  289. // Check scheduler is running on core B
  290. TEST_ASSERT_EQUAL(taskSCHEDULER_RUNNING, xTaskGetSchedulerState());
  291. // Suspend the scheduler on core B
  292. vTaskSuspendAll();
  293. TEST_ASSERT_EQUAL(taskSCHEDULER_SUSPENDED, xTaskGetSchedulerState());
  294. // Indicate to A1 that core B scheduler has been suspended
  295. test_unblk_sync = 1;
  296. while (test_unblk_sync != 2) {
  297. ; // Wait for trigger from A1
  298. }
  299. // Resume the scheduler on core B
  300. xTaskResumeAll();
  301. TEST_ASSERT_EQUAL(taskSCHEDULER_RUNNING, xTaskGetSchedulerState());
  302. // Indicate to A1 that core B scheduler has been resumed
  303. test_unblk_sync = 3;
  304. // Indicate done and wait to be deleted
  305. xSemaphoreGive(test_unblk_done_sem);
  306. vTaskSuspend(NULL);
  307. }
  308. TEST_CASE("Test vTaskSuspendAll allows scheduling on other cores", "[freertos]")
  309. {
  310. test_unblk_done_sem = xSemaphoreCreateCounting(portNUM_PROCESSORS, 0);
  311. TEST_ASSERT_NOT_EQUAL(NULL, test_unblk_done_sem);
  312. for (int i = 0; i < portNUM_PROCESSORS; i++) {
  313. test_unblk_sync = 0;
  314. // Create a tasks
  315. TaskHandle_t a1_task_hdl;
  316. TaskHandle_t b1_task_hdl;
  317. TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(test_unblk_b1_task, "B1", 8192, NULL, UNITY_FREERTOS_PRIORITY + 1, &b1_task_hdl, !i));
  318. TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(test_unblk_a1_task, "A1", 8192, (void *)b1_task_hdl, UNITY_FREERTOS_PRIORITY + 1, &a1_task_hdl, i));
  319. // Start A1 to and wait for both tasks to complete
  320. xTaskNotifyGive(a1_task_hdl);
  321. for (int j = 0; j < 2; j++) {
  322. xSemaphoreTake(test_unblk_done_sem, portMAX_DELAY);
  323. }
  324. // Cleanup tasks
  325. vTaskDelete(a1_task_hdl);
  326. vTaskDelete(b1_task_hdl);
  327. }
  328. vSemaphoreDelete(test_unblk_done_sem);
  329. }
  330. #endif // !CONFIG_FREERTOS_UNICORE
  331. /* ---------------------------------------------------------------------------------------------------------------------
  332. Test xTaskResumeAll() resumes pended tasks on the current core
  333. Purpose:
  334. - When the scheduler is suspended on a particular core, test that tasks unblocked by an ISR on that core will place
  335. those tasks on the core's pending ready list (regardless of the task's affinity).
  336. - When the scheduler is resumed on a particular core, test that the tasks on core's pending ready list will be
  337. scheduled.
  338. Procedure:
  339. Test for each core
  340. - Create some blocking tasks on the same core
  341. - Register an interrupt on the same core to unblock those tasks
  342. - Suspend the scheduler on the core
  343. - Trigger the interrupt to unblock those tasks
  344. - Resume the scheduler
  345. - Cleanup
  346. Expected:
  347. - When the ISR unblocks the blocked tasks, the task's state should be ready
  348. - When the scheduler is resumed, the tasks should be scheduled and run without issue.
  349. --------------------------------------------------------------------------------------------------------------------- */
  350. #define TEST_PENDED_NUM_BLOCKED_TASKS 4
  351. static bool test_pended_isr(void *arg)
  352. {
  353. TaskHandle_t *blkd_tsks = (TaskHandle_t *)arg;
  354. BaseType_t task_woken = pdFALSE;
  355. // Unblock the blocked tasks
  356. for (int i = 0; i < TEST_PENDED_NUM_BLOCKED_TASKS; i++) {
  357. vTaskNotifyGiveFromISR(blkd_tsks[i], &task_woken);
  358. }
  359. return (task_woken == pdTRUE);
  360. }
  361. static void test_pended_blkd_task(void *arg)
  362. {
  363. volatile bool *has_run = (bool *)arg;
  364. // Wait to be unblocked
  365. ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
  366. // Indicate the task been unblocked and has run
  367. *has_run = true;
  368. // Wait to be deleted
  369. vTaskSuspend(NULL);
  370. }
  371. static void test_pended_running_task(void *arg)
  372. {
  373. TaskHandle_t main_task_hdl = (TaskHandle_t)arg;
  374. TaskHandle_t blkd_tsks[TEST_PENDED_NUM_BLOCKED_TASKS];
  375. volatile bool has_run[TEST_PENDED_NUM_BLOCKED_TASKS];
  376. // Created blocked tasks pinned to each core
  377. for (int i = 0; i < TEST_PENDED_NUM_BLOCKED_TASKS; i++) {
  378. has_run[i] = false;
  379. TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(test_pended_blkd_task, "blkd", 4096, (void *)&has_run[i], UNITY_FREERTOS_PRIORITY + 2, &blkd_tsks[i], i % portNUM_PROCESSORS));
  380. }
  381. vTaskDelay(10);
  382. // Install an interrupt on the current core core
  383. register_intr_cb(test_pended_isr, (void *)blkd_tsks);
  384. // Checked that all tasks are blocked and have no run yet
  385. for (int i = 0; i < TEST_PENDED_NUM_BLOCKED_TASKS; i++) {
  386. TEST_ASSERT_EQUAL(eBlocked, eTaskGetState(blkd_tsks[i])); // Should be eSuspended due to portMAX_DELAY
  387. TEST_ASSERT_EQUAL(false, has_run[i]);
  388. }
  389. // Suspend the scheduler on the current core
  390. vTaskSuspendAll();
  391. // Trigger the interrupt to unblocked the blocked tasks
  392. trigger_intr_cb();
  393. esp_rom_delay_us(2000); // Short busy delay to ensure interrupt has triggered
  394. // Check that all tasks are unblocked (but should not have run since the scheduler is suspend)
  395. for (int i = 0; i < TEST_PENDED_NUM_BLOCKED_TASKS; i++) {
  396. // Note: We use eBlocked instead of eReady due to a bug in eTaskGetState(). See (IDF-5543)
  397. TEST_ASSERT_EQUAL(eBlocked, eTaskGetState(blkd_tsks[i]));
  398. TEST_ASSERT_EQUAL(false, has_run[i]);
  399. }
  400. // Resume the scheduler on the current core to schedule the unblocked tasks
  401. xTaskResumeAll();
  402. esp_rom_delay_us(10000); // Busy delay to ensure each task has enough time to run
  403. // Check that all tasks have run
  404. for (int i = 0; i < TEST_PENDED_NUM_BLOCKED_TASKS; i++) {
  405. TEST_ASSERT_EQUAL(true, has_run[i]);
  406. }
  407. // Clean up the interrupt and tasks
  408. deregister_intr_cb();
  409. for (int i = 0; i < TEST_PENDED_NUM_BLOCKED_TASKS; i++) {
  410. vTaskDelete(blkd_tsks[i]);
  411. }
  412. // Notify completion and wait for deletion
  413. xTaskNotifyGive(main_task_hdl);
  414. vTaskSuspend(NULL);
  415. }
  416. TEST_CASE("Test xTaskResumeAll resumes pended tasks", "[freertos]")
  417. {
  418. // Run the test on each core
  419. for (int i = 0; i < portNUM_PROCESSORS; i++) {
  420. TaskHandle_t susp_tsk_hdl;
  421. TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(test_pended_running_task, "susp", 2048, (void *)xTaskGetCurrentTaskHandle(), UNITY_FREERTOS_PRIORITY + 1, &susp_tsk_hdl, i));
  422. // Wait for to be notified to test completion
  423. ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
  424. vTaskDelete(susp_tsk_hdl);
  425. }
  426. }
  427. #endif // !CONFIG_FREERTOS_SMP