mirror of
https://github.com/espressif/esp-idf.git
synced 2025-09-30 19:19:21 +00:00
freertos: Fix bug with xTaskResumeAll() not resuming all tasks
Previously if multiple tasks had been added to xPendingReadyList for the CPU, only the first one was resumed. Includes a test case for resuming multiple (pending) tasks on xTaskResumeAll(). Document the limitation that while scheduler is suspended on one CPU, it can't wake tasks on either CPU.
This commit is contained in:

committed by
Angus Gratton

parent
3106ca1c1e
commit
f2f9170abc
@@ -12,7 +12,7 @@
|
||||
#include "driver/timer.h"
|
||||
|
||||
static SemaphoreHandle_t isr_semaphore;
|
||||
static volatile unsigned isr_count, task_count;
|
||||
static volatile unsigned isr_count;
|
||||
|
||||
/* Timer ISR increments an ISR counter, and signals a
|
||||
mutex semaphore to wake up another counter task */
|
||||
@@ -29,12 +29,18 @@ static void timer_group0_isr(void *vp_arg)
|
||||
}
|
||||
}
|
||||
|
||||
static void counter_task_fn(void *ignore)
|
||||
typedef struct {
|
||||
SemaphoreHandle_t trigger_sem;
|
||||
volatile unsigned counter;
|
||||
} counter_config_t;
|
||||
|
||||
static void counter_task_fn(void *vp_config)
|
||||
{
|
||||
counter_config_t *config = (counter_config_t *)vp_config;
|
||||
printf("counter_task running...\n");
|
||||
while(1) {
|
||||
xSemaphoreTake(isr_semaphore, portMAX_DELAY);
|
||||
task_count++;
|
||||
xSemaphoreTake(config->trigger_sem, portMAX_DELAY);
|
||||
config->counter++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,18 +51,21 @@ static void counter_task_fn(void *ignore)
|
||||
*/
|
||||
TEST_CASE("Handle pending context switch while scheduler disabled", "[freertos]")
|
||||
{
|
||||
task_count = 0;
|
||||
isr_count = 0;
|
||||
isr_semaphore = xSemaphoreCreateMutex();
|
||||
TaskHandle_t counter_task;
|
||||
intr_handle_t isr_handle = NULL;
|
||||
|
||||
counter_config_t count_config = {
|
||||
.trigger_sem = isr_semaphore,
|
||||
.counter = 0,
|
||||
};
|
||||
xTaskCreatePinnedToCore(counter_task_fn, "counter", 2048,
|
||||
NULL, UNITY_FREERTOS_PRIORITY + 1,
|
||||
&count_config, UNITY_FREERTOS_PRIORITY + 1,
|
||||
&counter_task, UNITY_FREERTOS_CPU);
|
||||
|
||||
/* Configure timer ISR */
|
||||
const timer_config_t config = {
|
||||
const timer_config_t timer_config = {
|
||||
.alarm_en = 1,
|
||||
.auto_reload = 1,
|
||||
.counter_dir = TIMER_COUNT_UP,
|
||||
@@ -65,7 +74,7 @@ TEST_CASE("Handle pending context switch while scheduler disabled", "[freertos]"
|
||||
.counter_en = TIMER_PAUSE,
|
||||
};
|
||||
/* Configure timer */
|
||||
timer_init(TIMER_GROUP_0, TIMER_0, &config);
|
||||
timer_init(TIMER_GROUP_0, TIMER_0, &timer_config);
|
||||
timer_pause(TIMER_GROUP_0, TIMER_0);
|
||||
timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0);
|
||||
timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, 1000);
|
||||
@@ -76,20 +85,20 @@ TEST_CASE("Handle pending context switch while scheduler disabled", "[freertos]"
|
||||
vTaskDelay(5);
|
||||
|
||||
// Check some counts have been triggered via the ISR
|
||||
TEST_ASSERT(task_count > 10);
|
||||
TEST_ASSERT(count_config.counter > 10);
|
||||
TEST_ASSERT(isr_count > 10);
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
vTaskSuspendAll();
|
||||
esp_intr_noniram_disable();
|
||||
|
||||
unsigned no_sched_task = task_count;
|
||||
unsigned no_sched_task = count_config.counter;
|
||||
|
||||
// scheduler off on this CPU...
|
||||
ets_delay_us(20 * 1000);
|
||||
|
||||
//TEST_ASSERT_NOT_EQUAL(no_sched_isr, isr_count);
|
||||
TEST_ASSERT_EQUAL(task_count, no_sched_task);
|
||||
TEST_ASSERT_EQUAL(count_config.counter, no_sched_task);
|
||||
|
||||
// disable timer interrupts
|
||||
timer_disable_intr(TIMER_GROUP_0, TIMER_0);
|
||||
@@ -99,7 +108,7 @@ TEST_CASE("Handle pending context switch while scheduler disabled", "[freertos]"
|
||||
esp_intr_noniram_enable();
|
||||
xTaskResumeAll();
|
||||
|
||||
TEST_ASSERT_NOT_EQUAL(task_count, no_sched_task);
|
||||
TEST_ASSERT_NOT_EQUAL(count_config.counter, no_sched_task);
|
||||
}
|
||||
|
||||
esp_intr_free(isr_handle);
|
||||
@@ -108,3 +117,81 @@ TEST_CASE("Handle pending context switch while scheduler disabled", "[freertos]"
|
||||
vTaskDelete(counter_task);
|
||||
vSemaphoreDelete(isr_semaphore);
|
||||
}
|
||||
|
||||
/* Multiple tasks on different cores can be added to the pending ready list
|
||||
while scheduler is suspended, and should be started once the scheduler
|
||||
resumes.
|
||||
*/
|
||||
TEST_CASE("Handle waking multiple tasks while scheduler suspended", "[freertos]")
|
||||
{
|
||||
#define TASKS_PER_PROC 4
|
||||
TaskHandle_t tasks[portNUM_PROCESSORS][TASKS_PER_PROC] = { 0 };
|
||||
counter_config_t counters[portNUM_PROCESSORS][TASKS_PER_PROC] = { 0 };
|
||||
|
||||
/* Start all the tasks, they will block on isr_semaphore */
|
||||
for (int p = 0; p < portNUM_PROCESSORS; p++) {
|
||||
for (int t = 0; t < TASKS_PER_PROC; t++) {
|
||||
counters[p][t].trigger_sem = xSemaphoreCreateMutex();
|
||||
TEST_ASSERT_NOT_NULL( counters[p][t].trigger_sem );
|
||||
TEST_ASSERT( xSemaphoreTake(counters[p][t].trigger_sem, 0) );
|
||||
xTaskCreatePinnedToCore(counter_task_fn, "counter", 2048,
|
||||
&counters[p][t], UNITY_FREERTOS_PRIORITY + 1,
|
||||
&tasks[p][t], p);
|
||||
TEST_ASSERT_NOT_NULL( tasks[p][t] );
|
||||
}
|
||||
}
|
||||
|
||||
/* takes a while to initialize tasks on both cores, sometimes... */
|
||||
vTaskDelay(TASKS_PER_PROC * portNUM_PROCESSORS * 3);
|
||||
|
||||
/* Check nothing is counting, each counter should be blocked on its trigger_sem */
|
||||
for (int p = 0; p < portNUM_PROCESSORS; p++) {
|
||||
for (int t = 0; t < TASKS_PER_PROC; t++) {
|
||||
TEST_ASSERT_EQUAL(0, counters[p][t].counter);
|
||||
}
|
||||
}
|
||||
|
||||
/* Suspend scheduler on this CPU */
|
||||
vTaskSuspendAll();
|
||||
|
||||
/* Give all the semaphores once. You might expect this will wake up tasks on the other
|
||||
CPU (where the scheduler is not suspended) but it doesn't do this in the current implementation
|
||||
- all tasks are added to xPendingReadyList and woken up later. See note in the freertos-smp docs.
|
||||
*/
|
||||
for (int p = 0; p < portNUM_PROCESSORS; p++) {
|
||||
for (int t = 0; t < TASKS_PER_PROC; t++) {
|
||||
xSemaphoreGive(counters[p][t].trigger_sem);
|
||||
}
|
||||
}
|
||||
|
||||
ets_delay_us(2 * 1000); /* Can't vTaskDelay() while scheduler is suspended, but let other CPU do some things */
|
||||
|
||||
for (int p = 0; p < portNUM_PROCESSORS; p++) {
|
||||
for (int t = 0; t < TASKS_PER_PROC; t++) {
|
||||
/* You might expect that this is '1' for the other CPU, but it's not (see comment above) */
|
||||
ets_printf("Checking CPU %d task %d (expected 0 actual %d)\n", p, t, counters[p][t].counter);
|
||||
TEST_ASSERT_EQUAL(0, counters[p][t].counter);
|
||||
}
|
||||
}
|
||||
|
||||
/* Resume scheduler */
|
||||
xTaskResumeAll();
|
||||
|
||||
vTaskDelay(TASKS_PER_PROC * 2);
|
||||
|
||||
/* Now the tasks on both CPUs should have been woken once and counted once. */
|
||||
for (int p = 0; p < portNUM_PROCESSORS; p++) {
|
||||
for (int t = 0; t < TASKS_PER_PROC; t++) {
|
||||
ets_printf("Checking CPU %d task %d (expected 1 actual %d)\n", p, t, counters[p][t].counter);
|
||||
TEST_ASSERT_EQUAL(1, counters[p][t].counter);
|
||||
}
|
||||
}
|
||||
|
||||
/* Clean up */
|
||||
for (int p = 0; p < portNUM_PROCESSORS; p++) {
|
||||
for (int t = 0; t < TASKS_PER_PROC; t++) {
|
||||
vTaskDelete(tasks[p][t]);
|
||||
vSemaphoreDelete(counters[p][t].trigger_sem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user