freertos: Fix event group task list race condition

FreeRTOS synchronization primitives (e.g., queues, eventgroups) use various event lists (i.e., task lists) to track what
tasks are blocked on a current primitive. Usually these event lists are accessed via one of the event lists functions
(such as vTask[PlaceOn|RemoveFrom]UnorderedEventList()), which in turn ensure that the global task list spinlock
(xTaskQueueMutex) is taken when accessing these lists.

However, some functions in event_groups.c manually traverse their event lists. Thus if a tick interrupt occurs on
another core during traversal and that tick interrupt unblocks a task on the event list being traversed, the event list
will be corrupted.

This commit modifies the following event_groups.c functions so that they take the global task list lock before
traversing their event list.

- xEventGroupSetBits()
- vEventGroupDelete()
This commit is contained in:
Darian Leung
2022-06-29 00:58:08 +08:00
parent 3df4c01d62
commit 1625a3aae2
3 changed files with 49 additions and 0 deletions

View File

@@ -607,6 +607,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. */
#ifdef ESP_PLATFORM // IDF-3755
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
vTaskSuspendAll();
#endif // ESP_PLATFORM
@@ -682,6 +686,8 @@ EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup,
pxEventBits->uxEventBits &= ~uxBitsToClear;
}
#ifdef ESP_PLATFORM // IDF-3755
/* Release the previously held task list spinlock, then release the event group spinlock. */
vTaskReleaseEventListLock();
taskEXIT_CRITICAL();
#else
( void ) xTaskResumeAll();
@@ -700,6 +706,12 @@ void vEventGroupDelete( EventGroupHandle_t xEventGroup )
// IDF-3755
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 )
{
@@ -709,6 +721,10 @@ void vEventGroupDelete( EventGroupHandle_t xEventGroup )
vTaskRemoveFromUnorderedEventList( pxTasksWaitingForBits->xListEnd.pxNext, eventUNBLOCKED_DUE_TO_BIT_SET );
}
}
#ifdef ESP_PLATFORM
/* Release the previously held task list spinlock. */
vTaskReleaseEventListLock();
#endif
taskEXIT_CRITICAL();
#if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) )