|
|
@@ -259,6 +259,15 @@ extern void esp_vApplicationIdleHook(void);
|
|
|
#define taskCAN_RUN_ON_CORE( xCore, xCoreID ) ( pdTRUE )
|
|
|
#endif /* configNUM_CORES > 1 */
|
|
|
|
|
|
+/* Check if a task is a currently running task. */
|
|
|
+#if ( configNUM_CORES > 1 )
|
|
|
+ #define taskIS_CURRENTLY_RUNNING( pxTCB ) ( ( ( pxTCB ) == pxCurrentTCB[ 0 ] ) || ( ( pxTCB ) == pxCurrentTCB[ 1 ] ) )
|
|
|
+ #define taskIS_CURRENTLY_RUNNING_ON_CORE( pxTCB, xCoreID ) ( ( pxTCB ) == pxCurrentTCB[ ( xCoreID ) ] )
|
|
|
+#else
|
|
|
+ #define taskIS_CURRENTLY_RUNNING( pxTCB ) ( ( pxTCB ) == pxCurrentTCB[ 0 ] )
|
|
|
+ #define taskIS_CURRENTLY_RUNNING_ON_CORE( pxTCB, xCoreID ) taskIS_CURRENTLY_RUNNING( ( pxTCB ) )
|
|
|
+#endif /* configNUM_CORES > 1 */
|
|
|
+
|
|
|
/*
|
|
|
* Several functions take a TaskHandle_t parameter that can optionally be NULL,
|
|
|
* where NULL is used to indicate that the handle of the currently executing
|
|
|
@@ -1363,14 +1372,17 @@ static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
|
|
|
void vTaskDelete( TaskHandle_t xTaskToDelete )
|
|
|
{
|
|
|
TCB_t * pxTCB;
|
|
|
- TCB_t * curTCB;
|
|
|
- BaseType_t core;
|
|
|
- BaseType_t xFreeNow = 0;
|
|
|
+ BaseType_t xFreeNow;
|
|
|
|
|
|
taskENTER_CRITICAL( &xKernelLock );
|
|
|
{
|
|
|
- core = xPortGetCoreID();
|
|
|
- curTCB = pxCurrentTCB[core];
|
|
|
+ BaseType_t xCurCoreID;
|
|
|
+ #if ( configNUM_CORES > 1 )
|
|
|
+ xCurCoreID = xPortGetCoreID();
|
|
|
+ #else
|
|
|
+ xCurCoreID = 0;
|
|
|
+ ( void ) xCurCoreID;
|
|
|
+ #endif
|
|
|
|
|
|
/* If null is passed in here then it is the calling task that is
|
|
|
* being deleted. */
|
|
|
@@ -1402,12 +1414,19 @@ static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
|
|
|
* not return. */
|
|
|
uxTaskNumber++;
|
|
|
|
|
|
- if( pxTCB == curTCB ||
|
|
|
- /* in SMP, we also can't immediately delete the task active on the other core */
|
|
|
- (configNUM_CORES > 1 && pxTCB == pxCurrentTCB[ !core ]) ||
|
|
|
- /* ... and we can't delete a non-running task pinned to the other core, as
|
|
|
- FPU cleanup has to happen on the same core */
|
|
|
- (configNUM_CORES > 1 && pxTCB->xCoreID == (!core)) )
|
|
|
+ /*
|
|
|
+ * We cannot immediately a task that is
|
|
|
+ * - Currently running on either core
|
|
|
+ * - If the task is not currently running but is pinned to the other (due to FPU cleanup)
|
|
|
+ * Todo: Allow deletion of tasks pinned to other core (IDF-5803)
|
|
|
+ */
|
|
|
+ #if ( configNUM_CORES > 1 )
|
|
|
+ xFreeNow = ( taskIS_CURRENTLY_RUNNING( pxTCB ) || ( pxTCB->xCoreID == !xCurCoreID ) ) ? pdFALSE : pdTRUE;
|
|
|
+ #else
|
|
|
+ xFreeNow = ( taskIS_CURRENTLY_RUNNING( pxTCB ) ) ? pdFALSE : pdTRUE;
|
|
|
+ #endif /* configNUM_CORES > 1 */
|
|
|
+
|
|
|
+ if( xFreeNow == pdFALSE )
|
|
|
{
|
|
|
/* A task is deleting itself. This cannot complete within the
|
|
|
* task itself, as a context switch to another task is required.
|
|
|
@@ -1421,43 +1440,47 @@ static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
|
|
|
* check the xTasksWaitingTermination list. */
|
|
|
++uxDeletedTasksWaitingCleanUp;
|
|
|
|
|
|
+ /* Call the delete hook before portPRE_TASK_DELETE_HOOK() as
|
|
|
+ * portPRE_TASK_DELETE_HOOK() does not return in the Win32 port. */
|
|
|
+ traceTASK_DELETE( pxTCB );
|
|
|
+
|
|
|
/* 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[core] );
|
|
|
+ portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending[ xCurCoreID ] );
|
|
|
|
|
|
- if (configNUM_CORES > 1 && pxTCB == pxCurrentTCB[ !core ])
|
|
|
- {
|
|
|
- /* SMP case of deleting a task running on a different core. Same issue
|
|
|
- as a task deleting itself, but we need to send a yield to this task now
|
|
|
- before we release xKernelLock.
|
|
|
-
|
|
|
- Specifically there is a case where the other core may already be spinning on
|
|
|
- xKernelLock waiting to go into a blocked state. A check is added in
|
|
|
- prvAddCurrentTaskToDelayedList() to prevent it from removing itself from
|
|
|
- xTasksWaitingTermination list in this case (instead it will immediately
|
|
|
- release xKernelLock again and be yielded before the FreeRTOS function
|
|
|
- returns.) */
|
|
|
- vPortYieldOtherCore( !core );
|
|
|
- }
|
|
|
+ #if ( configNUM_CORES > 1 )
|
|
|
+ if( taskIS_CURRENTLY_RUNNING_ON_CORE( pxTCB, !xCurCoreID ) )
|
|
|
+ {
|
|
|
+ /* SMP case of deleting a task running on a different core. Same issue
|
|
|
+ as a task deleting itself, but we need to send a yield to this task now
|
|
|
+ before we release xKernelLock.
|
|
|
+
|
|
|
+ Specifically there is a case where the other core may already be spinning on
|
|
|
+ xKernelLock waiting to go into a blocked state. A check is added in
|
|
|
+ prvAddCurrentTaskToDelayedList() to prevent it from removing itself from
|
|
|
+ xTasksWaitingTermination list in this case (instead it will immediately
|
|
|
+ release xKernelLock again and be yielded before the FreeRTOS function
|
|
|
+ returns.) */
|
|
|
+ vPortYieldOtherCore( !xCurCoreID );
|
|
|
+ }
|
|
|
+ #endif /* configNUM_CORES > 1 */
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
--uxCurrentNumberOfTasks;
|
|
|
- xFreeNow = pdTRUE;
|
|
|
+ traceTASK_DELETE( pxTCB );
|
|
|
|
|
|
/* Reset the next expected unblock time in case it referred to
|
|
|
* the task that has just been deleted. */
|
|
|
prvResetNextTaskUnblockTime();
|
|
|
}
|
|
|
-
|
|
|
- traceTASK_DELETE( pxTCB );
|
|
|
}
|
|
|
taskEXIT_CRITICAL( &xKernelLock );
|
|
|
|
|
|
- if(xFreeNow == pdTRUE) {
|
|
|
+ if( xFreeNow == pdTRUE ) {
|
|
|
#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS )
|
|
|
prvDeleteTLS( pxTCB );
|
|
|
#endif
|
|
|
@@ -1469,7 +1492,8 @@ static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
|
|
|
* been deleted. */
|
|
|
if( xSchedulerRunning != pdFALSE )
|
|
|
{
|
|
|
- if( pxTCB == curTCB )
|
|
|
+ taskENTER_CRITICAL( &xKernelLock );
|
|
|
+ if( taskIS_CURRENTLY_RUNNING_ON_CORE( pxTCB, xPortGetCoreID() ) )
|
|
|
{
|
|
|
configASSERT( xTaskGetSchedulerState() != taskSCHEDULER_SUSPENDED );
|
|
|
portYIELD_WITHIN_API();
|
|
|
@@ -1478,6 +1502,7 @@ static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
|
|
|
{
|
|
|
mtCOVERAGE_TEST_MARKER();
|
|
|
}
|
|
|
+ taskEXIT_CRITICAL( &xKernelLock );
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -1649,18 +1674,11 @@ static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
|
|
|
configASSERT( pxTCB );
|
|
|
|
|
|
taskENTER_CRITICAL( &xKernelLock ); //Need critical section incase either core context switches in between
|
|
|
- if( pxTCB == pxCurrentTCB[xPortGetCoreID()])
|
|
|
+ if( taskIS_CURRENTLY_RUNNING( pxTCB ) )
|
|
|
{
|
|
|
/* The task calling this function is querying its own state. */
|
|
|
eReturn = eRunning;
|
|
|
}
|
|
|
- #if (configNUM_CORES > 1)
|
|
|
- else if (pxTCB == pxCurrentTCB[!xPortGetCoreID()])
|
|
|
- {
|
|
|
- /* The task calling this function is querying its own state. */
|
|
|
- eReturn = eRunning;
|
|
|
- }
|
|
|
- #endif
|
|
|
else
|
|
|
{
|
|
|
pxStateList = listLIST_ITEM_CONTAINER( &( pxTCB->xStateListItem ) );
|
|
|
@@ -1847,7 +1865,7 @@ static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
|
|
|
* priority than the calling task. */
|
|
|
if( uxNewPriority > uxCurrentBasePriority )
|
|
|
{
|
|
|
- if( pxTCB != pxCurrentTCB[ xPortGetCoreID() ] )
|
|
|
+ if( !taskIS_CURRENTLY_RUNNING( pxTCB ) )
|
|
|
{
|
|
|
/* The priority of a task other than the currently
|
|
|
* running task is being raised. Is the priority being
|
|
|
@@ -1868,13 +1886,22 @@ static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
|
|
|
* priority task able to run so no yield is required. */
|
|
|
}
|
|
|
}
|
|
|
- else if( pxTCB == pxCurrentTCB[ xPortGetCoreID() ] )
|
|
|
+ else if( taskIS_CURRENTLY_RUNNING_ON_CORE( pxTCB, 0 ) )
|
|
|
{
|
|
|
/* Setting the priority of the running task down means
|
|
|
* there may now be another task of higher priority that
|
|
|
* is ready to execute. */
|
|
|
xYieldRequired = pdTRUE;
|
|
|
}
|
|
|
+ #if ( configNUM_CORES > 1 )
|
|
|
+ else if( taskIS_CURRENTLY_RUNNING_ON_CORE( pxTCB, 1 ) )
|
|
|
+ {
|
|
|
+ /* Setting the priority of the running task on the other
|
|
|
+ * core down means there may now be another task of
|
|
|
+ * higher priority that is ready to execute. */
|
|
|
+ vPortYieldOtherCore( 1 );
|
|
|
+ }
|
|
|
+ #endif /* configNUM_CORES > 1 */
|
|
|
else
|
|
|
{
|
|
|
/* Setting the priority of any other task down does not
|
|
|
@@ -1973,7 +2000,6 @@ static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
|
|
|
void vTaskSuspend( TaskHandle_t xTaskToSuspend )
|
|
|
{
|
|
|
TCB_t * pxTCB;
|
|
|
- TCB_t * curTCB;
|
|
|
|
|
|
taskENTER_CRITICAL( &xKernelLock );
|
|
|
{
|
|
|
@@ -2005,7 +2031,6 @@ static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
|
|
|
}
|
|
|
|
|
|
vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) );
|
|
|
- curTCB = pxCurrentTCB[ xPortGetCoreID() ];
|
|
|
|
|
|
#if ( configUSE_TASK_NOTIFICATIONS == 1 )
|
|
|
{
|
|
|
@@ -2022,76 +2047,70 @@ static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
|
|
|
}
|
|
|
}
|
|
|
#endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */
|
|
|
- }
|
|
|
- taskEXIT_CRITICAL( &xKernelLock );
|
|
|
|
|
|
- if( xSchedulerRunning != pdFALSE )
|
|
|
- {
|
|
|
- /* Reset the next expected unblock time in case it referred to the
|
|
|
- * task that is now in the Suspended state. */
|
|
|
- taskENTER_CRITICAL( &xKernelLock );
|
|
|
+ if( xSchedulerRunning != pdFALSE )
|
|
|
{
|
|
|
+ /* Reset the next expected unblock time in case it referred to the
|
|
|
+ * task that is now in the Suspended state. */
|
|
|
prvResetNextTaskUnblockTime();
|
|
|
}
|
|
|
- taskEXIT_CRITICAL( &xKernelLock );
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- mtCOVERAGE_TEST_MARKER();
|
|
|
- }
|
|
|
-
|
|
|
- if( pxTCB == curTCB )
|
|
|
- {
|
|
|
- if( xSchedulerRunning != pdFALSE )
|
|
|
+ else
|
|
|
{
|
|
|
- /* The current task has just been suspended. */
|
|
|
- taskENTER_CRITICAL( &xKernelLock );
|
|
|
- BaseType_t suspended = uxSchedulerSuspended[xPortGetCoreID()];
|
|
|
- taskEXIT_CRITICAL( &xKernelLock );
|
|
|
-
|
|
|
- configASSERT( suspended == 0 );
|
|
|
- (void)suspended;
|
|
|
- portYIELD_WITHIN_API();
|
|
|
+ mtCOVERAGE_TEST_MARKER();
|
|
|
}
|
|
|
- else
|
|
|
+
|
|
|
+ if( taskIS_CURRENTLY_RUNNING_ON_CORE( pxTCB, xPortGetCoreID() ) )
|
|
|
{
|
|
|
- /* The scheduler is not running, but the task that was pointed
|
|
|
- * to by pxCurrentTCB has just been suspended and pxCurrentTCB
|
|
|
- * must be adjusted to point to a different task. */
|
|
|
- if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks ) /*lint !e931 Right has no side effect, just volatile. */
|
|
|
+ if( xSchedulerRunning != pdFALSE )
|
|
|
{
|
|
|
- /* No other tasks are ready, so set pxCurrentTCB back to
|
|
|
- * NULL so when the next task is created pxCurrentTCB will
|
|
|
- * be set to point to it no matter what its relative priority
|
|
|
- * is. */
|
|
|
- taskENTER_CRITICAL( &xKernelLock );
|
|
|
- pxCurrentTCB[ xPortGetCoreID() ] = NULL;
|
|
|
- taskEXIT_CRITICAL( &xKernelLock );
|
|
|
+ /* The current task has just been suspended. */
|
|
|
+ configASSERT( uxSchedulerSuspended[ xPortGetCoreID() ] == 0 );
|
|
|
+ portYIELD_WITHIN_API();
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- vTaskSwitchContext();
|
|
|
+ /* The scheduler is not running, but the task that was pointed
|
|
|
+ * to by pxCurrentTCB has just been suspended and pxCurrentTCB
|
|
|
+ * must be adjusted to point to a different task. */
|
|
|
+ if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks ) /*lint !e931 Right has no side effect, just volatile. */
|
|
|
+ {
|
|
|
+ /* No other tasks are ready, so set pxCurrentTCB back to
|
|
|
+ * NULL so when the next task is created pxCurrentTCB will
|
|
|
+ * be set to point to it no matter what its relative priority
|
|
|
+ * is. */
|
|
|
+ pxCurrentTCB[ xPortGetCoreID() ] = NULL;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ vTaskSwitchContext();
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- if( xSchedulerRunning != pdFALSE )
|
|
|
- {
|
|
|
- /* A task other than the currently running task was suspended,
|
|
|
- * reset the next expected unblock time in case it referred to the
|
|
|
- * task that is now in the Suspended state. */
|
|
|
- taskENTER_CRITICAL( &xKernelLock );
|
|
|
+ #if ( configNUM_CORES > 1 )
|
|
|
+ else if( taskIS_CURRENTLY_RUNNING_ON_CORE( pxTCB, !xPortGetCoreID() ) )
|
|
|
{
|
|
|
- prvResetNextTaskUnblockTime();
|
|
|
+ /* The other core's current task has just been suspended */
|
|
|
+ if( xSchedulerRunning != pdFALSE )
|
|
|
+ {
|
|
|
+ vPortYieldOtherCore( !xPortGetCoreID() );
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ /* The scheduler is not running, but the task that was pointed
|
|
|
+ * to by pxCurrentTCB[ otherCore ] has just been suspended.
|
|
|
+ * We simply set the pxCurrentTCB[ otherCore ] to NULL for now.
|
|
|
+ * Todo: Update vTaskSwitchContext() to be runnable on
|
|
|
+ * behalf of the other core. */
|
|
|
+ pxCurrentTCB[ !xPortGetCoreID() ] = NULL;
|
|
|
+ }
|
|
|
}
|
|
|
- taskEXIT_CRITICAL( &xKernelLock );
|
|
|
- }
|
|
|
+ #endif /* configNUM_CORES > 1 */
|
|
|
else
|
|
|
{
|
|
|
mtCOVERAGE_TEST_MARKER();
|
|
|
}
|
|
|
}
|
|
|
+ taskEXIT_CRITICAL( &xKernelLock );
|
|
|
}
|
|
|
|
|
|
#endif /* INCLUDE_vTaskSuspend */
|
|
|
@@ -2157,7 +2176,7 @@ static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
|
|
|
{
|
|
|
/* The parameter cannot be NULL as it is impossible to resume the
|
|
|
* currently executing task. */
|
|
|
- if( ( pxTCB != pxCurrentTCB[xPortGetCoreID()] ) && ( pxTCB != NULL ) )
|
|
|
+ if( !taskIS_CURRENTLY_RUNNING( pxTCB ) && ( pxTCB != NULL ) )
|
|
|
{
|
|
|
if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE )
|
|
|
{
|
|
|
@@ -2457,47 +2476,45 @@ void vTaskSuspendAll( void )
|
|
|
|
|
|
#if ( configUSE_TICKLESS_IDLE != 0 )
|
|
|
|
|
|
-#if ( configNUM_CORES > 1 )
|
|
|
-
|
|
|
- static BaseType_t xHaveReadyTasks( void )
|
|
|
+ static TickType_t prvGetExpectedIdleTime( void )
|
|
|
{
|
|
|
- for (int i = tskIDLE_PRIORITY + 1; i < configMAX_PRIORITIES; ++i)
|
|
|
- {
|
|
|
- if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ i ] ) ) > 0 )
|
|
|
+ TickType_t xReturn;
|
|
|
+ UBaseType_t uxHigherPriorityReadyTasks = pdFALSE;
|
|
|
+
|
|
|
+ /* We need a critical section here as we are about to access kernel data structures */
|
|
|
+ taskENTER_CRITICAL( &xKernelLock );
|
|
|
+
|
|
|
+ /* uxHigherPriorityReadyTasks takes care of the case where
|
|
|
+ * configUSE_PREEMPTION is 0, so there may be tasks above the idle priority
|
|
|
+ * task that are in the Ready state, even though the idle task is
|
|
|
+ * running. */
|
|
|
+ #if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 )
|
|
|
{
|
|
|
- return pdTRUE;
|
|
|
+ if( uxTopReadyPriority > tskIDLE_PRIORITY )
|
|
|
+ {
|
|
|
+ uxHigherPriorityReadyTasks = pdTRUE;
|
|
|
+ }
|
|
|
}
|
|
|
- else
|
|
|
+ #else
|
|
|
{
|
|
|
- mtCOVERAGE_TEST_MARKER();
|
|
|
- }
|
|
|
- }
|
|
|
- return pdFALSE;
|
|
|
- }
|
|
|
-
|
|
|
-#endif // configNUM_CORES > 1
|
|
|
-
|
|
|
- static TickType_t prvGetExpectedIdleTime( void )
|
|
|
- {
|
|
|
- TickType_t xReturn;
|
|
|
+ const UBaseType_t uxLeastSignificantBit = ( UBaseType_t ) 0x01;
|
|
|
|
|
|
+ /* When port optimised task selection is used the uxTopReadyPriority
|
|
|
+ * variable is used as a bit map. If bits other than the least
|
|
|
+ * significant bit are set then there are tasks that have a priority
|
|
|
+ * above the idle priority that are in the Ready state. This takes
|
|
|
+ * care of the case where the co-operative scheduler is in use. */
|
|
|
+ if( uxTopReadyPriority > uxLeastSignificantBit )
|
|
|
+ {
|
|
|
+ uxHigherPriorityReadyTasks = pdTRUE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ #endif /* if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 ) */
|
|
|
|
|
|
- taskENTER_CRITICAL( &xKernelLock );
|
|
|
if( pxCurrentTCB[ xPortGetCoreID() ]->uxPriority > tskIDLE_PRIORITY )
|
|
|
{
|
|
|
xReturn = 0;
|
|
|
}
|
|
|
-#if configNUM_CORES > 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 // configNUM_CORES > 1
|
|
|
else if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > configNUM_CORES )
|
|
|
{
|
|
|
/* There are other idle priority tasks in the ready state. If
|
|
|
@@ -2505,10 +2522,18 @@ void vTaskSuspendAll( void )
|
|
|
* processed. */
|
|
|
xReturn = 0;
|
|
|
}
|
|
|
+ else if( uxHigherPriorityReadyTasks != pdFALSE )
|
|
|
+ {
|
|
|
+ /* There are tasks in the Ready state that have a priority above the
|
|
|
+ * idle priority. This path can only be reached if
|
|
|
+ * configUSE_PREEMPTION is 0. */
|
|
|
+ xReturn = 0;
|
|
|
+ }
|
|
|
else
|
|
|
{
|
|
|
xReturn = xNextTaskUnblockTime - xTickCount;
|
|
|
}
|
|
|
+
|
|
|
taskEXIT_CRITICAL( &xKernelLock );
|
|
|
|
|
|
return xReturn;
|