]> granicus.if.org Git - esp-idf/blobdiff - components/freertos/tasks.c
freertos: add configTASKLIST_INCLUDE_COREID
[esp-idf] / components / freertos / tasks.c
index ff549183a155e8a202baa301eb47dcdfacf13ecc..ac0b732a8b8dbefc9cf1e71af97ee1bf2cae6ffe 100644 (file)
@@ -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 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,
@@ -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();
                        }
                }
        }
@@ -2107,6 +2151,23 @@ void vTaskSuspendAll( void )
 
 #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;
@@ -2117,7 +2178,18 @@ void vTaskSuspendAll( void )
                {
                        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
@@ -3361,7 +3433,6 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters )
                #endif /* configUSE_IDLE_HOOK */
                {
                        /* Call the esp-idf hook system */
-                       extern void esp_vApplicationIdleHook( void );
                        esp_vApplicationIdleHook();
                }
 
@@ -3373,6 +3444,7 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters )
                #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
@@ -3383,7 +3455,6 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters )
 
                        if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
                        {
-//                             vTaskSuspendAll();
                                taskENTER_CRITICAL(&xTaskQueueMutex);
                                {
                                        /* Now the scheduler is suspended, the expected idle
@@ -3395,7 +3466,7 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters )
                                        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
@@ -3404,13 +3475,21 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters )
                                        }
                                }
                                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 */
        }
 }
@@ -3583,52 +3662,53 @@ 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) ){        //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
                        }
                }
        }
@@ -3705,6 +3785,10 @@ BaseType_t xTaskGetAffinity( TaskHandle_t xTask )
                                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
@@ -3831,9 +3915,12 @@ BaseType_t xTaskGetAffinity( TaskHandle_t xTask )
 
 #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 )
@@ -3876,7 +3963,6 @@ BaseType_t xTaskGetAffinity( TaskHandle_t xTask )
                                /* 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();
                        }
                }
@@ -3886,6 +3972,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;
@@ -4350,7 +4453,11 @@ For ESP32 FreeRTOS, vTaskExitCritical implements both portEXIT_CRITICAL and port
                                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 );
                        }