Merge branch 'bugfix/freertos_event_group_unblock_race_condition_v4.4' into 'release/v4.4'

FreeRTOS: Fix event group task list race condition (v4.4)

See merge request espressif/esp-idf!19103
This commit is contained in:
Jiang Jiang Jian 2022-08-04 14:43:45 +08:00
commit 869abf8a3f
4 changed files with 62 additions and 5 deletions

View File

@ -600,6 +600,10 @@ EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
pxListEnd = listGET_END_MARKER( pxList ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */ pxListEnd = listGET_END_MARKER( pxList ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */
#ifdef ESP_PLATFORM // IDF-3755 #ifdef ESP_PLATFORM // IDF-3755
taskENTER_CRITICAL(); taskENTER_CRITICAL();
/* The critical section above only takes the event groups spinlock. However, we are about to traverse a task list.
* Thus we need call the function below to take the task list spinlock located in tasks.c. Not doing so will risk
* the task list's being changed while be are traversing it. */
vTaskTakeEventListLock();
#else #else
vTaskSuspendAll(); vTaskSuspendAll();
#endif // ESP_PLATFORM #endif // ESP_PLATFORM
@ -675,6 +679,8 @@ EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
pxEventBits->uxEventBits &= ~uxBitsToClear; pxEventBits->uxEventBits &= ~uxBitsToClear;
} }
#ifdef ESP_PLATFORM // IDF-3755 #ifdef ESP_PLATFORM // IDF-3755
/* Release the previously held task list spinlock, then release the event group spinlock. */
vTaskReleaseEventListLock();
taskEXIT_CRITICAL(); taskEXIT_CRITICAL();
#else #else
( void ) xTaskResumeAll(); ( void ) xTaskResumeAll();
@ -693,6 +699,12 @@ void vEventGroupDelete( EventGroupHandle_t xEventGroup )
// IDF-3755 // IDF-3755
taskENTER_CRITICAL(); taskENTER_CRITICAL();
#ifdef ESP_PLATFORM
/* The critical section above only takes the event groups spinlock. However, we are about to traverse a task list.
* Thus we need call the function below to take the task list spinlock located in tasks.c. Not doing so will risk
* the task list's being changed while be are traversing it. */
vTaskTakeEventListLock();
#endif
{ {
while( listCURRENT_LIST_LENGTH( pxTasksWaitingForBits ) > ( UBaseType_t ) 0 ) while( listCURRENT_LIST_LENGTH( pxTasksWaitingForBits ) > ( UBaseType_t ) 0 )
{ {
@ -702,6 +714,10 @@ void vEventGroupDelete( EventGroupHandle_t xEventGroup )
vTaskRemoveFromUnorderedEventList( pxTasksWaitingForBits->xListEnd.pxNext, eventUNBLOCKED_DUE_TO_BIT_SET ); vTaskRemoveFromUnorderedEventList( pxTasksWaitingForBits->xListEnd.pxNext, eventUNBLOCKED_DUE_TO_BIT_SET );
} }
} }
#ifdef ESP_PLATFORM
/* Release the previously held task list spinlock. */
vTaskReleaseEventListLock();
#endif
taskEXIT_CRITICAL(); taskEXIT_CRITICAL();
#if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) ) #if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) )

View File

