xTaskResumeAll() in Smp

Hi
I am learning freertos smp kernel and there are questions about suspending the scheduler.
When use suspending thr scheduler to protected shared date, vTaskSuspenALL() will increment uxSchedulerSuspended to prevent all cores swtiching contextes and get task lock to prevent other tasks enter critical section.

if( xSchedulerRunning != pdFALSE )
    {
        /* It is possible that an ISR caused a task to be removed from an event
         * list while the scheduler was suspended.  If this was the case then the
         * removed task will have been added to the xPendingReadyList.  Once the
         * scheduler has been resumed it is safe to move all the pending ready
         * tasks from this list into their appropriate ready list. */
        taskENTER_CRITICAL();
        {
            BaseType_t xCoreID;

            xCoreID = portGET_CORE_ID();

            /* If uxSchedulerSuspended is zero then this function does not match a
             * previous call to vTaskSuspendAll(). */
            configASSERT( uxSchedulerSuspended );

            --uxSchedulerSuspended;
            portRELEASE_TASK_LOCK();

            if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
            {
                if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U )
                {
                    /* Move any readied tasks from the pending list into the
                     * appropriate ready list. */
                    while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE )
                    {
                        pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) ); 
                        ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
                        ( void ) uxListRemove( &( pxTCB->xStateListItem ) );
                        prvAddTaskToReadyList( pxTCB );

                        /* All appropriate tasks yield at the moment a task is added to xPendingReadyList.
                         * If the current core yielded then vTaskSwitchContext() has already been called
                         * which sets xYieldPendings for the current core to pdTRUE. */
                    }

                    if( pxTCB != NULL )
                    {
                        /* A task was unblocked while the scheduler was suspended,
                         * which may have prevented the next unblock time from being
                         * re-calculated, in which case re-calculate it now.  Mainly
                         * important for low power tickless implementations, where
                         * this can prevent an unnecessary exit from low power
                         * state. */
                        prvResetNextTaskUnblockTime();
                    }

                    /* If any ticks occurred while the scheduler was suspended then
                     * they should be processed now.  This ensures the tick count does
                     * not	slip, and that any delayed tasks are resumed at the correct
                     * time.
                     *
                     * It should be safe to call xTaskIncrementTick here from any core
                     * since we are in a critical section and xTaskIncrementTick itself
                     * protects itself within a critical section. Suspending the scheduler
                     * from any core causes xTaskIncrementTick to increment uxPendedCounts.*/
                    {
                        TickType_t xPendedCounts = xPendedTicks; /* Non-volatile copy. */

                        if( xPendedCounts > ( TickType_t ) 0U )
                        {
                            do
                            {
                                if( xTaskIncrementTick() != pdFALSE )
                                {
                                    xYieldPendings[ xCoreID ] = pdTRUE;
                                }
                                else
                                {
                                    mtCOVERAGE_TEST_MARKER();
                                }

                                --xPendedCounts;
                            } while( xPendedCounts > ( TickType_t ) 0U );

                            xPendedTicks = 0;
                        }
                        else
                        {
                            mtCOVERAGE_TEST_MARKER();
                        }
                    }

                    if( xYieldPendings[ xCoreID ] != pdFALSE )
                    {
                        xAlreadyYielded = pdTRUE;
                    }
                }
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        taskEXIT_CRITICAL();
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

But when call xTaskResumeAll() to resume the scheduler, it will call taskENTER_CRITICAL() to ensure decrement uxSchedulerSuspended aotic, but task lock which has been owned by xTaskSuspendAll() can not be got by xTaskResumeAll(), I am confused about this.

Any help would be appreciated.

Best regards
Rasty

Would you please elaborate what you mean by this? The task lock is released in xTaskResumeAll. Are you expecting something different and why?

Thank you for your reply.
In vTaskSuspendAll() the task get task lock.

void vTaskSuspendAll( void )
{
	...
	
    {
        ulState = portDISABLE_INTERRUPTS();

        portGET_TASK_LOCK();
        portGET_ISR_LOCK();

        ++uxSchedulerSuspended;
        portRELEASE_ISR_LOCK();
		
		...
    }
}

When calling vTaskResumeAll() to resume scheduler, taskENTER_CRITICAL() called which is defined as vTaskEnterCritical() also tries to get the task lock held by vTaskSuspendAll(). In this case , xTaskResumeAll() can not enter the critical section and release the task lock?

BaseType_t xTaskResumeAll( void )
{
    TCB_t * pxTCB = NULL;
    BaseType_t xAlreadyYielded = pdFALSE;

    if( xSchedulerRunning != pdFALSE )
    {
        /* It is possible that an ISR caused a task to be removed from an event
         * list while the scheduler was suspended.  If this was the case then the
         * removed task will have been added to the xPendingReadyList.  Once the
         * scheduler has been resumed it is safe to move all the pending ready
         * tasks from this list into their appropriate ready list. */
        taskENTER_CRITICAL();
        {
            BaseType_t xCoreID;

            xCoreID = portGET_CORE_ID();

            /* If uxSchedulerSuspended is zero then this function does not match a
             * previous call to vTaskSuspendAll(). */
            configASSERT( uxSchedulerSuspended );

            --uxSchedulerSuspended;
            portRELEASE_TASK_LOCK();
        }
        taskEXIT_CRITICAL();
    }

    return xAlreadyYielded;
}
void vTaskEnterCritical( void )
    {
        portDISABLE_INTERRUPTS();

        if( xSchedulerRunning != pdFALSE )
        {
            if( pxCurrentTCB->uxCriticalNesting == 0U )
            {
                if( portCHECK_IF_IN_ISR() == pdFALSE )
                {
                    portGET_TASK_LOCK();
                }

                portGET_ISR_LOCK();
            }
		
            ( pxCurrentTCB->uxCriticalNesting )++;

			...
			
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }

Thank you again.

Hi Saiiijchan,

The spinlock is supposed to be a recursive lock. A core can obtain the lock multiple times without being blocked if the lock is owned by this core. If the task lock is already obtained by a core in vTaskSuspendAll, this core should be able to call portGET_TASK_LOCK() in vTaskEnterCritical() without being blocked. You can reference RP2040 implementation.

3 Likes

Thank you Fresh, I review smp changes and aware of this.