mirror of
https://github.com/FreeRTOS/FreeRTOS-Kernel.git
synced 2025-04-19 21:11:57 -04:00
Enhanced priority dis-inheritance functionality in the case where a task that caused another task to inherit its priority times out before obtain a mutex.
Added test code to GenQTest to test the new priority dis-inheritance functionality. Allow the default names given to the Idle and Timer tasks to be overwridden by definitions in FreeRTOSConfig.h.
This commit is contained in:
parent
883541bc8e
commit
d67dcf9c74
|
@ -91,6 +91,7 @@
|
|||
|
||||
#define genqQUEUE_LENGTH ( 5 )
|
||||
#define intsemNO_BLOCK ( 0 )
|
||||
#define genqSHORT_BLOCK ( pdMS_TO_TICKS( 2 ) )
|
||||
|
||||
#define genqMUTEX_LOW_PRIORITY ( tskIDLE_PRIORITY )
|
||||
#define genqMUTEX_TEST_PRIORITY ( tskIDLE_PRIORITY + 1 )
|
||||
|
@ -125,6 +126,27 @@ static void prvLowPriorityMutexTask( void *pvParameters );
|
|||
static void prvMediumPriorityMutexTask( void *pvParameters );
|
||||
static void prvHighPriorityMutexTask( void *pvParameters );
|
||||
|
||||
/*
|
||||
* Tests the behaviour when a low priority task inherits the priority of a
|
||||
* higher priority task when taking two mutexes, and returns the mutexes in
|
||||
* first the same order as the two mutexes were obtained, and second the
|
||||
* opposite order as the two mutexes were obtained.
|
||||
*/
|
||||
static void prvTakeTwoMutexesReturnInSameOrder( SemaphoreHandle_t xMutex, SemaphoreHandle_t xLocalMutex );
|
||||
static void prvTakeTwoMutexesReturnInDifferentOrder( SemaphoreHandle_t xMutex, SemaphoreHandle_t xLocalMutex );
|
||||
|
||||
#if( INCLUDE_xTaskAbortDelay == 1 )
|
||||
|
||||
#if( configUSE_PREEMPTION == 0 )
|
||||
#error The additional tests included when INCLUDE_xTaskAbortDelay is 1 expect preemption to be used.
|
||||
#endif
|
||||
|
||||
/* Tests the behaviour when a low priority task inherits the priority of a
|
||||
high priority task only for the high priority task to timeout before
|
||||
obtaining the mutex. */
|
||||
static void prvHighPriorityTimeout( SemaphoreHandle_t xMutex );
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/* Flag that will be latched to pdTRUE should any unexpected behaviour be
|
||||
|
@ -143,6 +165,17 @@ static volatile uint32_t ulGuardedVariable = 0;
|
|||
priority mutex test tasks. */
|
||||
static TaskHandle_t xHighPriorityMutexTask, xMediumPriorityMutexTask;
|
||||
|
||||
/* If INCLUDE_xTaskAbortDelay is 1 additional tests are performed, requiring an
|
||||
additional task. */
|
||||
#if( INCLUDE_xTaskAbortDelay == 1 )
|
||||
static TaskHandle_t xSecondMediumPriorityMutexTask;
|
||||
#endif
|
||||
|
||||
/* Lets the high priority semaphore task know that its wait for the semaphore
|
||||
was aborted, in which case not being able to obtain the semaphore is not to be
|
||||
considered an error. */
|
||||
static volatile BaseType_t xBlockWasAborted = pdFALSE;
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void vStartGenericQueueTasks( UBaseType_t uxPriority )
|
||||
|
@ -189,13 +222,21 @@ SemaphoreHandle_t xMutex;
|
|||
xTaskCreate( prvLowPriorityMutexTask, "MuLow", configMINIMAL_STACK_SIZE, ( void * ) xMutex, genqMUTEX_LOW_PRIORITY, NULL );
|
||||
xTaskCreate( prvMediumPriorityMutexTask, "MuMed", configMINIMAL_STACK_SIZE, NULL, genqMUTEX_MEDIUM_PRIORITY, &xMediumPriorityMutexTask );
|
||||
xTaskCreate( prvHighPriorityMutexTask, "MuHigh", configMINIMAL_STACK_SIZE, ( void * ) xMutex, genqMUTEX_HIGH_PRIORITY, &xHighPriorityMutexTask );
|
||||
|
||||
/* If INCLUDE_xTaskAbortDelay is set then additional tests are performed,
|
||||
requiring two instances of prvHighPriorityMutexTask(). */
|
||||
#if( INCLUDE_xTaskAbortDelay == 1 )
|
||||
{
|
||||
xTaskCreate( prvHighPriorityMutexTask, "MuHigh2", configMINIMAL_STACK_SIZE, ( void * ) xMutex, genqMUTEX_MEDIUM_PRIORITY, &xSecondMediumPriorityMutexTask );
|
||||
}
|
||||
#endif /* INCLUDE_xTaskAbortDelay */
|
||||
}
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void prvSendFrontAndBackTest( void *pvParameters )
|
||||
{
|
||||
uint32_t ulData, ulData2;
|
||||
uint32_t ulData, ulData2, ulLoopCounterSnapshot;
|
||||
QueueHandle_t xQueue;
|
||||
|
||||
#ifdef USE_STDIO
|
||||
|
@ -215,7 +256,8 @@ QueueHandle_t xQueue;
|
|||
should have the same efect as sending it to the front of the queue.
|
||||
|
||||
First send to the front and check everything is as expected. */
|
||||
xQueueSendToFront( xQueue, ( void * ) &ulLoopCounter, intsemNO_BLOCK );
|
||||
ulLoopCounterSnapshot = ulLoopCounter;
|
||||
xQueueSendToFront( xQueue, ( void * ) &ulLoopCounterSnapshot, intsemNO_BLOCK );
|
||||
|
||||
if( uxQueueMessagesWaiting( xQueue ) != 1 )
|
||||
{
|
||||
|
@ -241,7 +283,8 @@ QueueHandle_t xQueue;
|
|||
xErrorDetected = pdTRUE;
|
||||
}
|
||||
|
||||
xQueueSendToBack( xQueue, ( void * ) &ulLoopCounter, intsemNO_BLOCK );
|
||||
ulLoopCounterSnapshot = ulLoopCounter;
|
||||
xQueueSendToBack( xQueue, ( void * ) &ulLoopCounterSnapshot, intsemNO_BLOCK );
|
||||
|
||||
if( uxQueueMessagesWaiting( xQueue ) != 1 )
|
||||
{
|
||||
|
@ -258,8 +301,8 @@ QueueHandle_t xQueue;
|
|||
xErrorDetected = pdTRUE;
|
||||
}
|
||||
|
||||
/* The data we sent to the queue should equal the data we just received
|
||||
from the queue. */
|
||||
/* The data sent to the queue should equal the data just received from
|
||||
the queue. */
|
||||
if( ulLoopCounter != ulData )
|
||||
{
|
||||
xErrorDetected = pdTRUE;
|
||||
|
@ -416,11 +459,204 @@ QueueHandle_t xQueue;
|
|||
xErrorDetected = pdTRUE;
|
||||
}
|
||||
|
||||
/* Increment the loop counter to indicate these tasks are still
|
||||
executing. */
|
||||
ulLoopCounter++;
|
||||
}
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#if( INCLUDE_xTaskAbortDelay == 1 )
|
||||
|
||||
static void prvHighPriorityTimeout( SemaphoreHandle_t xMutex )
|
||||
{
|
||||
static UBaseType_t uxLoopCount = 0;
|
||||
|
||||
/* The tests in this function are very similar, the slight variations
|
||||
are for code coverage purposes. */
|
||||
|
||||
/* Take the mutex. It should be available now. */
|
||||
if( xSemaphoreTake( xMutex, intsemNO_BLOCK ) != pdPASS )
|
||||
{
|
||||
xErrorDetected = pdTRUE;
|
||||
}
|
||||
|
||||
/* This task's priority should be as per that assigned when the task was
|
||||
created. */
|
||||
if( uxTaskPriorityGet( NULL ) != genqMUTEX_LOW_PRIORITY )
|
||||
{
|
||||
xErrorDetected = pdTRUE;
|
||||
}
|
||||
|
||||
/* Now unsuspend the high priority task. This will attempt to take the
|
||||
mutex, and block when it finds it cannot obtain it. */
|
||||
vTaskResume( xHighPriorityMutexTask );
|
||||
|
||||
/* This task should now have inherited the priority of the high priority
|
||||
task as by now the high priority task will have attempted to obtain the
|
||||
mutex. */
|
||||
if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )
|
||||
{
|
||||
xErrorDetected = pdTRUE;
|
||||
}
|
||||
|
||||
/* Unblock a second medium priority task. It too will attempt to take
|
||||
the mutex and enter the Blocked state - it won't run yet though as this
|
||||
task has inherited a priority above it. */
|
||||
vTaskResume( xSecondMediumPriorityMutexTask );
|
||||
|
||||
/* This task should still have the priority of the high priority task as
|
||||
that had already been inherited as is the highest priority of the three
|
||||
tasks using the mutex. */
|
||||
if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )
|
||||
{
|
||||
xErrorDetected = pdTRUE;
|
||||
}
|
||||
|
||||
/* On some loops, block for a short while to provide additional
|
||||
code coverage. Blocking here will allow the medium priority task to
|
||||
execute and so also block on the mutex so when the high priority task
|
||||
causes this task to disinherit the high priority it is inherited down to
|
||||
the priority of the medium priority task. When there is no delay the
|
||||
medium priority task will not run until after the disinheritance, so
|
||||
this task will disinherit back to its base priority, then only up to the
|
||||
medium priority after the medium priority has executed. */
|
||||
vTaskDelay( uxLoopCount & ( UBaseType_t ) 0x07 );
|
||||
|
||||
/* Now force the high priority task to unblock. It will fail to obtain
|
||||
the mutex and go back to the suspended state - allowing this task to
|
||||
execute again. xBlockWasAborted is set to pdTRUE so the higher priority
|
||||
task knows that its failure to obtain the semaphore is not an error. */
|
||||
xBlockWasAborted = pdTRUE;
|
||||
if( xTaskAbortDelay( xHighPriorityMutexTask ) != pdPASS )
|
||||
{
|
||||
xErrorDetected = pdTRUE;
|
||||
}
|
||||
|
||||
/* This task has inherited the priority of xHighPriorityMutexTask so
|
||||
could still be running even though xHighPriorityMutexTask is no longer
|
||||
blocked. Delay for a short while to ensure xHighPriorityMutexTask gets
|
||||
a chance to run - indicated by this task changing priority. It should
|
||||
disinherit the high priority task, but then inherit the priority of the
|
||||
medium priority task that is waiting for the same mutex. */
|
||||
while( uxTaskPriorityGet( NULL ) != genqMUTEX_MEDIUM_PRIORITY )
|
||||
{
|
||||
/* If this task gets stuck here then the check variables will stop
|
||||
incrementing and the check task will detect the error. */
|
||||
vTaskDelay( genqSHORT_BLOCK );
|
||||
}
|
||||
|
||||
/* Now force the medium priority task to unblock. xBlockWasAborted is
|
||||
set to pdTRUE so the medium priority task knows that its failure to
|
||||
obtain the semaphore is not an error. */
|
||||
xBlockWasAborted = pdTRUE;
|
||||
if( xTaskAbortDelay( xSecondMediumPriorityMutexTask ) != pdPASS )
|
||||
{
|
||||
xErrorDetected = pdTRUE;
|
||||
}
|
||||
|
||||
/* This time no other tasks are waiting for the mutex, so this task
|
||||
should return to its base priority. This might not happen straight
|
||||
away as it is running at the same priority as the task it just
|
||||
unblocked. */
|
||||
while( uxTaskPriorityGet( NULL ) != genqMUTEX_LOW_PRIORITY )
|
||||
{
|
||||
/* If this task gets stuck here then the check variables will stop
|
||||
incrementing and the check task will detect the error. */
|
||||
vTaskDelay( genqSHORT_BLOCK );
|
||||
}
|
||||
|
||||
/* Give the semaphore back ready for the next test. */
|
||||
xSemaphoreGive( xMutex );
|
||||
|
||||
configASSERT( xErrorDetected == pdFALSE );
|
||||
|
||||
|
||||
|
||||
/* Now do the same again, but this time unsuspend the tasks in the
|
||||
opposite order. This takes a different path though the code because
|
||||
when the high priority task has its block aborted there is already
|
||||
another task in the list of tasks waiting for the mutex, and the
|
||||
low priority task drops down to that priority, rather than dropping
|
||||
down to its base priority before inheriting the priority of the medium
|
||||
priority task. */
|
||||
if( xSemaphoreTake( xMutex, intsemNO_BLOCK ) != pdPASS )
|
||||
{
|
||||
xErrorDetected = pdTRUE;
|
||||
}
|
||||
|
||||
if( uxTaskPriorityGet( NULL ) != genqMUTEX_LOW_PRIORITY )
|
||||
{
|
||||
xErrorDetected = pdTRUE;
|
||||
}
|
||||
|
||||
/* This time unsuspend the medium priority task first. This will
|
||||
attempt to take the mutex, and block when it finds it cannot obtain it. */
|
||||
vTaskResume( xSecondMediumPriorityMutexTask );
|
||||
|
||||
/* This time this task should now have inherited the priority of the
|
||||
medium task. */
|
||||
if( uxTaskPriorityGet( NULL ) != genqMUTEX_MEDIUM_PRIORITY )
|
||||
{
|
||||
xErrorDetected = pdTRUE;
|
||||
}
|
||||
|
||||
/* This time the high priority task in unsuspended second. */
|
||||
vTaskResume( xHighPriorityMutexTask );
|
||||
|
||||
/* The high priority task should already have run, causing this task to
|
||||
inherit a priority for the second time. */
|
||||
if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )
|
||||
{
|
||||
xErrorDetected = pdTRUE;
|
||||
}
|
||||
|
||||
/* This time, when the high priority task has its delay aborted and it
|
||||
fails to obtain the mutex this task will immediately have its priority
|
||||
lowered down to that of the highest priority task waiting on the mutex,
|
||||
which is the medium priority task. */
|
||||
xBlockWasAborted = pdTRUE;
|
||||
if( xTaskAbortDelay( xHighPriorityMutexTask ) != pdPASS )
|
||||
{
|
||||
xErrorDetected = pdTRUE;
|
||||
}
|
||||
|
||||
while( uxTaskPriorityGet( NULL ) != genqMUTEX_MEDIUM_PRIORITY )
|
||||
{
|
||||
/* If this task gets stuck here then the check variables will stop
|
||||
incrementing and the check task will detect the error. */
|
||||
vTaskDelay( genqSHORT_BLOCK );
|
||||
}
|
||||
|
||||
/* And finally, when the medium priority task also have its delay
|
||||
aborted there are no other tasks waiting for the mutex so this task
|
||||
returns to its base priority. */
|
||||
xBlockWasAborted = pdTRUE;
|
||||
if( xTaskAbortDelay( xSecondMediumPriorityMutexTask ) != pdPASS )
|
||||
{
|
||||
xErrorDetected = pdTRUE;
|
||||
}
|
||||
|
||||
while( uxTaskPriorityGet( NULL ) != genqMUTEX_LOW_PRIORITY )
|
||||
{
|
||||
/* If this task gets stuck here then the check variables will stop
|
||||
incrementing and the check task will detect the error. */
|
||||
vTaskDelay( genqSHORT_BLOCK );
|
||||
}
|
||||
|
||||
/* Give the semaphore back ready for the next test. */
|
||||
xSemaphoreGive( xMutex );
|
||||
|
||||
configASSERT( xErrorDetected == pdFALSE );
|
||||
|
||||
/* uxLoopCount is used to add a variable delay, and in-so-doing provide
|
||||
additional code coverage. */
|
||||
uxLoopCount++;
|
||||
}
|
||||
|
||||
#endif /* INCLUDE_xTaskAbortDelay == 1 */
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void prvTakeTwoMutexesReturnInDifferentOrder( SemaphoreHandle_t xMutex, SemaphoreHandle_t xLocalMutex )
|
||||
{
|
||||
/* Take the mutex. It should be available now. */
|
||||
|
@ -455,8 +691,9 @@ static void prvTakeTwoMutexesReturnInDifferentOrder( SemaphoreHandle_t xMutex, S
|
|||
}
|
||||
#endif /* INCLUDE_eTaskGetState */
|
||||
|
||||
/* The priority of the high priority task should now have been inherited
|
||||
as by now it will have attempted to get the mutex. */
|
||||
/* This task should now have inherited the priority of the high priority
|
||||
task as by now the high priority task will have attempted to obtain the
|
||||
mutex. */
|
||||
if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )
|
||||
{
|
||||
xErrorDetected = pdTRUE;
|
||||
|
@ -585,8 +822,9 @@ static void prvTakeTwoMutexesReturnInSameOrder( SemaphoreHandle_t xMutex, Semaph
|
|||
}
|
||||
#endif /* INCLUDE_eTaskGetState */
|
||||
|
||||
/* The priority of the high priority task should now have been inherited
|
||||
as by now it will have attempted to get the mutex. */
|
||||
/* This task should now have inherited the priority of the high priority
|
||||
task as by now the high priority task will have attempted to obtain the
|
||||
mutex. */
|
||||
if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )
|
||||
{
|
||||
xErrorDetected = pdTRUE;
|
||||
|
@ -708,6 +946,15 @@ SemaphoreHandle_t xMutex = ( SemaphoreHandle_t ) pvParameters, xLocalMutex;
|
|||
#if configUSE_PREEMPTION == 0
|
||||
taskYIELD();
|
||||
#endif
|
||||
|
||||
#if( INCLUDE_xTaskAbortDelay == 1 )
|
||||
{
|
||||
/* Tests the behaviour when a low priority task inherits the
|
||||
priority of a high priority task only for the high priority task to
|
||||
timeout before obtaining the mutex. */
|
||||
prvHighPriorityTimeout( xMutex );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
@ -740,14 +987,24 @@ SemaphoreHandle_t xMutex = ( SemaphoreHandle_t ) pvParameters;
|
|||
priority task will unsuspend this task when required. */
|
||||
vTaskSuspend( NULL );
|
||||
|
||||
/* When this task unsuspends all it does is attempt to obtain
|
||||
the mutex. It should find the mutex is not available so a
|
||||
block time is specified. */
|
||||
/* When this task unsuspends all it does is attempt to obtain the
|
||||
mutex. It should find the mutex is not available so a block time is
|
||||
specified. */
|
||||
if( xSemaphoreTake( xMutex, portMAX_DELAY ) != pdPASS )
|
||||
{
|
||||
/* This task would expect to obtain the mutex unless its wait for
|
||||
the mutex was aborted. */
|
||||
if( xBlockWasAborted == pdFALSE )
|
||||
{
|
||||
xErrorDetected = pdTRUE;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
xBlockWasAborted = pdFALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* When the mutex is eventually obtained it is just given back before
|
||||
returning to suspend ready for the next cycle. */
|
||||
if( xSemaphoreGive( xMutex ) != pdPASS )
|
||||
|
@ -756,6 +1013,7 @@ SemaphoreHandle_t xMutex = ( SemaphoreHandle_t ) pvParameters;
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
|
||||
|
|
|
@ -314,7 +314,7 @@ static void prvRecursiveMutexPollingTask( void *pvParameters )
|
|||
|
||||
for( ;; )
|
||||
{
|
||||
/* Keep attempting to obtain the mutex. We should only obtain it when
|
||||
/* Keep attempting to obtain the mutex. It should only be obtained when
|
||||
the blocking task has suspended itself, which in turn should only
|
||||
happen when the controlling task is also suspended. */
|
||||
if( xSemaphoreTakeRecursive( xMutex, recmuNO_DELAY ) == pdPASS )
|
||||
|
|
|
@ -205,7 +205,7 @@ typedef struct xMINI_LIST_ITEM MiniListItem_t;
|
|||
typedef struct xLIST
|
||||
{
|
||||
listFIRST_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
|
||||
configLIST_VOLATILE UBaseType_t uxNumberOfItems;
|
||||
volatile UBaseType_t uxNumberOfItems;
|
||||
ListItem_t * configLIST_VOLATILE pxIndex; /*< Used to walk through the list. Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */
|
||||
MiniListItem_t xListEnd; /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */
|
||||
listSECOND_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
|
||||
|
|
|
@ -2311,6 +2311,16 @@ BaseType_t xTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) PRIVILEGED_F
|
|||
*/
|
||||
BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ) PRIVILEGED_FUNCTION;
|
||||
|
||||
/*
|
||||
* If a higher priority task attempting to obtain a mutex caused a lower
|
||||
* priority task to inherit the higher priority task's priority - but the higher
|
||||
* priority task then timed out without obtaining the mutex, then the lower
|
||||
* priority task will disinherit the priority again - but only down as far as
|
||||
* the highest priority task that is still waiting for the mutex (if there were
|
||||
* more than one task waiting for the mutex).
|
||||
*/
|
||||
void vTaskPriorityDisinheritAfterTimeout( TaskHandle_t const pxMutexHolder, UBaseType_t uxHighestPriorityWaitingTask ) PRIVILEGED_FUNCTION;
|
||||
|
||||
/*
|
||||
* Get the uxTCBNumber assigned to the task referenced by the xTask parameter.
|
||||
*/
|
||||
|
|
|
@ -255,6 +255,16 @@ static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseT
|
|||
static void prvInitialiseMutex( Queue_t *pxNewQueue ) PRIVILEGED_FUNCTION;
|
||||
#endif
|
||||
|
||||
#if( configUSE_MUTEXES == 1 )
|
||||
/*
|
||||
* If a task waiting for a mutex causes the mutex holder to inherit a
|
||||
* priority, but the waiting task times out, then the holder should
|
||||
* disinherit the priority - but only down to the highest priority of any
|
||||
* other tasks that are waiting for the same mutex. This function returns
|
||||
* that priority.
|
||||
*/
|
||||
static UBaseType_t prvGetDisinheritPriorityAfterTimeout( const Queue_t * const pxQueue ) PRIVILEGED_FUNCTION;
|
||||
#endif
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
|
@ -1408,6 +1418,10 @@ BaseType_t xEntryTimeSet = pdFALSE;
|
|||
TimeOut_t xTimeOut;
|
||||
Queue_t * const pxQueue = ( Queue_t * ) xQueue;
|
||||
|
||||
#if( configUSE_MUTEXES == 1 )
|
||||
BaseType_t xInheritanceOccurred = pdFALSE;
|
||||
#endif
|
||||
|
||||
/* Check the queue pointer is not NULL. */
|
||||
configASSERT( ( pxQueue ) );
|
||||
|
||||
|
@ -1485,6 +1499,11 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue;
|
|||
{
|
||||
if( xTicksToWait == ( TickType_t ) 0 )
|
||||
{
|
||||
/* For inheritance to have occurred there must have been an
|
||||
initial timeout, and an adjusted timeout cannot become 0, as
|
||||
if it were 0 the function would have exited. */
|
||||
configASSERT( xInheritanceOccurred == pdFALSE );
|
||||
|
||||
/* The semaphore count was 0 and no block time is specified
|
||||
(or the block time has expired) so exit now. */
|
||||
taskEXIT_CRITICAL();
|
||||
|
@ -1530,7 +1549,7 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue;
|
|||
{
|
||||
taskENTER_CRITICAL();
|
||||
{
|
||||
xTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder );
|
||||
xInheritanceOccurred = xTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder );
|
||||
}
|
||||
taskEXIT_CRITICAL();
|
||||
}
|
||||
|
@ -1572,6 +1591,30 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue;
|
|||
queue being empty is equivalent to the semaphore count being 0. */
|
||||
if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
|
||||
{
|
||||
#if ( configUSE_MUTEXES == 1 )
|
||||
{
|
||||
/* xInheritanceOccurred could only have be set if
|
||||
pxQueue->uxQueueType == queueQUEUE_IS_MUTEX so no need to
|
||||
test the mutex type again to check it is actually a mutex. */
|
||||
if( xInheritanceOccurred != pdFALSE )
|
||||
{
|
||||
taskENTER_CRITICAL();
|
||||
{
|
||||
UBaseType_t uxHighestWaitingPriority;
|
||||
|
||||
/* This task blocking on the mutex caused another
|
||||
task to inherit this task's priority. Now this task
|
||||
has timed out the priority should be disinherited
|
||||
again, but only as low as the next highest priority
|
||||
task that is waiting for the same mutex. */
|
||||
uxHighestWaitingPriority = prvGetDisinheritPriorityAfterTimeout( pxQueue );
|
||||
vTaskPriorityDisinheritAfterTimeout( ( void * ) pxQueue->pxMutexHolder, uxHighestWaitingPriority );
|
||||
}
|
||||
taskEXIT_CRITICAL();
|
||||
}
|
||||
}
|
||||
#endif /* configUSE_MUTEXES */
|
||||
|
||||
traceQUEUE_RECEIVE_FAILED( pxQueue );
|
||||
return errQUEUE_EMPTY;
|
||||
}
|
||||
|
@ -1997,6 +2040,33 @@ Queue_t * const pxQueue = ( Queue_t * ) xQueue;
|
|||
#endif /* configUSE_TRACE_FACILITY */
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#if( configUSE_MUTEXES == 1 )
|
||||
|
||||
static UBaseType_t prvGetDisinheritPriorityAfterTimeout( const Queue_t * const pxQueue )
|
||||
{
|
||||
UBaseType_t uxHighestPriorityOfWaitingTasks;
|
||||
|
||||
/* If a task waiting for a mutex causes the mutex holder to inherit a
|
||||
priority, but the waiting task times out, then the holder should
|
||||
disinherit the priority - but only down to the highest priority of any
|
||||
other tasks that are waiting for the same mutex. For this purpose,
|
||||
return the priority of the highest priority task that is waiting for the
|
||||
mutex. */
|
||||
if( listCURRENT_LIST_LENGTH( &( pxQueue->xTasksWaitingToReceive ) ) > 0 )
|
||||
{
|
||||
uxHighestPriorityOfWaitingTasks = configMAX_PRIORITIES - listGET_ITEM_VALUE_OF_HEAD_ENTRY( &( pxQueue->xTasksWaitingToReceive ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
uxHighestPriorityOfWaitingTasks = tskIDLE_PRIORITY;
|
||||
}
|
||||
|
||||
return uxHighestPriorityOfWaitingTasks;
|
||||
}
|
||||
|
||||
#endif /* configUSE_MUTEXES */
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue, const void *pvItemToQueue, const BaseType_t xPosition )
|
||||
{
|
||||
BaseType_t xReturn = pdFALSE;
|
||||
|
|
|
@ -164,6 +164,12 @@ set then don't fill the stack so there is no unnecessary dependency on memset. *
|
|||
#define static
|
||||
#endif
|
||||
|
||||
/* The name allocated to the Idle task. This can be overridden by defining
|
||||
tskIDLE_TASK_NAME in FreeRTOSConfig.h. */
|
||||
#ifndef tskIDLE_TASK_NAME
|
||||
#define tskIDLE_TASK_NAME "IDLE"
|
||||
#endif
|
||||
|
||||
#if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 )
|
||||
|
||||
/* If configUSE_PORT_OPTIMISED_TASK_SELECTION is 0 then task selection is
|
||||
|
@ -713,7 +719,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) PRIVILEGED_FUNCTION;
|
|||
#endif /* ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) */
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#if( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
|
||||
#if( ( portUSING_MPU_WRAPPERS == 1 ) && ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) )
|
||||
|
||||
BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask )
|
||||
{
|
||||
|
@ -1610,14 +1616,14 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
|
|||
}
|
||||
|
||||
/* If the task is in the blocked or suspended list we need do
|
||||
nothing more than change it's priority variable. However, if
|
||||
nothing more than change its priority variable. However, if
|
||||
the task is in a ready list it needs to be removed and placed
|
||||
in the list appropriate to its new priority. */
|
||||
if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE )
|
||||
{
|
||||
/* The task is currently in its ready list - remove before adding
|
||||
it to it's new ready list. As we are in a critical section we
|
||||
can do this even if the scheduler is suspended. */
|
||||
/* The task is currently in its ready list - remove before
|
||||
adding it to it's new ready list. As we are in a critical
|
||||
section we can do this even if the scheduler is suspended. */
|
||||
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
|
||||
{
|
||||
/* It is known that the task is in its ready list so
|
||||
|
@ -1944,7 +1950,7 @@ BaseType_t xReturn;
|
|||
address of the RAM then create the idle task. */
|
||||
vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize );
|
||||
xIdleTaskHandle = xTaskCreateStatic( prvIdleTask,
|
||||
"IDLE",
|
||||
tskIDLE_TASK_NAME,
|
||||
ulIdleTaskStackSize,
|
||||
( void * ) NULL, /*lint !e961. The cast is not redundant for all compilers. */
|
||||
( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
|
||||
|
@ -1964,7 +1970,8 @@ BaseType_t xReturn;
|
|||
{
|
||||
/* The Idle task is being created using dynamically allocated RAM. */
|
||||
xReturn = xTaskCreate( prvIdleTask,
|
||||
"IDLE", configMINIMAL_STACK_SIZE,
|
||||
tskIDLE_TASK_NAME,
|
||||
configMINIMAL_STACK_SIZE,
|
||||
( void * ) NULL,
|
||||
( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
|
||||
&xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */
|
||||
|
@ -2545,7 +2552,7 @@ implementations require configUSE_TICKLESS_IDLE to be set to a value other than
|
|||
BaseType_t xTaskAbortDelay( TaskHandle_t xTask )
|
||||
{
|
||||
TCB_t *pxTCB = ( TCB_t * ) xTask;
|
||||
BaseType_t xReturn = pdFALSE;
|
||||
BaseType_t xReturn;
|
||||
|
||||
configASSERT( pxTCB );
|
||||
|
||||
|
@ -2555,6 +2562,8 @@ implementations require configUSE_TICKLESS_IDLE to be set to a value other than
|
|||
it is actually in the Blocked state. */
|
||||
if( eTaskGetState( xTask ) == eBlocked )
|
||||
{
|
||||
xReturn = pdPASS;
|
||||
|
||||
/* Remove the reference to the task from the blocked list. An
|
||||
interrupt won't touch the xStateListItem because the
|
||||
scheduler is suspended. */
|
||||
|
@ -2603,7 +2612,7 @@ implementations require configUSE_TICKLESS_IDLE to be set to a value other than
|
|||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
xReturn = pdFAIL;
|
||||
}
|
||||
}
|
||||
( void ) xTaskResumeAll();
|
||||
|
@ -3130,6 +3139,7 @@ BaseType_t xReturn;
|
|||
{
|
||||
/* Minor optimisation. The tick count cannot change in this block. */
|
||||
const TickType_t xConstTickCount = xTickCount;
|
||||
const TickType_t xElapsedTime = xConstTickCount - pxTimeOut->xTimeOnEntering;
|
||||
|
||||
#if( INCLUDE_xTaskAbortDelay == 1 )
|
||||
if( pxCurrentTCB->ucDelayAborted != pdFALSE )
|
||||
|
@ -3162,10 +3172,10 @@ BaseType_t xReturn;
|
|||
was called. */
|
||||
xReturn = pdTRUE;
|
||||
}
|
||||
else if( ( ( TickType_t ) ( xConstTickCount - pxTimeOut->xTimeOnEntering ) ) < *pxTicksToWait ) /*lint !e961 Explicit casting is only redundant with some compilers, whereas others require it to prevent integer conversion errors. */
|
||||
else if( xElapsedTime < *pxTicksToWait ) /*lint !e961 Explicit casting is only redundant with some compilers, whereas others require it to prevent integer conversion errors. */
|
||||
{
|
||||
/* Not a genuine timeout. Adjust parameters for time remaining. */
|
||||
*pxTicksToWait -= ( xConstTickCount - pxTimeOut->xTimeOnEntering );
|
||||
*pxTicksToWait -= xElapsedTime;
|
||||
vTaskSetTimeOutState( pxTimeOut );
|
||||
xReturn = pdFALSE;
|
||||
}
|
||||
|
@ -3817,25 +3827,25 @@ TCB_t *pxTCB;
|
|||
|
||||
BaseType_t xTaskPriorityInherit( TaskHandle_t const pxMutexHolder )
|
||||
{
|
||||
TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder;
|
||||
TCB_t * const pxMutexHolderTCB = ( TCB_t * ) pxMutexHolder;
|
||||
BaseType_t xReturn = pdFALSE;
|
||||
|
||||
/* If the mutex was given back by an interrupt while the queue was
|
||||
locked then the mutex holder might now be NULL. _RB_ Is this still
|
||||
needed as interrupt can no longer use mutexes? */
|
||||
needed as interrupts can no longer use mutexes? */
|
||||
if( pxMutexHolder != NULL )
|
||||
{
|
||||
/* If the holder of the mutex has a priority below the priority of
|
||||
the task attempting to obtain the mutex then it will temporarily
|
||||
inherit the priority of the task attempting to obtain the mutex. */
|
||||
if( pxTCB->uxPriority < pxCurrentTCB->uxPriority )
|
||||
if( pxMutexHolderTCB->uxPriority < pxCurrentTCB->uxPriority )
|
||||
{
|
||||
/* Adjust the mutex holder state to account for its new
|
||||
priority. Only reset the event list item value if the value is
|
||||
not being used for anything else. */
|
||||
if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL )
|
||||
if( ( listGET_LIST_ITEM_VALUE( &( pxMutexHolderTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL )
|
||||
{
|
||||
listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
|
||||
listSET_LIST_ITEM_VALUE( &( pxMutexHolderTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3844,11 +3854,11 @@ TCB_t *pxTCB;
|
|||
|
||||
/* If the task being modified is in the ready state it will need
|
||||
to be moved into a new list. */
|
||||
if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxTCB->uxPriority ] ), &( pxTCB->xStateListItem ) ) != pdFALSE )
|
||||
if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxMutexHolderTCB->uxPriority ] ), &( pxMutexHolderTCB->xStateListItem ) ) != pdFALSE )
|
||||
{
|
||||
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
|
||||
if( uxListRemove( &( pxMutexHolderTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
|
||||
{
|
||||
taskRESET_READY_PRIORITY( pxTCB->uxPriority );
|
||||
taskRESET_READY_PRIORITY( pxMutexHolderTCB->uxPriority );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3856,25 +3866,39 @@ TCB_t *pxTCB;
|
|||
}
|
||||
|
||||
/* Inherit the priority before being moved into the new list. */
|
||||
pxTCB->uxPriority = pxCurrentTCB->uxPriority;
|
||||
prvAddTaskToReadyList( pxTCB );
|
||||
pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority;
|
||||
prvAddTaskToReadyList( pxMutexHolderTCB );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Just inherit the priority. */
|
||||
pxTCB->uxPriority = pxCurrentTCB->uxPriority;
|
||||
pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority;
|
||||
}
|
||||
|
||||
traceTASK_PRIORITY_INHERIT( pxTCB, pxCurrentTCB->uxPriority );
|
||||
traceTASK_PRIORITY_INHERIT( pxMutexHolderTCB, pxCurrentTCB->uxPriority );
|
||||
|
||||
/* Inheritance occurred. */
|
||||
xReturn = pdTRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( pxMutexHolderTCB->uxBasePriority < pxCurrentTCB->uxPriority )
|
||||
{
|
||||
/* The base priority of the mutex holder is lower than the
|
||||
priority of the task attempting to take the mutex, but the
|
||||
current priority of the mutex holder is not lower than the
|
||||
priority of the task attempting to take the mutex.
|
||||
Therefore the mutex holder must have already inherited a
|
||||
priority, but inheritance would have occurred if that had
|
||||
not been the case. */
|
||||
xReturn = pdTRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
|
@ -3900,7 +3924,6 @@ TCB_t *pxTCB;
|
|||
interrupt, and if a mutex is given by the holding task then it must
|
||||
be the running state task. */
|
||||
configASSERT( pxTCB == pxCurrentTCB );
|
||||
|
||||
configASSERT( pxTCB->uxMutexesHeld );
|
||||
( pxTCB->uxMutexesHeld )--;
|
||||
|
||||
|
@ -3967,6 +3990,108 @@ TCB_t *pxTCB;
|
|||
#endif /* configUSE_MUTEXES */
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#if ( configUSE_MUTEXES == 1 )
|
||||
|
||||
void vTaskPriorityDisinheritAfterTimeout( TaskHandle_t const pxMutexHolder, UBaseType_t uxHighestPriorityWaitingTask )
|
||||
{
|
||||
TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder;
|
||||
UBaseType_t uxPriorityUsedOnEntry, uxPriorityToUse;
|
||||
const UBaseType_t uxOnlyOneMutexHeld = ( UBaseType_t ) 1;
|
||||
|
||||
if( pxMutexHolder != NULL )
|
||||
{
|
||||
/* If pxMutexHolder is not NULL then the holder must hold at least
|
||||
one mutex. */
|
||||
configASSERT( pxTCB->uxMutexesHeld );
|
||||
|
||||
/* Determine the priority to which the priority of the task that
|
||||
holds the mutex should be set. This will be the greater of the
|
||||
holding task's base priority and the priority of the highest
|
||||
priority task that is waiting to obtain the mutex. */
|
||||
if( pxTCB->uxBasePriority < uxHighestPriorityWaitingTask )
|
||||
{
|
||||
uxPriorityToUse = uxHighestPriorityWaitingTask;
|
||||
}
|
||||
else
|
||||
{
|
||||
uxPriorityToUse = pxTCB->uxBasePriority;
|
||||
}
|
||||
|
||||
/* Does the priority need to change? */
|
||||
if( pxTCB->uxPriority != uxPriorityToUse )
|
||||
{
|
||||
/* Only disinherit if no other mutexes are held. This is a
|
||||
simplification in the priority inheritance implementation. If
|
||||
the task that holds the mutex is also holding other mutexes then
|
||||
the other mutexes may have caused the priority inheritance. */
|
||||
if( pxTCB->uxMutexesHeld == uxOnlyOneMutexHeld )
|
||||
{
|
||||
/* If a task has timed out because it already holds the
|
||||
mutex it was trying to obtain then it cannot of inherited
|
||||
its own priority. */
|
||||
configASSERT( pxTCB != pxCurrentTCB );
|
||||
|
||||
/* Disinherit the priority, remembering the previous
|
||||
priority to facilitate determining the subject task's
|
||||
state. */
|
||||
traceTASK_PRIORITY_DISINHERIT( pxTCB, pxTCB->uxBasePriority );
|
||||
uxPriorityUsedOnEntry = pxTCB->uxPriority;
|
||||
pxTCB->uxPriority = uxPriorityToUse;
|
||||
|
||||
/* Only reset the event list item value if the value is not
|
||||
being used for anything else. */
|
||||
if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL )
|
||||
{
|
||||
listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriorityToUse ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
|
||||
/* If the running task is not the task that holds the mutex
|
||||
then the task that holds the mutex could be in either the
|
||||
Ready, Blocked or Suspended states. Only remove the task
|
||||
from its current state list if it is in the Ready state as
|
||||
the task's priority is going to change and there is one
|
||||
Ready list per priority. */
|
||||
if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE )
|
||||
{
|
||||
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
|
||||
{
|
||||
taskRESET_READY_PRIORITY( pxTCB->uxPriority );
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
|
||||
prvAddTaskToReadyList( pxTCB );
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mtCOVERAGE_TEST_MARKER();
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* configUSE_MUTEXES */
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
#if ( portCRITICAL_NESTING_IN_TCB == 1 )
|
||||
|
||||
void vTaskEnterCritical( void )
|
||||
|
|
|
@ -100,6 +100,12 @@ configUSE_TIMERS is set to 1 in FreeRTOSConfig.h. */
|
|||
/* Misc definitions. */
|
||||
#define tmrNO_DELAY ( TickType_t ) 0U
|
||||
|
||||
/* The name assigned to the timer service task. This can be overridden by
|
||||
defining trmTIMER_SERVICE_TASK_NAME in FreeRTOSConfig.h. */
|
||||
#ifndef tmrTIMER_SERVICE_TASK_NAME
|
||||
#define tmrTIMER_SERVICE_TASK_NAME "Tmr Svc"
|
||||
#endif
|
||||
|
||||
/* The definition of the timers themselves. */
|
||||
typedef struct tmrTimerControl
|
||||
{
|
||||
|
@ -276,7 +282,7 @@ BaseType_t xReturn = pdFAIL;
|
|||
|
||||
vApplicationGetTimerTaskMemory( &pxTimerTaskTCBBuffer, &pxTimerTaskStackBuffer, &ulTimerTaskStackSize );
|
||||
xTimerTaskHandle = xTaskCreateStatic( prvTimerTask,
|
||||
"Tmr Svc",
|
||||
tmrTIMER_SERVICE_TASK_NAME,
|
||||
ulTimerTaskStackSize,
|
||||
NULL,
|
||||
( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT,
|
||||
|
@ -291,7 +297,7 @@ BaseType_t xReturn = pdFAIL;
|
|||
#else
|
||||
{
|
||||
xReturn = xTaskCreate( prvTimerTask,
|
||||
"Tmr Svc",
|
||||
tmrTIMER_SERVICE_TASK_NAME,
|
||||
configTIMER_TASK_STACK_DEPTH,
|
||||
NULL,
|
||||
( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT,
|
||||
|
|
Loading…
Reference in a new issue