/* * 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 /* 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; }