mirror of
https://github.com/FreeRTOS/FreeRTOS-Kernel.git
synced 2025-04-19 21:11:57 -04:00
Update task notification scheduler suspension usage (#982)
* Update task notification scheduler suspension Previously ulTaskGenericNotifyTake() and xTaskGenericNotifyWait() would suspend the scheduler while inside a critical section. This commit changes the order by wrapping the critical sections in a scheduler suspension block. This logic is more inuitive and allows the SMP scheduler suspension logic to be simplified. * tasks.c: Fix typo * Use a complete sentence in comment * Check portGET_CRITICAL_NESTING_COUNT when scheduler is running * Prevent potential NULL pointer access when scheduler is not running --------- Co-authored-by: Paul Bartell <pbartell@amazon.com> Co-authored-by: chinglee-iot <61685396+chinglee-iot@users.noreply.github.com> Co-authored-by: Ching-Hsin Lee <chinglee@amazon.com>
This commit is contained in:
parent
57a5ed7f67
commit
7284d84dc8
211
tasks.c
211
tasks.c
|
@ -3822,6 +3822,9 @@ void vTaskSuspendAll( void )
|
|||
|
||||
if( xSchedulerRunning != pdFALSE )
|
||||
{
|
||||
/* This must never be called from inside a critical section. */
|
||||
configASSERT( portGET_CRITICAL_NESTING_COUNT() == 0 );
|
||||
|
||||
/* Writes to uxSchedulerSuspended must be protected by both the task AND ISR locks.
|
||||
* We must disable interrupts before we grab the locks in the event that this task is
|
||||
* interrupted and switches context before incrementing uxSchedulerSuspended.
|
||||
|
@ -3840,14 +3843,7 @@ void vTaskSuspendAll( void )
|
|||
* it. */
|
||||
if( uxSchedulerSuspended == 0U )
|
||||
{
|
||||
if( portGET_CRITICAL_NESTING_COUNT() == 0U )
|
||||
{
|
||||
prvCheckForRunStateChange();
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
prvCheckForRunStateChange();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -7676,83 +7672,67 @@ TickType_t uxTaskResetEventItemValue( void )
|
|||
TickType_t xTicksToWait )
|
||||
{
|
||||
uint32_t ulReturn;
|
||||
BaseType_t xAlreadyYielded;
|
||||
BaseType_t xAlreadyYielded, xShouldBlock = pdFALSE;
|
||||
|
||||
traceENTER_ulTaskGenericNotifyTake( uxIndexToWaitOn, xClearCountOnExit, xTicksToWait );
|
||||
|
||||
configASSERT( uxIndexToWaitOn < configTASK_NOTIFICATION_ARRAY_ENTRIES );
|
||||
|
||||
taskENTER_CRITICAL();
|
||||
|
||||
/* Only block if the notification count is not already non-zero. */
|
||||
if( pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] == 0UL )
|
||||
/* We suspend the scheduler here as prvAddCurrentTaskToDelayedList is a
|
||||
* non-deterministic operation. */
|
||||
vTaskSuspendAll();
|
||||
{
|
||||
/* Mark this task as waiting for a notification. */
|
||||
pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] = taskWAITING_NOTIFICATION;
|
||||
|
||||
if( xTicksToWait > ( TickType_t ) 0 )
|
||||
/* We MUST enter a critical section to atomically check if a notification
|
||||
* has occurred and set the flag to indicate that we are waiting for
|
||||
* a notification. If we do not do so, a notification sent from an ISR
|
||||
* will get lost. */
|
||||
taskENTER_CRITICAL();
|
||||
{
|
||||
traceTASK_NOTIFY_TAKE_BLOCK( uxIndexToWaitOn );
|
||||
|
||||
/* We MUST suspend the scheduler before exiting the critical
|
||||
* section (i.e. before enabling interrupts).
|
||||
*
|
||||
* If we do not do so, a notification sent from an ISR, which
|
||||
* happens after exiting the critical section and before
|
||||
* suspending the scheduler, will get lost. The sequence of
|
||||
* events will be:
|
||||
* 1. Exit critical section.
|
||||
* 2. Interrupt - ISR calls xTaskNotifyFromISR which adds the
|
||||
* task to the Ready list.
|
||||
* 3. Suspend scheduler.
|
||||
* 4. prvAddCurrentTaskToDelayedList moves the task to the
|
||||
* delayed or suspended list.
|
||||
* 5. Resume scheduler does not touch the task (because it is
|
||||
* not on the pendingReady list), effectively losing the
|
||||
* notification from the ISR.
|
||||
*
|
||||
* The same does not happen when we suspend the scheduler before
|
||||
* exiting the critical section. The sequence of events in this
|
||||
* case will be:
|
||||
* 1. Suspend scheduler.
|
||||
* 2. Exit critical section.
|
||||
* 3. Interrupt - ISR calls xTaskNotifyFromISR which adds the
|
||||
* task to the pendingReady list as the scheduler is
|
||||
* suspended.
|
||||
* 4. prvAddCurrentTaskToDelayedList adds the task to delayed or
|
||||
* suspended list. Note that this operation does not nullify
|
||||
* the add to pendingReady list done in the above step because
|
||||
* a different list item, namely xEventListItem, is used for
|
||||
* adding the task to the pendingReady list. In other words,
|
||||
* the task still remains on the pendingReady list.
|
||||
* 5. Resume scheduler moves the task from pendingReady list to
|
||||
* the Ready list.
|
||||
*/
|
||||
vTaskSuspendAll();
|
||||
/* Only block if the notification count is not already non-zero. */
|
||||
if( pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] == 0UL )
|
||||
{
|
||||
taskEXIT_CRITICAL();
|
||||
/* Mark this task as waiting for a notification. */
|
||||
pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] = taskWAITING_NOTIFICATION;
|
||||
|
||||
prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
|
||||
}
|
||||
xAlreadyYielded = xTaskResumeAll();
|
||||
|
||||
if( xAlreadyYielded == pdFALSE )
|
||||
{
|
||||
taskYIELD_WITHIN_API();
|
||||
if( xTicksToWait > ( TickType_t ) 0 )
|
||||
{
|
||||
xShouldBlock = pdTRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
taskEXIT_CRITICAL();
|
||||
|
||||
/* We are now out of the critical section but the scheduler is still
|
||||
* suspended, so we are safe to do non-deterministic operations such
|
||||
* as prvAddCurrentTaskToDelayedList. */
|
||||
if( xShouldBlock == pdTRUE )
|
||||
{
|
||||
traceTASK_NOTIFY_TAKE_BLOCK( uxIndexToWaitOn );
|
||||
prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
|
||||
}
|
||||
else
|
||||
{
|
||||
taskEXIT_CRITICAL();
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
xAlreadyYielded = xTaskResumeAll();
|
||||
|
||||
/* Force a reschedule if xTaskResumeAll has not already done so. */
|
||||
if( ( xShouldBlock == pdTRUE ) && ( xAlreadyYielded == pdFALSE ) )
|
||||
{
|
||||
taskYIELD_WITHIN_API();
|
||||
}
|
||||
else
|
||||
{
|
||||
taskEXIT_CRITICAL();
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
|
||||
taskENTER_CRITICAL();
|
||||
|
@ -7796,88 +7776,71 @@ TickType_t uxTaskResetEventItemValue( void )
|
|||
uint32_t * pulNotificationValue,
|
||||
TickType_t xTicksToWait )
|
||||
{
|
||||
BaseType_t xReturn, xAlreadyYielded;
|
||||
BaseType_t xReturn, xAlreadyYielded, xShouldBlock = pdFALSE;
|
||||
|
||||
traceENTER_xTaskGenericNotifyWait( uxIndexToWaitOn, ulBitsToClearOnEntry, ulBitsToClearOnExit, pulNotificationValue, xTicksToWait );
|
||||
|
||||
configASSERT( uxIndexToWaitOn < configTASK_NOTIFICATION_ARRAY_ENTRIES );
|
||||
|
||||
taskENTER_CRITICAL();
|
||||
|
||||
/* Only block if a notification is not already pending. */
|
||||
if( pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] != taskNOTIFICATION_RECEIVED )
|
||||
/* We suspend the scheduler here as prvAddCurrentTaskToDelayedList is a
|
||||
* non-deterministic operation. */
|
||||
vTaskSuspendAll();
|
||||
{
|
||||
/* Clear bits in the task's notification value as bits may get
|
||||
* set by the notifying task or interrupt. This can be used to
|
||||
* clear the value to zero. */
|
||||
pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] &= ~ulBitsToClearOnEntry;
|
||||
|
||||
/* Mark this task as waiting for a notification. */
|
||||
pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] = taskWAITING_NOTIFICATION;
|
||||
|
||||
if( xTicksToWait > ( TickType_t ) 0 )
|
||||
/* We MUST enter a critical section to atomically check and update the
|
||||
* task notification value. If we do not do so, a notification from
|
||||
* an ISR will get lost. */
|
||||
taskENTER_CRITICAL();
|
||||
{
|
||||
traceTASK_NOTIFY_WAIT_BLOCK( uxIndexToWaitOn );
|
||||
|
||||
/* We MUST suspend the scheduler before exiting the critical
|
||||
* section (i.e. before enabling interrupts).
|
||||
*
|
||||
* If we do not do so, a notification sent from an ISR, which
|
||||
* happens after exiting the critical section and before
|
||||
* suspending the scheduler, will get lost. The sequence of
|
||||
* events will be:
|
||||
* 1. Exit critical section.
|
||||
* 2. Interrupt - ISR calls xTaskNotifyFromISR which adds the
|
||||
* task to the Ready list.
|
||||
* 3. Suspend scheduler.
|
||||
* 4. prvAddCurrentTaskToDelayedList moves the task to the
|
||||
* delayed or suspended list.
|
||||
* 5. Resume scheduler does not touch the task (because it is
|
||||
* not on the pendingReady list), effectively losing the
|
||||
* notification from the ISR.
|
||||
*
|
||||
* The same does not happen when we suspend the scheduler before
|
||||
* exiting the critical section. The sequence of events in this
|
||||
* case will be:
|
||||
* 1. Suspend scheduler.
|
||||
* 2. Exit critical section.
|
||||
* 3. Interrupt - ISR calls xTaskNotifyFromISR which adds the
|
||||
* task to the pendingReady list as the scheduler is
|
||||
* suspended.
|
||||
* 4. prvAddCurrentTaskToDelayedList adds the task to delayed or
|
||||
* suspended list. Note that this operation does not nullify
|
||||
* the add to pendingReady list done in the above step because
|
||||
* a different list item, namely xEventListItem, is used for
|
||||
* adding the task to the pendingReady list. In other words,
|
||||
* the task still remains on the pendingReady list.
|
||||
* 5. Resume scheduler moves the task from pendingReady list to
|
||||
* the Ready list.
|
||||
*/
|
||||
vTaskSuspendAll();
|
||||
/* Only block if a notification is not already pending. */
|
||||
if( pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] != taskNOTIFICATION_RECEIVED )
|
||||
{
|
||||
taskEXIT_CRITICAL();
|
||||
/* Clear bits in the task's notification value as bits may get
|
||||
* set by the notifying task or interrupt. This can be used
|
||||
* to clear the value to zero. */
|
||||
pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] &= ~ulBitsToClearOnEntry;
|
||||
|
||||
prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
|
||||
}
|
||||
xAlreadyYielded = xTaskResumeAll();
|
||||
/* Mark this task as waiting for a notification. */
|
||||
pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] = taskWAITING_NOTIFICATION;
|
||||
|
||||
if( xAlreadyYielded == pdFALSE )
|
||||
{
|
||||
taskYIELD_WITHIN_API();
|
||||
if( xTicksToWait > ( TickType_t ) 0 )
|
||||
{
|
||||
xShouldBlock = pdTRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
taskEXIT_CRITICAL();
|
||||
|
||||
/* We are now out of the critical section but the scheduler is still
|
||||
* suspended, so we are safe to do non-deterministic operations such
|
||||
* as prvAddCurrentTaskToDelayedList. */
|
||||
if( xShouldBlock == pdTRUE )
|
||||
{
|
||||
traceTASK_NOTIFY_WAIT_BLOCK( uxIndexToWaitOn );
|
||||
prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
|
||||
}
|
||||
else
|
||||
{
|
||||
taskEXIT_CRITICAL();
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
xAlreadyYielded = xTaskResumeAll();
|
||||
|
||||
/* Force a reschedule if xTaskResumeAll has not already done so. */
|
||||
if( ( xShouldBlock == pdTRUE ) && ( xAlreadyYielded == pdFALSE ) )
|
||||
{
|
||||
taskYIELD_WITHIN_API();
|
||||
}
|
||||
else
|
||||
{
|
||||
taskEXIT_CRITICAL();
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
|
||||
taskENTER_CRITICAL();
|
||||
|
|
Loading…
Reference in a new issue