mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
3e4fcf66ef
vTaskDeleteWithCaps() leaked memory when a task uses the API to delete itself. This commit adds a fix to avoid the memory leak. Closes https://github.com/espressif/esp-idf/issues/14222
496 lines
18 KiB
C
496 lines
18 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/*
|
|
* This file contains the implementation for some the functions in
|
|
* idf_additions.h
|
|
*/
|
|
|
|
#include "sdkconfig.h"
|
|
#include <stdint.h>
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "freertos/queue.h"
|
|
#include "freertos/semphr.h"
|
|
#include "freertos/stream_buffer.h"
|
|
#include "freertos/message_buffer.h"
|
|
#include "freertos/event_groups.h"
|
|
#include "freertos/timers.h"
|
|
#include "freertos/idf_additions.h"
|
|
#include "esp_heap_caps.h"
|
|
#include "esp_log.h"
|
|
#include "freertos/portmacro.h"
|
|
|
|
/* -------------------------------------------- Creation With Memory Caps ------------------------------------------- */
|
|
|
|
/* ---------------------------------- Tasks --------------------------------- */
|
|
|
|
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
|
|
|
BaseType_t xTaskCreatePinnedToCoreWithCaps( TaskFunction_t pvTaskCode,
|
|
const char * const pcName,
|
|
const configSTACK_DEPTH_TYPE usStackDepth,
|
|
void * const pvParameters,
|
|
UBaseType_t uxPriority,
|
|
TaskHandle_t * const pvCreatedTask,
|
|
const BaseType_t xCoreID,
|
|
UBaseType_t uxMemoryCaps )
|
|
{
|
|
TaskHandle_t xHandle;
|
|
StaticTask_t * pxTaskBuffer;
|
|
StackType_t * pxStack;
|
|
|
|
/* Allocate memory for the task's TCB. We use pvPortMalloc() here as the
|
|
* TCB must be in internal memory. */
|
|
pxTaskBuffer = pvPortMalloc( sizeof( StaticTask_t ) );
|
|
|
|
/* Allocate memory for the task's stack using the provided memory caps
|
|
* */
|
|
pxStack = heap_caps_malloc( usStackDepth, ( uint32_t ) uxMemoryCaps );
|
|
|
|
if( ( pxTaskBuffer == NULL ) || ( pxStack == NULL ) )
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
/* Create the task using static creation API*/
|
|
xHandle = xTaskCreateStaticPinnedToCore( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxStack, pxTaskBuffer, xCoreID );
|
|
|
|
if( xHandle == NULL )
|
|
{
|
|
/* Failed to create task */
|
|
goto err;
|
|
}
|
|
else if( pvCreatedTask != NULL )
|
|
{
|
|
/* Task created successfully. Return the task handle */
|
|
*pvCreatedTask = xHandle;
|
|
}
|
|
|
|
return pdPASS;
|
|
|
|
err:
|
|
heap_caps_free( pxStack );
|
|
vPortFree( pxTaskBuffer );
|
|
return pdFAIL;
|
|
}
|
|
|
|
#endif /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */
|
|
/*----------------------------------------------------------*/
|
|
|
|
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
|
|
|
static void prvTaskDeleteWithCapsTask( void * pvParameters )
|
|
{
|
|
TaskHandle_t xTaskToDelete = ( TaskHandle_t ) pvParameters;
|
|
|
|
/* The task to be deleted must not be running */
|
|
configASSERT( eRunning != eTaskGetState( xTaskToDelete ) );
|
|
|
|
/* Delete the WithCaps task */
|
|
vTaskDeleteWithCaps( xTaskToDelete );
|
|
|
|
/* Delete the temporary clean up task */
|
|
vTaskDelete( NULL );
|
|
}
|
|
|
|
void vTaskDeleteWithCaps( TaskHandle_t xTaskToDelete )
|
|
{
|
|
/* THIS FUNCTION SHOULD NOT BE CALLED FROM AN INTERRUPT CONTEXT. */
|
|
/*TODO: Update it to use portASSERT_IF_IN_ISR() instead. (IDF-10540) */
|
|
vPortAssertIfInISR();
|
|
|
|
TaskHandle_t xCurrentTaskHandle = xTaskGetCurrentTaskHandle();
|
|
configASSERT( xCurrentTaskHandle != NULL );
|
|
|
|
if( ( xTaskToDelete == NULL ) || ( xTaskToDelete == xCurrentTaskHandle ) )
|
|
{
|
|
/* The WithCaps task is deleting itself. While, the task can put itself on the
|
|
* xTasksWaitingTermination list via the vTaskDelete() call, the idle
|
|
* task will not free the task TCB and stack memories we created statically
|
|
* during xTaskCreateWithCaps() or xTaskCreatePinnedToCoreWithCaps(). This
|
|
* task will never be rescheduled once it is on the xTasksWaitingTermination
|
|
* list and will not be able to clear the memories. Therefore, it will leak memory.
|
|
*
|
|
* To avoid this, we create a new "temporary clean up" task to delete the current task.
|
|
* This task is created at the priority of the task to be deleted with the same core
|
|
* affitinty. Its limited purpose is to delete the self-deleting task created WithCaps.
|
|
*
|
|
* This approach has the following problems -
|
|
* 1. Once a WithCaps task deletes itself via vTaskDeleteWithCaps(), it may end up in the
|
|
* suspended tasks lists for a short time before being deleted. This can give an incorrect
|
|
* picture about the system state.
|
|
*
|
|
* 2. This approach is wasteful and can be error prone. The temporary clean up task will need
|
|
* system resources to get scheduled and cleanup the WithCaps task. It can be a problem if the system
|
|
* has several self-deleting WithCaps tasks.
|
|
*
|
|
* TODO: A better approach could be either -
|
|
*
|
|
* 1. Delegate memory management to the application/user. This way the kernel needn't bother about freeing
|
|
* the memory (like other static memory task creation APIs like xTaskCreateStatic()) (IDF-10521)
|
|
*
|
|
* 2. Have a post deletion hook/callback from the IDLE task to notify higher layers when it is safe to
|
|
* perform activities such as clearing up the TCB and stack memories. (IDF-10522) */
|
|
if( xTaskCreatePinnedToCore( ( TaskFunction_t ) prvTaskDeleteWithCapsTask, "prvTaskDeleteWithCapsTask", configMINIMAL_STACK_SIZE, xCurrentTaskHandle, uxTaskPriorityGet( xTaskToDelete ), NULL, xPortGetCoreID() ) != pdFAIL )
|
|
{
|
|
/* Although the current task should get preemted immediately when prvTaskDeleteWithCapsTask is created,
|
|
* for safety, we suspend the current task and wait for prvTaskDeleteWithCapsTask to delete it. */
|
|
vTaskSuspend( xTaskToDelete );
|
|
|
|
/* Should never reach here */
|
|
ESP_LOGE( "freertos_additions", "%s: Failed to suspend the task to be deleted", __func__ );
|
|
abort();
|
|
}
|
|
else
|
|
{
|
|
/* Failed to create the task to delete the current task. */
|
|
ESP_LOGE( "freertos_additions", "%s: Failed to create the task to delete the current task", __func__ );
|
|
abort();
|
|
}
|
|
}
|
|
|
|
#if ( configNUM_CORES > 1 )
|
|
else if( eRunning == eTaskGetState( xTaskToDelete ) )
|
|
{
|
|
/* The WithCaps task is running on another core.
|
|
* We suspend the task first and then delete it. */
|
|
vTaskSuspend( xTaskToDelete );
|
|
|
|
/* Wait for the task to be suspended */
|
|
while( eRunning == eTaskGetState( xTaskToDelete ) )
|
|
{
|
|
portYIELD_WITHIN_API();
|
|
}
|
|
|
|
BaseType_t xResult;
|
|
StaticTask_t * pxTaskBuffer;
|
|
StackType_t * puxStackBuffer;
|
|
|
|
xResult = xTaskGetStaticBuffers( xTaskToDelete, &puxStackBuffer, &pxTaskBuffer );
|
|
configASSERT( xResult == pdTRUE );
|
|
configASSERT( puxStackBuffer != NULL );
|
|
configASSERT( pxTaskBuffer != NULL );
|
|
|
|
/* Delete the task */
|
|
vTaskDelete( xTaskToDelete );
|
|
|
|
/* Free the memory buffers */
|
|
heap_caps_free( puxStackBuffer );
|
|
vPortFree( pxTaskBuffer );
|
|
}
|
|
#endif /* if ( configNUM_CORES > 1 ) */
|
|
else
|
|
{
|
|
/* The WithCaps task is not running and is being deleted
|
|
* from another task's context. */
|
|
configASSERT( eRunning != eTaskGetState( xTaskToDelete ) );
|
|
|
|
BaseType_t xResult;
|
|
StaticTask_t * pxTaskBuffer;
|
|
StackType_t * puxStackBuffer;
|
|
|
|
xResult = xTaskGetStaticBuffers( xTaskToDelete, &puxStackBuffer, &pxTaskBuffer );
|
|
configASSERT( xResult == pdTRUE );
|
|
configASSERT( puxStackBuffer != NULL );
|
|
configASSERT( pxTaskBuffer != NULL );
|
|
|
|
/* We can delete the task and free the memory buffers. */
|
|
vTaskDelete( xTaskToDelete );
|
|
|
|
/* Free the memory buffers */
|
|
heap_caps_free( puxStackBuffer );
|
|
vPortFree( pxTaskBuffer );
|
|
} /* if( ( xTaskToDelete == NULL ) || ( xTaskToDelete == xCurrentTaskHandle ) ) */
|
|
}
|
|
|
|
#endif /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */
|
|
/*----------------------------------------------------------*/
|
|
|
|
/* ---------------------------------- Queue --------------------------------- */
|
|
|
|
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
|
|
|
QueueHandle_t xQueueCreateWithCaps( UBaseType_t uxQueueLength,
|
|
UBaseType_t uxItemSize,
|
|
UBaseType_t uxMemoryCaps )
|
|
{
|
|
QueueHandle_t xQueue;
|
|
StaticQueue_t * pxQueueBuffer;
|
|
uint8_t * pucQueueStorageBuffer;
|
|
|
|
/* Allocate memory for the queue using the provided memory caps */
|
|
pxQueueBuffer = heap_caps_malloc( sizeof( StaticQueue_t ), ( uint32_t ) uxMemoryCaps );
|
|
|
|
if( uxItemSize == 0 )
|
|
{
|
|
pucQueueStorageBuffer = NULL;
|
|
}
|
|
else
|
|
{
|
|
pucQueueStorageBuffer = heap_caps_malloc( uxQueueLength * uxItemSize, ( uint32_t ) uxMemoryCaps );
|
|
}
|
|
|
|
if( ( pxQueueBuffer == NULL ) || ( ( uxItemSize > 0 ) && ( pucQueueStorageBuffer == NULL ) ) )
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
/* Create the queue using static creation API */
|
|
xQueue = xQueueCreateStatic( uxQueueLength, uxItemSize, pucQueueStorageBuffer, pxQueueBuffer );
|
|
|
|
if( xQueue == NULL )
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
return xQueue;
|
|
|
|
err:
|
|
heap_caps_free( pucQueueStorageBuffer );
|
|
heap_caps_free( pxQueueBuffer );
|
|
return NULL;
|
|
}
|
|
|
|
#endif /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */
|
|
/*----------------------------------------------------------*/
|
|
|
|
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
|
|
|
void vQueueDeleteWithCaps( QueueHandle_t xQueue )
|
|
{
|
|
BaseType_t xResult;
|
|
StaticQueue_t * pxQueueBuffer;
|
|
uint8_t * pucQueueStorageBuffer;
|
|
|
|
/* Retrieve the buffers used to create the queue before deleting it */
|
|
xResult = xQueueGetStaticBuffers( xQueue, &pucQueueStorageBuffer, &pxQueueBuffer );
|
|
configASSERT( xResult == pdTRUE );
|
|
|
|
/* Delete the queue */
|
|
vQueueDelete( xQueue );
|
|
|
|
/* Free the memory buffers */
|
|
heap_caps_free( pxQueueBuffer );
|
|
heap_caps_free( pucQueueStorageBuffer );
|
|
}
|
|
|
|
#endif /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */
|
|
/*----------------------------------------------------------*/
|
|
|
|
/* -------------------------------- Semaphore ------------------------------- */
|
|
|
|
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
|
|
|
SemaphoreHandle_t xSemaphoreCreateGenericWithCaps( UBaseType_t uxMaxCount,
|
|
UBaseType_t uxInitialCount,
|
|
const uint8_t ucQueueType,
|
|
UBaseType_t uxMemoryCaps )
|
|
{
|
|
SemaphoreHandle_t xSemaphore;
|
|
StaticSemaphore_t * pxSemaphoreBuffer;
|
|
|
|
/* Allocate memory for the semaphore using the provided memory caps */
|
|
pxSemaphoreBuffer = heap_caps_malloc( sizeof( StaticSemaphore_t ), ( uint32_t ) uxMemoryCaps );
|
|
|
|
if( pxSemaphoreBuffer == NULL )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* Create the semaphore using static creation API */
|
|
if( ucQueueType == queueQUEUE_TYPE_MUTEX )
|
|
{
|
|
xSemaphore = xSemaphoreCreateMutexStatic( pxSemaphoreBuffer );
|
|
}
|
|
else if( ucQueueType == queueQUEUE_TYPE_COUNTING_SEMAPHORE )
|
|
{
|
|
xSemaphore = xSemaphoreCreateCountingStatic( uxMaxCount, uxInitialCount, pxSemaphoreBuffer );
|
|
}
|
|
else if( ucQueueType == queueQUEUE_TYPE_BINARY_SEMAPHORE )
|
|
{
|
|
xSemaphore = xSemaphoreCreateBinaryStatic( pxSemaphoreBuffer );
|
|
}
|
|
else /* ucQueueType == queueQUEUE_TYPE_RECURSIVE_MUTEX */
|
|
{
|
|
xSemaphore = xSemaphoreCreateRecursiveMutexStatic( pxSemaphoreBuffer );
|
|
}
|
|
|
|
if( xSemaphore == NULL )
|
|
{
|
|
heap_caps_free( pxSemaphoreBuffer );
|
|
}
|
|
|
|
return xSemaphore;
|
|
}
|
|
|
|
#endif /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */
|
|
/*----------------------------------------------------------*/
|
|
|
|
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
|
|
|
void vSemaphoreDeleteWithCaps( SemaphoreHandle_t xSemaphore )
|
|
{
|
|
BaseType_t xResult;
|
|
StaticSemaphore_t * pxSemaphoreBuffer;
|
|
|
|
/* Retrieve the buffer used to create the semaphore before deleting it
|
|
* */
|
|
xResult = xSemaphoreGetStaticBuffer( xSemaphore, &pxSemaphoreBuffer );
|
|
configASSERT( xResult == pdTRUE );
|
|
|
|
/* Delete the semaphore */
|
|
vSemaphoreDelete( xSemaphore );
|
|
|
|
/* Free the memory buffer */
|
|
heap_caps_free( pxSemaphoreBuffer );
|
|
}
|
|
|
|
#endif /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */
|
|
/*----------------------------------------------------------*/
|
|
|
|
/* ------------------------- Stream & Message Buffers ----------------------- */
|
|
|
|
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
|
|
|
StreamBufferHandle_t xStreamBufferGenericCreateWithCaps( size_t xBufferSizeBytes,
|
|
size_t xTriggerLevelBytes,
|
|
BaseType_t xIsMessageBuffer,
|
|
UBaseType_t uxMemoryCaps )
|
|
{
|
|
StreamBufferHandle_t xStreamBuffer;
|
|
StaticStreamBuffer_t * pxStaticStreamBuffer;
|
|
uint8_t * pucStreamBufferStorageArea;
|
|
|
|
/* Allocate memory for the stream or message buffer using the provided
|
|
* memory caps */
|
|
pxStaticStreamBuffer = heap_caps_malloc( sizeof( StaticStreamBuffer_t ), ( uint32_t ) uxMemoryCaps );
|
|
pucStreamBufferStorageArea = heap_caps_malloc( xBufferSizeBytes, ( uint32_t ) uxMemoryCaps );
|
|
|
|
if( ( pxStaticStreamBuffer == NULL ) || ( pucStreamBufferStorageArea == NULL ) )
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
/* Create the stream or message buffer using static creation API */
|
|
if( xIsMessageBuffer == pdTRUE )
|
|
{
|
|
xStreamBuffer = ( StreamBufferHandle_t ) xMessageBufferCreateStatic( xBufferSizeBytes, pucStreamBufferStorageArea, pxStaticStreamBuffer );
|
|
}
|
|
else
|
|
{
|
|
xStreamBuffer = xStreamBufferCreateStatic( xBufferSizeBytes, xTriggerLevelBytes, pucStreamBufferStorageArea, pxStaticStreamBuffer );
|
|
}
|
|
|
|
if( xStreamBuffer == NULL )
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
return xStreamBuffer;
|
|
|
|
err:
|
|
heap_caps_free( pucStreamBufferStorageArea );
|
|
heap_caps_free( pxStaticStreamBuffer );
|
|
return NULL;
|
|
}
|
|
|
|
#endif /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */
|
|
/*----------------------------------------------------------*/
|
|
|
|
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
|
|
|
void vStreamBufferGenericDeleteWithCaps( StreamBufferHandle_t xStreamBuffer,
|
|
BaseType_t xIsMessageBuffer )
|
|
{
|
|
BaseType_t xResult;
|
|
StaticStreamBuffer_t * pxStaticStreamBuffer;
|
|
uint8_t * pucStreamBufferStorageArea;
|
|
|
|
/* Retrieve the buffers used to create the stream or message buffer
|
|
* before deleting it */
|
|
if( xIsMessageBuffer == pdTRUE )
|
|
{
|
|
xResult = xMessageBufferGetStaticBuffers( xStreamBuffer, &pucStreamBufferStorageArea, &pxStaticStreamBuffer );
|
|
}
|
|
else
|
|
{
|
|
xResult = xStreamBufferGetStaticBuffers( xStreamBuffer, &pucStreamBufferStorageArea, &pxStaticStreamBuffer );
|
|
}
|
|
|
|
configASSERT( xResult == pdTRUE );
|
|
|
|
/* Delete the stream or message buffer */
|
|
if( xIsMessageBuffer == pdTRUE )
|
|
{
|
|
vMessageBufferDelete( xStreamBuffer );
|
|
}
|
|
else
|
|
{
|
|
vSemaphoreDelete( xStreamBuffer );
|
|
}
|
|
|
|
/* Free the memory buffers */
|
|
heap_caps_free( pxStaticStreamBuffer );
|
|
heap_caps_free( pucStreamBufferStorageArea );
|
|
}
|
|
|
|
#endif /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */
|
|
/*----------------------------------------------------------*/
|
|
|
|
/* ------------------------------ Event Groups ------------------------------ */
|
|
|
|
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
|
|
|
EventGroupHandle_t xEventGroupCreateWithCaps( UBaseType_t uxMemoryCaps )
|
|
{
|
|
EventGroupHandle_t xEventGroup;
|
|
StaticEventGroup_t * pxEventGroupBuffer;
|
|
|
|
/* Allocate memory for the event group using the provided memory caps */
|
|
pxEventGroupBuffer = heap_caps_malloc( sizeof( StaticEventGroup_t ), uxMemoryCaps );
|
|
|
|
if( pxEventGroupBuffer == NULL )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* Create the event group using static creation API */
|
|
xEventGroup = xEventGroupCreateStatic( pxEventGroupBuffer );
|
|
|
|
if( xEventGroup == NULL )
|
|
{
|
|
heap_caps_free( pxEventGroupBuffer );
|
|
}
|
|
|
|
return xEventGroup;
|
|
}
|
|
|
|
#endif /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */
|
|
/*----------------------------------------------------------*/
|
|
|
|
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
|
|
|
void vEventGroupDeleteWithCaps( EventGroupHandle_t xEventGroup )
|
|
{
|
|
BaseType_t xResult;
|
|
StaticEventGroup_t * pxEventGroupBuffer;
|
|
|
|
/* Retrieve the buffer used to create the event group before deleting it
|
|
* */
|
|
xResult = xEventGroupGetStaticBuffer( xEventGroup, &pxEventGroupBuffer );
|
|
configASSERT( xResult == pdTRUE );
|
|
|
|
/* Delete the event group */
|
|
vEventGroupDelete( xEventGroup );
|
|
|
|
/* Free the memory buffer */
|
|
heap_caps_free( pxEventGroupBuffer );
|
|
}
|
|
|
|
#endif /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */
|
|
/*----------------------------------------------------------*/
|