EBIKE-FreeRTOS/Common/Minimal/GenQTest.c

1040 lines
37 KiB
C
Raw Normal View History

2024-04-14 18:38:39 +08:00
/*
* FreeRTOS V202212.00
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
/*
* Tests the extra queue functionality introduced in FreeRTOS.org V4.5.0 -
* including xQueueSendToFront(), xQueueSendToBack(), xQueuePeek() and
* mutex behaviour.
*
* See the comments above the prvSendFrontAndBackTest() and
* prvLowPriorityMutexTask() prototypes below for more information.
*/
/* Standard includes. */
#include <stdlib.h>
/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
/* Demo program include files. */
#include "GenQTest.h"
#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 )
#define genqMUTEX_MEDIUM_PRIORITY ( tskIDLE_PRIORITY + 2 )
#define genqMUTEX_HIGH_PRIORITY ( tskIDLE_PRIORITY + 3 )
#ifndef genqMUTEX_TEST_TASK_STACK_SIZE
#define genqMUTEX_TEST_TASK_STACK_SIZE configMINIMAL_STACK_SIZE
#endif
#ifndef genqGENERIC_QUEUE_TEST_TASK_STACK_SIZE
#define genqGENERIC_QUEUE_TEST_TASK_STACK_SIZE configMINIMAL_STACK_SIZE
#endif
/*-----------------------------------------------------------*/
/*
* Tests the behaviour of the xQueueSendToFront() and xQueueSendToBack()
* macros by using both to fill a queue, then reading from the queue to
* check the resultant queue order is as expected. Queue data is also
* peeked.
*/
static void prvSendFrontAndBackTest( void * pvParameters );
/*
* The following three tasks are used to demonstrate the mutex behaviour.
* Each task is given a different priority to demonstrate the priority
* inheritance mechanism.
*
* The low priority task obtains a mutex. After this a high priority task
* attempts to obtain the same mutex, causing its priority to be inherited
* by the low priority task. The task with the inherited high priority then
* resumes a medium priority task to ensure it is not blocked by the medium
* priority task while it holds the inherited high priority. Once the mutex
* is returned the task with the inherited priority returns to its original
* low priority, and is therefore immediately preempted by first the high
* priority task and then the medium priority task before it can continue.
*/
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
* detected in any of the tasks. */
static volatile BaseType_t xErrorDetected = pdFALSE;
/* Counters that are incremented on each cycle of a test. This is used to
* detect a stalled task - a test that is no longer running. */
static volatile uint32_t ulLoopCounter = 0;
static volatile uint32_t ulLoopCounter2 = 0;
/* The variable that is guarded by the mutex in the mutex demo tasks. */
static volatile uint32_t ulGuardedVariable = 0;
/* Handles used in the mutex test to suspend and resume the high and medium
* 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 )
{
QueueHandle_t xQueue;
SemaphoreHandle_t xMutex;
/* Create the queue that we are going to use for the
* prvSendFrontAndBackTest demo. */
xQueue = xQueueCreate( genqQUEUE_LENGTH, sizeof( uint32_t ) );
if( xQueue != NULL )
{
/* vQueueAddToRegistry() adds the queue to the queue registry, if one
* is in use. The queue registry is provided as a means for kernel aware
* debuggers to locate queues and has no purpose if a kernel aware debugger
* is not being used. The call to vQueueAddToRegistry() will be removed
* by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is
* defined to be less than 1. */
vQueueAddToRegistry( xQueue, "Gen_Queue_Test" );
/* Create the demo task and pass it the queue just created. We are
* passing the queue handle by value so it does not matter that it is
* declared on the stack here. */
xTaskCreate( prvSendFrontAndBackTest, "GenQ", genqGENERIC_QUEUE_TEST_TASK_STACK_SIZE, ( void * ) xQueue, uxPriority, NULL );
}
/* Create the mutex used by the prvMutexTest task. */
xMutex = xSemaphoreCreateMutex();
if( xMutex != NULL )
{
/* vQueueAddToRegistry() adds the mutex to the registry, if one is
* in use. The registry is provided as a means for kernel aware
* debuggers to locate mutexes and has no purpose if a kernel aware
* debugger is not being used. The call to vQueueAddToRegistry() will be
* removed by the pre-processor if configQUEUE_REGISTRY_SIZE is not
* defined or is defined to be less than 1. */
vQueueAddToRegistry( ( QueueHandle_t ) xMutex, "Gen_Queue_Mutex" );
/* Create the mutex demo tasks and pass it the mutex just created. We
* are passing the mutex handle by value so it does not matter that it is
* declared on the stack here. */
xTaskCreate( prvLowPriorityMutexTask, "MuLow", genqMUTEX_TEST_TASK_STACK_SIZE, ( void * ) xMutex, genqMUTEX_LOW_PRIORITY, NULL );
xTaskCreate( prvMediumPriorityMutexTask, "MuMed", configMINIMAL_STACK_SIZE, NULL, genqMUTEX_MEDIUM_PRIORITY, &xMediumPriorityMutexTask );
xTaskCreate( prvHighPriorityMutexTask, "MuHigh", genqMUTEX_TEST_TASK_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, ulLoopCounterSnapshot;
QueueHandle_t xQueue;
#ifdef USE_STDIO
void vPrintDisplayMessage( const char * const * ppcMessageToSend );
const char * const pcTaskStartMsg = "Queue SendToFront/SendToBack/Peek test started.\r\n";
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
#endif
xQueue = ( QueueHandle_t ) pvParameters;
for( ; ; )
{
/* The queue is empty, so sending an item to the back of the queue
* should have the same effect as sending it to the front of the queue.
*
* First send to the front and check everything is as expected. */
ulLoopCounterSnapshot = ulLoopCounter;
xQueueSendToFront( xQueue, ( void * ) &ulLoopCounterSnapshot, intsemNO_BLOCK );
if( uxQueueMessagesWaiting( xQueue ) != 1 )
{
xErrorDetected = pdTRUE;
}
if( xQueueReceive( xQueue, ( void * ) &ulData, intsemNO_BLOCK ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
/* The data we sent to the queue should equal the data we just received
* from the queue. */
if( ulLoopCounter != ulData )
{
xErrorDetected = pdTRUE;
}
/* Then do the same, sending the data to the back, checking everything
* is as expected. */
if( uxQueueMessagesWaiting( xQueue ) != 0 )
{
xErrorDetected = pdTRUE;
}
ulLoopCounterSnapshot = ulLoopCounter;
xQueueSendToBack( xQueue, ( void * ) &ulLoopCounterSnapshot, intsemNO_BLOCK );
if( uxQueueMessagesWaiting( xQueue ) != 1 )
{
xErrorDetected = pdTRUE;
}
if( xQueueReceive( xQueue, ( void * ) &ulData, intsemNO_BLOCK ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
if( uxQueueMessagesWaiting( xQueue ) != 0 )
{
xErrorDetected = pdTRUE;
}
/* The data sent to the queue should equal the data just received from
* the queue. */
if( ulLoopCounter != ulData )
{
xErrorDetected = pdTRUE;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
/* Place 2, 3, 4 into the queue, adding items to the back of the queue. */
for( ulData = 2; ulData < 5; ulData++ )
{
xQueueSendToBack( xQueue, ( void * ) &ulData, intsemNO_BLOCK );
}
/* Now the order in the queue should be 2, 3, 4, with 2 being the first
* thing to be read out. Now add 1 then 0 to the front of the queue. */
if( uxQueueMessagesWaiting( xQueue ) != 3 )
{
xErrorDetected = pdTRUE;
}
ulData = 1;
xQueueSendToFront( xQueue, ( void * ) &ulData, intsemNO_BLOCK );
ulData = 0;
xQueueSendToFront( xQueue, ( void * ) &ulData, intsemNO_BLOCK );
/* Now the queue should be full, and when we read the data out we
* should receive 0, 1, 2, 3, 4. */
if( uxQueueMessagesWaiting( xQueue ) != 5 )
{
xErrorDetected = pdTRUE;
}
if( xQueueSendToFront( xQueue, ( void * ) &ulData, intsemNO_BLOCK ) != errQUEUE_FULL )
{
xErrorDetected = pdTRUE;
}
if( xQueueSendToBack( xQueue, ( void * ) &ulData, intsemNO_BLOCK ) != errQUEUE_FULL )
{
xErrorDetected = pdTRUE;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
/* Check the data we read out is in the expected order. */
for( ulData = 0; ulData < genqQUEUE_LENGTH; ulData++ )
{
/* Try peeking the data first. */
if( xQueuePeek( xQueue, &ulData2, intsemNO_BLOCK ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
if( ulData != ulData2 )
{
xErrorDetected = pdTRUE;
}
/* Now try receiving the data for real. The value should be the
* same. Clobber the value first so we know we really received it. */
ulData2 = ~ulData2;
if( xQueueReceive( xQueue, &ulData2, intsemNO_BLOCK ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
if( ulData != ulData2 )
{
xErrorDetected = pdTRUE;
}
}
/* The queue should now be empty again. */
if( uxQueueMessagesWaiting( xQueue ) != 0 )
{
xErrorDetected = pdTRUE;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
/* Our queue is empty once more, add 10, 11 to the back. */
ulData = 10;
if( xQueueSend( xQueue, &ulData, intsemNO_BLOCK ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
ulData = 11;
if( xQueueSend( xQueue, &ulData, intsemNO_BLOCK ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
if( uxQueueMessagesWaiting( xQueue ) != 2 )
{
xErrorDetected = pdTRUE;
}
/* Now we should have 10, 11 in the queue. Add 7, 8, 9 to the
* front. */
for( ulData = 9; ulData >= 7; ulData-- )
{
if( xQueueSendToFront( xQueue, ( void * ) &ulData, intsemNO_BLOCK ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
}
/* Now check that the queue is full, and that receiving data provides
* the expected sequence of 7, 8, 9, 10, 11. */
if( uxQueueMessagesWaiting( xQueue ) != 5 )
{
xErrorDetected = pdTRUE;
}
if( xQueueSendToFront( xQueue, ( void * ) &ulData, intsemNO_BLOCK ) != errQUEUE_FULL )
{
xErrorDetected = pdTRUE;
}
if( xQueueSendToBack( xQueue, ( void * ) &ulData, intsemNO_BLOCK ) != errQUEUE_FULL )
{
xErrorDetected = pdTRUE;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
/* Check the data we read out is in the expected order. */
for( ulData = 7; ulData < ( 7 + genqQUEUE_LENGTH ); ulData++ )
{
if( xQueueReceive( xQueue, &ulData2, intsemNO_BLOCK ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
if( ulData != ulData2 )
{
xErrorDetected = pdTRUE;
}
}
if( uxQueueMessagesWaiting( xQueue ) != 0 )
{
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. Check before and after
* taking that the holder is reported correctly. */
if( xSemaphoreGetMutexHolder( xMutex ) != NULL )
{
xErrorDetected = pdTRUE;
}
if( xSemaphoreTake( xMutex, intsemNO_BLOCK ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
if( xSemaphoreGetMutexHolder( xMutex ) != xTaskGetCurrentTaskHandle() )
{
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. Check the mutex
* holder before and after using the "FromISR" version for code coverage. */
if( xSemaphoreGetMutexHolderFromISR( xMutex ) != xTaskGetCurrentTaskHandle() )
{
xErrorDetected = pdTRUE;
}
xSemaphoreGive( xMutex );
if( xSemaphoreGetMutexHolderFromISR( xMutex ) != NULL )
{
xErrorDetected = pdTRUE;
}
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. */
if( xSemaphoreTake( xMutex, intsemNO_BLOCK ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
/* Set the guarded variable to a known start value. */
ulGuardedVariable = 0;
/* 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 );
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
/* Ensure the task is reporting its priority as blocked and not
* suspended (as it would have done in versions up to V7.5.3). */
#if ( INCLUDE_eTaskGetState == 1 )
{
configASSERT( eTaskGetState( xHighPriorityMutexTask ) == eBlocked );
}
#endif /* INCLUDE_eTaskGetState */
/* 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;
}
/* Attempt to set the priority of this task to the test priority -
* between the idle priority and the medium/high test priorities, but the
* actual priority should remain at the high priority. */
vTaskPrioritySet( NULL, genqMUTEX_TEST_PRIORITY );
if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )
{
xErrorDetected = pdTRUE;
}
/* Now unsuspend the medium priority task. This should not run as the
* inherited priority of this task is above that of the medium priority
* task. */
vTaskResume( xMediumPriorityMutexTask );
/* If the medium priority task did run then it will have incremented the
* guarded variable. */
if( ulGuardedVariable != 0 )
{
xErrorDetected = pdTRUE;
}
/* Take the local mutex too, so two mutexes are now held. */
if( xSemaphoreTake( xLocalMutex, intsemNO_BLOCK ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
/* When the semaphore is given back the priority of this task should not
* yet be disinherited because the local mutex is still held. This is a
* simplification to allow FreeRTOS to be integrated with middleware that
* attempts to hold multiple mutexes without bloating the code with complex
* algorithms. It is possible that the high priority mutex task will
* execute as it shares a priority with this task. */
if( xSemaphoreGive( xMutex ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
/* The guarded variable is only incremented by the medium priority task,
* which still should not have executed as this task should remain at the
* higher priority, ensure this is the case. */
if( ulGuardedVariable != 0 )
{
xErrorDetected = pdTRUE;
}
if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )
{
xErrorDetected = pdTRUE;
}
/* Now also give back the local mutex, taking the held count back to 0.
* This time the priority of this task should be disinherited back to the
* priority to which it was set while the mutex was held. This means
* the medium priority task should execute and increment the guarded
* variable. When this task next runs both the high and medium priority
* tasks will have been suspended again. */
if( xSemaphoreGive( xLocalMutex ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
/* Check the guarded variable did indeed increment... */
if( ulGuardedVariable != 1 )
{
xErrorDetected = pdTRUE;
}
/* ... and that the priority of this task has been disinherited to
* genqMUTEX_TEST_PRIORITY. */
if( uxTaskPriorityGet( NULL ) != genqMUTEX_TEST_PRIORITY )
{
xErrorDetected = pdTRUE;
}
/* Set the priority of this task back to its original value, ready for
* the next loop around this test. */
vTaskPrioritySet( NULL, genqMUTEX_LOW_PRIORITY );
}
/*-----------------------------------------------------------*/
static void prvTakeTwoMutexesReturnInSameOrder( SemaphoreHandle_t xMutex,
SemaphoreHandle_t xLocalMutex )
{
/* Take the mutex. It should be available now. */
if( xSemaphoreTake( xMutex, intsemNO_BLOCK ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
/* Set the guarded variable to a known start value. */
ulGuardedVariable = 0;
/* 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 );
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
/* Ensure the task is reporting its priority as blocked and not
* suspended (as it would have done in versions up to V7.5.3). */
#if ( INCLUDE_eTaskGetState == 1 )
{
configASSERT( eTaskGetState( xHighPriorityMutexTask ) == eBlocked );
}
#endif /* INCLUDE_eTaskGetState */
/* 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;
}
/* Now unsuspend the medium priority task. This should not run as the
* inherited priority of this task is above that of the medium priority
* task. */
vTaskResume( xMediumPriorityMutexTask );
/* If the medium priority task did run then it will have incremented the
* guarded variable. */
if( ulGuardedVariable != 0 )
{
xErrorDetected = pdTRUE;
}
/* Take the local mutex too, so two mutexes are now held. */
if( xSemaphoreTake( xLocalMutex, intsemNO_BLOCK ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
/* When the local semaphore is given back the priority of this task should
* not yet be disinherited because the shared mutex is still held. This is a
* simplification to allow FreeRTOS to be integrated with middleware that
* attempts to hold multiple mutexes without bloating the code with complex
* algorithms. It is possible that the high priority mutex task will
* execute as it shares a priority with this task. */
if( xSemaphoreGive( xLocalMutex ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
/* The guarded variable is only incremented by the medium priority task,
* which still should not have executed as this task should remain at the
* higher priority, ensure this is the case. */
if( ulGuardedVariable != 0 )
{
xErrorDetected = pdTRUE;
}
if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )
{
xErrorDetected = pdTRUE;
}
/* Now also give back the shared mutex, taking the held count back to 0.
* This time the priority of this task should be disinherited back to the
* priority at which it was created. This means the medium priority task
* should execute and increment the guarded variable. When this task next runs
* both the high and medium priority tasks will have been suspended again. */
if( xSemaphoreGive( xMutex ) != pdPASS )
{
xErrorDetected = pdTRUE;
}
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
/* Check the guarded variable did indeed increment... */
if( ulGuardedVariable != 1 )
{
xErrorDetected = pdTRUE;
}
/* ... and that the priority of this task has been disinherited to
* genqMUTEX_LOW_PRIORITY. */
if( uxTaskPriorityGet( NULL ) != genqMUTEX_LOW_PRIORITY )
{
xErrorDetected = pdTRUE;
}
}
/*-----------------------------------------------------------*/
static void prvLowPriorityMutexTask( void * pvParameters )
{
SemaphoreHandle_t xMutex = ( SemaphoreHandle_t ) pvParameters, xLocalMutex;
#ifdef USE_STDIO
void vPrintDisplayMessage( const char * const * ppcMessageToSend );
const char * const pcTaskStartMsg = "Mutex with priority inheritance test started.\r\n";
/* Queue a message for printing to say the task has started. */
vPrintDisplayMessage( &pcTaskStartMsg );
#endif
/* The local mutex is used to check the 'mutex held' count. */
xLocalMutex = xSemaphoreCreateMutex();
configASSERT( xLocalMutex );
for( ; ; )
{
/* The first tests exercise the priority inheritance when two mutexes
* are taken then returned in a different order to which they were
* taken. */
prvTakeTwoMutexesReturnInDifferentOrder( xMutex, xLocalMutex );
/* Just to show this task is still running. */
ulLoopCounter2++;
#if configUSE_PREEMPTION == 0
taskYIELD();
#endif
/* The second tests exercise the priority inheritance when two mutexes
* are taken then returned in the same order in which they were taken. */
prvTakeTwoMutexesReturnInSameOrder( xMutex, xLocalMutex );
/* Just to show this task is still running. */
ulLoopCounter2++;
#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
}
}
/*-----------------------------------------------------------*/
static void prvMediumPriorityMutexTask( void * pvParameters )
{
( void ) pvParameters;
for( ; ; )
{
/* The medium priority task starts by suspending itself. The low
* priority task will unsuspend this task when required. */
vTaskSuspend( NULL );
/* When this task unsuspends all it does is increment the guarded
* variable, this is so the low priority task knows that it has
* executed. */
ulGuardedVariable++;
}
}
/*-----------------------------------------------------------*/
static void prvHighPriorityMutexTask( void * pvParameters )
{
SemaphoreHandle_t xMutex = ( SemaphoreHandle_t ) pvParameters;
for( ; ; )
{
/* The high priority task starts by suspending itself. The low
* 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. */
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 )
{
xErrorDetected = pdTRUE;
}
}
}
}
/*-----------------------------------------------------------*/
/* This is called to check that all the created tasks are still running. */
BaseType_t xAreGenericQueueTasksStillRunning( void )
{
static uint32_t ulLastLoopCounter = 0, ulLastLoopCounter2 = 0;
/* If the demo task is still running then we expect the loop counters to
* have incremented since this function was last called. */
if( ulLastLoopCounter == ulLoopCounter )
{
xErrorDetected = pdTRUE;
}
if( ulLastLoopCounter2 == ulLoopCounter2 )
{
xErrorDetected = pdTRUE;
}
ulLastLoopCounter = ulLoopCounter;
ulLastLoopCounter2 = ulLoopCounter2;
/* Errors detected in the task itself will have latched xErrorDetected
* to true. */
return ( BaseType_t ) !xErrorDetected;
}