@ -3367,6 +3367,25 @@ void vTaskPlaceOnEventListRestricted( List_t * const pxEventList,
TickType_t xTicksToWait, TickType_t xTicksToWait,
const BaseType_t xWaitIndefinitely ) PRIVILEGED_FUNCTION; const BaseType_t xWaitIndefinitely ) PRIVILEGED_FUNCTION;
#ifdef ESP_PLATFORM
/*
* THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN
* INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER.
*
* This function is a wrapper to take the "xTaskQueueMutex" spinlock of tasks.c.
* This lock is taken whenver any of the task lists or event lists are
* accessed/modified, such as when adding/removing tasks to/from the delayed
* task list or various event lists.
*
* This functions is meant to be called by xEventGroupSetBits() and
* vEventGroupDelete() as both those functions will access event lists (instead
* of delegating the entire responsibility to one of vTask...EventList()
* functions).
*/
void vTaskTakeEventListLock( void );
void vTaskReleaseEventListLock( void );
#endif // ESP_PLATFORM
/* /*
* THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN
* INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER.

View File

@ -3733,6 +3733,20 @@ BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList )
} }
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
#ifdef ESP_PLATFORM
void vTaskTakeEventListLock( void )
{
/* We call the tasks.c critical section macro to take xTaskQueueMutex */
taskENTER_CRITICAL();
}
void vTaskReleaseEventListLock( void )
{
/* We call the tasks.c critical section macro to release xTaskQueueMutex */
taskEXIT_CRITICAL();
}
#endif // ESP_PLATFORM
void vTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, void vTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem,
const TickType_t xItemValue ) const TickType_t xItemValue )
{ {

View File

@ -7,6 +7,7 @@
#include "freertos/event_groups.h" #include "freertos/event_groups.h"
#include "driver/timer.h" #include "driver/timer.h"
#include "unity.h" #include "unity.h"
#include "test_utils.h"
#ifdef CONFIG_IDF_TARGET_ESP32S2 #ifdef CONFIG_IDF_TARGET_ESP32S2
#define int_clr_timers int_clr #define int_clr_timers int_clr
@ -40,7 +41,8 @@ static void task_event_group_call_response(void *param)
printf("Task %d done\n", task_num); printf("Task %d done\n", task_num);
xSemaphoreGive(done_sem); xSemaphoreGive(done_sem);
vTaskDelete(NULL); // Wait to be deleted
vTaskSuspend(NULL);
} }
TEST_CASE("FreeRTOS Event Groups", "[freertos]") TEST_CASE("FreeRTOS Event Groups", "[freertos]")
@ -48,6 +50,8 @@ TEST_CASE("FreeRTOS Event Groups", "[freertos]")
eg = xEventGroupCreate(); eg = xEventGroupCreate();
done_sem = xSemaphoreCreateCounting(NUM_TASKS, 0); done_sem = xSemaphoreCreateCounting(NUM_TASKS, 0);
TaskHandle_t task_handles[NUM_TASKS];
/* Note: task_event_group_call_response all have higher priority than this task, so on this core /* Note: task_event_group_call_response all have higher priority than this task, so on this core
they will always preempt this task. they will always preempt this task.
@ -55,7 +59,7 @@ TEST_CASE("FreeRTOS Event Groups", "[freertos]")
or they get out of sync. or they get out of sync.
*/ */
for (int c = 0; c < NUM_TASKS; c++) { for (int c = 0; c < NUM_TASKS; c++) {
xTaskCreatePinnedToCore(task_event_group_call_response, "tsk_call_resp", 4096, (void *)c, configMAX_PRIORITIES - 1, NULL, c % portNUM_PROCESSORS); xTaskCreatePinnedToCore(task_event_group_call_response, "tsk_call_resp", 4096, (void *)c, configMAX_PRIORITIES - 1, &task_handles[c], c % portNUM_PROCESSORS);
} }
/* Tasks all start instantly, but this task will resume running at the same time as the higher priority tasks on the /* Tasks all start instantly, but this task will resume running at the same time as the higher priority tasks on the
@ -66,15 +70,19 @@ TEST_CASE("FreeRTOS Event Groups", "[freertos]")
/* signal all tasks with "CALL" bit... */ /* signal all tasks with "CALL" bit... */
xEventGroupSetBits(eg, BIT_CALL); xEventGroupSetBits(eg, BIT_CALL);
/* Only wait for 1 tick, the wakeup should be immediate... */ /* Wait until all tasks have set their respective response bits */
TEST_ASSERT_EQUAL_HEX16(ALL_RESPONSE_BITS, xEventGroupWaitBits(eg, ALL_RESPONSE_BITS, true, true, 1)); TEST_ASSERT_EQUAL_HEX16(ALL_RESPONSE_BITS, xEventGroupWaitBits(eg, ALL_RESPONSE_BITS, true, true, portMAX_DELAY));
} }
/* Ensure all tasks cleaned up correctly */ /* Ensure all tasks have suspend themselves */
for (int c = 0; c < NUM_TASKS; c++) { for (int c = 0; c < NUM_TASKS; c++) {
TEST_ASSERT( xSemaphoreTake(done_sem, 100/portTICK_PERIOD_MS) ); TEST_ASSERT( xSemaphoreTake(done_sem, 100/portTICK_PERIOD_MS) );
} }
for (int c = 0; c < NUM_TASKS; c++) {
test_utils_task_delete(task_handles[c]);
}
vSemaphoreDelete(done_sem); vSemaphoreDelete(done_sem);
vEventGroupDelete(eg); vEventGroupDelete(eg);
} }