Merge branch 'bugfix/freertos_check_task_waiting_termination' into 'master'
Freertos Task Deletion backport and FPU documentation See merge request !1591
This commit is contained in:
commit
2b92119840
|
@ -541,6 +541,12 @@ static portTASK_FUNCTION_PROTO( prvIdleTask, pvParameters );
|
|||
|
||||
#endif
|
||||
|
||||
//Function to call the Thread Local Storage Pointer Deletion Callbacks. Will be
|
||||
//called during task deletion before prvDeleteTCB is called.
|
||||
#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS )
|
||||
static void prvDeleteTLS( TCB_t *pxTCB );
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Used only by the idle task. This checks to see if anything has been placed
|
||||
* in the list of tasks waiting to be deleted. If so the task is cleaned up
|
||||
|
@ -1201,19 +1207,25 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
|
|||
/*-----------------------------------------------------------*/
|
||||
|
||||
#if ( INCLUDE_vTaskDelete == 1 )
|
||||
|
||||
void vTaskDelete( TaskHandle_t xTaskToDelete )
|
||||
{
|
||||
//The following vTaskDelete() is backported from FreeRTOS v9.0.0 and modified for SMP.
|
||||
//v9.0.0 vTaskDelete() will immediately free task memory if the task being deleted is
|
||||
//NOT currently running and not pinned to the other core. Otherwise, freeing of task memory
|
||||
//will still be delegated to the Idle Task.
|
||||
|
||||
TCB_t *pxTCB;
|
||||
int core = xPortGetCoreID(); //Current core
|
||||
UBaseType_t free_now; //Flag to indicate if task memory can be freed immediately
|
||||
|
||||
taskENTER_CRITICAL(&xTaskQueueMutex);
|
||||
{
|
||||
/* If null is passed in here then it is the calling task that is
|
||||
being deleted. */
|
||||
pxTCB = prvGetTCBFromHandle( xTaskToDelete );
|
||||
|
||||
/* Remove task from the ready list and place in the termination list.
|
||||
This will stop the task from be scheduled. The idle task will check
|
||||
the termination list and free up any memory allocated by the
|
||||
scheduler for the TCB and stack. */
|
||||
/* Remove task from the ready list. */
|
||||
if( uxListRemove( &( pxTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 )
|
||||
{
|
||||
taskRESET_READY_PRIORITY( pxTCB->uxPriority );
|
||||
|
@ -1233,29 +1245,67 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
|
|||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
|
||||
vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xGenericListItem ) );
|
||||
|
||||
/* Increment the ucTasksDeleted variable so the idle task knows
|
||||
there is a task that has been deleted and that it should therefore
|
||||
check the xTasksWaitingTermination list. */
|
||||
++uxTasksDeleted;
|
||||
|
||||
/* Increment the uxTaskNumberVariable also so kernel aware debuggers
|
||||
can detect that the task lists need re-generating. */
|
||||
/* Increment the uxTaskNumber also so kernel aware debuggers can
|
||||
detect that the task lists need re-generating. This is done before
|
||||
portPRE_TASK_DELETE_HOOK() as in the Windows port that macro will
|
||||
not return. */
|
||||
uxTaskNumber++;
|
||||
|
||||
//If task to be deleted is currently running on either core or is pinned to the other core. Let Idle free memory
|
||||
if( pxTCB == pxCurrentTCB[ core ] ||
|
||||
(portNUM_PROCESSORS > 1 && pxTCB == pxCurrentTCB[ !core ]) ||
|
||||
(portNUM_PROCESSORS > 1 && pxTCB->xCoreID == (!core)) )
|
||||
{
|
||||
/* Deleting a currently running task. This cannot complete
|
||||
within the task itself, as a context switch to another task is
|
||||
required. Place the task in the termination list. The idle task
|
||||
will check the termination list and free up any memory allocated
|
||||
by the scheduler for the TCB and stack of the deleted task. */
|
||||
vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xGenericListItem ) );
|
||||
|
||||
/* Increment the ucTasksDeleted variable so the idle task knows
|
||||
there is a task that has been deleted and that it should therefore
|
||||
check the xTasksWaitingTermination list. */
|
||||
++uxTasksDeleted;
|
||||
|
||||
/* The pre-delete hook is primarily for the Windows simulator,
|
||||
in which Windows specific clean up operations are performed,
|
||||
after which it is not possible to yield away from this task -
|
||||
hence xYieldPending is used to latch that a context switch is
|
||||
required. */
|
||||
portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );
|
||||
|
||||
free_now = pdFALSE; //Let Idle Task free task memory
|
||||
}
|
||||
else //Task is not currently running and not pinned to the other core
|
||||
{
|
||||
--uxCurrentNumberOfTasks;
|
||||
|
||||
/* Reset the next expected unblock time in case it referred to
|
||||
the task that has just been deleted. */
|
||||
prvResetNextTaskUnblockTime();
|
||||
free_now = pdTRUE; //Set flag to free task memory immediately
|
||||
}
|
||||
|
||||
traceTASK_DELETE( pxTCB );
|
||||
}
|
||||
taskEXIT_CRITICAL(&xTaskQueueMutex);
|
||||
|
||||
if(free_now == pdTRUE){ //Free task memory. Outside critical section due to deletion callbacks
|
||||
#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS )
|
||||
prvDeleteTLS( pxTCB ); //Run deletion callbacks before deleting TCB
|
||||
#endif
|
||||
prvDeleteTCB( pxTCB ); //Must only be called after del cb
|
||||
}
|
||||
|
||||
/* Force a reschedule if it is the currently running task that has just
|
||||
been deleted. */
|
||||
if( xSchedulerRunning != pdFALSE )
|
||||
{
|
||||
//No mux; no harm done if this misfires. The deleted task won't get scheduled anyway.
|
||||
if( pxTCB == pxCurrentTCB[ xPortGetCoreID() ] )
|
||||
if( pxTCB == pxCurrentTCB[ core ] ) //If task was currently running on this core
|
||||
{
|
||||
configASSERT( uxSchedulerSuspended[ xPortGetCoreID() ] == 0 );
|
||||
configASSERT( uxSchedulerSuspended[ core ] == 0 );
|
||||
|
||||
/* The pre-delete hook is primarily for the Windows simulator,
|
||||
in which Windows specific clean up operations are performed,
|
||||
|
@ -1265,20 +1315,14 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode
|
|||
portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending[xPortGetCoreID()] );
|
||||
portYIELD_WITHIN_API();
|
||||
}
|
||||
else if ( portNUM_PROCESSORS > 1 && pxTCB == pxCurrentTCB[ !xPortGetCoreID() ] )
|
||||
else if ( portNUM_PROCESSORS > 1 && pxTCB == pxCurrentTCB[ !core] ) //If task was currently running on the other core
|
||||
{
|
||||
/* if task is running on the other CPU, force a yield on that CPU to take it off */
|
||||
vPortYieldOtherCore( !xPortGetCoreID() );
|
||||
vPortYieldOtherCore( !core );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Reset the next expected unblock time in case it referred to
|
||||
the task that has just been deleted. */
|
||||
taskENTER_CRITICAL(&xTaskQueueMutex);
|
||||
{
|
||||
prvResetNextTaskUnblockTime();
|
||||
}
|
||||
taskEXIT_CRITICAL(&xTaskQueueMutex);
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3583,52 +3627,48 @@ static void prvCheckTasksWaitingTermination( void )
|
|||
#if ( INCLUDE_vTaskDelete == 1 )
|
||||
{
|
||||
BaseType_t xListIsEmpty;
|
||||
int core = xPortGetCoreID();
|
||||
|
||||
/* ucTasksDeleted is used to prevent vTaskSuspendAll() being called
|
||||
too often in the idle task. */
|
||||
while(uxTasksDeleted > ( UBaseType_t ) 0U )
|
||||
{
|
||||
TCB_t *pxTCB = NULL;
|
||||
|
||||
taskENTER_CRITICAL(&xTaskQueueMutex);
|
||||
{
|
||||
xListIsEmpty = listLIST_IS_EMPTY( &xTasksWaitingTermination );
|
||||
}
|
||||
|
||||
if( xListIsEmpty == pdFALSE )
|
||||
{
|
||||
if( xListIsEmpty == pdFALSE )
|
||||
{
|
||||
pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) );
|
||||
/* We only want to kill tasks that ran on this core because e.g. _xt_coproc_release needs to
|
||||
be called on the core the process is pinned on, if any */
|
||||
if( pxTCB->xCoreID == tskNO_AFFINITY || pxTCB->xCoreID == xPortGetCoreID()) {
|
||||
( void ) uxListRemove( &( pxTCB->xGenericListItem ) );
|
||||
be called on the core the process is pinned on, if any */
|
||||
ListItem_t *target = listGET_HEAD_ENTRY(&xTasksWaitingTermination);
|
||||
for( ; target != listGET_END_MARKER(&xTasksWaitingTermination); target = listGET_NEXT(target) ){
|
||||
int coreid = (( TCB_t * )listGET_LIST_ITEM_OWNER(target))->xCoreID;
|
||||
if(coreid == core || coreid == tskNO_AFFINITY){ //Find first item not pinned to other core
|
||||
pxTCB = ( TCB_t * )listGET_LIST_ITEM_OWNER(target);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(pxTCB != NULL){
|
||||
( void ) uxListRemove( target ); //Remove list item from list
|
||||
--uxCurrentNumberOfTasks;
|
||||
--uxTasksDeleted;
|
||||
} else {
|
||||
/* Need to wait until the idle task on the other processor kills that task first. */
|
||||
taskEXIT_CRITICAL(&xTaskQueueMutex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
taskEXIT_CRITICAL(&xTaskQueueMutex);
|
||||
taskEXIT_CRITICAL(&xTaskQueueMutex); //Need to call deletion callbacks outside critical section
|
||||
|
||||
if (pxTCB != NULL) {
|
||||
#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS )
|
||||
int x;
|
||||
for( x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ )
|
||||
{
|
||||
if (pxTCB->pvThreadLocalStoragePointersDelCallback[ x ] != NULL)
|
||||
{
|
||||
pxTCB->pvThreadLocalStoragePointersDelCallback[ x ](x, pxTCB->pvThreadLocalStoragePointers[ x ]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
prvDeleteTCB( pxTCB );
|
||||
if (pxTCB != NULL) { //Call deletion callbacks and free TCB memory
|
||||
#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS )
|
||||
prvDeleteTLS( pxTCB );
|
||||
#endif
|
||||
prvDeleteTCB( pxTCB );
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
break; //No TCB found that could be freed by this core, break out of loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3831,7 +3871,6 @@ BaseType_t xTaskGetAffinity( TaskHandle_t xTask )
|
|||
|
||||
#if ( INCLUDE_vTaskDelete == 1 )
|
||||
|
||||
|
||||
static void prvDeleteTCB( TCB_t *pxTCB )
|
||||
{
|
||||
/* Free up the memory allocated by the scheduler for the task. It is up
|
||||
|
@ -3886,6 +3925,23 @@ BaseType_t xTaskGetAffinity( TaskHandle_t xTask )
|
|||
#endif /* INCLUDE_vTaskDelete */
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS )
|
||||
|
||||
static void prvDeleteTLS( TCB_t *pxTCB )
|
||||
{
|
||||
configASSERT( pxTCB );
|
||||
for( int x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ )
|
||||
{
|
||||
if (pxTCB->pvThreadLocalStoragePointersDelCallback[ x ] != NULL) //If del cb is set
|
||||
{
|
||||
pxTCB->pvThreadLocalStoragePointersDelCallback[ x ](x, pxTCB->pvThreadLocalStoragePointers[ x ]); //Call del cb
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) */
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void prvResetNextTaskUnblockTime( void )
|
||||
{
|
||||
TCB_t *pxTCB;
|
||||
|
|
|
@ -1,26 +1,66 @@
|
|||
/*
|
||||
* Test backported deletion behavior by creating tasks of various affinities and
|
||||
* check if the task memory is freed immediately under the correct conditions.
|
||||
*
|
||||
* The behavior of vTaskDelete() has been backported form FreeRTOS v9.0.0. This
|
||||
* results in the immediate freeing of task memory and the immediate execution
|
||||
* of deletion callbacks under the following conditions...
|
||||
* - When deleting a task that is not currently running on either core
|
||||
* - When deleting a task that is pinned to the same core (with respect to
|
||||
* the core that calls vTaskDelete()
|
||||
*
|
||||
* If the two conditions are not met, freeing of task memory and execution of
|
||||
* deletion callbacks will still be carried out by the Idle Task.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
#include "unity.h"
|
||||
|
||||
static void task_delete_self(void *param)
|
||||
#define NO_OF_TSKS 3
|
||||
#define DELAY_TICKS 2
|
||||
#define HEAP_CAPS (MALLOC_CAP_INTERNAL|MALLOC_CAP_DEFAULT)
|
||||
|
||||
|
||||
static void tsk_self_del(void *param)
|
||||
{
|
||||
printf("Task %p running on core %d. Deleting shortly...\n", xTaskGetCurrentTaskHandle(), xPortGetCoreID());
|
||||
vTaskDelay(5);
|
||||
vTaskDelete(NULL);
|
||||
vTaskDelete(NULL); //Deleting self means deleting currently running task
|
||||
}
|
||||
|
||||
static void tsk_extern_del(void *param)
|
||||
{
|
||||
vTaskDelay(portMAX_DELAY); //Await external deletion
|
||||
}
|
||||
|
||||
TEST_CASE("FreeRTOS Delete Tasks", "[freertos]")
|
||||
{
|
||||
/* -------------- Test vTaskDelete() on currently running tasks ----------------*/
|
||||
uint32_t before_count = uxTaskGetNumberOfTasks();
|
||||
|
||||
xTaskCreatePinnedToCore(task_delete_self, "tsk_self_a", 4096, NULL, configMAX_PRIORITIES - 1, NULL, 0);
|
||||
xTaskCreatePinnedToCore(task_delete_self, "tsk_self_a", 4096, NULL, configMAX_PRIORITIES - 1, NULL, 0);
|
||||
TEST_ASSERT_EQUAL(before_count + 2, uxTaskGetNumberOfTasks());
|
||||
vTaskDelay(200 / portTICK_PERIOD_MS);
|
||||
uint32_t before_heap = heap_caps_get_free_size(HEAP_CAPS);
|
||||
for(int i = 0; i < portNUM_PROCESSORS; i++){
|
||||
for(int j = 0; j < NO_OF_TSKS; j++){
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(tsk_self_del, "tsk_self", 1024, NULL, configMAX_PRIORITIES - 1, NULL, i));
|
||||
}
|
||||
}
|
||||
vTaskDelay(DELAY_TICKS); //Minimal delay to see if Idle task cleans up all tasks awaiting deletion in a single tick
|
||||
TEST_ASSERT_EQUAL(before_count, uxTaskGetNumberOfTasks());
|
||||
TEST_ASSERT_EQUAL(before_heap, heap_caps_get_free_size(HEAP_CAPS));
|
||||
|
||||
/* ------------- Test vTaskDelete() on not currently running tasks ------------ */
|
||||
TaskHandle_t handles[NO_OF_TSKS];
|
||||
before_heap = heap_caps_get_free_size(HEAP_CAPS);
|
||||
//Create task pinned to the same core that will not run during task deletion
|
||||
for(int j = 0 ; j < NO_OF_TSKS; j++){
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(tsk_extern_del, "tsk_extern", 4096, NULL, configMAX_PRIORITIES - 1, &handles[j], xPortGetCoreID()));
|
||||
}
|
||||
TEST_ASSERT_NOT_EQUAL(before_heap, heap_caps_get_free_size(HEAP_CAPS)); //Check tasks have been created
|
||||
//Delete the tasks, memory should be freed immediately
|
||||
for(int j = 0; j < NO_OF_TSKS; j++){
|
||||
vTaskDelete(handles[j]);
|
||||
}
|
||||
TEST_ASSERT_EQUAL(before_heap, heap_caps_get_free_size(HEAP_CAPS));
|
||||
|
||||
}
|
||||
|
|
|
@ -50,12 +50,25 @@ scheduler and interrupts of the calling core. However the other core is left
|
|||
unaffected. If the other core attemps to take same mutex, it will spin until
|
||||
the calling core has released the mutex by exiting the critical section.
|
||||
|
||||
:ref:`deletion-callbacks`: ESP-IDF FreeRTOS has
|
||||
backported the Thread Local Storage Pointers feature. However they have the
|
||||
extra feature of deletion callbacks. Deletion callbacks are used to
|
||||
automatically free memory used by Thread Local Storage Pointers during the task
|
||||
deletion. Call ``vTaskSetThreadLocalStoragePointerAndDelCallback()``
|
||||
to set Thread Local Storage Pointers and deletion callbacks.
|
||||
:ref:`floating-points`: The ESP32 supports hardware acceleration of single
|
||||
precision floating point arithmetic (`float`). However the use of hardware
|
||||
acceleration leads to some behavioral restrictions in ESP-IDF FreeRTOS.
|
||||
Therefore, tasks that utilize `float` will automatically be pinned to a core if
|
||||
not done so already. Furthermore, `float` cannot be used in interrupt service
|
||||
routines.
|
||||
|
||||
:ref:`task-deletion`: Task deletion behavior has been backported from FreeRTOS
|
||||
v9.0.0 and modified to be SMP compatible. Task memory will be freed immediately
|
||||
when `vTaskDelete()` is called to delete a task that is not currently running
|
||||
and not pinned to the other core. Otherwise, freeing of task memory will still
|
||||
be delegated to the Idle Task.
|
||||
|
||||
:ref:`deletion-callbacks`: ESP-IDF FreeRTOS has backported the Thread Local
|
||||
Storage Pointers (TLSP) feature. However the extra feature of Deletion Callbacks has been
|
||||
added. Deletion callbacks are called automatically during task deletion and are
|
||||
used to free memory pointed to by TLSP. Call
|
||||
``vTaskSetThreadLocalStoragePointerAndDelCallback()`` to set TLSP and Deletion
|
||||
Callbacks.
|
||||
|
||||
:ref:`FreeRTOS Hooks<hooks_api_reference>`: Vanilla FreeRTOS Hooks were not designed for SMP.
|
||||
ESP-IDF provides its own Idle and Tick Hooks in addition to the Vanilla FreeRTOS
|
||||
|
@ -375,39 +388,83 @@ mutex is provided upon entering and exiting, the type of call should not
|
|||
matter.
|
||||
|
||||
|
||||
.. _floating-points:
|
||||
|
||||
Floating Point Aritmetic
|
||||
------------------------
|
||||
|
||||
The ESP32 supports hardware acceleration of single precision floating point
|
||||
arithmetic (`float`) via Floating Point Units (FPU, also known as coprocessors)
|
||||
attached to each core. The use of the FPUs imposes some behavioral restrictions
|
||||
on ESP-IDF FreeRTOS.
|
||||
|
||||
ESP-IDF FreeRTOS implements Lazy Context Switching for FPUs. In other words,
|
||||
the state of a core's FPU registers are not immediately saved when a context
|
||||
switch occurs. Therefore, tasks that utilize `float` must be pinned to a
|
||||
particular core upon creation. If not, ESP-IDF FreeRTOS will automatically pin
|
||||
the task in question to whichever core the task was running on upon the task's
|
||||
first use of `float`. Likewise due to Lazy Context Switching, interrupt service
|
||||
routines must also not use `float`.
|
||||
|
||||
ESP32 does not support hardware acceleration for double precision floating point
|
||||
arithmetic (`double`). Instead `double` is implemented via software hence the
|
||||
behavioral restrictions with regards to `float` do not apply to `double`. Note
|
||||
that due to the lack of hardware acceleration, `double` operations may consume
|
||||
significantly larger amount of CPU time in comparison to `float`.
|
||||
|
||||
|
||||
.. _task-deletion:
|
||||
|
||||
Task Deletion
|
||||
-------------
|
||||
|
||||
FreeRTOS task deletion prior to v9.0.0 delegated the freeing of task memory
|
||||
entirely to the Idle Task. Currently, the freeing of task memory will occur
|
||||
immediately (within `vTaskDelete()`) if the task being deleted is not currently
|
||||
running or is not pinned to the other core (with respect to the core
|
||||
`vTaskDelete()` is called on). TLSP deletion callbacks will also run immediately
|
||||
if the same conditions are met.
|
||||
|
||||
However, calling `vTaskDelete()` to delete a task that is either currently
|
||||
running or pinned to the other core will still result in the freeing of memory
|
||||
being delegated to the Idle Task.
|
||||
|
||||
|
||||
.. _deletion-callbacks:
|
||||
|
||||
Thread Local Storage Pointers & Deletion Callbacks
|
||||
--------------------------------------------------
|
||||
|
||||
Thread Local Storage Pointers are pointers stored directly in the TCB which
|
||||
allows each task to have a pointer to a data structure containing that is
|
||||
specific to that task. However vanilla FreeRTOS provides no functionality to
|
||||
free the memory pointed to by the Thread Local Storage Pointers. Therefore if
|
||||
the memory pointed to by the Thread Local Storage Pointers is not explicitly
|
||||
freed by the user before a task is deleted, memory leak will occur.
|
||||
Thread Local Storage Pointers (TLSP) are pointers stored directly in the TCB.
|
||||
TLSP allow each task to have its own unique set of pointers to data structures.
|
||||
However task deletion behavior in vanilla FreeRTOS does not automatically
|
||||
free the memory pointed to by TLSP. Therefore if the memory pointed to by
|
||||
TLSP is not explicitly freed by the user before task deletion, memory leak will
|
||||
occur.
|
||||
|
||||
ESP-IDF FreeRTOS provides the added feature of deletion callbacks. These
|
||||
deletion callbacks are used to automatically free the memory pointed to by the
|
||||
Thread Local Storage Pointers when a task is deleted. Each Thread Local Storage
|
||||
Pointer can have its own call back, and these call backs are called when the
|
||||
Idle tasks cleans up a deleted tasks.
|
||||
ESP-IDF FreeRTOS provides the added feature of Deletion Callbacks. Deletion
|
||||
Callbacks are called automatically during task deletion to free memory pointed
|
||||
to by TLSP. Each TLSP can have its own Deletion Callback. Note that due to the
|
||||
to :ref:`task-deletion` behavior, there can be instances where Deletion
|
||||
Callbacks are called in the context of the Idle Tasks. Therefore Deletion
|
||||
Callbacks **should never attempt to block** and critical sections should be kept
|
||||
as short as possible to minimize priority inversion.
|
||||
|
||||
Vanilla FreeRTOS sets a Thread Local Storage Pointers using
|
||||
``vTaskSetThreadLocalStoragePointer()`` whereas ESP-IDF FreeRTOS sets a Thread
|
||||
Local Storage Pointers and Deletion Callbacks using
|
||||
``vTaskSetThreadLocalStoragePointerAndDelCallback()`` which accepts a pointer
|
||||
to the deletion call back as an extra parameter of type
|
||||
```TlsDeleteCallbackFunction_t``. Calling the vanilla FreeRTOS API
|
||||
``vTaskSetThreadLocalStoragePointer()`` is still valid however it is internally
|
||||
defined to call ``vTaskSetThreadLocalStoragePointerAndDelCallback()`` with a
|
||||
``NULL`` pointer as the deletion call back. This results in the selected Thread
|
||||
Local Storage Pointer to have no deletion call back.
|
||||
Deletion callbacks are of type
|
||||
``void (*TlsDeleteCallbackFunction_t)( int, void * )`` where the first parameter
|
||||
is the index number of the associated TLSP, and the second parameter is the
|
||||
TLSP itself.
|
||||
|
||||
In IDF the FreeRTOS thread local storage at index 0 is reserved and is used to implement
|
||||
the pthreads API thread local storage (pthread_getspecific() & pthread_setspecific()).
|
||||
Other indexes can be used for any purpose, provided
|
||||
:ref:`CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS` is set to a high enough value.
|
||||
Deletion callbacks are set alongside TLSP by calling
|
||||
``vTaskSetThreadLocalStoragePointerAndDelCallback()``. Calling the vanilla
|
||||
FreeRTOS function ``vTaskSetThreadLocalStoragePointer()`` will simply set the
|
||||
TLSP's associated Deletion Callback to `NULL` meaning that no callback will be
|
||||
called for that TLSP during task deletion. If a deletion callback is `NULL`,
|
||||
users should manually free the memory pointed to by the associated TLSP before
|
||||
task deletion in order to avoid memory leak.
|
||||
|
||||
:ref:`CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS` in menuconfig can be used
|
||||
to configure the number TLSP and Deletion Callbacks a TCB will have.
|
||||
|
||||
For more details see :component_file:`freertos/include/freertos/task.h`
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
void unityTask(void *pvParameters)
|
||||
{
|
||||
vTaskDelay(30); /* Delay a bit to let the main task be deleted */
|
||||
vTaskDelay(2); /* Delay a bit to let the main task be deleted */
|
||||
unity_run_menu(); /* Doesn't return */
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue