#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
/*-----------------------------------------------------------*/
#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 );
mtCOVERAGE_TEST_MARKER();
}
- vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xGenericListItem ) );
+ /* 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++;
- /* 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;
+ //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;
- /* Increment the uxTaskNumberVariable also so kernel aware debuggers
- can detect that the task lists need re-generating. */
- uxTaskNumber++;
+ /* 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,
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();
}
}
}
#if ( configUSE_TICKLESS_IDLE != 0 )
+ static BaseType_t xHaveReadyTasks()
+ {
+ for (int i = tskIDLE_PRIORITY + 1; i < configMAX_PRIORITIES; ++i)
+ {
+ if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ i ] ) ) > 0 )
+ {
+ return pdTRUE;
+ }
+ else
+ {
+ mtCOVERAGE_TEST_MARKER();
+ }
+ }
+ return pdFALSE;
+ }
+
+
static TickType_t prvGetExpectedIdleTime( void )
{
TickType_t xReturn;
{
xReturn = 0;
}
- else if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > 1 )
+#if portNUM_PROCESSORS > 1
+ /* This function is called from Idle task; in single core case this
+ * means that no higher priority tasks are ready to run, and we can
+ * enter sleep. In SMP case, there might be ready tasks waiting for
+ * the other CPU, so need to check all ready lists.
+ */
+ else if( xHaveReadyTasks() )
+ {
+ xReturn = 0;
+ }
+#endif // portNUM_PROCESSORS > 1
+ else if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > portNUM_PROCESSORS )
{
/* There are other idle priority tasks in the ready state. If
time slicing is used then the very next tick interrupt must be
#endif /* configUSE_IDLE_HOOK */
{
/* Call the esp-idf hook system */
- extern void esp_vApplicationIdleHook( void );
esp_vApplicationIdleHook();
}
#if ( configUSE_TICKLESS_IDLE != 0 )
{
TickType_t xExpectedIdleTime;
+ BaseType_t xEnteredSleep = pdFALSE;
/* It is not desirable to suspend then resume the scheduler on
each iteration of the idle task. Therefore, a preliminary
if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
{
-// vTaskSuspendAll();
taskENTER_CRITICAL(&xTaskQueueMutex);
{
/* Now the scheduler is suspended, the expected idle
if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
{
traceLOW_POWER_IDLE_BEGIN();
- portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime );
+ xEnteredSleep = portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime );
traceLOW_POWER_IDLE_END();
}
else
}
}
taskEXIT_CRITICAL(&xTaskQueueMutex);
-// ( void ) xTaskResumeAll();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
+ /* It might be possible to enter tickless idle again, so skip
+ * the fallback sleep hook if tickless idle was successful
+ */
+ if ( !xEnteredSleep )
+ {
+ esp_vApplicationWaitiHook();
+ }
}
+ #else
+ esp_vApplicationWaitiHook();
#endif /* configUSE_TICKLESS_IDLE */
}
}
#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) ){ //Walk the list
+ TCB_t *tgt_tcb = ( TCB_t * )listGET_LIST_ITEM_OWNER(target);
+ int affinity = tgt_tcb->xCoreID;
+ //Self deleting tasks are added to Termination List before they switch context. Ensure they aren't still currently running
+ if( pxCurrentTCB[core] == tgt_tcb || (portNUM_PROCESSORS > 1 && pxCurrentTCB[!core] == tgt_tcb) ){
+ continue; //Can't free memory of task that is still running
+ }
+ if(affinity == core || affinity == tskNO_AFFINITY){ //Find first item not pinned to other core
+ pxTCB = tgt_tcb;
+ 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
}
}
}
pxTaskStatusArray[ uxTask ].eCurrentState = eState;
pxTaskStatusArray[ uxTask ].uxCurrentPriority = pxNextTCB->uxPriority;
+ #if ( configTASKLIST_INCLUDE_COREID == 1 )
+ pxTaskStatusArray[ uxTask ].xCoreID = pxNextTCB->xCoreID;
+ #endif /* configTASKLIST_INCLUDE_COREID */
+
#if ( INCLUDE_vTaskSuspend == 1 )
{
/* If the task is in the suspended list then there is a chance
#if ( INCLUDE_vTaskDelete == 1 )
-
static void prvDeleteTCB( TCB_t *pxTCB )
{
+ /* This call is required for any port specific cleanup related to task.
+ It must be above the vPortFree() calls. */
+ portCLEAN_UP_TCB( pxTCB );
+
/* Free up the memory allocated by the scheduler for the task. It is up
to the task to free any memory allocated at the application level. */
#if ( configUSE_NEWLIB_REENTRANT == 1 )
/* Neither the stack nor the TCB were allocated dynamically, so
nothing needs to be freed. */
configASSERT( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB )
- portCLEAN_UP_TCB( pxTCB );
mtCOVERAGE_TEST_MARKER();
}
}
#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;
pcWriteBuffer = prvWriteNameToBuffer( pcWriteBuffer, pxTaskStatusArray[ x ].pcTaskName );
/* Write the rest of the string. */
+#if configTASKLIST_INCLUDE_COREID
+ sprintf( pcWriteBuffer, "\t%c\t%u\t%u\t%u\t%hd\r\n", cStatus, ( unsigned int ) pxTaskStatusArray[ x ].uxCurrentPriority, ( unsigned int ) pxTaskStatusArray[ x ].usStackHighWaterMark, ( unsigned int ) pxTaskStatusArray[ x ].xTaskNumber, ( int ) pxTaskStatusArray[ x ].xCoreID );
+#else
sprintf( pcWriteBuffer, "\t%c\t%u\t%u\t%u\r\n", cStatus, ( unsigned int ) pxTaskStatusArray[ x ].uxCurrentPriority, ( unsigned int ) pxTaskStatusArray[ x ].usStackHighWaterMark, ( unsigned int ) pxTaskStatusArray[ x ].xTaskNumber );
+#endif
pcWriteBuffer += strlen( pcWriteBuffer );
}