From acc41a6a5fef55e647cac7db4ac9d17096fd4d68 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 18 Feb 2024 15:24:22 +0000 Subject: [PATCH] Uncrustify: triggered by comment. --- FreeRTOS/Demo/Common/Minimal/AbortDelay.c | 1598 ++++++------ FreeRTOS/Demo/Common/Minimal/IntSemTest.c | 1074 ++++---- .../Demo/Common/Minimal/StaticAllocation.c | 2226 ++++++++--------- FreeRTOS/Demo/Common/Minimal/blocktim.c | 1194 ++++----- FreeRTOS/Demo/Common/Minimal/recmutex.c | 822 +++--- 5 files changed, 3457 insertions(+), 3457 deletions(-) diff --git a/FreeRTOS/Demo/Common/Minimal/AbortDelay.c b/FreeRTOS/Demo/Common/Minimal/AbortDelay.c index 2c505052837..55f0ba638e8 100644 --- a/FreeRTOS/Demo/Common/Minimal/AbortDelay.c +++ b/FreeRTOS/Demo/Common/Minimal/AbortDelay.c @@ -1,799 +1,799 @@ -/* - * 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 - * - */ - -/* - * This file contains some test scenarios that ensure tasks respond correctly - * to xTaskAbortDelay() calls. It also ensures tasks return the correct state - * of eBlocked when blocked indefinitely in both the case where a task is - * blocked on an object and when a task is blocked on a notification. - */ - -/* Standard includes. */ -#include "limits.h" - -/* Kernel includes. */ -#include "FreeRTOS.h" -#include "task.h" -#include "queue.h" -#include "semphr.h" -#include "event_groups.h" -#include "stream_buffer.h" - -/* Demo includes. */ -#include "AbortDelay.h" - -/* This file can only be used if the functionality it tests is included in the - * build. Remove the whole file if this is not the case. */ -#if ( INCLUDE_xTaskAbortDelay == 1 ) - - #if ( INCLUDE_xTaskGetHandle != 1 ) - #error This test file uses the xTaskGetHandle() API function so INCLUDE_xTaskGetHandle must be set to 1 in FreeRTOSConfig.h. - #endif - -/* Task priorities. Allow these to be overridden. */ - #ifndef abtCONTROLLING_PRIORITY - #define abtCONTROLLING_PRIORITY ( configMAX_PRIORITIES - 3 ) - #endif - - #ifndef abtBLOCKING_PRIORITY - #define abtBLOCKING_PRIORITY ( configMAX_PRIORITIES - 2 ) - #endif - -/* The tests that are performed. */ - #define abtNOTIFY_WAIT_ABORTS 0 - #define abtNOTIFY_TAKE_ABORTS 1 - #define abtDELAY_ABORTS 2 - #define abtDELAY_UNTIL_ABORTS 3 - #define abtSEMAPHORE_TAKE_ABORTS 4 - #define abtEVENT_GROUP_ABORTS 5 - #define abtQUEUE_SEND_ABORTS 6 - #define abtSTREAM_BUFFER_RECEIVE 7 - #define abtMAX_TESTS 8 - -/*-----------------------------------------------------------*/ - -/* - * The two test tasks. The controlling task specifies which test to executed. - * More information is provided in the comments within the tasks. - */ - static void prvControllingTask( void * pvParameters ); - static void prvBlockingTask( void * pvParameters ); - -/* - * Test functions called by the blocking task. Each function follows the same - * pattern, but the way the task blocks is different in each case. - * - * In each function three blocking calls are made. The first and third - * blocking call is expected to time out, while the middle blocking call is - * expected to be aborted by the controlling task half way through the block - * time. - */ - static void prvTestAbortingTaskNotifyWait( void ); - static void prvTestAbortingTaskNotifyTake( void ); - static void prvTestAbortingTaskDelay( void ); - static void prvTestAbortingTaskDelayUntil( void ); - static void prvTestAbortingSemaphoreTake( void ); - static void prvTestAbortingEventGroupWait( void ); - static void prvTestAbortingQueueSend( void ); - static void prvTestAbortingStreamBufferReceive( void ); - -/* - * Performs a few tests to cover code paths not otherwise covered by the continuous - * tests. - */ - static void prvPerformSingleTaskTests( void ); - -/* - * Checks the amount of time a task spent in the Blocked state is within the - * expected bounds. - */ - static void prvCheckExpectedTimeIsWithinAnAcceptableMargin( TickType_t xStartTime, - TickType_t xExpectedBlockTime ); - -/*-----------------------------------------------------------*/ - -/* Used to ensure that tasks are still executing without error. */ - static volatile BaseType_t xControllingCycles = 0, xBlockingCycles = 0; - static volatile BaseType_t xErrorOccurred = pdFALSE; - -/* Each task needs to know the other tasks handle so they can send signals to - * each other. The handle is obtained from the task's name. */ - static const char * pcControllingTaskName = "AbtCtrl", * pcBlockingTaskName = "AbtBlk"; - -/* The maximum amount of time a task will block for. */ - const TickType_t xMaxBlockTime = pdMS_TO_TICKS( 100 ); - const TickType_t xHalfMaxBlockTime = pdMS_TO_TICKS( 50 ); - -/* The actual block time is dependent on the priority of other tasks in the - * system so the actual block time might be greater than that expected, but it - * should be within an acceptable upper bound. */ - const TickType_t xAllowableMargin = pdMS_TO_TICKS( 7 ); - -/*-----------------------------------------------------------*/ - - void vCreateAbortDelayTasks( void ) - { - /* Create the two test tasks described above. */ - xTaskCreate( prvControllingTask, pcControllingTaskName, configMINIMAL_STACK_SIZE, NULL, abtCONTROLLING_PRIORITY, NULL ); - xTaskCreate( prvBlockingTask, pcBlockingTaskName, configMINIMAL_STACK_SIZE, NULL, abtBLOCKING_PRIORITY, NULL ); - } -/*-----------------------------------------------------------*/ - - static void prvControllingTask( void * pvParameters ) - { - TaskHandle_t xBlockingTask; - uint32_t ulTestToPerform = abtNOTIFY_WAIT_ABORTS; - TickType_t xTimeAtStart; - const TickType_t xStartMargin = 2UL; - - /* Just to remove compiler warnings. */ - ( void ) pvParameters; - - xBlockingTask = xTaskGetHandle( pcBlockingTaskName ); - configASSERT( xBlockingTask ); - - for( ; ; ) - { - /* Tell the secondary task to perform the next test. */ - xTimeAtStart = xTaskGetTickCount(); - xTaskNotify( xBlockingTask, ulTestToPerform, eSetValueWithOverwrite ); - - /* The secondary task has a higher priority, so will now be in the - * Blocked state to wait for a maximum of xMaxBlockTime. It expects that - * period to complete with a timeout. It will then block for - * xMaxBlockTimeAgain, but this time it expects to the block time to abort - * half way through. Block until it is time to send the abort to the - * secondary task. xStartMargin is used because this task takes timing - * from the beginning of the test, whereas the blocking task takes timing - * from the entry into the Blocked state - and as the tasks run at - * different priorities, there may be some discrepancy. Also, temporarily - * raise the priority of the controlling task to that of the blocking - * task to minimise discrepancies. */ - vTaskPrioritySet( NULL, abtBLOCKING_PRIORITY ); - vTaskDelay( xMaxBlockTime + xHalfMaxBlockTime + xStartMargin ); - - if( xTaskAbortDelay( xBlockingTask ) != pdPASS ) - { - xErrorOccurred = __LINE__; - } - - /* Reset the priority to the normal controlling priority. */ - vTaskPrioritySet( NULL, abtCONTROLLING_PRIORITY ); - - /* Now wait to be notified that the secondary task has completed its - * test. */ - ulTaskNotifyTake( pdTRUE, portMAX_DELAY ); - - /* Did the entire test run for the expected time, which is two full - * block times plus the half block time caused by calling - * xTaskAbortDelay()? */ - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, ( xMaxBlockTime + xMaxBlockTime + xHalfMaxBlockTime ) ); - - /* Move onto the next test. */ - ulTestToPerform++; - - if( ulTestToPerform >= abtMAX_TESTS ) - { - ulTestToPerform = 0; - } - - /* To indicate this task is still executing. */ - xControllingCycles++; - } - } -/*-----------------------------------------------------------*/ - - static void prvBlockingTask( void * pvParameters ) - { - TaskHandle_t xControllingTask; - uint32_t ulNotificationValue; - const uint32_t ulMax = 0xffffffffUL; - - /* Just to remove compiler warnings. */ - ( void ) pvParameters; - - /* Start by performing a few tests to cover code not exercised in the loops - * below. */ - prvPerformSingleTaskTests(); - - xControllingTask = xTaskGetHandle( pcControllingTaskName ); - configASSERT( xControllingTask ); - - for( ; ; ) - { - /* Wait to be notified of the test that is to be performed next. */ - xTaskNotifyWait( 0, ulMax, &ulNotificationValue, portMAX_DELAY ); - - switch( ulNotificationValue ) - { - case abtNOTIFY_WAIT_ABORTS: - prvTestAbortingTaskNotifyWait(); - break; - - case abtNOTIFY_TAKE_ABORTS: - prvTestAbortingTaskNotifyTake(); - break; - - case abtDELAY_ABORTS: - prvTestAbortingTaskDelay(); - break; - - case abtDELAY_UNTIL_ABORTS: - prvTestAbortingTaskDelayUntil(); - break; - - case abtSEMAPHORE_TAKE_ABORTS: - prvTestAbortingSemaphoreTake(); - break; - - case abtEVENT_GROUP_ABORTS: - prvTestAbortingEventGroupWait(); - break; - - case abtQUEUE_SEND_ABORTS: - prvTestAbortingQueueSend(); - break; - - case abtSTREAM_BUFFER_RECEIVE: - prvTestAbortingStreamBufferReceive(); - break; - - default: - /* Should not get here. */ - break; - } - - /* Let the primary task know the test is complete. */ - xTaskNotifyGive( xControllingTask ); - - /* To indicate this task is still executing. */ - xBlockingCycles++; - } - } -/*-----------------------------------------------------------*/ - - static void prvPerformSingleTaskTests( void ) - { - TaskHandle_t xThisTask; - BaseType_t xReturned; - - /* Try unblocking this task using both the task and ISR versions of the API - - * both should return false as this task is not blocked. */ - xThisTask = xTaskGetCurrentTaskHandle(); - - xReturned = xTaskAbortDelay( xThisTask ); - - if( xReturned != pdFALSE ) - { - xErrorOccurred = __LINE__; - } - } -/*-----------------------------------------------------------*/ - - static void prvTestAbortingTaskDelayUntil( void ) - { - TickType_t xTimeAtStart, xLastBlockTime; - BaseType_t xReturned; - - /* Note the time before the delay so the length of the delay is known. */ - xTimeAtStart = xTaskGetTickCount(); - - /* Take a copy of the time as it is updated in the call to - * xTaskDelayUntil() but its original value is needed to determine the actual - * time spend in the Blocked state. */ - xLastBlockTime = xTimeAtStart; - - /* This first delay should just time out. */ - xReturned = xTaskDelayUntil( &xLastBlockTime, xMaxBlockTime ); - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); - configASSERT( xReturned == pdTRUE ); - - /* Remove compiler warning about value being set but not used in the case - * configASSERT() is not defined. */ - ( void ) xReturned; - - /* This second delay should be aborted by the primary task half way - * through. Again take a copy of the time as it is updated in the call to - * vTaskDelayUntil() buts its original value is needed to determine the amount - * of time actually spent in the Blocked state. This uses vTaskDelayUntil() - * in place of xTaskDelayUntil() for test coverage. */ - xTimeAtStart = xTaskGetTickCount(); - xLastBlockTime = xTimeAtStart; - vTaskDelayUntil( &xLastBlockTime, xMaxBlockTime ); - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xHalfMaxBlockTime ); - - /* As with the other tests, the third block period should not time out. */ - xTimeAtStart = xTaskGetTickCount(); - xLastBlockTime = xTimeAtStart; - xReturned = xTaskDelayUntil( &xLastBlockTime, xMaxBlockTime ); - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); - configASSERT( xReturned == pdTRUE ); - - /* Remove compiler warning about value being set but not used in the case - * configASSERT() is not defined. */ - ( void ) xReturned; - } -/*-----------------------------------------------------------*/ - - static void prvTestAbortingTaskDelay( void ) - { - TickType_t xTimeAtStart; - - /* Note the time before the delay so the length of the delay is known. */ - xTimeAtStart = xTaskGetTickCount(); - - /* This first delay should just time out. */ - vTaskDelay( xMaxBlockTime ); - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); - - /* Note the time before the delay so the length of the delay is known. */ - xTimeAtStart = xTaskGetTickCount(); - - /* This second delay should be aborted by the primary task half way - * through. */ - vTaskDelay( xMaxBlockTime ); - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xHalfMaxBlockTime ); - - /* Note the time before the delay so the length of the delay is known. */ - xTimeAtStart = xTaskGetTickCount(); - - /* This third delay should just time out again. */ - vTaskDelay( xMaxBlockTime ); - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); - } -/*-----------------------------------------------------------*/ - - static void prvTestAbortingTaskNotifyTake( void ) - { - TickType_t xTimeAtStart; - uint32_t ulReturn; - - /* Note the time before the delay so the length of the delay is known. */ - xTimeAtStart = xTaskGetTickCount(); - - /* This first delay should just time out. */ - ulReturn = ulTaskNotifyTake( pdFALSE, xMaxBlockTime ); - - if( ulReturn != 0 ) - { - xErrorOccurred = __LINE__; - } - - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); - - /* Note the time before the delay so the length of the delay is known. */ - xTimeAtStart = xTaskGetTickCount(); - - /* This second delay should be aborted by the primary task half way - * through. */ - ulReturn = ulTaskNotifyTake( pdFALSE, xMaxBlockTime ); - - if( ulReturn != 0 ) - { - xErrorOccurred = __LINE__; - } - - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xHalfMaxBlockTime ); - - /* Note the time before the delay so the length of the delay is known. */ - xTimeAtStart = xTaskGetTickCount(); - - /* This third delay should just time out again. */ - ulReturn = ulTaskNotifyTake( pdFALSE, xMaxBlockTime ); - - if( ulReturn != 0 ) - { - xErrorOccurred = __LINE__; - } - - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); - } -/*-----------------------------------------------------------*/ - - static void prvTestAbortingEventGroupWait( void ) - { - TickType_t xTimeAtStart; - EventGroupHandle_t xEventGroup; - EventBits_t xBitsToWaitFor = ( EventBits_t ) 0x01, xReturn; - - #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) - { - static StaticEventGroup_t xEventGroupBuffer; - - /* Create the event group. Statically allocated memory is used so the - * creation cannot fail. */ - xEventGroup = xEventGroupCreateStatic( &xEventGroupBuffer ); - } - #else - { - xEventGroup = xEventGroupCreate(); - configASSERT( xEventGroup ); - } - #endif /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ - - /* Note the time before the delay so the length of the delay is known. */ - xTimeAtStart = xTaskGetTickCount(); - - /* This first delay should just time out. */ - xReturn = xEventGroupWaitBits( xEventGroup, xBitsToWaitFor, pdTRUE, pdTRUE, xMaxBlockTime ); - - if( xReturn != 0x00 ) - { - xErrorOccurred = __LINE__; - } - - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); - - /* Note the time before the delay so the length of the delay is known. */ - xTimeAtStart = xTaskGetTickCount(); - - /* This second delay should be aborted by the primary task half way - * through. */ - xReturn = xEventGroupWaitBits( xEventGroup, xBitsToWaitFor, pdTRUE, pdTRUE, xMaxBlockTime ); - - if( xReturn != 0x00 ) - { - xErrorOccurred = __LINE__; - } - - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xHalfMaxBlockTime ); - - /* Note the time before the delay so the length of the delay is known. */ - xTimeAtStart = xTaskGetTickCount(); - - /* This third delay should just time out again. */ - xReturn = xEventGroupWaitBits( xEventGroup, xBitsToWaitFor, pdTRUE, pdTRUE, xMaxBlockTime ); - - if( xReturn != 0x00 ) - { - xErrorOccurred = __LINE__; - } - - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); - - /* Not really necessary in this case, but for completeness. */ - vEventGroupDelete( xEventGroup ); - } -/*-----------------------------------------------------------*/ - - static void prvTestAbortingStreamBufferReceive( void ) - { - TickType_t xTimeAtStart; - StreamBufferHandle_t xStreamBuffer; - size_t xReturn; - const size_t xTriggerLevelBytes = ( size_t ) 1; - uint8_t uxRxData; - - #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) - { - /* Defines the memory that will actually hold the streams within the - * stream buffer. */ - static uint8_t ucStorageBuffer[ sizeof( configMESSAGE_BUFFER_LENGTH_TYPE ) + 1 ]; - - /* The variable used to hold the stream buffer structure. */ - StaticStreamBuffer_t xStreamBufferStruct; - - - xStreamBuffer = xStreamBufferCreateStatic( sizeof( ucStorageBuffer ), - xTriggerLevelBytes, - ucStorageBuffer, - &xStreamBufferStruct ); - } - #else /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ - { - xStreamBuffer = xStreamBufferCreate( sizeof( uint8_t ), xTriggerLevelBytes ); - configASSERT( xStreamBuffer ); - } - #endif /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ - - /* Note the time before the delay so the length of the delay is known. */ - xTimeAtStart = xTaskGetTickCount(); - - /* This first delay should just time out. */ - xReturn = xStreamBufferReceive( xStreamBuffer, &uxRxData, sizeof( uxRxData ), xMaxBlockTime ); - - if( xReturn != 0x00 ) - { - xErrorOccurred = __LINE__; - } - - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); - - /* Note the time before the delay so the length of the delay is known. */ - xTimeAtStart = xTaskGetTickCount(); - - /* This second delay should be aborted by the primary task half way - * through xMaxBlockTime. */ - xReturn = xStreamBufferReceive( xStreamBuffer, &uxRxData, sizeof( uxRxData ), xMaxBlockTime ); - - if( xReturn != 0x00 ) - { - xErrorOccurred = __LINE__; - } - - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xHalfMaxBlockTime ); - - /* Note the time before the delay so the length of the delay is known. */ - xTimeAtStart = xTaskGetTickCount(); - - /* This third delay should just time out again. */ - xReturn = xStreamBufferReceive( xStreamBuffer, &uxRxData, sizeof( uxRxData ), xMaxBlockTime ); - - if( xReturn != 0x00 ) - { - xErrorOccurred = __LINE__; - } - - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); - - /* Not really necessary in this case, but for completeness. */ - vStreamBufferDelete( xStreamBuffer ); - } -/*-----------------------------------------------------------*/ - - static void prvTestAbortingQueueSend( void ) - { - TickType_t xTimeAtStart; - BaseType_t xReturn; - const UBaseType_t xQueueLength = ( UBaseType_t ) 1; - QueueHandle_t xQueue; - uint8_t ucItemToQueue; - - #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) - { - static StaticQueue_t xQueueBuffer; - static uint8_t ucQueueStorage[ sizeof( uint8_t ) ]; - - /* Create the queue. Statically allocated memory is used so the - * creation cannot fail. */ - xQueue = xQueueCreateStatic( xQueueLength, sizeof( uint8_t ), ucQueueStorage, &xQueueBuffer ); - } - #else - { - xQueue = xQueueCreate( xQueueLength, sizeof( uint8_t ) ); - configASSERT( xQueue ); - } - #endif /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ - - /* This function tests aborting when in the blocked state waiting to send, - * so the queue must be full. There is only one space in the queue. */ - xReturn = xQueueSend( xQueue, &ucItemToQueue, xMaxBlockTime ); - - if( xReturn != pdPASS ) - { - xErrorOccurred = __LINE__; - } - - /* Note the time before the delay so the length of the delay is known. */ - xTimeAtStart = xTaskGetTickCount(); - - /* This first delay should just time out. */ - xReturn = xQueueSend( xQueue, &ucItemToQueue, xMaxBlockTime ); - - if( xReturn != pdFALSE ) - { - xErrorOccurred = __LINE__; - } - - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); - - /* Note the time before the delay so the length of the delay is known. */ - xTimeAtStart = xTaskGetTickCount(); - - /* This second delay should be aborted by the primary task half way - * through. */ - xReturn = xQueueSend( xQueue, &ucItemToQueue, xMaxBlockTime ); - - if( xReturn != pdFALSE ) - { - xErrorOccurred = __LINE__; - } - - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xHalfMaxBlockTime ); - - /* Note the time before the delay so the length of the delay is known. */ - xTimeAtStart = xTaskGetTickCount(); - - /* This third delay should just time out again. */ - xReturn = xQueueSend( xQueue, &ucItemToQueue, xMaxBlockTime ); - - if( xReturn != pdFALSE ) - { - xErrorOccurred = __LINE__; - } - - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); - - /* Not really necessary in this case, but for completeness. */ - vQueueDelete( xQueue ); - } -/*-----------------------------------------------------------*/ - - static void prvTestAbortingSemaphoreTake( void ) - { - TickType_t xTimeAtStart; - BaseType_t xReturn; - SemaphoreHandle_t xSemaphore; - - #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) - { - static StaticSemaphore_t xSemaphoreBuffer; - - /* Create the semaphore. Statically allocated memory is used so the - * creation cannot fail. */ - xSemaphore = xSemaphoreCreateBinaryStatic( &xSemaphoreBuffer ); - } - #else - { - xSemaphore = xSemaphoreCreateBinary(); - } - #endif - - /* Note the time before the delay so the length of the delay is known. */ - xTimeAtStart = xTaskGetTickCount(); - - /* This first delay should just time out. */ - xReturn = xSemaphoreTake( xSemaphore, xMaxBlockTime ); - - if( xReturn != pdFALSE ) - { - xErrorOccurred = __LINE__; - } - - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); - - /* Note the time before the delay so the length of the delay is known. */ - xTimeAtStart = xTaskGetTickCount(); - - /* This second delay should be aborted by the primary task half way - * through xMaxBlockTime. */ - xReturn = xSemaphoreTake( xSemaphore, portMAX_DELAY ); - - if( xReturn != pdFALSE ) - { - xErrorOccurred = __LINE__; - } - - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xHalfMaxBlockTime ); - - /* Note the time before the delay so the length of the delay is known. */ - xTimeAtStart = xTaskGetTickCount(); - - /* This third delay should just time out again. */ - xReturn = xSemaphoreTake( xSemaphore, xMaxBlockTime ); - - if( xReturn != pdFALSE ) - { - xErrorOccurred = __LINE__; - } - - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); - - /* Not really necessary in this case, but for completeness. */ - vSemaphoreDelete( xSemaphore ); - } -/*-----------------------------------------------------------*/ - - static void prvTestAbortingTaskNotifyWait( void ) - { - TickType_t xTimeAtStart; - BaseType_t xReturn; - - /* Note the time before the delay so the length of the delay is known. */ - xTimeAtStart = xTaskGetTickCount(); - - /* This first delay should just time out. */ - xReturn = xTaskNotifyWait( 0, 0, NULL, xMaxBlockTime ); - - if( xReturn != pdFALSE ) - { - xErrorOccurred = __LINE__; - } - - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); - - /* Note the time before the delay so the length of the delay is known. */ - xTimeAtStart = xTaskGetTickCount(); - - /* This second delay should be aborted by the primary task half way - * through xMaxBlockTime. */ - xReturn = xTaskNotifyWait( 0, 0, NULL, portMAX_DELAY ); - - if( xReturn != pdFALSE ) - { - xErrorOccurred = __LINE__; - } - - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xHalfMaxBlockTime ); - - /* Note the time before the delay so the length of the delay is known. */ - xTimeAtStart = xTaskGetTickCount(); - - /* This third delay should just time out again. */ - xReturn = xTaskNotifyWait( 0, 0, NULL, xMaxBlockTime ); - - if( xReturn != pdFALSE ) - { - xErrorOccurred = __LINE__; - } - - prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); - } -/*-----------------------------------------------------------*/ - - static void prvCheckExpectedTimeIsWithinAnAcceptableMargin( TickType_t xStartTime, - TickType_t xExpectedBlockTime ) - { - TickType_t xTimeNow, xActualBlockTime; - - xTimeNow = xTaskGetTickCount(); - xActualBlockTime = xTimeNow - xStartTime; - - /* The actual block time should not be less than the expected block time. */ - if( xActualBlockTime < xExpectedBlockTime ) - { - xErrorOccurred = __LINE__; - } - - /* The actual block time can be greater than the expected block time, as it - * depends on the priority of the other tasks, but it should be within an - * acceptable margin. */ - if( xActualBlockTime > ( xExpectedBlockTime + xAllowableMargin ) ) - { - xErrorOccurred = __LINE__; - } - } -/*-----------------------------------------------------------*/ - - BaseType_t xAreAbortDelayTestTasksStillRunning( void ) - { - static BaseType_t xLastControllingCycleCount = 0, xLastBlockingCycleCount = 0; - BaseType_t xReturn = pdPASS; - - /* Have both tasks performed at least one cycle since this function was - * last called? */ - if( xControllingCycles == xLastControllingCycleCount ) - { - xReturn = pdFAIL; - } - - if( xBlockingCycles == xLastBlockingCycleCount ) - { - xReturn = pdFAIL; - } - - if( xErrorOccurred != pdFALSE ) - { - xReturn = pdFAIL; - } - - xLastBlockingCycleCount = xBlockingCycles; - xLastControllingCycleCount = xControllingCycles; - - return xReturn; - } - -#endif /* INCLUDE_xTaskAbortDelay == 1 */ +/* + * 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 + * + */ + +/* + * This file contains some test scenarios that ensure tasks respond correctly + * to xTaskAbortDelay() calls. It also ensures tasks return the correct state + * of eBlocked when blocked indefinitely in both the case where a task is + * blocked on an object and when a task is blocked on a notification. + */ + +/* Standard includes. */ +#include "limits.h" + +/* Kernel includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" +#include "event_groups.h" +#include "stream_buffer.h" + +/* Demo includes. */ +#include "AbortDelay.h" + +/* This file can only be used if the functionality it tests is included in the + * build. Remove the whole file if this is not the case. */ +#if ( INCLUDE_xTaskAbortDelay == 1 ) + + #if ( INCLUDE_xTaskGetHandle != 1 ) + #error This test file uses the xTaskGetHandle() API function so INCLUDE_xTaskGetHandle must be set to 1 in FreeRTOSConfig.h. + #endif + +/* Task priorities. Allow these to be overridden. */ + #ifndef abtCONTROLLING_PRIORITY + #define abtCONTROLLING_PRIORITY ( configMAX_PRIORITIES - 3 ) + #endif + + #ifndef abtBLOCKING_PRIORITY + #define abtBLOCKING_PRIORITY ( configMAX_PRIORITIES - 2 ) + #endif + +/* The tests that are performed. */ + #define abtNOTIFY_WAIT_ABORTS 0 + #define abtNOTIFY_TAKE_ABORTS 1 + #define abtDELAY_ABORTS 2 + #define abtDELAY_UNTIL_ABORTS 3 + #define abtSEMAPHORE_TAKE_ABORTS 4 + #define abtEVENT_GROUP_ABORTS 5 + #define abtQUEUE_SEND_ABORTS 6 + #define abtSTREAM_BUFFER_RECEIVE 7 + #define abtMAX_TESTS 8 + +/*-----------------------------------------------------------*/ + +/* + * The two test tasks. The controlling task specifies which test to executed. + * More information is provided in the comments within the tasks. + */ + static void prvControllingTask( void * pvParameters ); + static void prvBlockingTask( void * pvParameters ); + +/* + * Test functions called by the blocking task. Each function follows the same + * pattern, but the way the task blocks is different in each case. + * + * In each function three blocking calls are made. The first and third + * blocking call is expected to time out, while the middle blocking call is + * expected to be aborted by the controlling task half way through the block + * time. + */ + static void prvTestAbortingTaskNotifyWait( void ); + static void prvTestAbortingTaskNotifyTake( void ); + static void prvTestAbortingTaskDelay( void ); + static void prvTestAbortingTaskDelayUntil( void ); + static void prvTestAbortingSemaphoreTake( void ); + static void prvTestAbortingEventGroupWait( void ); + static void prvTestAbortingQueueSend( void ); + static void prvTestAbortingStreamBufferReceive( void ); + +/* + * Performs a few tests to cover code paths not otherwise covered by the continuous + * tests. + */ + static void prvPerformSingleTaskTests( void ); + +/* + * Checks the amount of time a task spent in the Blocked state is within the + * expected bounds. + */ + static void prvCheckExpectedTimeIsWithinAnAcceptableMargin( TickType_t xStartTime, + TickType_t xExpectedBlockTime ); + +/*-----------------------------------------------------------*/ + +/* Used to ensure that tasks are still executing without error. */ + static volatile BaseType_t xControllingCycles = 0, xBlockingCycles = 0; + static volatile BaseType_t xErrorOccurred = pdFALSE; + +/* Each task needs to know the other tasks handle so they can send signals to + * each other. The handle is obtained from the task's name. */ + static const char * pcControllingTaskName = "AbtCtrl", * pcBlockingTaskName = "AbtBlk"; + +/* The maximum amount of time a task will block for. */ + const TickType_t xMaxBlockTime = pdMS_TO_TICKS( 100 ); + const TickType_t xHalfMaxBlockTime = pdMS_TO_TICKS( 50 ); + +/* The actual block time is dependent on the priority of other tasks in the + * system so the actual block time might be greater than that expected, but it + * should be within an acceptable upper bound. */ + const TickType_t xAllowableMargin = pdMS_TO_TICKS( 7 ); + +/*-----------------------------------------------------------*/ + + void vCreateAbortDelayTasks( void ) + { + /* Create the two test tasks described above. */ + xTaskCreate( prvControllingTask, pcControllingTaskName, configMINIMAL_STACK_SIZE, NULL, abtCONTROLLING_PRIORITY, NULL ); + xTaskCreate( prvBlockingTask, pcBlockingTaskName, configMINIMAL_STACK_SIZE, NULL, abtBLOCKING_PRIORITY, NULL ); + } +/*-----------------------------------------------------------*/ + + static void prvControllingTask( void * pvParameters ) + { + TaskHandle_t xBlockingTask; + uint32_t ulTestToPerform = abtNOTIFY_WAIT_ABORTS; + TickType_t xTimeAtStart; + const TickType_t xStartMargin = 2UL; + + /* Just to remove compiler warnings. */ + ( void ) pvParameters; + + xBlockingTask = xTaskGetHandle( pcBlockingTaskName ); + configASSERT( xBlockingTask ); + + for( ; ; ) + { + /* Tell the secondary task to perform the next test. */ + xTimeAtStart = xTaskGetTickCount(); + xTaskNotify( xBlockingTask, ulTestToPerform, eSetValueWithOverwrite ); + + /* The secondary task has a higher priority, so will now be in the + * Blocked state to wait for a maximum of xMaxBlockTime. It expects that + * period to complete with a timeout. It will then block for + * xMaxBlockTimeAgain, but this time it expects to the block time to abort + * half way through. Block until it is time to send the abort to the + * secondary task. xStartMargin is used because this task takes timing + * from the beginning of the test, whereas the blocking task takes timing + * from the entry into the Blocked state - and as the tasks run at + * different priorities, there may be some discrepancy. Also, temporarily + * raise the priority of the controlling task to that of the blocking + * task to minimise discrepancies. */ + vTaskPrioritySet( NULL, abtBLOCKING_PRIORITY ); + vTaskDelay( xMaxBlockTime + xHalfMaxBlockTime + xStartMargin ); + + if( xTaskAbortDelay( xBlockingTask ) != pdPASS ) + { + xErrorOccurred = __LINE__; + } + + /* Reset the priority to the normal controlling priority. */ + vTaskPrioritySet( NULL, abtCONTROLLING_PRIORITY ); + + /* Now wait to be notified that the secondary task has completed its + * test. */ + ulTaskNotifyTake( pdTRUE, portMAX_DELAY ); + + /* Did the entire test run for the expected time, which is two full + * block times plus the half block time caused by calling + * xTaskAbortDelay()? */ + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, ( xMaxBlockTime + xMaxBlockTime + xHalfMaxBlockTime ) ); + + /* Move onto the next test. */ + ulTestToPerform++; + + if( ulTestToPerform >= abtMAX_TESTS ) + { + ulTestToPerform = 0; + } + + /* To indicate this task is still executing. */ + xControllingCycles++; + } + } +/*-----------------------------------------------------------*/ + + static void prvBlockingTask( void * pvParameters ) + { + TaskHandle_t xControllingTask; + uint32_t ulNotificationValue; + const uint32_t ulMax = 0xffffffffUL; + + /* Just to remove compiler warnings. */ + ( void ) pvParameters; + + /* Start by performing a few tests to cover code not exercised in the loops + * below. */ + prvPerformSingleTaskTests(); + + xControllingTask = xTaskGetHandle( pcControllingTaskName ); + configASSERT( xControllingTask ); + + for( ; ; ) + { + /* Wait to be notified of the test that is to be performed next. */ + xTaskNotifyWait( 0, ulMax, &ulNotificationValue, portMAX_DELAY ); + + switch( ulNotificationValue ) + { + case abtNOTIFY_WAIT_ABORTS: + prvTestAbortingTaskNotifyWait(); + break; + + case abtNOTIFY_TAKE_ABORTS: + prvTestAbortingTaskNotifyTake(); + break; + + case abtDELAY_ABORTS: + prvTestAbortingTaskDelay(); + break; + + case abtDELAY_UNTIL_ABORTS: + prvTestAbortingTaskDelayUntil(); + break; + + case abtSEMAPHORE_TAKE_ABORTS: + prvTestAbortingSemaphoreTake(); + break; + + case abtEVENT_GROUP_ABORTS: + prvTestAbortingEventGroupWait(); + break; + + case abtQUEUE_SEND_ABORTS: + prvTestAbortingQueueSend(); + break; + + case abtSTREAM_BUFFER_RECEIVE: + prvTestAbortingStreamBufferReceive(); + break; + + default: + /* Should not get here. */ + break; + } + + /* Let the primary task know the test is complete. */ + xTaskNotifyGive( xControllingTask ); + + /* To indicate this task is still executing. */ + xBlockingCycles++; + } + } +/*-----------------------------------------------------------*/ + + static void prvPerformSingleTaskTests( void ) + { + TaskHandle_t xThisTask; + BaseType_t xReturned; + + /* Try unblocking this task using both the task and ISR versions of the API - + * both should return false as this task is not blocked. */ + xThisTask = xTaskGetCurrentTaskHandle(); + + xReturned = xTaskAbortDelay( xThisTask ); + + if( xReturned != pdFALSE ) + { + xErrorOccurred = __LINE__; + } + } +/*-----------------------------------------------------------*/ + + static void prvTestAbortingTaskDelayUntil( void ) + { + TickType_t xTimeAtStart, xLastBlockTime; + BaseType_t xReturned; + + /* Note the time before the delay so the length of the delay is known. */ + xTimeAtStart = xTaskGetTickCount(); + + /* Take a copy of the time as it is updated in the call to + * xTaskDelayUntil() but its original value is needed to determine the actual + * time spend in the Blocked state. */ + xLastBlockTime = xTimeAtStart; + + /* This first delay should just time out. */ + xReturned = xTaskDelayUntil( &xLastBlockTime, xMaxBlockTime ); + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); + configASSERT( xReturned == pdTRUE ); + + /* Remove compiler warning about value being set but not used in the case + * configASSERT() is not defined. */ + ( void ) xReturned; + + /* This second delay should be aborted by the primary task half way + * through. Again take a copy of the time as it is updated in the call to + * vTaskDelayUntil() buts its original value is needed to determine the amount + * of time actually spent in the Blocked state. This uses vTaskDelayUntil() + * in place of xTaskDelayUntil() for test coverage. */ + xTimeAtStart = xTaskGetTickCount(); + xLastBlockTime = xTimeAtStart; + vTaskDelayUntil( &xLastBlockTime, xMaxBlockTime ); + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xHalfMaxBlockTime ); + + /* As with the other tests, the third block period should not time out. */ + xTimeAtStart = xTaskGetTickCount(); + xLastBlockTime = xTimeAtStart; + xReturned = xTaskDelayUntil( &xLastBlockTime, xMaxBlockTime ); + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); + configASSERT( xReturned == pdTRUE ); + + /* Remove compiler warning about value being set but not used in the case + * configASSERT() is not defined. */ + ( void ) xReturned; + } +/*-----------------------------------------------------------*/ + + static void prvTestAbortingTaskDelay( void ) + { + TickType_t xTimeAtStart; + + /* Note the time before the delay so the length of the delay is known. */ + xTimeAtStart = xTaskGetTickCount(); + + /* This first delay should just time out. */ + vTaskDelay( xMaxBlockTime ); + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); + + /* Note the time before the delay so the length of the delay is known. */ + xTimeAtStart = xTaskGetTickCount(); + + /* This second delay should be aborted by the primary task half way + * through. */ + vTaskDelay( xMaxBlockTime ); + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xHalfMaxBlockTime ); + + /* Note the time before the delay so the length of the delay is known. */ + xTimeAtStart = xTaskGetTickCount(); + + /* This third delay should just time out again. */ + vTaskDelay( xMaxBlockTime ); + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); + } +/*-----------------------------------------------------------*/ + + static void prvTestAbortingTaskNotifyTake( void ) + { + TickType_t xTimeAtStart; + uint32_t ulReturn; + + /* Note the time before the delay so the length of the delay is known. */ + xTimeAtStart = xTaskGetTickCount(); + + /* This first delay should just time out. */ + ulReturn = ulTaskNotifyTake( pdFALSE, xMaxBlockTime ); + + if( ulReturn != 0 ) + { + xErrorOccurred = __LINE__; + } + + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); + + /* Note the time before the delay so the length of the delay is known. */ + xTimeAtStart = xTaskGetTickCount(); + + /* This second delay should be aborted by the primary task half way + * through. */ + ulReturn = ulTaskNotifyTake( pdFALSE, xMaxBlockTime ); + + if( ulReturn != 0 ) + { + xErrorOccurred = __LINE__; + } + + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xHalfMaxBlockTime ); + + /* Note the time before the delay so the length of the delay is known. */ + xTimeAtStart = xTaskGetTickCount(); + + /* This third delay should just time out again. */ + ulReturn = ulTaskNotifyTake( pdFALSE, xMaxBlockTime ); + + if( ulReturn != 0 ) + { + xErrorOccurred = __LINE__; + } + + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); + } +/*-----------------------------------------------------------*/ + + static void prvTestAbortingEventGroupWait( void ) + { + TickType_t xTimeAtStart; + EventGroupHandle_t xEventGroup; + EventBits_t xBitsToWaitFor = ( EventBits_t ) 0x01, xReturn; + + #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + static StaticEventGroup_t xEventGroupBuffer; + + /* Create the event group. Statically allocated memory is used so the + * creation cannot fail. */ + xEventGroup = xEventGroupCreateStatic( &xEventGroupBuffer ); + } + #else + { + xEventGroup = xEventGroupCreate(); + configASSERT( xEventGroup ); + } + #endif /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ + + /* Note the time before the delay so the length of the delay is known. */ + xTimeAtStart = xTaskGetTickCount(); + + /* This first delay should just time out. */ + xReturn = xEventGroupWaitBits( xEventGroup, xBitsToWaitFor, pdTRUE, pdTRUE, xMaxBlockTime ); + + if( xReturn != 0x00 ) + { + xErrorOccurred = __LINE__; + } + + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); + + /* Note the time before the delay so the length of the delay is known. */ + xTimeAtStart = xTaskGetTickCount(); + + /* This second delay should be aborted by the primary task half way + * through. */ + xReturn = xEventGroupWaitBits( xEventGroup, xBitsToWaitFor, pdTRUE, pdTRUE, xMaxBlockTime ); + + if( xReturn != 0x00 ) + { + xErrorOccurred = __LINE__; + } + + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xHalfMaxBlockTime ); + + /* Note the time before the delay so the length of the delay is known. */ + xTimeAtStart = xTaskGetTickCount(); + + /* This third delay should just time out again. */ + xReturn = xEventGroupWaitBits( xEventGroup, xBitsToWaitFor, pdTRUE, pdTRUE, xMaxBlockTime ); + + if( xReturn != 0x00 ) + { + xErrorOccurred = __LINE__; + } + + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); + + /* Not really necessary in this case, but for completeness. */ + vEventGroupDelete( xEventGroup ); + } +/*-----------------------------------------------------------*/ + + static void prvTestAbortingStreamBufferReceive( void ) + { + TickType_t xTimeAtStart; + StreamBufferHandle_t xStreamBuffer; + size_t xReturn; + const size_t xTriggerLevelBytes = ( size_t ) 1; + uint8_t uxRxData; + + #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + /* Defines the memory that will actually hold the streams within the + * stream buffer. */ + static uint8_t ucStorageBuffer[ sizeof( configMESSAGE_BUFFER_LENGTH_TYPE ) + 1 ]; + + /* The variable used to hold the stream buffer structure. */ + StaticStreamBuffer_t xStreamBufferStruct; + + + xStreamBuffer = xStreamBufferCreateStatic( sizeof( ucStorageBuffer ), + xTriggerLevelBytes, + ucStorageBuffer, + &xStreamBufferStruct ); + } + #else /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ + { + xStreamBuffer = xStreamBufferCreate( sizeof( uint8_t ), xTriggerLevelBytes ); + configASSERT( xStreamBuffer ); + } + #endif /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ + + /* Note the time before the delay so the length of the delay is known. */ + xTimeAtStart = xTaskGetTickCount(); + + /* This first delay should just time out. */ + xReturn = xStreamBufferReceive( xStreamBuffer, &uxRxData, sizeof( uxRxData ), xMaxBlockTime ); + + if( xReturn != 0x00 ) + { + xErrorOccurred = __LINE__; + } + + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); + + /* Note the time before the delay so the length of the delay is known. */ + xTimeAtStart = xTaskGetTickCount(); + + /* This second delay should be aborted by the primary task half way + * through xMaxBlockTime. */ + xReturn = xStreamBufferReceive( xStreamBuffer, &uxRxData, sizeof( uxRxData ), xMaxBlockTime ); + + if( xReturn != 0x00 ) + { + xErrorOccurred = __LINE__; + } + + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xHalfMaxBlockTime ); + + /* Note the time before the delay so the length of the delay is known. */ + xTimeAtStart = xTaskGetTickCount(); + + /* This third delay should just time out again. */ + xReturn = xStreamBufferReceive( xStreamBuffer, &uxRxData, sizeof( uxRxData ), xMaxBlockTime ); + + if( xReturn != 0x00 ) + { + xErrorOccurred = __LINE__; + } + + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); + + /* Not really necessary in this case, but for completeness. */ + vStreamBufferDelete( xStreamBuffer ); + } +/*-----------------------------------------------------------*/ + + static void prvTestAbortingQueueSend( void ) + { + TickType_t xTimeAtStart; + BaseType_t xReturn; + const UBaseType_t xQueueLength = ( UBaseType_t ) 1; + QueueHandle_t xQueue; + uint8_t ucItemToQueue; + + #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + static StaticQueue_t xQueueBuffer; + static uint8_t ucQueueStorage[ sizeof( uint8_t ) ]; + + /* Create the queue. Statically allocated memory is used so the + * creation cannot fail. */ + xQueue = xQueueCreateStatic( xQueueLength, sizeof( uint8_t ), ucQueueStorage, &xQueueBuffer ); + } + #else + { + xQueue = xQueueCreate( xQueueLength, sizeof( uint8_t ) ); + configASSERT( xQueue ); + } + #endif /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ + + /* This function tests aborting when in the blocked state waiting to send, + * so the queue must be full. There is only one space in the queue. */ + xReturn = xQueueSend( xQueue, &ucItemToQueue, xMaxBlockTime ); + + if( xReturn != pdPASS ) + { + xErrorOccurred = __LINE__; + } + + /* Note the time before the delay so the length of the delay is known. */ + xTimeAtStart = xTaskGetTickCount(); + + /* This first delay should just time out. */ + xReturn = xQueueSend( xQueue, &ucItemToQueue, xMaxBlockTime ); + + if( xReturn != pdFALSE ) + { + xErrorOccurred = __LINE__; + } + + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); + + /* Note the time before the delay so the length of the delay is known. */ + xTimeAtStart = xTaskGetTickCount(); + + /* This second delay should be aborted by the primary task half way + * through. */ + xReturn = xQueueSend( xQueue, &ucItemToQueue, xMaxBlockTime ); + + if( xReturn != pdFALSE ) + { + xErrorOccurred = __LINE__; + } + + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xHalfMaxBlockTime ); + + /* Note the time before the delay so the length of the delay is known. */ + xTimeAtStart = xTaskGetTickCount(); + + /* This third delay should just time out again. */ + xReturn = xQueueSend( xQueue, &ucItemToQueue, xMaxBlockTime ); + + if( xReturn != pdFALSE ) + { + xErrorOccurred = __LINE__; + } + + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); + + /* Not really necessary in this case, but for completeness. */ + vQueueDelete( xQueue ); + } +/*-----------------------------------------------------------*/ + + static void prvTestAbortingSemaphoreTake( void ) + { + TickType_t xTimeAtStart; + BaseType_t xReturn; + SemaphoreHandle_t xSemaphore; + + #if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + static StaticSemaphore_t xSemaphoreBuffer; + + /* Create the semaphore. Statically allocated memory is used so the + * creation cannot fail. */ + xSemaphore = xSemaphoreCreateBinaryStatic( &xSemaphoreBuffer ); + } + #else + { + xSemaphore = xSemaphoreCreateBinary(); + } + #endif + + /* Note the time before the delay so the length of the delay is known. */ + xTimeAtStart = xTaskGetTickCount(); + + /* This first delay should just time out. */ + xReturn = xSemaphoreTake( xSemaphore, xMaxBlockTime ); + + if( xReturn != pdFALSE ) + { + xErrorOccurred = __LINE__; + } + + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); + + /* Note the time before the delay so the length of the delay is known. */ + xTimeAtStart = xTaskGetTickCount(); + + /* This second delay should be aborted by the primary task half way + * through xMaxBlockTime. */ + xReturn = xSemaphoreTake( xSemaphore, portMAX_DELAY ); + + if( xReturn != pdFALSE ) + { + xErrorOccurred = __LINE__; + } + + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xHalfMaxBlockTime ); + + /* Note the time before the delay so the length of the delay is known. */ + xTimeAtStart = xTaskGetTickCount(); + + /* This third delay should just time out again. */ + xReturn = xSemaphoreTake( xSemaphore, xMaxBlockTime ); + + if( xReturn != pdFALSE ) + { + xErrorOccurred = __LINE__; + } + + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); + + /* Not really necessary in this case, but for completeness. */ + vSemaphoreDelete( xSemaphore ); + } +/*-----------------------------------------------------------*/ + + static void prvTestAbortingTaskNotifyWait( void ) + { + TickType_t xTimeAtStart; + BaseType_t xReturn; + + /* Note the time before the delay so the length of the delay is known. */ + xTimeAtStart = xTaskGetTickCount(); + + /* This first delay should just time out. */ + xReturn = xTaskNotifyWait( 0, 0, NULL, xMaxBlockTime ); + + if( xReturn != pdFALSE ) + { + xErrorOccurred = __LINE__; + } + + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); + + /* Note the time before the delay so the length of the delay is known. */ + xTimeAtStart = xTaskGetTickCount(); + + /* This second delay should be aborted by the primary task half way + * through xMaxBlockTime. */ + xReturn = xTaskNotifyWait( 0, 0, NULL, portMAX_DELAY ); + + if( xReturn != pdFALSE ) + { + xErrorOccurred = __LINE__; + } + + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xHalfMaxBlockTime ); + + /* Note the time before the delay so the length of the delay is known. */ + xTimeAtStart = xTaskGetTickCount(); + + /* This third delay should just time out again. */ + xReturn = xTaskNotifyWait( 0, 0, NULL, xMaxBlockTime ); + + if( xReturn != pdFALSE ) + { + xErrorOccurred = __LINE__; + } + + prvCheckExpectedTimeIsWithinAnAcceptableMargin( xTimeAtStart, xMaxBlockTime ); + } +/*-----------------------------------------------------------*/ + + static void prvCheckExpectedTimeIsWithinAnAcceptableMargin( TickType_t xStartTime, + TickType_t xExpectedBlockTime ) + { + TickType_t xTimeNow, xActualBlockTime; + + xTimeNow = xTaskGetTickCount(); + xActualBlockTime = xTimeNow - xStartTime; + + /* The actual block time should not be less than the expected block time. */ + if( xActualBlockTime < xExpectedBlockTime ) + { + xErrorOccurred = __LINE__; + } + + /* The actual block time can be greater than the expected block time, as it + * depends on the priority of the other tasks, but it should be within an + * acceptable margin. */ + if( xActualBlockTime > ( xExpectedBlockTime + xAllowableMargin ) ) + { + xErrorOccurred = __LINE__; + } + } +/*-----------------------------------------------------------*/ + + BaseType_t xAreAbortDelayTestTasksStillRunning( void ) + { + static BaseType_t xLastControllingCycleCount = 0, xLastBlockingCycleCount = 0; + BaseType_t xReturn = pdPASS; + + /* Have both tasks performed at least one cycle since this function was + * last called? */ + if( xControllingCycles == xLastControllingCycleCount ) + { + xReturn = pdFAIL; + } + + if( xBlockingCycles == xLastBlockingCycleCount ) + { + xReturn = pdFAIL; + } + + if( xErrorOccurred != pdFALSE ) + { + xReturn = pdFAIL; + } + + xLastBlockingCycleCount = xBlockingCycles; + xLastControllingCycleCount = xControllingCycles; + + return xReturn; + } + +#endif /* INCLUDE_xTaskAbortDelay == 1 */ diff --git a/FreeRTOS/Demo/Common/Minimal/IntSemTest.c b/FreeRTOS/Demo/Common/Minimal/IntSemTest.c index 229a27ff69f..e4be9b83c3f 100644 --- a/FreeRTOS/Demo/Common/Minimal/IntSemTest.c +++ b/FreeRTOS/Demo/Common/Minimal/IntSemTest.c @@ -1,537 +1,537 @@ -/* - * 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 - * - */ - - -/* - * Demonstrates and tests mutexes being used from an interrupt. - */ - - -#include - -/* Scheduler include files. */ -#include "FreeRTOS.h" -#include "task.h" -#include "semphr.h" - -/* Demo program include files. */ -#include "IntSemTest.h" - -/*-----------------------------------------------------------*/ - -/* The priorities of the test tasks. */ -#define intsemMASTER_PRIORITY ( tskIDLE_PRIORITY ) -#define intsemSLAVE_PRIORITY ( tskIDLE_PRIORITY + 1 ) - -/* The rate at which the tick hook will give the mutex. */ -#define intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS ( 100 ) - -/* A block time of 0 means 'don't block'. */ -#define intsemNO_BLOCK 0 - -/* The maximum count value for the counting semaphore given from an - * interrupt. */ -#define intsemMAX_COUNT 3 - -/*-----------------------------------------------------------*/ - -/* - * The master is a task that receives a mutex that is given from an interrupt - - * although generally mutexes should not be used given in interrupts (and - * definitely never taken in an interrupt) there are some circumstances when it - * may be desirable. - * - * The slave task is just used by the master task to force priority inheritance - * on a mutex that is shared between the master and the slave - which is a - * separate mutex to that given by the interrupt. - */ -static void vInterruptMutexSlaveTask( void * pvParameters ); -static void vInterruptMutexMasterTask( void * pvParameters ); - -/* - * A test whereby the master takes the shared and interrupt mutexes in that - * order, then gives them back in the same order, ensuring the priority - * inheritance is behaving as expected at each step. - */ -static void prvTakeAndGiveInTheSameOrder( void ); - -/* - * A test whereby the master takes the shared and interrupt mutexes in that - * order, then gives them back in the opposite order to which they were taken, - * ensuring the priority inheritance is behaving as expected at each step. - */ -static void prvTakeAndGiveInTheOppositeOrder( void ); - -/* - * A simple task that interacts with an interrupt using a counting semaphore, - * primarily for code coverage purposes. - */ -static void vInterruptCountingSemaphoreTask( void * pvParameters ); - -/*-----------------------------------------------------------*/ - -/* 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 ulMasterLoops = 0, ulCountingSemaphoreLoops = 0; - -/* Handles of the test tasks that must be accessed from other test tasks. */ -static TaskHandle_t xSlaveHandle; - -/* A mutex which is given from an interrupt - although generally mutexes should - * not be used given in interrupts (and definitely never taken in an interrupt) - * there are some circumstances when it may be desirable. */ -static SemaphoreHandle_t xISRMutex = NULL; - -/* A counting semaphore which is given from an interrupt. */ -static SemaphoreHandle_t xISRCountingSemaphore = NULL; - -/* A mutex which is shared between the master and slave tasks - the master - * does both sharing of this mutex with the slave and receiving a mutex from the - * interrupt. */ -static SemaphoreHandle_t xMasterSlaveMutex = NULL; - -/* Flag that allows the master task to control when the interrupt gives or does - * not give the mutex. There is no mutual exclusion on this variable, but this is - * only test code and it should be fine in the 32=bit test environment. */ -static BaseType_t xOkToGiveMutex = pdFALSE, xOkToGiveCountingSemaphore = pdFALSE; - -/* Used to coordinate timing between tasks and the interrupt. */ -const TickType_t xInterruptGivePeriod = pdMS_TO_TICKS( intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS ); - -/*-----------------------------------------------------------*/ - -void vStartInterruptSemaphoreTasks( void ) -{ - /* Create the semaphores that are given from an interrupt. */ - xISRMutex = xSemaphoreCreateMutex(); - configASSERT( xISRMutex ); - xISRCountingSemaphore = xSemaphoreCreateCounting( intsemMAX_COUNT, 0 ); - configASSERT( xISRCountingSemaphore ); - - /* Create the mutex that is shared between the master and slave tasks (the - * master receives a mutex from an interrupt and shares a mutex with the - * slave. */ - xMasterSlaveMutex = xSemaphoreCreateMutex(); - configASSERT( xMasterSlaveMutex ); - - /* Create the tasks that share mutexes between then and with interrupts. */ - xTaskCreate( vInterruptMutexSlaveTask, "IntMuS", configMINIMAL_STACK_SIZE, NULL, intsemSLAVE_PRIORITY, &xSlaveHandle ); - xTaskCreate( vInterruptMutexMasterTask, "IntMuM", configMINIMAL_STACK_SIZE, NULL, intsemMASTER_PRIORITY, NULL ); - - /* Create the task that blocks on the counting semaphore. */ - xTaskCreate( vInterruptCountingSemaphoreTask, "IntCnt", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL ); -} -/*-----------------------------------------------------------*/ - -static void vInterruptMutexMasterTask( void * pvParameters ) -{ - /* Just to avoid compiler warnings. */ - ( void ) pvParameters; - - for( ; ; ) - { - prvTakeAndGiveInTheSameOrder(); - - /* Ensure not to starve out other tests. */ - ulMasterLoops++; - vTaskDelay( intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS ); - - prvTakeAndGiveInTheOppositeOrder(); - - /* Ensure not to starve out other tests. */ - ulMasterLoops++; - vTaskDelay( intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS ); - } -} -/*-----------------------------------------------------------*/ - -static void prvTakeAndGiveInTheSameOrder( void ) -{ - /* Ensure the slave is suspended, and that this task is running at the - * lower priority as expected as the start conditions. */ - #if ( INCLUDE_eTaskGetState == 1 ) - { - configASSERT( eTaskGetState( xSlaveHandle ) == eSuspended ); - } - #endif /* INCLUDE_eTaskGetState */ - - if( uxTaskPriorityGet( NULL ) != intsemMASTER_PRIORITY ) - { - xErrorDetected = __LINE__; - } - - /* Take the semaphore that is shared with the slave. */ - if( xSemaphoreTake( xMasterSlaveMutex, intsemNO_BLOCK ) != pdPASS ) - { - xErrorDetected = __LINE__; - } - - /* This task now has the mutex. Unsuspend the slave so it too - * attempts to take the mutex. */ - vTaskResume( xSlaveHandle ); - - /* The slave has the higher priority so should now have executed and - * blocked on the semaphore. */ - #if ( INCLUDE_eTaskGetState == 1 ) - { - configASSERT( eTaskGetState( xSlaveHandle ) == eBlocked ); - } - #endif /* INCLUDE_eTaskGetState */ - - /* This task should now have inherited the priority of the slave - * task. */ - if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY ) - { - xErrorDetected = __LINE__; - } - - /* Now wait a little longer than the time between ISR gives to also - * obtain the ISR mutex. */ - xOkToGiveMutex = pdTRUE; - - if( xSemaphoreTake( xISRMutex, ( xInterruptGivePeriod * 2 ) ) != pdPASS ) - { - xErrorDetected = __LINE__; - } - - xOkToGiveMutex = pdFALSE; - - /* Attempting to take again immediately should fail as the mutex is - * already held. */ - if( xSemaphoreTake( xISRMutex, intsemNO_BLOCK ) != pdFAIL ) - { - xErrorDetected = __LINE__; - } - - /* Should still be at the priority of the slave task. */ - if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY ) - { - xErrorDetected = __LINE__; - } - - /* Give back the ISR semaphore to ensure the priority is not - * disinherited as the shared mutex (which the higher priority task is - * attempting to obtain) is still held. */ - if( xSemaphoreGive( xISRMutex ) != pdPASS ) - { - xErrorDetected = __LINE__; - } - - if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY ) - { - xErrorDetected = __LINE__; - } - - /* Finally give back the shared mutex. This time the higher priority - * task should run before this task runs again - so this task should have - * disinherited the priority and the higher priority task should be in the - * suspended state again. */ - if( xSemaphoreGive( xMasterSlaveMutex ) != pdPASS ) - { - xErrorDetected = __LINE__; - } - - if( uxTaskPriorityGet( NULL ) != intsemMASTER_PRIORITY ) - { - xErrorDetected = __LINE__; - } - - #if ( INCLUDE_eTaskGetState == 1 ) - { - configASSERT( eTaskGetState( xSlaveHandle ) == eSuspended ); - } - #endif /* INCLUDE_eTaskGetState */ - - /* Reset the mutex ready for the next round. */ - xQueueReset( xISRMutex ); -} -/*-----------------------------------------------------------*/ - -static void prvTakeAndGiveInTheOppositeOrder( void ) -{ - /* Ensure the slave is suspended, and that this task is running at the - * lower priority as expected as the start conditions. */ - #if ( INCLUDE_eTaskGetState == 1 ) - { - configASSERT( eTaskGetState( xSlaveHandle ) == eSuspended ); - } - #endif /* INCLUDE_eTaskGetState */ - - if( uxTaskPriorityGet( NULL ) != intsemMASTER_PRIORITY ) - { - xErrorDetected = __LINE__; - } - - /* Take the semaphore that is shared with the slave. */ - if( xSemaphoreTake( xMasterSlaveMutex, intsemNO_BLOCK ) != pdPASS ) - { - xErrorDetected = __LINE__; - } - - /* This task now has the mutex. Unsuspend the slave so it too - * attempts to take the mutex. */ - vTaskResume( xSlaveHandle ); - - /* The slave has the higher priority so should now have executed and - * blocked on the semaphore. */ - #if ( INCLUDE_eTaskGetState == 1 ) - { - configASSERT( eTaskGetState( xSlaveHandle ) == eBlocked ); - } - #endif /* INCLUDE_eTaskGetState */ - - /* This task should now have inherited the priority of the slave - * task. */ - if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY ) - { - xErrorDetected = __LINE__; - } - - /* Now wait a little longer than the time between ISR gives to also - * obtain the ISR mutex. */ - xOkToGiveMutex = pdTRUE; - - if( xSemaphoreTake( xISRMutex, ( xInterruptGivePeriod * 2 ) ) != pdPASS ) - { - xErrorDetected = __LINE__; - } - - xOkToGiveMutex = pdFALSE; - - /* Attempting to take again immediately should fail as the mutex is - * already held. */ - if( xSemaphoreTake( xISRMutex, intsemNO_BLOCK ) != pdFAIL ) - { - xErrorDetected = __LINE__; - } - - /* Should still be at the priority of the slave task. */ - if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY ) - { - xErrorDetected = __LINE__; - } - - /* Give back the shared semaphore to ensure the priority is not disinherited - * as the ISR mutex is still held. The higher priority slave task should run - * before this task runs again. */ - if( xSemaphoreGive( xMasterSlaveMutex ) != pdPASS ) - { - xErrorDetected = __LINE__; - } - - /* Should still be at the priority of the slave task as this task still - * holds one semaphore (this is a simplification in the priority inheritance - * mechanism. */ - if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY ) - { - xErrorDetected = __LINE__; - } - - /* Give back the ISR semaphore, which should result in the priority being - * disinherited as it was the last mutex held. */ - if( xSemaphoreGive( xISRMutex ) != pdPASS ) - { - xErrorDetected = __LINE__; - } - - if( uxTaskPriorityGet( NULL ) != intsemMASTER_PRIORITY ) - { - xErrorDetected = __LINE__; - } - - /* Reset the mutex ready for the next round. */ - xQueueReset( xISRMutex ); -} -/*-----------------------------------------------------------*/ - -static void vInterruptMutexSlaveTask( void * pvParameters ) -{ - /* Just to avoid compiler warnings. */ - ( void ) pvParameters; - - for( ; ; ) - { - /* This task starts by suspending itself so when it executes can be - * controlled by the master task. */ - vTaskSuspend( NULL ); - - /* This task will execute when the master task already holds the mutex. - * Attempting to take the mutex will place this task in the Blocked - * state. */ - if( xSemaphoreTake( xMasterSlaveMutex, portMAX_DELAY ) != pdPASS ) - { - xErrorDetected = __LINE__; - } - - if( xSemaphoreGive( xMasterSlaveMutex ) != pdPASS ) - { - xErrorDetected = __LINE__; - } - } -} -/*-----------------------------------------------------------*/ - -static void vInterruptCountingSemaphoreTask( void * pvParameters ) -{ - BaseType_t xCount; - const TickType_t xDelay = pdMS_TO_TICKS( intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS ) * ( intsemMAX_COUNT + 1 ); - - ( void ) pvParameters; - - for( ; ; ) - { - /* Expect to start with the counting semaphore empty. */ - if( uxQueueMessagesWaiting( ( QueueHandle_t ) xISRCountingSemaphore ) != 0 ) - { - xErrorDetected = __LINE__; - } - - /* Wait until it is expected that the interrupt will have filled the - * counting semaphore. */ - xOkToGiveCountingSemaphore = pdTRUE; - vTaskDelay( xDelay ); - xOkToGiveCountingSemaphore = pdFALSE; - - /* Now it is expected that the counting semaphore is full. */ - if( uxQueueMessagesWaiting( ( QueueHandle_t ) xISRCountingSemaphore ) != intsemMAX_COUNT ) - { - xErrorDetected = __LINE__; - } - - if( uxQueueSpacesAvailable( ( QueueHandle_t ) xISRCountingSemaphore ) != 0 ) - { - xErrorDetected = __LINE__; - } - - ulCountingSemaphoreLoops++; - - /* Expect to be able to take the counting semaphore intsemMAX_COUNT - * times. A block time of 0 is used as the semaphore should already be - * there. */ - xCount = 0; - - while( xSemaphoreTake( xISRCountingSemaphore, 0 ) == pdPASS ) - { - xCount++; - } - - if( xCount != intsemMAX_COUNT ) - { - xErrorDetected = __LINE__; - } - - /* Now raise the priority of this task so it runs immediately that the - * semaphore is given from the interrupt. */ - vTaskPrioritySet( NULL, configMAX_PRIORITIES - 1 ); - - /* Block to wait for the semaphore to be given from the interrupt. */ - xOkToGiveCountingSemaphore = pdTRUE; - xSemaphoreTake( xISRCountingSemaphore, portMAX_DELAY ); - xSemaphoreTake( xISRCountingSemaphore, portMAX_DELAY ); - xOkToGiveCountingSemaphore = pdFALSE; - - /* Reset the priority so as not to disturb other tests too much. */ - vTaskPrioritySet( NULL, tskIDLE_PRIORITY ); - - ulCountingSemaphoreLoops++; - } -} -/*-----------------------------------------------------------*/ - -void vInterruptSemaphorePeriodicTest( void ) -{ - static TickType_t xLastGiveTime = 0; - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - TickType_t xTimeNow; - - /* No mutual exclusion on xOkToGiveMutex, but this is only test code (and - * only executed on a 32-bit architecture) so ignore that in this case. */ - xTimeNow = xTaskGetTickCountFromISR(); - - if( ( ( TickType_t ) ( xTimeNow - xLastGiveTime ) ) >= pdMS_TO_TICKS( intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS ) ) - { - configASSERT( xISRMutex ); - - if( xOkToGiveMutex != pdFALSE ) - { - /* Null is used as the second parameter in this give, and non-NULL - * in the other gives for code coverage reasons. */ - xSemaphoreGiveFromISR( xISRMutex, NULL ); - - /* Second give attempt should fail. */ - configASSERT( xSemaphoreGiveFromISR( xISRMutex, &xHigherPriorityTaskWoken ) == pdFAIL ); - } - - if( xOkToGiveCountingSemaphore != pdFALSE ) - { - xSemaphoreGiveFromISR( xISRCountingSemaphore, &xHigherPriorityTaskWoken ); - } - - xLastGiveTime = xTimeNow; - } - - /* Remove compiler warnings about the value being set but not used. */ - ( void ) xHigherPriorityTaskWoken; -} -/*-----------------------------------------------------------*/ - -/* This is called to check that all the created tasks are still running. */ -BaseType_t xAreInterruptSemaphoreTasksStillRunning( void ) -{ - static uint32_t ulLastMasterLoopCounter = 0, ulLastCountingSemaphoreLoops = 0; - BaseType_t xReturn; - - /* If the demo tasks are running then it is expected that the loop counters - * will have changed since this function was last called. */ - if( ulLastMasterLoopCounter == ulMasterLoops ) - { - xErrorDetected = __LINE__; - } - - ulLastMasterLoopCounter = ulMasterLoops; - - if( ulLastCountingSemaphoreLoops == ulCountingSemaphoreLoops ) - { - xErrorDetected = __LINE__; - } - - ulLastCountingSemaphoreLoops = ulCountingSemaphoreLoops++; - - if( xErrorDetected != pdFALSE ) - { - xReturn = pdFALSE; - } - else - { - xReturn = pdTRUE; - } - - return xReturn; -} +/* + * 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 + * + */ + + +/* + * Demonstrates and tests mutexes being used from an interrupt. + */ + + +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" + +/* Demo program include files. */ +#include "IntSemTest.h" + +/*-----------------------------------------------------------*/ + +/* The priorities of the test tasks. */ +#define intsemMASTER_PRIORITY ( tskIDLE_PRIORITY ) +#define intsemSLAVE_PRIORITY ( tskIDLE_PRIORITY + 1 ) + +/* The rate at which the tick hook will give the mutex. */ +#define intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS ( 100 ) + +/* A block time of 0 means 'don't block'. */ +#define intsemNO_BLOCK 0 + +/* The maximum count value for the counting semaphore given from an + * interrupt. */ +#define intsemMAX_COUNT 3 + +/*-----------------------------------------------------------*/ + +/* + * The master is a task that receives a mutex that is given from an interrupt - + * although generally mutexes should not be used given in interrupts (and + * definitely never taken in an interrupt) there are some circumstances when it + * may be desirable. + * + * The slave task is just used by the master task to force priority inheritance + * on a mutex that is shared between the master and the slave - which is a + * separate mutex to that given by the interrupt. + */ +static void vInterruptMutexSlaveTask( void * pvParameters ); +static void vInterruptMutexMasterTask( void * pvParameters ); + +/* + * A test whereby the master takes the shared and interrupt mutexes in that + * order, then gives them back in the same order, ensuring the priority + * inheritance is behaving as expected at each step. + */ +static void prvTakeAndGiveInTheSameOrder( void ); + +/* + * A test whereby the master takes the shared and interrupt mutexes in that + * order, then gives them back in the opposite order to which they were taken, + * ensuring the priority inheritance is behaving as expected at each step. + */ +static void prvTakeAndGiveInTheOppositeOrder( void ); + +/* + * A simple task that interacts with an interrupt using a counting semaphore, + * primarily for code coverage purposes. + */ +static void vInterruptCountingSemaphoreTask( void * pvParameters ); + +/*-----------------------------------------------------------*/ + +/* 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 ulMasterLoops = 0, ulCountingSemaphoreLoops = 0; + +/* Handles of the test tasks that must be accessed from other test tasks. */ +static TaskHandle_t xSlaveHandle; + +/* A mutex which is given from an interrupt - although generally mutexes should + * not be used given in interrupts (and definitely never taken in an interrupt) + * there are some circumstances when it may be desirable. */ +static SemaphoreHandle_t xISRMutex = NULL; + +/* A counting semaphore which is given from an interrupt. */ +static SemaphoreHandle_t xISRCountingSemaphore = NULL; + +/* A mutex which is shared between the master and slave tasks - the master + * does both sharing of this mutex with the slave and receiving a mutex from the + * interrupt. */ +static SemaphoreHandle_t xMasterSlaveMutex = NULL; + +/* Flag that allows the master task to control when the interrupt gives or does + * not give the mutex. There is no mutual exclusion on this variable, but this is + * only test code and it should be fine in the 32=bit test environment. */ +static BaseType_t xOkToGiveMutex = pdFALSE, xOkToGiveCountingSemaphore = pdFALSE; + +/* Used to coordinate timing between tasks and the interrupt. */ +const TickType_t xInterruptGivePeriod = pdMS_TO_TICKS( intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS ); + +/*-----------------------------------------------------------*/ + +void vStartInterruptSemaphoreTasks( void ) +{ + /* Create the semaphores that are given from an interrupt. */ + xISRMutex = xSemaphoreCreateMutex(); + configASSERT( xISRMutex ); + xISRCountingSemaphore = xSemaphoreCreateCounting( intsemMAX_COUNT, 0 ); + configASSERT( xISRCountingSemaphore ); + + /* Create the mutex that is shared between the master and slave tasks (the + * master receives a mutex from an interrupt and shares a mutex with the + * slave. */ + xMasterSlaveMutex = xSemaphoreCreateMutex(); + configASSERT( xMasterSlaveMutex ); + + /* Create the tasks that share mutexes between then and with interrupts. */ + xTaskCreate( vInterruptMutexSlaveTask, "IntMuS", configMINIMAL_STACK_SIZE, NULL, intsemSLAVE_PRIORITY, &xSlaveHandle ); + xTaskCreate( vInterruptMutexMasterTask, "IntMuM", configMINIMAL_STACK_SIZE, NULL, intsemMASTER_PRIORITY, NULL ); + + /* Create the task that blocks on the counting semaphore. */ + xTaskCreate( vInterruptCountingSemaphoreTask, "IntCnt", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL ); +} +/*-----------------------------------------------------------*/ + +static void vInterruptMutexMasterTask( void * pvParameters ) +{ + /* Just to avoid compiler warnings. */ + ( void ) pvParameters; + + for( ; ; ) + { + prvTakeAndGiveInTheSameOrder(); + + /* Ensure not to starve out other tests. */ + ulMasterLoops++; + vTaskDelay( intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS ); + + prvTakeAndGiveInTheOppositeOrder(); + + /* Ensure not to starve out other tests. */ + ulMasterLoops++; + vTaskDelay( intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS ); + } +} +/*-----------------------------------------------------------*/ + +static void prvTakeAndGiveInTheSameOrder( void ) +{ + /* Ensure the slave is suspended, and that this task is running at the + * lower priority as expected as the start conditions. */ + #if ( INCLUDE_eTaskGetState == 1 ) + { + configASSERT( eTaskGetState( xSlaveHandle ) == eSuspended ); + } + #endif /* INCLUDE_eTaskGetState */ + + if( uxTaskPriorityGet( NULL ) != intsemMASTER_PRIORITY ) + { + xErrorDetected = __LINE__; + } + + /* Take the semaphore that is shared with the slave. */ + if( xSemaphoreTake( xMasterSlaveMutex, intsemNO_BLOCK ) != pdPASS ) + { + xErrorDetected = __LINE__; + } + + /* This task now has the mutex. Unsuspend the slave so it too + * attempts to take the mutex. */ + vTaskResume( xSlaveHandle ); + + /* The slave has the higher priority so should now have executed and + * blocked on the semaphore. */ + #if ( INCLUDE_eTaskGetState == 1 ) + { + configASSERT( eTaskGetState( xSlaveHandle ) == eBlocked ); + } + #endif /* INCLUDE_eTaskGetState */ + + /* This task should now have inherited the priority of the slave + * task. */ + if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY ) + { + xErrorDetected = __LINE__; + } + + /* Now wait a little longer than the time between ISR gives to also + * obtain the ISR mutex. */ + xOkToGiveMutex = pdTRUE; + + if( xSemaphoreTake( xISRMutex, ( xInterruptGivePeriod * 2 ) ) != pdPASS ) + { + xErrorDetected = __LINE__; + } + + xOkToGiveMutex = pdFALSE; + + /* Attempting to take again immediately should fail as the mutex is + * already held. */ + if( xSemaphoreTake( xISRMutex, intsemNO_BLOCK ) != pdFAIL ) + { + xErrorDetected = __LINE__; + } + + /* Should still be at the priority of the slave task. */ + if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY ) + { + xErrorDetected = __LINE__; + } + + /* Give back the ISR semaphore to ensure the priority is not + * disinherited as the shared mutex (which the higher priority task is + * attempting to obtain) is still held. */ + if( xSemaphoreGive( xISRMutex ) != pdPASS ) + { + xErrorDetected = __LINE__; + } + + if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY ) + { + xErrorDetected = __LINE__; + } + + /* Finally give back the shared mutex. This time the higher priority + * task should run before this task runs again - so this task should have + * disinherited the priority and the higher priority task should be in the + * suspended state again. */ + if( xSemaphoreGive( xMasterSlaveMutex ) != pdPASS ) + { + xErrorDetected = __LINE__; + } + + if( uxTaskPriorityGet( NULL ) != intsemMASTER_PRIORITY ) + { + xErrorDetected = __LINE__; + } + + #if ( INCLUDE_eTaskGetState == 1 ) + { + configASSERT( eTaskGetState( xSlaveHandle ) == eSuspended ); + } + #endif /* INCLUDE_eTaskGetState */ + + /* Reset the mutex ready for the next round. */ + xQueueReset( xISRMutex ); +} +/*-----------------------------------------------------------*/ + +static void prvTakeAndGiveInTheOppositeOrder( void ) +{ + /* Ensure the slave is suspended, and that this task is running at the + * lower priority as expected as the start conditions. */ + #if ( INCLUDE_eTaskGetState == 1 ) + { + configASSERT( eTaskGetState( xSlaveHandle ) == eSuspended ); + } + #endif /* INCLUDE_eTaskGetState */ + + if( uxTaskPriorityGet( NULL ) != intsemMASTER_PRIORITY ) + { + xErrorDetected = __LINE__; + } + + /* Take the semaphore that is shared with the slave. */ + if( xSemaphoreTake( xMasterSlaveMutex, intsemNO_BLOCK ) != pdPASS ) + { + xErrorDetected = __LINE__; + } + + /* This task now has the mutex. Unsuspend the slave so it too + * attempts to take the mutex. */ + vTaskResume( xSlaveHandle ); + + /* The slave has the higher priority so should now have executed and + * blocked on the semaphore. */ + #if ( INCLUDE_eTaskGetState == 1 ) + { + configASSERT( eTaskGetState( xSlaveHandle ) == eBlocked ); + } + #endif /* INCLUDE_eTaskGetState */ + + /* This task should now have inherited the priority of the slave + * task. */ + if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY ) + { + xErrorDetected = __LINE__; + } + + /* Now wait a little longer than the time between ISR gives to also + * obtain the ISR mutex. */ + xOkToGiveMutex = pdTRUE; + + if( xSemaphoreTake( xISRMutex, ( xInterruptGivePeriod * 2 ) ) != pdPASS ) + { + xErrorDetected = __LINE__; + } + + xOkToGiveMutex = pdFALSE; + + /* Attempting to take again immediately should fail as the mutex is + * already held. */ + if( xSemaphoreTake( xISRMutex, intsemNO_BLOCK ) != pdFAIL ) + { + xErrorDetected = __LINE__; + } + + /* Should still be at the priority of the slave task. */ + if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY ) + { + xErrorDetected = __LINE__; + } + + /* Give back the shared semaphore to ensure the priority is not disinherited + * as the ISR mutex is still held. The higher priority slave task should run + * before this task runs again. */ + if( xSemaphoreGive( xMasterSlaveMutex ) != pdPASS ) + { + xErrorDetected = __LINE__; + } + + /* Should still be at the priority of the slave task as this task still + * holds one semaphore (this is a simplification in the priority inheritance + * mechanism. */ + if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY ) + { + xErrorDetected = __LINE__; + } + + /* Give back the ISR semaphore, which should result in the priority being + * disinherited as it was the last mutex held. */ + if( xSemaphoreGive( xISRMutex ) != pdPASS ) + { + xErrorDetected = __LINE__; + } + + if( uxTaskPriorityGet( NULL ) != intsemMASTER_PRIORITY ) + { + xErrorDetected = __LINE__; + } + + /* Reset the mutex ready for the next round. */ + xQueueReset( xISRMutex ); +} +/*-----------------------------------------------------------*/ + +static void vInterruptMutexSlaveTask( void * pvParameters ) +{ + /* Just to avoid compiler warnings. */ + ( void ) pvParameters; + + for( ; ; ) + { + /* This task starts by suspending itself so when it executes can be + * controlled by the master task. */ + vTaskSuspend( NULL ); + + /* This task will execute when the master task already holds the mutex. + * Attempting to take the mutex will place this task in the Blocked + * state. */ + if( xSemaphoreTake( xMasterSlaveMutex, portMAX_DELAY ) != pdPASS ) + { + xErrorDetected = __LINE__; + } + + if( xSemaphoreGive( xMasterSlaveMutex ) != pdPASS ) + { + xErrorDetected = __LINE__; + } + } +} +/*-----------------------------------------------------------*/ + +static void vInterruptCountingSemaphoreTask( void * pvParameters ) +{ + BaseType_t xCount; + const TickType_t xDelay = pdMS_TO_TICKS( intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS ) * ( intsemMAX_COUNT + 1 ); + + ( void ) pvParameters; + + for( ; ; ) + { + /* Expect to start with the counting semaphore empty. */ + if( uxQueueMessagesWaiting( ( QueueHandle_t ) xISRCountingSemaphore ) != 0 ) + { + xErrorDetected = __LINE__; + } + + /* Wait until it is expected that the interrupt will have filled the + * counting semaphore. */ + xOkToGiveCountingSemaphore = pdTRUE; + vTaskDelay( xDelay ); + xOkToGiveCountingSemaphore = pdFALSE; + + /* Now it is expected that the counting semaphore is full. */ + if( uxQueueMessagesWaiting( ( QueueHandle_t ) xISRCountingSemaphore ) != intsemMAX_COUNT ) + { + xErrorDetected = __LINE__; + } + + if( uxQueueSpacesAvailable( ( QueueHandle_t ) xISRCountingSemaphore ) != 0 ) + { + xErrorDetected = __LINE__; + } + + ulCountingSemaphoreLoops++; + + /* Expect to be able to take the counting semaphore intsemMAX_COUNT + * times. A block time of 0 is used as the semaphore should already be + * there. */ + xCount = 0; + + while( xSemaphoreTake( xISRCountingSemaphore, 0 ) == pdPASS ) + { + xCount++; + } + + if( xCount != intsemMAX_COUNT ) + { + xErrorDetected = __LINE__; + } + + /* Now raise the priority of this task so it runs immediately that the + * semaphore is given from the interrupt. */ + vTaskPrioritySet( NULL, configMAX_PRIORITIES - 1 ); + + /* Block to wait for the semaphore to be given from the interrupt. */ + xOkToGiveCountingSemaphore = pdTRUE; + xSemaphoreTake( xISRCountingSemaphore, portMAX_DELAY ); + xSemaphoreTake( xISRCountingSemaphore, portMAX_DELAY ); + xOkToGiveCountingSemaphore = pdFALSE; + + /* Reset the priority so as not to disturb other tests too much. */ + vTaskPrioritySet( NULL, tskIDLE_PRIORITY ); + + ulCountingSemaphoreLoops++; + } +} +/*-----------------------------------------------------------*/ + +void vInterruptSemaphorePeriodicTest( void ) +{ + static TickType_t xLastGiveTime = 0; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + TickType_t xTimeNow; + + /* No mutual exclusion on xOkToGiveMutex, but this is only test code (and + * only executed on a 32-bit architecture) so ignore that in this case. */ + xTimeNow = xTaskGetTickCountFromISR(); + + if( ( ( TickType_t ) ( xTimeNow - xLastGiveTime ) ) >= pdMS_TO_TICKS( intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS ) ) + { + configASSERT( xISRMutex ); + + if( xOkToGiveMutex != pdFALSE ) + { + /* Null is used as the second parameter in this give, and non-NULL + * in the other gives for code coverage reasons. */ + xSemaphoreGiveFromISR( xISRMutex, NULL ); + + /* Second give attempt should fail. */ + configASSERT( xSemaphoreGiveFromISR( xISRMutex, &xHigherPriorityTaskWoken ) == pdFAIL ); + } + + if( xOkToGiveCountingSemaphore != pdFALSE ) + { + xSemaphoreGiveFromISR( xISRCountingSemaphore, &xHigherPriorityTaskWoken ); + } + + xLastGiveTime = xTimeNow; + } + + /* Remove compiler warnings about the value being set but not used. */ + ( void ) xHigherPriorityTaskWoken; +} +/*-----------------------------------------------------------*/ + +/* This is called to check that all the created tasks are still running. */ +BaseType_t xAreInterruptSemaphoreTasksStillRunning( void ) +{ + static uint32_t ulLastMasterLoopCounter = 0, ulLastCountingSemaphoreLoops = 0; + BaseType_t xReturn; + + /* If the demo tasks are running then it is expected that the loop counters + * will have changed since this function was last called. */ + if( ulLastMasterLoopCounter == ulMasterLoops ) + { + xErrorDetected = __LINE__; + } + + ulLastMasterLoopCounter = ulMasterLoops; + + if( ulLastCountingSemaphoreLoops == ulCountingSemaphoreLoops ) + { + xErrorDetected = __LINE__; + } + + ulLastCountingSemaphoreLoops = ulCountingSemaphoreLoops++; + + if( xErrorDetected != pdFALSE ) + { + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + + return xReturn; +} diff --git a/FreeRTOS/Demo/Common/Minimal/StaticAllocation.c b/FreeRTOS/Demo/Common/Minimal/StaticAllocation.c index 9846c5820e6..0cc08d97792 100644 --- a/FreeRTOS/Demo/Common/Minimal/StaticAllocation.c +++ b/FreeRTOS/Demo/Common/Minimal/StaticAllocation.c @@ -1,1113 +1,1113 @@ -/* - * 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 - * - */ - - -/* - * Demonstrates how to create FreeRTOS objects using pre-allocated memory, - * rather than the normal dynamically allocated memory, and tests objects being - * created and deleted with both statically allocated memory and dynamically - * allocated memory. - * - * See http://www.FreeRTOS.org/Static_Vs_Dynamic_Memory_Allocation.html - */ - -/* Scheduler include files. */ -#include "FreeRTOS.h" -#include "task.h" -#include "queue.h" -#include "semphr.h" -#include "event_groups.h" -#include "timers.h" - -/* Demo program include files. */ -#include "StaticAllocation.h" - -/* Exclude the entire file if configSUPPORT_STATIC_ALLOCATION is 0. */ -#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) - -/* The priority at which the task that performs the tests is created. */ - #define staticTASK_PRIORITY ( tskIDLE_PRIORITY + 2 ) - -/* The length of the queue, in items, not bytes, used in the queue static - * allocation tests. */ - #define staticQUEUE_LENGTH_IN_ITEMS ( 5 ) - -/* A block time of 0 simply means "don't block". */ - #define staticDONT_BLOCK ( ( TickType_t ) 0 ) - -/* Binary semaphores have a maximum count of 1. */ - #define staticBINARY_SEMAPHORE_MAX_COUNT ( 1 ) - -/* The size of the stack used by the task that runs the tests. */ - #define staticCREATOR_TASK_STACK_SIZE ( configMINIMAL_STACK_SIZE * 2 ) - -/* The number of times the software timer will execute before stopping itself. */ - #define staticMAX_TIMER_CALLBACK_EXECUTIONS ( 5 ) - - -/*-----------------------------------------------------------*/ - -/* - * The task that repeatedly creates and deletes statically allocated tasks, and - * other RTOS objects. - */ - static void prvStaticallyAllocatedCreator( void * pvParameters ); - -/* - * The callback function used by the software timer that is repeatedly created - * and deleted using both static and dynamically allocated memory. - */ - static void prvTimerCallback( TimerHandle_t xExpiredTimer ); - -/* - * A task that is created and deleted multiple times, using both statically and - * dynamically allocated stack and TCB. - */ - static void prvStaticallyAllocatedTask( void * pvParameters ); - -/* - * A function that demonstrates and tests the API functions that create and - * delete tasks using both statically and dynamically allocated TCBs and stacks. - */ - static void prvCreateAndDeleteStaticallyAllocatedTasks( void ); - -/* - * A function that demonstrates and tests the API functions that create and - * delete event groups using both statically and dynamically allocated RAM. - */ - static void prvCreateAndDeleteStaticallyAllocatedEventGroups( void ); - -/* - * A function that demonstrates and tests the API functions that create and - * delete queues using both statically and dynamically allocated RAM. - */ - static void prvCreateAndDeleteStaticallyAllocatedQueues( void ); - -/* - * A function that demonstrates and tests the API functions that create and - * delete binary semaphores using both statically and dynamically allocated RAM. - */ - static void prvCreateAndDeleteStaticallyAllocatedBinarySemaphores( void ); - -/* - * A function that demonstrates and tests the API functions that create and - * delete software timers using both statically and dynamically allocated RAM. - */ - static void prvCreateAndDeleteStaticallyAllocatedTimers( void ); - -/* - * A function that demonstrates and tests the API functions that create and - * delete mutexes using both statically and dynamically allocated RAM. - */ - static void prvCreateAndDeleteStaticallyAllocatedMutexes( void ); - -/* - * A function that demonstrates and tests the API functions that create and - * delete counting semaphores using both statically and dynamically allocated - * RAM. - */ - static void prvCreateAndDeleteStaticallyAllocatedCountingSemaphores( void ); - -/* - * A function that demonstrates and tests the API functions that create and - * delete recursive mutexes using both statically and dynamically allocated RAM. - */ - static void prvCreateAndDeleteStaticallyAllocatedRecursiveMutexes( void ); - -/* - * Utility function to create pseudo random numbers. - */ - static UBaseType_t prvRand( void ); - -/* - * The task that creates and deletes other tasks has to delay occasionally to - * ensure lower priority tasks are not starved of processing time. A pseudo - * random delay time is used just to add a little bit of randomisation into the - * execution pattern. prvGetNextDelayTime() generates the pseudo random delay. - */ - static TickType_t prvGetNextDelayTime( void ); - -/* - * Checks the basic operation of a queue after it has been created. - */ - static void prvSanityCheckCreatedQueue( QueueHandle_t xQueue ); - -/* - * Checks the basic operation of a recursive mutex after it has been created. - */ - static void prvSanityCheckCreatedRecursiveMutex( SemaphoreHandle_t xSemaphore ); - -/* - * Checks the basic operation of a binary semaphore after it has been created. - */ - static void prvSanityCheckCreatedSemaphore( SemaphoreHandle_t xSemaphore, - UBaseType_t uxMaxCount ); - -/* - * Checks the basic operation of an event group after it has been created. - */ - static void prvSanityCheckCreatedEventGroup( EventGroupHandle_t xEventGroup ); - -/*-----------------------------------------------------------*/ - -/* StaticTask_t is a publicly accessible structure that has the same size and - * alignment requirements as the real TCB structure. It is provided as a mechanism - * for applications to know the size of the TCB (which is dependent on the - * architecture and configuration file settings) without breaking the strict data - * hiding policy by exposing the real TCB. This StaticTask_t variable is passed - * into the xTaskCreateStatic() function that creates the - * prvStaticallyAllocatedCreator() task, and will hold the TCB of the created - * tasks. */ - static StaticTask_t xCreatorTaskTCBBuffer; - -/* This is the stack that will be used by the prvStaticallyAllocatedCreator() - * task, which is itself created using statically allocated buffers (so without any - * dynamic memory allocation). */ - static StackType_t uxCreatorTaskStackBuffer[ staticCREATOR_TASK_STACK_SIZE ]; - -/* Used by the pseudo random number generating function. */ - static uint32_t ulNextRand = 0; - -/* Used so a check task can ensure this test is still executing, and not - * stalled. */ - static volatile UBaseType_t uxCycleCounter = 0; - -/* A variable that gets set to pdTRUE if an error is detected. */ - static volatile BaseType_t xErrorOccurred = pdFALSE; - -/*-----------------------------------------------------------*/ - - void vStartStaticallyAllocatedTasks( void ) - { - /* Create a single task, which then repeatedly creates and deletes the other - * RTOS objects using both statically and dynamically allocated RAM. */ - xTaskCreateStatic( prvStaticallyAllocatedCreator, /* The function that implements the task being created. */ - "StatCreate", /* Text name for the task - not used by the RTOS, its just to assist debugging. */ - staticCREATOR_TASK_STACK_SIZE, /* Size of the buffer passed in as the stack - in words, not bytes! */ - NULL, /* Parameter passed into the task - not used in this case. */ - staticTASK_PRIORITY, /* Priority of the task. */ - &( uxCreatorTaskStackBuffer[ 0 ] ), /* The buffer to use as the task's stack. */ - &xCreatorTaskTCBBuffer ); /* The variable that will hold the task's TCB. */ - } -/*-----------------------------------------------------------*/ - - static void prvStaticallyAllocatedCreator( void * pvParameters ) - { - /* Avoid compiler warnings. */ - ( void ) pvParameters; - - for( ; ; ) - { - /* Loop, running functions that create and delete the various RTOS - * objects that can be optionally created using either static or dynamic - * memory allocation. */ - prvCreateAndDeleteStaticallyAllocatedTasks(); - prvCreateAndDeleteStaticallyAllocatedQueues(); - - /* Delay to ensure lower priority tasks get CPU time, and increment the - * cycle counter so a 'check' task can determine that this task is still - * executing. */ - vTaskDelay( prvGetNextDelayTime() ); - uxCycleCounter++; - - prvCreateAndDeleteStaticallyAllocatedBinarySemaphores(); - prvCreateAndDeleteStaticallyAllocatedCountingSemaphores(); - - vTaskDelay( prvGetNextDelayTime() ); - uxCycleCounter++; - - prvCreateAndDeleteStaticallyAllocatedMutexes(); - prvCreateAndDeleteStaticallyAllocatedRecursiveMutexes(); - - vTaskDelay( prvGetNextDelayTime() ); - uxCycleCounter++; - - prvCreateAndDeleteStaticallyAllocatedEventGroups(); - prvCreateAndDeleteStaticallyAllocatedTimers(); - } - } -/*-----------------------------------------------------------*/ - - static void prvCreateAndDeleteStaticallyAllocatedCountingSemaphores( void ) - { - SemaphoreHandle_t xSemaphore; - const UBaseType_t uxMaxCount = ( UBaseType_t ) 10; - -/* StaticSemaphore_t is a publicly accessible structure that has the same size - * and alignment requirements as the real semaphore structure. It is provided as a - * mechanism for applications to know the size of the semaphore (which is dependent - * on the architecture and configuration file settings) without breaking the strict - * data hiding policy by exposing the real semaphore internals. This - * StaticSemaphore_t variable is passed into the xSemaphoreCreateCountingStatic() - * function calls within this function. NOTE: In most usage scenarios now it is - * faster and more memory efficient to use a direct to task notification instead of - * a counting semaphore. http://www.freertos.org/RTOS-task-notifications.html */ - StaticSemaphore_t xSemaphoreBuffer; - - /* Create the semaphore. xSemaphoreCreateCountingStatic() has one more - * parameter than the usual xSemaphoreCreateCounting() function. The parameter - * is a pointer to the pre-allocated StaticSemaphore_t structure, which will - * hold information on the semaphore in an anonymous way. If the pointer is - * passed as NULL then the structure will be allocated dynamically, just as - * when xSemaphoreCreateCounting() is called. */ - xSemaphore = xSemaphoreCreateCountingStatic( uxMaxCount, 0, &xSemaphoreBuffer ); - - /* The semaphore handle should equal the static semaphore structure passed - * into the xSemaphoreCreateBinaryStatic() function. */ - configASSERT( xSemaphore == ( SemaphoreHandle_t ) &xSemaphoreBuffer ); - - /* Ensure the semaphore passes a few sanity checks as a valid semaphore. */ - prvSanityCheckCreatedSemaphore( xSemaphore, uxMaxCount ); - - /* Delete the semaphore again so the buffers can be reused. */ - vSemaphoreDelete( xSemaphore ); - - #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - { - /* Now do the same but using dynamically allocated buffers to ensure the - * delete functions are working correctly in both the static and dynamic - * allocation cases. */ - xSemaphore = xSemaphoreCreateCounting( uxMaxCount, 0 ); - configASSERT( xSemaphore != NULL ); - prvSanityCheckCreatedSemaphore( xSemaphore, uxMaxCount ); - vSemaphoreDelete( xSemaphore ); - } - #endif - } -/*-----------------------------------------------------------*/ - - static void prvCreateAndDeleteStaticallyAllocatedRecursiveMutexes( void ) - { - SemaphoreHandle_t xSemaphore; - -/* StaticSemaphore_t is a publicly accessible structure that has the same size - * and alignment requirements as the real semaphore structure. It is provided as a - * mechanism for applications to know the size of the semaphore (which is dependent - * on the architecture and configuration file settings) without breaking the strict - * data hiding policy by exposing the real semaphore internals. This - * StaticSemaphore_t variable is passed into the - * xSemaphoreCreateRecursiveMutexStatic() function calls within this function. */ - StaticSemaphore_t xSemaphoreBuffer; - - /* Create the semaphore. xSemaphoreCreateRecursiveMutexStatic() has one - * more parameter than the usual xSemaphoreCreateRecursiveMutex() function. - * The parameter is a pointer to the pre-allocated StaticSemaphore_t structure, - * which will hold information on the semaphore in an anonymous way. If the - * pointer is passed as NULL then the structure will be allocated dynamically, - * just as when xSemaphoreCreateRecursiveMutex() is called. */ - xSemaphore = xSemaphoreCreateRecursiveMutexStatic( &xSemaphoreBuffer ); - - /* The semaphore handle should equal the static semaphore structure passed - * into the xSemaphoreCreateBinaryStatic() function. */ - configASSERT( xSemaphore == ( SemaphoreHandle_t ) &xSemaphoreBuffer ); - - /* Ensure the semaphore passes a few sanity checks as a valid - * recursive semaphore. */ - prvSanityCheckCreatedRecursiveMutex( xSemaphore ); - - /* Delete the semaphore again so the buffers can be reused. */ - vSemaphoreDelete( xSemaphore ); - - /* Now do the same using dynamically allocated buffers to ensure the delete - * functions are working correctly in both the static and dynamic memory - * allocation cases. */ - #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - { - xSemaphore = xSemaphoreCreateRecursiveMutex(); - configASSERT( xSemaphore != NULL ); - prvSanityCheckCreatedRecursiveMutex( xSemaphore ); - vSemaphoreDelete( xSemaphore ); - } - #endif - } -/*-----------------------------------------------------------*/ - - static void prvCreateAndDeleteStaticallyAllocatedQueues( void ) - { - QueueHandle_t xQueue; - -/* StaticQueue_t is a publicly accessible structure that has the same size and - * alignment requirements as the real queue structure. It is provided as a - * mechanism for applications to know the size of the queue (which is dependent on - * the architecture and configuration file settings) without breaking the strict - * data hiding policy by exposing the real queue internals. This StaticQueue_t - * variable is passed into the xQueueCreateStatic() function calls within this - * function. */ - static StaticQueue_t xStaticQueue; - -/* The queue storage area must be large enough to hold the maximum number of - * items it is possible for the queue to hold at any one time, which equals the - * queue length (in items, not bytes) multiplied by the size of each item. In this - * case the queue will hold staticQUEUE_LENGTH_IN_ITEMS 64-bit items. See - * http://www.freertos.org/Embedded-RTOS-Queues.html */ - static uint8_t ucQueueStorageArea[ staticQUEUE_LENGTH_IN_ITEMS * sizeof( uint64_t ) ]; - - /* Create the queue. xQueueCreateStatic() has two more parameters than the - * usual xQueueCreate() function. The first new parameter is a pointer to the - * pre-allocated queue storage area. The second new parameter is a pointer to - * the StaticQueue_t structure that will hold the queue state information in - * an anonymous way. If the two pointers are passed as NULL then the data - * will be allocated dynamically as if xQueueCreate() had been called. */ - xQueue = xQueueCreateStatic( staticQUEUE_LENGTH_IN_ITEMS, /* The maximum number of items the queue can hold. */ - sizeof( uint64_t ), /* The size of each item. */ - ucQueueStorageArea, /* The buffer used to hold items within the queue. */ - &xStaticQueue ); /* The static queue structure that will hold the state of the queue. */ - - /* The queue handle should equal the static queue structure passed into the - * xQueueCreateStatic() function. */ - configASSERT( xQueue == ( QueueHandle_t ) &xStaticQueue ); - - /* Ensure the queue passes a few sanity checks as a valid queue. */ - prvSanityCheckCreatedQueue( xQueue ); - - /* Delete the queue again so the buffers can be reused. */ - vQueueDelete( xQueue ); - - /* Now do the same using a dynamically allocated queue to ensure the delete - * function is working correctly in both the static and dynamic memory - * allocation cases. */ - #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - { - xQueue = xQueueCreate( staticQUEUE_LENGTH_IN_ITEMS, /* The maximum number of items the queue can hold. */ - sizeof( uint64_t ) ); /* The size of each item. */ - - /* The queue handle should equal the static queue structure passed into the - * xQueueCreateStatic() function. */ - configASSERT( xQueue != NULL ); - - /* Ensure the queue passes a few sanity checks as a valid queue. */ - prvSanityCheckCreatedQueue( xQueue ); - - /* Delete the queue again so the buffers can be reused. */ - vQueueDelete( xQueue ); - } - #endif /* if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) */ - } -/*-----------------------------------------------------------*/ - - static void prvCreateAndDeleteStaticallyAllocatedMutexes( void ) - { - SemaphoreHandle_t xSemaphore; - BaseType_t xReturned; - -/* StaticSemaphore_t is a publicly accessible structure that has the same size - * and alignment requirements as the real semaphore structure. It is provided as a - * mechanism for applications to know the size of the semaphore (which is dependent - * on the architecture and configuration file settings) without breaking the strict - * data hiding policy by exposing the real semaphore internals. This - * StaticSemaphore_t variable is passed into the xSemaphoreCreateMutexStatic() - * function calls within this function. */ - StaticSemaphore_t xSemaphoreBuffer; - - /* Create the semaphore. xSemaphoreCreateMutexStatic() has one more - * parameter than the usual xSemaphoreCreateMutex() function. The parameter - * is a pointer to the pre-allocated StaticSemaphore_t structure, which will - * hold information on the semaphore in an anonymous way. If the pointer is - * passed as NULL then the structure will be allocated dynamically, just as - * when xSemaphoreCreateMutex() is called. */ - xSemaphore = xSemaphoreCreateMutexStatic( &xSemaphoreBuffer ); - - /* The semaphore handle should equal the static semaphore structure passed - * into the xSemaphoreCreateMutexStatic() function. */ - configASSERT( xSemaphore == ( SemaphoreHandle_t ) &xSemaphoreBuffer ); - - /* Take the mutex so the mutex is in the state expected by the - * prvSanityCheckCreatedSemaphore() function. */ - xReturned = xSemaphoreTake( xSemaphore, staticDONT_BLOCK ); - - if( xReturned != pdPASS ) - { - xErrorOccurred = __LINE__; - } - - /* Ensure the semaphore passes a few sanity checks as a valid semaphore. */ - prvSanityCheckCreatedSemaphore( xSemaphore, staticBINARY_SEMAPHORE_MAX_COUNT ); - - /* Delete the semaphore again so the buffers can be reused. */ - vSemaphoreDelete( xSemaphore ); - - /* Now do the same using a dynamically allocated mutex to ensure the delete - * function is working correctly in both the static and dynamic allocation - * cases. */ - #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - { - xSemaphore = xSemaphoreCreateMutex(); - - /* The semaphore handle should equal the static semaphore structure - * passed into the xSemaphoreCreateMutexStatic() function. */ - configASSERT( xSemaphore != NULL ); - - /* Take the mutex so the mutex is in the state expected by the - * prvSanityCheckCreatedSemaphore() function. */ - xReturned = xSemaphoreTake( xSemaphore, staticDONT_BLOCK ); - - if( xReturned != pdPASS ) - { - xErrorOccurred = __LINE__; - } - - /* Ensure the semaphore passes a few sanity checks as a valid semaphore. */ - prvSanityCheckCreatedSemaphore( xSemaphore, staticBINARY_SEMAPHORE_MAX_COUNT ); - - /* Delete the semaphore again so the buffers can be reused. */ - vSemaphoreDelete( xSemaphore ); - } - #endif /* if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) */ - } -/*-----------------------------------------------------------*/ - - static void prvCreateAndDeleteStaticallyAllocatedBinarySemaphores( void ) - { - SemaphoreHandle_t xSemaphore; - -/* StaticSemaphore_t is a publicly accessible structure that has the same size -* and alignment requirements as the real semaphore structure. It is provided as a -* mechanism for applications to know the size of the semaphore (which is dependent -* on the architecture and configuration file settings) without breaking the strict -* data hiding policy by exposing the real semaphore internals. This -* StaticSemaphore_t variable is passed into the xSemaphoreCreateBinaryStatic() -* function calls within this function. NOTE: In most usage scenarios now it is -* faster and more memory efficient to use a direct to task notification instead of -* a binary semaphore. http://www.freertos.org/RTOS-task-notifications.html */ - StaticSemaphore_t xSemaphoreBuffer; - - /* Create the semaphore. xSemaphoreCreateBinaryStatic() has one more - * parameter than the usual xSemaphoreCreateBinary() function. The parameter - * is a pointer to the pre-allocated StaticSemaphore_t structure, which will - * hold information on the semaphore in an anonymous way. If the pointer is - * passed as NULL then the structure will be allocated dynamically, just as - * when xSemaphoreCreateBinary() is called. */ - xSemaphore = xSemaphoreCreateBinaryStatic( &xSemaphoreBuffer ); - - /* The semaphore handle should equal the static semaphore structure passed - * into the xSemaphoreCreateBinaryStatic() function. */ - configASSERT( xSemaphore == ( SemaphoreHandle_t ) &xSemaphoreBuffer ); - - /* Ensure the semaphore passes a few sanity checks as a valid semaphore. */ - prvSanityCheckCreatedSemaphore( xSemaphore, staticBINARY_SEMAPHORE_MAX_COUNT ); - - /* Delete the semaphore again so the buffers can be reused. */ - vSemaphoreDelete( xSemaphore ); - - /* Now do the same using a dynamically allocated semaphore to check the - * delete function is working correctly in both the static and dynamic - * allocation cases. */ - #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - { - xSemaphore = xSemaphoreCreateBinary(); - configASSERT( xSemaphore != NULL ); - prvSanityCheckCreatedSemaphore( xSemaphore, staticBINARY_SEMAPHORE_MAX_COUNT ); - vSemaphoreDelete( xSemaphore ); - } - #endif - - /* There isn't a static version of the old and deprecated - * vSemaphoreCreateBinary() macro (because its deprecated!), but check it is - * still functioning correctly. */ - #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - { - vSemaphoreCreateBinary( xSemaphore ); - - /* The macro starts with the binary semaphore available, but the test - * function expects it to be unavailable. */ - if( xSemaphoreTake( xSemaphore, staticDONT_BLOCK ) == pdFAIL ) - { - xErrorOccurred = __LINE__; - } - - prvSanityCheckCreatedSemaphore( xSemaphore, staticBINARY_SEMAPHORE_MAX_COUNT ); - vSemaphoreDelete( xSemaphore ); - } - #endif /* if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) */ - } -/*-----------------------------------------------------------*/ - - static void prvTimerCallback( TimerHandle_t xExpiredTimer ) - { - UBaseType_t * puxVariableToIncrement; - BaseType_t xReturned; - - /* The timer callback just demonstrates it is executing by incrementing a - * variable - the address of which is passed into the timer as its ID. Obtain - * the address of the variable to increment. */ - puxVariableToIncrement = ( UBaseType_t * ) pvTimerGetTimerID( xExpiredTimer ); - - /* Increment the variable to show the timer callback has executed. */ - ( *puxVariableToIncrement )++; - - /* If this callback has executed the required number of times, stop the - * timer. */ - if( *puxVariableToIncrement == staticMAX_TIMER_CALLBACK_EXECUTIONS ) - { - /* This is called from a timer callback so must not block. See - * http://www.FreeRTOS.org/FreeRTOS-timers-xTimerStop.html */ - xReturned = xTimerStop( xExpiredTimer, staticDONT_BLOCK ); - - if( xReturned != pdPASS ) - { - xErrorOccurred = __LINE__; - } - } - } -/*-----------------------------------------------------------*/ - - static void prvCreateAndDeleteStaticallyAllocatedTimers( void ) - { - TimerHandle_t xTimer; - UBaseType_t uxVariableToIncrement; - const TickType_t xTimerPeriod = pdMS_TO_TICKS( 20 ); - BaseType_t xReturned; - -/* StaticTimer_t is a publicly accessible structure that has the same size - * and alignment requirements as the real timer structure. It is provided as a - * mechanism for applications to know the size of the timer structure (which is - * dependent on the architecture and configuration file settings) without breaking - * the strict data hiding policy by exposing the real timer internals. This - * StaticTimer_t variable is passed into the xTimerCreateStatic() function calls - * within this function. */ - StaticTimer_t xTimerBuffer; - - /* Create the software time. xTimerCreateStatic() has an extra parameter - * than the normal xTimerCreate() API function. The parameter is a pointer to - * the StaticTimer_t structure that will hold the software timer structure. If - * the parameter is passed as NULL then the structure will be allocated - * dynamically, just as if xTimerCreate() had been called. */ - xTimer = xTimerCreateStatic( "T1", /* Text name for the task. Helps debugging only. Not used by FreeRTOS. */ - xTimerPeriod, /* The period of the timer in ticks. */ - pdTRUE, /* This is an auto-reload timer. */ - ( void * ) &uxVariableToIncrement, /* The variable incremented by the test is passed into the timer callback using the timer ID. */ - prvTimerCallback, /* The function to execute when the timer expires. */ - &xTimerBuffer ); /* The buffer that will hold the software timer structure. */ - - /* The timer handle should equal the static timer structure passed into the - * xTimerCreateStatic() function. */ - configASSERT( xTimer == ( TimerHandle_t ) &xTimerBuffer ); - - /* Set the variable to 0, wait for a few timer periods to expire, then check - * the timer callback has incremented the variable to the expected value. */ - uxVariableToIncrement = 0; - - /* This is a low priority so a block time should not be needed. */ - xReturned = xTimerStart( xTimer, staticDONT_BLOCK ); - - if( xReturned != pdPASS ) - { - xErrorOccurred = __LINE__; - } - - vTaskDelay( xTimerPeriod * staticMAX_TIMER_CALLBACK_EXECUTIONS ); - - /* By now the timer should have expired staticMAX_TIMER_CALLBACK_EXECUTIONS - * times, and then stopped itself. */ - if( uxVariableToIncrement != staticMAX_TIMER_CALLBACK_EXECUTIONS ) - { - xErrorOccurred = __LINE__; - } - - /* Finished with the timer, delete it. */ - xReturned = xTimerDelete( xTimer, staticDONT_BLOCK ); - - /* Again, as this is a low priority task it is expected that the timer - * command will have been sent even without a block time being used. */ - if( xReturned != pdPASS ) - { - xErrorOccurred = __LINE__; - } - - /* Just to show the check task that this task is still executing. */ - uxCycleCounter++; - - /* Now do the same using a dynamically allocated software timer to ensure - * the delete function is working correctly in both the static and dynamic - * allocation cases. */ - #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - { - xTimer = xTimerCreate( "T1", /* Text name for the task. Helps debugging only. Not used by FreeRTOS. */ - xTimerPeriod, /* The period of the timer in ticks. */ - pdTRUE, /* This is an auto-reload timer. */ - ( void * ) &uxVariableToIncrement, /* The variable incremented by the test is passed into the timer callback using the timer ID. */ - prvTimerCallback ); /* The function to execute when the timer expires. */ - - configASSERT( xTimer != NULL ); - - uxVariableToIncrement = 0; - xReturned = xTimerStart( xTimer, staticDONT_BLOCK ); - - if( xReturned != pdPASS ) - { - xErrorOccurred = __LINE__; - } - - vTaskDelay( xTimerPeriod * staticMAX_TIMER_CALLBACK_EXECUTIONS ); - - if( uxVariableToIncrement != staticMAX_TIMER_CALLBACK_EXECUTIONS ) - { - xErrorOccurred = __LINE__; - } - - xReturned = xTimerDelete( xTimer, staticDONT_BLOCK ); - - if( xReturned != pdPASS ) - { - xErrorOccurred = __LINE__; - } - } - #endif /* if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) */ - } -/*-----------------------------------------------------------*/ - - static void prvCreateAndDeleteStaticallyAllocatedEventGroups( void ) - { - EventGroupHandle_t xEventGroup; - -/* StaticEventGroup_t is a publicly accessible structure that has the same size - * and alignment requirements as the real event group structure. It is provided as - * a mechanism for applications to know the size of the event group (which is - * dependent on the architecture and configuration file settings) without breaking - * the strict data hiding policy by exposing the real event group internals. This - * StaticEventGroup_t variable is passed into the xSemaphoreCreateEventGroupStatic() - * function calls within this function. */ - StaticEventGroup_t xEventGroupBuffer; - - /* Create the event group. xEventGroupCreateStatic() has an extra parameter - * than the normal xEventGroupCreate() API function. The parameter is a - * pointer to the StaticEventGroup_t structure that will hold the event group - * structure. */ - xEventGroup = xEventGroupCreateStatic( &xEventGroupBuffer ); - - /* The event group handle should equal the static event group structure - * passed into the xEventGroupCreateStatic() function. */ - configASSERT( xEventGroup == ( EventGroupHandle_t ) &xEventGroupBuffer ); - - /* Ensure the event group passes a few sanity checks as a valid event - * group. */ - prvSanityCheckCreatedEventGroup( xEventGroup ); - - /* Delete the event group again so the buffers can be reused. */ - vEventGroupDelete( xEventGroup ); - - /* Now do the same using a dynamically allocated event group to ensure the - * delete function is working correctly in both the static and dynamic - * allocation cases. */ - #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - { - xEventGroup = xEventGroupCreate(); - configASSERT( xEventGroup != NULL ); - prvSanityCheckCreatedEventGroup( xEventGroup ); - vEventGroupDelete( xEventGroup ); - } - #endif - } -/*-----------------------------------------------------------*/ - - static void prvCreateAndDeleteStaticallyAllocatedTasks( void ) - { - TaskHandle_t xCreatedTask; - -/* The variable that will hold the TCB of tasks created by this function. See - * the comments above the declaration of the xCreatorTaskTCBBuffer variable for - * more information. NOTE: This is not static so relies on the tasks that use it - * being deleted before this function returns and deallocates its stack. That will - * only be the case if configUSE_PREEMPTION is set to 1. */ - StaticTask_t xTCBBuffer; - -/* This buffer that will be used as the stack of tasks created by this function. - * See the comments above the declaration of the uxCreatorTaskStackBuffer[] array - * above for more information. */ - static StackType_t uxStackBuffer[ configMINIMAL_STACK_SIZE ]; - - /* Create the task. xTaskCreateStatic() has two more parameters than - * the usual xTaskCreate() function. The first new parameter is a pointer to - * the pre-allocated stack. The second new parameter is a pointer to the - * StaticTask_t structure that will hold the task's TCB. If both pointers are - * passed as NULL then the respective object will be allocated dynamically as - * if xTaskCreate() had been called. */ - xCreatedTask = xTaskCreateStatic( - prvStaticallyAllocatedTask, /* Function that implements the task. */ - "Static", /* Human readable name for the task. */ - configMINIMAL_STACK_SIZE, /* Task's stack size, in words (not bytes!). */ - NULL, /* Parameter to pass into the task. */ - uxTaskPriorityGet( NULL ) + 1, /* The priority of the task. */ - &( uxStackBuffer[ 0 ] ), /* The buffer to use as the task's stack. */ - &xTCBBuffer ); /* The variable that will hold that task's TCB. */ - - /* Check the task was created correctly, then delete the task. */ - if( xCreatedTask == NULL ) - { - xErrorOccurred = __LINE__; - } - else if( eTaskGetState( xCreatedTask ) != eSuspended ) - { - /* The created task had a higher priority so should have executed and - * suspended itself by now. */ - xErrorOccurred = __LINE__; - } - else - { - vTaskDelete( xCreatedTask ); - } - - /* Now do the same using a dynamically allocated task to ensure the delete - * function is working correctly in both the static and dynamic allocation - * cases. */ - #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) - { - BaseType_t xReturned; - - xReturned = xTaskCreate( - prvStaticallyAllocatedTask, /* Function that implements the task - the same function is used but is actually dynamically allocated this time. */ - "Static", /* Human readable name for the task. */ - configMINIMAL_STACK_SIZE, /* Task's stack size, in words (not bytes!). */ - NULL, /* Parameter to pass into the task. */ - uxTaskPriorityGet( NULL ) + 1, /* The priority of the task. */ - &xCreatedTask ); /* Handle of the task being created. */ - - if( eTaskGetState( xCreatedTask ) != eSuspended ) - { - xErrorOccurred = __LINE__; - } - - configASSERT( xReturned == pdPASS ); - - if( xReturned != pdPASS ) - { - xErrorOccurred = __LINE__; - } - - vTaskDelete( xCreatedTask ); - } - #endif /* if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) */ - } -/*-----------------------------------------------------------*/ - - static void prvStaticallyAllocatedTask( void * pvParameters ) - { - ( void ) pvParameters; - - /* The created task just suspends itself to wait to get deleted. The task - * that creates this task checks this task is in the expected Suspended state - * before deleting it. */ - vTaskSuspend( NULL ); - } -/*-----------------------------------------------------------*/ - - static UBaseType_t prvRand( void ) - { - const uint32_t ulMultiplier = 0x015a4e35UL, ulIncrement = 1UL; - - /* Utility function to generate a pseudo random number. */ - ulNextRand = ( ulMultiplier * ulNextRand ) + ulIncrement; - return( ( ulNextRand >> 16UL ) & 0x7fffUL ); - } -/*-----------------------------------------------------------*/ - - static TickType_t prvGetNextDelayTime( void ) - { - TickType_t xNextDelay; - const TickType_t xMaxDelay = pdMS_TO_TICKS( ( TickType_t ) 150 ); - const TickType_t xMinDelay = pdMS_TO_TICKS( ( TickType_t ) 75 ); - const TickType_t xTinyDelay = pdMS_TO_TICKS( ( TickType_t ) 2 ); - - /* Generate the next delay time. This is kept within a narrow band so as - * not to disturb the timing of other tests - but does add in some pseudo - * randomisation into the tests. */ - do - { - xNextDelay = prvRand() % xMaxDelay; - - /* Just in case this loop is executed lots of times. */ - vTaskDelay( xTinyDelay ); - } while( xNextDelay < xMinDelay ); - - return xNextDelay; - } -/*-----------------------------------------------------------*/ - - static void prvSanityCheckCreatedEventGroup( EventGroupHandle_t xEventGroup ) - { - EventBits_t xEventBits; - const EventBits_t xFirstTestBits = ( EventBits_t ) 0xaa, xSecondTestBits = ( EventBits_t ) 0x55; - - /* The event group should not have any bits set yet. */ - xEventBits = xEventGroupGetBits( xEventGroup ); - - if( xEventBits != ( EventBits_t ) 0 ) - { - xErrorOccurred = __LINE__; - } - - /* Some some bits, then read them back to check they are as expected. */ - xEventGroupSetBits( xEventGroup, xFirstTestBits ); - - xEventBits = xEventGroupGetBits( xEventGroup ); - - if( xEventBits != xFirstTestBits ) - { - xErrorOccurred = __LINE__; - } - - xEventGroupSetBits( xEventGroup, xSecondTestBits ); - - xEventBits = xEventGroupGetBits( xEventGroup ); - - if( xEventBits != ( xFirstTestBits | xSecondTestBits ) ) - { - xErrorOccurred = __LINE__; - } - - /* Finally try clearing some bits too and check that operation proceeds as - * expected. */ - xEventGroupClearBits( xEventGroup, xFirstTestBits ); - - xEventBits = xEventGroupGetBits( xEventGroup ); - - if( xEventBits != xSecondTestBits ) - { - xErrorOccurred = __LINE__; - } - } -/*-----------------------------------------------------------*/ - - static void prvSanityCheckCreatedSemaphore( SemaphoreHandle_t xSemaphore, - UBaseType_t uxMaxCount ) - { - BaseType_t xReturned; - UBaseType_t x; - const TickType_t xShortBlockTime = pdMS_TO_TICKS( 10 ); - TickType_t xTickCount; - - /* The binary semaphore should start 'empty', so a call to xSemaphoreTake() - * should fail. */ - xTickCount = xTaskGetTickCount(); - xReturned = xSemaphoreTake( xSemaphore, xShortBlockTime ); - - if( ( ( TickType_t ) ( xTaskGetTickCount() - xTickCount ) ) < xShortBlockTime ) - { - /* Did not block on the semaphore as long as expected. */ - xErrorOccurred = __LINE__; - } - - if( xReturned != pdFAIL ) - { - xErrorOccurred = __LINE__; - } - - /* Should be possible to 'give' the semaphore up to a maximum of uxMaxCount - * times. */ - for( x = 0; x < uxMaxCount; x++ ) - { - xReturned = xSemaphoreGive( xSemaphore ); - - if( xReturned == pdFAIL ) - { - xErrorOccurred = __LINE__; - } - } - - /* Giving the semaphore again should fail, as it is 'full'. */ - xReturned = xSemaphoreGive( xSemaphore ); - - if( xReturned != pdFAIL ) - { - xErrorOccurred = __LINE__; - } - - configASSERT( uxSemaphoreGetCount( xSemaphore ) == uxMaxCount ); - - /* Should now be possible to 'take' the semaphore up to a maximum of - * uxMaxCount times without blocking. */ - for( x = 0; x < uxMaxCount; x++ ) - { - xReturned = xSemaphoreTake( xSemaphore, staticDONT_BLOCK ); - - if( xReturned == pdFAIL ) - { - xErrorOccurred = __LINE__; - } - } - - /* Back to the starting condition, where the semaphore should not be - * available. */ - xTickCount = xTaskGetTickCount(); - xReturned = xSemaphoreTake( xSemaphore, xShortBlockTime ); - - if( ( ( TickType_t ) ( xTaskGetTickCount() - xTickCount ) ) < xShortBlockTime ) - { - /* Did not block on the semaphore as long as expected. */ - xErrorOccurred = __LINE__; - } - - if( xReturned != pdFAIL ) - { - xErrorOccurred = __LINE__; - } - - configASSERT( uxSemaphoreGetCount( xSemaphore ) == 0 ); - } -/*-----------------------------------------------------------*/ - - static void prvSanityCheckCreatedQueue( QueueHandle_t xQueue ) - { - uint64_t ull, ullRead; - BaseType_t xReturned, xLoop; - - /* This test is done twice to ensure the queue storage area wraps. */ - for( xLoop = 0; xLoop < 2; xLoop++ ) - { - /* A very basic test that the queue can be written to and read from as - * expected. First the queue should be empty. */ - xReturned = xQueueReceive( xQueue, &ull, staticDONT_BLOCK ); - - if( xReturned != errQUEUE_EMPTY ) - { - xErrorOccurred = __LINE__; - } - - /* Now it should be possible to write to the queue staticQUEUE_LENGTH_IN_ITEMS - * times. */ - for( ull = 0; ull < staticQUEUE_LENGTH_IN_ITEMS; ull++ ) - { - xReturned = xQueueSend( xQueue, &ull, staticDONT_BLOCK ); - - if( xReturned != pdPASS ) - { - xErrorOccurred = __LINE__; - } - } - - /* Should not now be possible to write to the queue again. */ - xReturned = xQueueSend( xQueue, &ull, staticDONT_BLOCK ); - - if( xReturned != errQUEUE_FULL ) - { - xErrorOccurred = __LINE__; - } - - /* Now read back from the queue to ensure the data read back matches that - * written. */ - for( ull = 0; ull < staticQUEUE_LENGTH_IN_ITEMS; ull++ ) - { - xReturned = xQueueReceive( xQueue, &ullRead, staticDONT_BLOCK ); - - if( xReturned != pdPASS ) - { - xErrorOccurred = __LINE__; - } - - if( ullRead != ull ) - { - xErrorOccurred = __LINE__; - } - } - - /* The queue should be empty again. */ - xReturned = xQueueReceive( xQueue, &ull, staticDONT_BLOCK ); - - if( xReturned != errQUEUE_EMPTY ) - { - xErrorOccurred = __LINE__; - } - } - } -/*-----------------------------------------------------------*/ - - static void prvSanityCheckCreatedRecursiveMutex( SemaphoreHandle_t xSemaphore ) - { - const BaseType_t xLoops = 5; - BaseType_t x, xReturned; - - /* A very basic test that the recursive semaphore behaved like a recursive - * semaphore. First the semaphore should not be able to be given, as it has not - * yet been taken. */ - xReturned = xSemaphoreGiveRecursive( xSemaphore ); - - if( xReturned != pdFAIL ) - { - xErrorOccurred = __LINE__; - } - - /* Now it should be possible to take the mutex a number of times. */ - for( x = 0; x < xLoops; x++ ) - { - xReturned = xSemaphoreTakeRecursive( xSemaphore, staticDONT_BLOCK ); - - if( xReturned != pdPASS ) - { - xErrorOccurred = __LINE__; - } - } - - /* Should be possible to give the semaphore the same number of times as it - * was given in the loop above. */ - for( x = 0; x < xLoops; x++ ) - { - xReturned = xSemaphoreGiveRecursive( xSemaphore ); - - if( xReturned != pdPASS ) - { - xErrorOccurred = __LINE__; - } - } - - /* No more gives should be possible though. */ - xReturned = xSemaphoreGiveRecursive( xSemaphore ); - - if( xReturned != pdFAIL ) - { - xErrorOccurred = __LINE__; - } - } -/*-----------------------------------------------------------*/ - - BaseType_t xAreStaticAllocationTasksStillRunning( void ) - { - static UBaseType_t uxLastCycleCounter = 0; - BaseType_t xReturn; - - if( uxCycleCounter == uxLastCycleCounter ) - { - xErrorOccurred = __LINE__; - } - else - { - uxLastCycleCounter = uxCycleCounter; - } - - if( xErrorOccurred != pdFALSE ) - { - xReturn = pdFAIL; - } - else - { - xReturn = pdPASS; - } - - return xReturn; - } -/*-----------------------------------------------------------*/ - -/* Exclude the entire file if configSUPPORT_STATIC_ALLOCATION is 0. */ -#endif /* configSUPPORT_STATIC_ALLOCATION == 1 */ +/* + * 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 + * + */ + + +/* + * Demonstrates how to create FreeRTOS objects using pre-allocated memory, + * rather than the normal dynamically allocated memory, and tests objects being + * created and deleted with both statically allocated memory and dynamically + * allocated memory. + * + * See http://www.FreeRTOS.org/Static_Vs_Dynamic_Memory_Allocation.html + */ + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" +#include "event_groups.h" +#include "timers.h" + +/* Demo program include files. */ +#include "StaticAllocation.h" + +/* Exclude the entire file if configSUPPORT_STATIC_ALLOCATION is 0. */ +#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) + +/* The priority at which the task that performs the tests is created. */ + #define staticTASK_PRIORITY ( tskIDLE_PRIORITY + 2 ) + +/* The length of the queue, in items, not bytes, used in the queue static + * allocation tests. */ + #define staticQUEUE_LENGTH_IN_ITEMS ( 5 ) + +/* A block time of 0 simply means "don't block". */ + #define staticDONT_BLOCK ( ( TickType_t ) 0 ) + +/* Binary semaphores have a maximum count of 1. */ + #define staticBINARY_SEMAPHORE_MAX_COUNT ( 1 ) + +/* The size of the stack used by the task that runs the tests. */ + #define staticCREATOR_TASK_STACK_SIZE ( configMINIMAL_STACK_SIZE * 2 ) + +/* The number of times the software timer will execute before stopping itself. */ + #define staticMAX_TIMER_CALLBACK_EXECUTIONS ( 5 ) + + +/*-----------------------------------------------------------*/ + +/* + * The task that repeatedly creates and deletes statically allocated tasks, and + * other RTOS objects. + */ + static void prvStaticallyAllocatedCreator( void * pvParameters ); + +/* + * The callback function used by the software timer that is repeatedly created + * and deleted using both static and dynamically allocated memory. + */ + static void prvTimerCallback( TimerHandle_t xExpiredTimer ); + +/* + * A task that is created and deleted multiple times, using both statically and + * dynamically allocated stack and TCB. + */ + static void prvStaticallyAllocatedTask( void * pvParameters ); + +/* + * A function that demonstrates and tests the API functions that create and + * delete tasks using both statically and dynamically allocated TCBs and stacks. + */ + static void prvCreateAndDeleteStaticallyAllocatedTasks( void ); + +/* + * A function that demonstrates and tests the API functions that create and + * delete event groups using both statically and dynamically allocated RAM. + */ + static void prvCreateAndDeleteStaticallyAllocatedEventGroups( void ); + +/* + * A function that demonstrates and tests the API functions that create and + * delete queues using both statically and dynamically allocated RAM. + */ + static void prvCreateAndDeleteStaticallyAllocatedQueues( void ); + +/* + * A function that demonstrates and tests the API functions that create and + * delete binary semaphores using both statically and dynamically allocated RAM. + */ + static void prvCreateAndDeleteStaticallyAllocatedBinarySemaphores( void ); + +/* + * A function that demonstrates and tests the API functions that create and + * delete software timers using both statically and dynamically allocated RAM. + */ + static void prvCreateAndDeleteStaticallyAllocatedTimers( void ); + +/* + * A function that demonstrates and tests the API functions that create and + * delete mutexes using both statically and dynamically allocated RAM. + */ + static void prvCreateAndDeleteStaticallyAllocatedMutexes( void ); + +/* + * A function that demonstrates and tests the API functions that create and + * delete counting semaphores using both statically and dynamically allocated + * RAM. + */ + static void prvCreateAndDeleteStaticallyAllocatedCountingSemaphores( void ); + +/* + * A function that demonstrates and tests the API functions that create and + * delete recursive mutexes using both statically and dynamically allocated RAM. + */ + static void prvCreateAndDeleteStaticallyAllocatedRecursiveMutexes( void ); + +/* + * Utility function to create pseudo random numbers. + */ + static UBaseType_t prvRand( void ); + +/* + * The task that creates and deletes other tasks has to delay occasionally to + * ensure lower priority tasks are not starved of processing time. A pseudo + * random delay time is used just to add a little bit of randomisation into the + * execution pattern. prvGetNextDelayTime() generates the pseudo random delay. + */ + static TickType_t prvGetNextDelayTime( void ); + +/* + * Checks the basic operation of a queue after it has been created. + */ + static void prvSanityCheckCreatedQueue( QueueHandle_t xQueue ); + +/* + * Checks the basic operation of a recursive mutex after it has been created. + */ + static void prvSanityCheckCreatedRecursiveMutex( SemaphoreHandle_t xSemaphore ); + +/* + * Checks the basic operation of a binary semaphore after it has been created. + */ + static void prvSanityCheckCreatedSemaphore( SemaphoreHandle_t xSemaphore, + UBaseType_t uxMaxCount ); + +/* + * Checks the basic operation of an event group after it has been created. + */ + static void prvSanityCheckCreatedEventGroup( EventGroupHandle_t xEventGroup ); + +/*-----------------------------------------------------------*/ + +/* StaticTask_t is a publicly accessible structure that has the same size and + * alignment requirements as the real TCB structure. It is provided as a mechanism + * for applications to know the size of the TCB (which is dependent on the + * architecture and configuration file settings) without breaking the strict data + * hiding policy by exposing the real TCB. This StaticTask_t variable is passed + * into the xTaskCreateStatic() function that creates the + * prvStaticallyAllocatedCreator() task, and will hold the TCB of the created + * tasks. */ + static StaticTask_t xCreatorTaskTCBBuffer; + +/* This is the stack that will be used by the prvStaticallyAllocatedCreator() + * task, which is itself created using statically allocated buffers (so without any + * dynamic memory allocation). */ + static StackType_t uxCreatorTaskStackBuffer[ staticCREATOR_TASK_STACK_SIZE ]; + +/* Used by the pseudo random number generating function. */ + static uint32_t ulNextRand = 0; + +/* Used so a check task can ensure this test is still executing, and not + * stalled. */ + static volatile UBaseType_t uxCycleCounter = 0; + +/* A variable that gets set to pdTRUE if an error is detected. */ + static volatile BaseType_t xErrorOccurred = pdFALSE; + +/*-----------------------------------------------------------*/ + + void vStartStaticallyAllocatedTasks( void ) + { + /* Create a single task, which then repeatedly creates and deletes the other + * RTOS objects using both statically and dynamically allocated RAM. */ + xTaskCreateStatic( prvStaticallyAllocatedCreator, /* The function that implements the task being created. */ + "StatCreate", /* Text name for the task - not used by the RTOS, its just to assist debugging. */ + staticCREATOR_TASK_STACK_SIZE, /* Size of the buffer passed in as the stack - in words, not bytes! */ + NULL, /* Parameter passed into the task - not used in this case. */ + staticTASK_PRIORITY, /* Priority of the task. */ + &( uxCreatorTaskStackBuffer[ 0 ] ), /* The buffer to use as the task's stack. */ + &xCreatorTaskTCBBuffer ); /* The variable that will hold the task's TCB. */ + } +/*-----------------------------------------------------------*/ + + static void prvStaticallyAllocatedCreator( void * pvParameters ) + { + /* Avoid compiler warnings. */ + ( void ) pvParameters; + + for( ; ; ) + { + /* Loop, running functions that create and delete the various RTOS + * objects that can be optionally created using either static or dynamic + * memory allocation. */ + prvCreateAndDeleteStaticallyAllocatedTasks(); + prvCreateAndDeleteStaticallyAllocatedQueues(); + + /* Delay to ensure lower priority tasks get CPU time, and increment the + * cycle counter so a 'check' task can determine that this task is still + * executing. */ + vTaskDelay( prvGetNextDelayTime() ); + uxCycleCounter++; + + prvCreateAndDeleteStaticallyAllocatedBinarySemaphores(); + prvCreateAndDeleteStaticallyAllocatedCountingSemaphores(); + + vTaskDelay( prvGetNextDelayTime() ); + uxCycleCounter++; + + prvCreateAndDeleteStaticallyAllocatedMutexes(); + prvCreateAndDeleteStaticallyAllocatedRecursiveMutexes(); + + vTaskDelay( prvGetNextDelayTime() ); + uxCycleCounter++; + + prvCreateAndDeleteStaticallyAllocatedEventGroups(); + prvCreateAndDeleteStaticallyAllocatedTimers(); + } + } +/*-----------------------------------------------------------*/ + + static void prvCreateAndDeleteStaticallyAllocatedCountingSemaphores( void ) + { + SemaphoreHandle_t xSemaphore; + const UBaseType_t uxMaxCount = ( UBaseType_t ) 10; + +/* StaticSemaphore_t is a publicly accessible structure that has the same size + * and alignment requirements as the real semaphore structure. It is provided as a + * mechanism for applications to know the size of the semaphore (which is dependent + * on the architecture and configuration file settings) without breaking the strict + * data hiding policy by exposing the real semaphore internals. This + * StaticSemaphore_t variable is passed into the xSemaphoreCreateCountingStatic() + * function calls within this function. NOTE: In most usage scenarios now it is + * faster and more memory efficient to use a direct to task notification instead of + * a counting semaphore. http://www.freertos.org/RTOS-task-notifications.html */ + StaticSemaphore_t xSemaphoreBuffer; + + /* Create the semaphore. xSemaphoreCreateCountingStatic() has one more + * parameter than the usual xSemaphoreCreateCounting() function. The parameter + * is a pointer to the pre-allocated StaticSemaphore_t structure, which will + * hold information on the semaphore in an anonymous way. If the pointer is + * passed as NULL then the structure will be allocated dynamically, just as + * when xSemaphoreCreateCounting() is called. */ + xSemaphore = xSemaphoreCreateCountingStatic( uxMaxCount, 0, &xSemaphoreBuffer ); + + /* The semaphore handle should equal the static semaphore structure passed + * into the xSemaphoreCreateBinaryStatic() function. */ + configASSERT( xSemaphore == ( SemaphoreHandle_t ) &xSemaphoreBuffer ); + + /* Ensure the semaphore passes a few sanity checks as a valid semaphore. */ + prvSanityCheckCreatedSemaphore( xSemaphore, uxMaxCount ); + + /* Delete the semaphore again so the buffers can be reused. */ + vSemaphoreDelete( xSemaphore ); + + #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + /* Now do the same but using dynamically allocated buffers to ensure the + * delete functions are working correctly in both the static and dynamic + * allocation cases. */ + xSemaphore = xSemaphoreCreateCounting( uxMaxCount, 0 ); + configASSERT( xSemaphore != NULL ); + prvSanityCheckCreatedSemaphore( xSemaphore, uxMaxCount ); + vSemaphoreDelete( xSemaphore ); + } + #endif + } +/*-----------------------------------------------------------*/ + + static void prvCreateAndDeleteStaticallyAllocatedRecursiveMutexes( void ) + { + SemaphoreHandle_t xSemaphore; + +/* StaticSemaphore_t is a publicly accessible structure that has the same size + * and alignment requirements as the real semaphore structure. It is provided as a + * mechanism for applications to know the size of the semaphore (which is dependent + * on the architecture and configuration file settings) without breaking the strict + * data hiding policy by exposing the real semaphore internals. This + * StaticSemaphore_t variable is passed into the + * xSemaphoreCreateRecursiveMutexStatic() function calls within this function. */ + StaticSemaphore_t xSemaphoreBuffer; + + /* Create the semaphore. xSemaphoreCreateRecursiveMutexStatic() has one + * more parameter than the usual xSemaphoreCreateRecursiveMutex() function. + * The parameter is a pointer to the pre-allocated StaticSemaphore_t structure, + * which will hold information on the semaphore in an anonymous way. If the + * pointer is passed as NULL then the structure will be allocated dynamically, + * just as when xSemaphoreCreateRecursiveMutex() is called. */ + xSemaphore = xSemaphoreCreateRecursiveMutexStatic( &xSemaphoreBuffer ); + + /* The semaphore handle should equal the static semaphore structure passed + * into the xSemaphoreCreateBinaryStatic() function. */ + configASSERT( xSemaphore == ( SemaphoreHandle_t ) &xSemaphoreBuffer ); + + /* Ensure the semaphore passes a few sanity checks as a valid + * recursive semaphore. */ + prvSanityCheckCreatedRecursiveMutex( xSemaphore ); + + /* Delete the semaphore again so the buffers can be reused. */ + vSemaphoreDelete( xSemaphore ); + + /* Now do the same using dynamically allocated buffers to ensure the delete + * functions are working correctly in both the static and dynamic memory + * allocation cases. */ + #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + xSemaphore = xSemaphoreCreateRecursiveMutex(); + configASSERT( xSemaphore != NULL ); + prvSanityCheckCreatedRecursiveMutex( xSemaphore ); + vSemaphoreDelete( xSemaphore ); + } + #endif + } +/*-----------------------------------------------------------*/ + + static void prvCreateAndDeleteStaticallyAllocatedQueues( void ) + { + QueueHandle_t xQueue; + +/* StaticQueue_t is a publicly accessible structure that has the same size and + * alignment requirements as the real queue structure. It is provided as a + * mechanism for applications to know the size of the queue (which is dependent on + * the architecture and configuration file settings) without breaking the strict + * data hiding policy by exposing the real queue internals. This StaticQueue_t + * variable is passed into the xQueueCreateStatic() function calls within this + * function. */ + static StaticQueue_t xStaticQueue; + +/* The queue storage area must be large enough to hold the maximum number of + * items it is possible for the queue to hold at any one time, which equals the + * queue length (in items, not bytes) multiplied by the size of each item. In this + * case the queue will hold staticQUEUE_LENGTH_IN_ITEMS 64-bit items. See + * http://www.freertos.org/Embedded-RTOS-Queues.html */ + static uint8_t ucQueueStorageArea[ staticQUEUE_LENGTH_IN_ITEMS * sizeof( uint64_t ) ]; + + /* Create the queue. xQueueCreateStatic() has two more parameters than the + * usual xQueueCreate() function. The first new parameter is a pointer to the + * pre-allocated queue storage area. The second new parameter is a pointer to + * the StaticQueue_t structure that will hold the queue state information in + * an anonymous way. If the two pointers are passed as NULL then the data + * will be allocated dynamically as if xQueueCreate() had been called. */ + xQueue = xQueueCreateStatic( staticQUEUE_LENGTH_IN_ITEMS, /* The maximum number of items the queue can hold. */ + sizeof( uint64_t ), /* The size of each item. */ + ucQueueStorageArea, /* The buffer used to hold items within the queue. */ + &xStaticQueue ); /* The static queue structure that will hold the state of the queue. */ + + /* The queue handle should equal the static queue structure passed into the + * xQueueCreateStatic() function. */ + configASSERT( xQueue == ( QueueHandle_t ) &xStaticQueue ); + + /* Ensure the queue passes a few sanity checks as a valid queue. */ + prvSanityCheckCreatedQueue( xQueue ); + + /* Delete the queue again so the buffers can be reused. */ + vQueueDelete( xQueue ); + + /* Now do the same using a dynamically allocated queue to ensure the delete + * function is working correctly in both the static and dynamic memory + * allocation cases. */ + #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + xQueue = xQueueCreate( staticQUEUE_LENGTH_IN_ITEMS, /* The maximum number of items the queue can hold. */ + sizeof( uint64_t ) ); /* The size of each item. */ + + /* The queue handle should equal the static queue structure passed into the + * xQueueCreateStatic() function. */ + configASSERT( xQueue != NULL ); + + /* Ensure the queue passes a few sanity checks as a valid queue. */ + prvSanityCheckCreatedQueue( xQueue ); + + /* Delete the queue again so the buffers can be reused. */ + vQueueDelete( xQueue ); + } + #endif /* if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) */ + } +/*-----------------------------------------------------------*/ + + static void prvCreateAndDeleteStaticallyAllocatedMutexes( void ) + { + SemaphoreHandle_t xSemaphore; + BaseType_t xReturned; + +/* StaticSemaphore_t is a publicly accessible structure that has the same size + * and alignment requirements as the real semaphore structure. It is provided as a + * mechanism for applications to know the size of the semaphore (which is dependent + * on the architecture and configuration file settings) without breaking the strict + * data hiding policy by exposing the real semaphore internals. This + * StaticSemaphore_t variable is passed into the xSemaphoreCreateMutexStatic() + * function calls within this function. */ + StaticSemaphore_t xSemaphoreBuffer; + + /* Create the semaphore. xSemaphoreCreateMutexStatic() has one more + * parameter than the usual xSemaphoreCreateMutex() function. The parameter + * is a pointer to the pre-allocated StaticSemaphore_t structure, which will + * hold information on the semaphore in an anonymous way. If the pointer is + * passed as NULL then the structure will be allocated dynamically, just as + * when xSemaphoreCreateMutex() is called. */ + xSemaphore = xSemaphoreCreateMutexStatic( &xSemaphoreBuffer ); + + /* The semaphore handle should equal the static semaphore structure passed + * into the xSemaphoreCreateMutexStatic() function. */ + configASSERT( xSemaphore == ( SemaphoreHandle_t ) &xSemaphoreBuffer ); + + /* Take the mutex so the mutex is in the state expected by the + * prvSanityCheckCreatedSemaphore() function. */ + xReturned = xSemaphoreTake( xSemaphore, staticDONT_BLOCK ); + + if( xReturned != pdPASS ) + { + xErrorOccurred = __LINE__; + } + + /* Ensure the semaphore passes a few sanity checks as a valid semaphore. */ + prvSanityCheckCreatedSemaphore( xSemaphore, staticBINARY_SEMAPHORE_MAX_COUNT ); + + /* Delete the semaphore again so the buffers can be reused. */ + vSemaphoreDelete( xSemaphore ); + + /* Now do the same using a dynamically allocated mutex to ensure the delete + * function is working correctly in both the static and dynamic allocation + * cases. */ + #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + xSemaphore = xSemaphoreCreateMutex(); + + /* The semaphore handle should equal the static semaphore structure + * passed into the xSemaphoreCreateMutexStatic() function. */ + configASSERT( xSemaphore != NULL ); + + /* Take the mutex so the mutex is in the state expected by the + * prvSanityCheckCreatedSemaphore() function. */ + xReturned = xSemaphoreTake( xSemaphore, staticDONT_BLOCK ); + + if( xReturned != pdPASS ) + { + xErrorOccurred = __LINE__; + } + + /* Ensure the semaphore passes a few sanity checks as a valid semaphore. */ + prvSanityCheckCreatedSemaphore( xSemaphore, staticBINARY_SEMAPHORE_MAX_COUNT ); + + /* Delete the semaphore again so the buffers can be reused. */ + vSemaphoreDelete( xSemaphore ); + } + #endif /* if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) */ + } +/*-----------------------------------------------------------*/ + + static void prvCreateAndDeleteStaticallyAllocatedBinarySemaphores( void ) + { + SemaphoreHandle_t xSemaphore; + +/* StaticSemaphore_t is a publicly accessible structure that has the same size +* and alignment requirements as the real semaphore structure. It is provided as a +* mechanism for applications to know the size of the semaphore (which is dependent +* on the architecture and configuration file settings) without breaking the strict +* data hiding policy by exposing the real semaphore internals. This +* StaticSemaphore_t variable is passed into the xSemaphoreCreateBinaryStatic() +* function calls within this function. NOTE: In most usage scenarios now it is +* faster and more memory efficient to use a direct to task notification instead of +* a binary semaphore. http://www.freertos.org/RTOS-task-notifications.html */ + StaticSemaphore_t xSemaphoreBuffer; + + /* Create the semaphore. xSemaphoreCreateBinaryStatic() has one more + * parameter than the usual xSemaphoreCreateBinary() function. The parameter + * is a pointer to the pre-allocated StaticSemaphore_t structure, which will + * hold information on the semaphore in an anonymous way. If the pointer is + * passed as NULL then the structure will be allocated dynamically, just as + * when xSemaphoreCreateBinary() is called. */ + xSemaphore = xSemaphoreCreateBinaryStatic( &xSemaphoreBuffer ); + + /* The semaphore handle should equal the static semaphore structure passed + * into the xSemaphoreCreateBinaryStatic() function. */ + configASSERT( xSemaphore == ( SemaphoreHandle_t ) &xSemaphoreBuffer ); + + /* Ensure the semaphore passes a few sanity checks as a valid semaphore. */ + prvSanityCheckCreatedSemaphore( xSemaphore, staticBINARY_SEMAPHORE_MAX_COUNT ); + + /* Delete the semaphore again so the buffers can be reused. */ + vSemaphoreDelete( xSemaphore ); + + /* Now do the same using a dynamically allocated semaphore to check the + * delete function is working correctly in both the static and dynamic + * allocation cases. */ + #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + xSemaphore = xSemaphoreCreateBinary(); + configASSERT( xSemaphore != NULL ); + prvSanityCheckCreatedSemaphore( xSemaphore, staticBINARY_SEMAPHORE_MAX_COUNT ); + vSemaphoreDelete( xSemaphore ); + } + #endif + + /* There isn't a static version of the old and deprecated + * vSemaphoreCreateBinary() macro (because its deprecated!), but check it is + * still functioning correctly. */ + #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + vSemaphoreCreateBinary( xSemaphore ); + + /* The macro starts with the binary semaphore available, but the test + * function expects it to be unavailable. */ + if( xSemaphoreTake( xSemaphore, staticDONT_BLOCK ) == pdFAIL ) + { + xErrorOccurred = __LINE__; + } + + prvSanityCheckCreatedSemaphore( xSemaphore, staticBINARY_SEMAPHORE_MAX_COUNT ); + vSemaphoreDelete( xSemaphore ); + } + #endif /* if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) */ + } +/*-----------------------------------------------------------*/ + + static void prvTimerCallback( TimerHandle_t xExpiredTimer ) + { + UBaseType_t * puxVariableToIncrement; + BaseType_t xReturned; + + /* The timer callback just demonstrates it is executing by incrementing a + * variable - the address of which is passed into the timer as its ID. Obtain + * the address of the variable to increment. */ + puxVariableToIncrement = ( UBaseType_t * ) pvTimerGetTimerID( xExpiredTimer ); + + /* Increment the variable to show the timer callback has executed. */ + ( *puxVariableToIncrement )++; + + /* If this callback has executed the required number of times, stop the + * timer. */ + if( *puxVariableToIncrement == staticMAX_TIMER_CALLBACK_EXECUTIONS ) + { + /* This is called from a timer callback so must not block. See + * http://www.FreeRTOS.org/FreeRTOS-timers-xTimerStop.html */ + xReturned = xTimerStop( xExpiredTimer, staticDONT_BLOCK ); + + if( xReturned != pdPASS ) + { + xErrorOccurred = __LINE__; + } + } + } +/*-----------------------------------------------------------*/ + + static void prvCreateAndDeleteStaticallyAllocatedTimers( void ) + { + TimerHandle_t xTimer; + UBaseType_t uxVariableToIncrement; + const TickType_t xTimerPeriod = pdMS_TO_TICKS( 20 ); + BaseType_t xReturned; + +/* StaticTimer_t is a publicly accessible structure that has the same size + * and alignment requirements as the real timer structure. It is provided as a + * mechanism for applications to know the size of the timer structure (which is + * dependent on the architecture and configuration file settings) without breaking + * the strict data hiding policy by exposing the real timer internals. This + * StaticTimer_t variable is passed into the xTimerCreateStatic() function calls + * within this function. */ + StaticTimer_t xTimerBuffer; + + /* Create the software time. xTimerCreateStatic() has an extra parameter + * than the normal xTimerCreate() API function. The parameter is a pointer to + * the StaticTimer_t structure that will hold the software timer structure. If + * the parameter is passed as NULL then the structure will be allocated + * dynamically, just as if xTimerCreate() had been called. */ + xTimer = xTimerCreateStatic( "T1", /* Text name for the task. Helps debugging only. Not used by FreeRTOS. */ + xTimerPeriod, /* The period of the timer in ticks. */ + pdTRUE, /* This is an auto-reload timer. */ + ( void * ) &uxVariableToIncrement, /* The variable incremented by the test is passed into the timer callback using the timer ID. */ + prvTimerCallback, /* The function to execute when the timer expires. */ + &xTimerBuffer ); /* The buffer that will hold the software timer structure. */ + + /* The timer handle should equal the static timer structure passed into the + * xTimerCreateStatic() function. */ + configASSERT( xTimer == ( TimerHandle_t ) &xTimerBuffer ); + + /* Set the variable to 0, wait for a few timer periods to expire, then check + * the timer callback has incremented the variable to the expected value. */ + uxVariableToIncrement = 0; + + /* This is a low priority so a block time should not be needed. */ + xReturned = xTimerStart( xTimer, staticDONT_BLOCK ); + + if( xReturned != pdPASS ) + { + xErrorOccurred = __LINE__; + } + + vTaskDelay( xTimerPeriod * staticMAX_TIMER_CALLBACK_EXECUTIONS ); + + /* By now the timer should have expired staticMAX_TIMER_CALLBACK_EXECUTIONS + * times, and then stopped itself. */ + if( uxVariableToIncrement != staticMAX_TIMER_CALLBACK_EXECUTIONS ) + { + xErrorOccurred = __LINE__; + } + + /* Finished with the timer, delete it. */ + xReturned = xTimerDelete( xTimer, staticDONT_BLOCK ); + + /* Again, as this is a low priority task it is expected that the timer + * command will have been sent even without a block time being used. */ + if( xReturned != pdPASS ) + { + xErrorOccurred = __LINE__; + } + + /* Just to show the check task that this task is still executing. */ + uxCycleCounter++; + + /* Now do the same using a dynamically allocated software timer to ensure + * the delete function is working correctly in both the static and dynamic + * allocation cases. */ + #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + xTimer = xTimerCreate( "T1", /* Text name for the task. Helps debugging only. Not used by FreeRTOS. */ + xTimerPeriod, /* The period of the timer in ticks. */ + pdTRUE, /* This is an auto-reload timer. */ + ( void * ) &uxVariableToIncrement, /* The variable incremented by the test is passed into the timer callback using the timer ID. */ + prvTimerCallback ); /* The function to execute when the timer expires. */ + + configASSERT( xTimer != NULL ); + + uxVariableToIncrement = 0; + xReturned = xTimerStart( xTimer, staticDONT_BLOCK ); + + if( xReturned != pdPASS ) + { + xErrorOccurred = __LINE__; + } + + vTaskDelay( xTimerPeriod * staticMAX_TIMER_CALLBACK_EXECUTIONS ); + + if( uxVariableToIncrement != staticMAX_TIMER_CALLBACK_EXECUTIONS ) + { + xErrorOccurred = __LINE__; + } + + xReturned = xTimerDelete( xTimer, staticDONT_BLOCK ); + + if( xReturned != pdPASS ) + { + xErrorOccurred = __LINE__; + } + } + #endif /* if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) */ + } +/*-----------------------------------------------------------*/ + + static void prvCreateAndDeleteStaticallyAllocatedEventGroups( void ) + { + EventGroupHandle_t xEventGroup; + +/* StaticEventGroup_t is a publicly accessible structure that has the same size + * and alignment requirements as the real event group structure. It is provided as + * a mechanism for applications to know the size of the event group (which is + * dependent on the architecture and configuration file settings) without breaking + * the strict data hiding policy by exposing the real event group internals. This + * StaticEventGroup_t variable is passed into the xSemaphoreCreateEventGroupStatic() + * function calls within this function. */ + StaticEventGroup_t xEventGroupBuffer; + + /* Create the event group. xEventGroupCreateStatic() has an extra parameter + * than the normal xEventGroupCreate() API function. The parameter is a + * pointer to the StaticEventGroup_t structure that will hold the event group + * structure. */ + xEventGroup = xEventGroupCreateStatic( &xEventGroupBuffer ); + + /* The event group handle should equal the static event group structure + * passed into the xEventGroupCreateStatic() function. */ + configASSERT( xEventGroup == ( EventGroupHandle_t ) &xEventGroupBuffer ); + + /* Ensure the event group passes a few sanity checks as a valid event + * group. */ + prvSanityCheckCreatedEventGroup( xEventGroup ); + + /* Delete the event group again so the buffers can be reused. */ + vEventGroupDelete( xEventGroup ); + + /* Now do the same using a dynamically allocated event group to ensure the + * delete function is working correctly in both the static and dynamic + * allocation cases. */ + #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + xEventGroup = xEventGroupCreate(); + configASSERT( xEventGroup != NULL ); + prvSanityCheckCreatedEventGroup( xEventGroup ); + vEventGroupDelete( xEventGroup ); + } + #endif + } +/*-----------------------------------------------------------*/ + + static void prvCreateAndDeleteStaticallyAllocatedTasks( void ) + { + TaskHandle_t xCreatedTask; + +/* The variable that will hold the TCB of tasks created by this function. See + * the comments above the declaration of the xCreatorTaskTCBBuffer variable for + * more information. NOTE: This is not static so relies on the tasks that use it + * being deleted before this function returns and deallocates its stack. That will + * only be the case if configUSE_PREEMPTION is set to 1. */ + StaticTask_t xTCBBuffer; + +/* This buffer that will be used as the stack of tasks created by this function. + * See the comments above the declaration of the uxCreatorTaskStackBuffer[] array + * above for more information. */ + static StackType_t uxStackBuffer[ configMINIMAL_STACK_SIZE ]; + + /* Create the task. xTaskCreateStatic() has two more parameters than + * the usual xTaskCreate() function. The first new parameter is a pointer to + * the pre-allocated stack. The second new parameter is a pointer to the + * StaticTask_t structure that will hold the task's TCB. If both pointers are + * passed as NULL then the respective object will be allocated dynamically as + * if xTaskCreate() had been called. */ + xCreatedTask = xTaskCreateStatic( + prvStaticallyAllocatedTask, /* Function that implements the task. */ + "Static", /* Human readable name for the task. */ + configMINIMAL_STACK_SIZE, /* Task's stack size, in words (not bytes!). */ + NULL, /* Parameter to pass into the task. */ + uxTaskPriorityGet( NULL ) + 1, /* The priority of the task. */ + &( uxStackBuffer[ 0 ] ), /* The buffer to use as the task's stack. */ + &xTCBBuffer ); /* The variable that will hold that task's TCB. */ + + /* Check the task was created correctly, then delete the task. */ + if( xCreatedTask == NULL ) + { + xErrorOccurred = __LINE__; + } + else if( eTaskGetState( xCreatedTask ) != eSuspended ) + { + /* The created task had a higher priority so should have executed and + * suspended itself by now. */ + xErrorOccurred = __LINE__; + } + else + { + vTaskDelete( xCreatedTask ); + } + + /* Now do the same using a dynamically allocated task to ensure the delete + * function is working correctly in both the static and dynamic allocation + * cases. */ + #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + BaseType_t xReturned; + + xReturned = xTaskCreate( + prvStaticallyAllocatedTask, /* Function that implements the task - the same function is used but is actually dynamically allocated this time. */ + "Static", /* Human readable name for the task. */ + configMINIMAL_STACK_SIZE, /* Task's stack size, in words (not bytes!). */ + NULL, /* Parameter to pass into the task. */ + uxTaskPriorityGet( NULL ) + 1, /* The priority of the task. */ + &xCreatedTask ); /* Handle of the task being created. */ + + if( eTaskGetState( xCreatedTask ) != eSuspended ) + { + xErrorOccurred = __LINE__; + } + + configASSERT( xReturned == pdPASS ); + + if( xReturned != pdPASS ) + { + xErrorOccurred = __LINE__; + } + + vTaskDelete( xCreatedTask ); + } + #endif /* if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) */ + } +/*-----------------------------------------------------------*/ + + static void prvStaticallyAllocatedTask( void * pvParameters ) + { + ( void ) pvParameters; + + /* The created task just suspends itself to wait to get deleted. The task + * that creates this task checks this task is in the expected Suspended state + * before deleting it. */ + vTaskSuspend( NULL ); + } +/*-----------------------------------------------------------*/ + + static UBaseType_t prvRand( void ) + { + const uint32_t ulMultiplier = 0x015a4e35UL, ulIncrement = 1UL; + + /* Utility function to generate a pseudo random number. */ + ulNextRand = ( ulMultiplier * ulNextRand ) + ulIncrement; + return( ( ulNextRand >> 16UL ) & 0x7fffUL ); + } +/*-----------------------------------------------------------*/ + + static TickType_t prvGetNextDelayTime( void ) + { + TickType_t xNextDelay; + const TickType_t xMaxDelay = pdMS_TO_TICKS( ( TickType_t ) 150 ); + const TickType_t xMinDelay = pdMS_TO_TICKS( ( TickType_t ) 75 ); + const TickType_t xTinyDelay = pdMS_TO_TICKS( ( TickType_t ) 2 ); + + /* Generate the next delay time. This is kept within a narrow band so as + * not to disturb the timing of other tests - but does add in some pseudo + * randomisation into the tests. */ + do + { + xNextDelay = prvRand() % xMaxDelay; + + /* Just in case this loop is executed lots of times. */ + vTaskDelay( xTinyDelay ); + } while( xNextDelay < xMinDelay ); + + return xNextDelay; + } +/*-----------------------------------------------------------*/ + + static void prvSanityCheckCreatedEventGroup( EventGroupHandle_t xEventGroup ) + { + EventBits_t xEventBits; + const EventBits_t xFirstTestBits = ( EventBits_t ) 0xaa, xSecondTestBits = ( EventBits_t ) 0x55; + + /* The event group should not have any bits set yet. */ + xEventBits = xEventGroupGetBits( xEventGroup ); + + if( xEventBits != ( EventBits_t ) 0 ) + { + xErrorOccurred = __LINE__; + } + + /* Some some bits, then read them back to check they are as expected. */ + xEventGroupSetBits( xEventGroup, xFirstTestBits ); + + xEventBits = xEventGroupGetBits( xEventGroup ); + + if( xEventBits != xFirstTestBits ) + { + xErrorOccurred = __LINE__; + } + + xEventGroupSetBits( xEventGroup, xSecondTestBits ); + + xEventBits = xEventGroupGetBits( xEventGroup ); + + if( xEventBits != ( xFirstTestBits | xSecondTestBits ) ) + { + xErrorOccurred = __LINE__; + } + + /* Finally try clearing some bits too and check that operation proceeds as + * expected. */ + xEventGroupClearBits( xEventGroup, xFirstTestBits ); + + xEventBits = xEventGroupGetBits( xEventGroup ); + + if( xEventBits != xSecondTestBits ) + { + xErrorOccurred = __LINE__; + } + } +/*-----------------------------------------------------------*/ + + static void prvSanityCheckCreatedSemaphore( SemaphoreHandle_t xSemaphore, + UBaseType_t uxMaxCount ) + { + BaseType_t xReturned; + UBaseType_t x; + const TickType_t xShortBlockTime = pdMS_TO_TICKS( 10 ); + TickType_t xTickCount; + + /* The binary semaphore should start 'empty', so a call to xSemaphoreTake() + * should fail. */ + xTickCount = xTaskGetTickCount(); + xReturned = xSemaphoreTake( xSemaphore, xShortBlockTime ); + + if( ( ( TickType_t ) ( xTaskGetTickCount() - xTickCount ) ) < xShortBlockTime ) + { + /* Did not block on the semaphore as long as expected. */ + xErrorOccurred = __LINE__; + } + + if( xReturned != pdFAIL ) + { + xErrorOccurred = __LINE__; + } + + /* Should be possible to 'give' the semaphore up to a maximum of uxMaxCount + * times. */ + for( x = 0; x < uxMaxCount; x++ ) + { + xReturned = xSemaphoreGive( xSemaphore ); + + if( xReturned == pdFAIL ) + { + xErrorOccurred = __LINE__; + } + } + + /* Giving the semaphore again should fail, as it is 'full'. */ + xReturned = xSemaphoreGive( xSemaphore ); + + if( xReturned != pdFAIL ) + { + xErrorOccurred = __LINE__; + } + + configASSERT( uxSemaphoreGetCount( xSemaphore ) == uxMaxCount ); + + /* Should now be possible to 'take' the semaphore up to a maximum of + * uxMaxCount times without blocking. */ + for( x = 0; x < uxMaxCount; x++ ) + { + xReturned = xSemaphoreTake( xSemaphore, staticDONT_BLOCK ); + + if( xReturned == pdFAIL ) + { + xErrorOccurred = __LINE__; + } + } + + /* Back to the starting condition, where the semaphore should not be + * available. */ + xTickCount = xTaskGetTickCount(); + xReturned = xSemaphoreTake( xSemaphore, xShortBlockTime ); + + if( ( ( TickType_t ) ( xTaskGetTickCount() - xTickCount ) ) < xShortBlockTime ) + { + /* Did not block on the semaphore as long as expected. */ + xErrorOccurred = __LINE__; + } + + if( xReturned != pdFAIL ) + { + xErrorOccurred = __LINE__; + } + + configASSERT( uxSemaphoreGetCount( xSemaphore ) == 0 ); + } +/*-----------------------------------------------------------*/ + + static void prvSanityCheckCreatedQueue( QueueHandle_t xQueue ) + { + uint64_t ull, ullRead; + BaseType_t xReturned, xLoop; + + /* This test is done twice to ensure the queue storage area wraps. */ + for( xLoop = 0; xLoop < 2; xLoop++ ) + { + /* A very basic test that the queue can be written to and read from as + * expected. First the queue should be empty. */ + xReturned = xQueueReceive( xQueue, &ull, staticDONT_BLOCK ); + + if( xReturned != errQUEUE_EMPTY ) + { + xErrorOccurred = __LINE__; + } + + /* Now it should be possible to write to the queue staticQUEUE_LENGTH_IN_ITEMS + * times. */ + for( ull = 0; ull < staticQUEUE_LENGTH_IN_ITEMS; ull++ ) + { + xReturned = xQueueSend( xQueue, &ull, staticDONT_BLOCK ); + + if( xReturned != pdPASS ) + { + xErrorOccurred = __LINE__; + } + } + + /* Should not now be possible to write to the queue again. */ + xReturned = xQueueSend( xQueue, &ull, staticDONT_BLOCK ); + + if( xReturned != errQUEUE_FULL ) + { + xErrorOccurred = __LINE__; + } + + /* Now read back from the queue to ensure the data read back matches that + * written. */ + for( ull = 0; ull < staticQUEUE_LENGTH_IN_ITEMS; ull++ ) + { + xReturned = xQueueReceive( xQueue, &ullRead, staticDONT_BLOCK ); + + if( xReturned != pdPASS ) + { + xErrorOccurred = __LINE__; + } + + if( ullRead != ull ) + { + xErrorOccurred = __LINE__; + } + } + + /* The queue should be empty again. */ + xReturned = xQueueReceive( xQueue, &ull, staticDONT_BLOCK ); + + if( xReturned != errQUEUE_EMPTY ) + { + xErrorOccurred = __LINE__; + } + } + } +/*-----------------------------------------------------------*/ + + static void prvSanityCheckCreatedRecursiveMutex( SemaphoreHandle_t xSemaphore ) + { + const BaseType_t xLoops = 5; + BaseType_t x, xReturned; + + /* A very basic test that the recursive semaphore behaved like a recursive + * semaphore. First the semaphore should not be able to be given, as it has not + * yet been taken. */ + xReturned = xSemaphoreGiveRecursive( xSemaphore ); + + if( xReturned != pdFAIL ) + { + xErrorOccurred = __LINE__; + } + + /* Now it should be possible to take the mutex a number of times. */ + for( x = 0; x < xLoops; x++ ) + { + xReturned = xSemaphoreTakeRecursive( xSemaphore, staticDONT_BLOCK ); + + if( xReturned != pdPASS ) + { + xErrorOccurred = __LINE__; + } + } + + /* Should be possible to give the semaphore the same number of times as it + * was given in the loop above. */ + for( x = 0; x < xLoops; x++ ) + { + xReturned = xSemaphoreGiveRecursive( xSemaphore ); + + if( xReturned != pdPASS ) + { + xErrorOccurred = __LINE__; + } + } + + /* No more gives should be possible though. */ + xReturned = xSemaphoreGiveRecursive( xSemaphore ); + + if( xReturned != pdFAIL ) + { + xErrorOccurred = __LINE__; + } + } +/*-----------------------------------------------------------*/ + + BaseType_t xAreStaticAllocationTasksStillRunning( void ) + { + static UBaseType_t uxLastCycleCounter = 0; + BaseType_t xReturn; + + if( uxCycleCounter == uxLastCycleCounter ) + { + xErrorOccurred = __LINE__; + } + else + { + uxLastCycleCounter = uxCycleCounter; + } + + if( xErrorOccurred != pdFALSE ) + { + xReturn = pdFAIL; + } + else + { + xReturn = pdPASS; + } + + return xReturn; + } +/*-----------------------------------------------------------*/ + +/* Exclude the entire file if configSUPPORT_STATIC_ALLOCATION is 0. */ +#endif /* configSUPPORT_STATIC_ALLOCATION == 1 */ diff --git a/FreeRTOS/Demo/Common/Minimal/blocktim.c b/FreeRTOS/Demo/Common/Minimal/blocktim.c index 9d258b6129a..e7a0385a510 100644 --- a/FreeRTOS/Demo/Common/Minimal/blocktim.c +++ b/FreeRTOS/Demo/Common/Minimal/blocktim.c @@ -1,597 +1,597 @@ -/* - * 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 - * - */ - -/* - * This file contains some test scenarios that ensure tasks do not exit queue - * send or receive functions prematurely. A description of the tests is - * included within the code. - */ - -/* Kernel includes. */ -#include "FreeRTOS.h" -#include "task.h" -#include "queue.h" - -/* Demo includes. */ -#include "blocktim.h" - -/* Task priorities and stack sizes. Allow these to be overridden. */ -#ifndef bktPRIMARY_PRIORITY - #define bktPRIMARY_PRIORITY ( configMAX_PRIORITIES - 3 ) -#endif - -#ifndef bktSECONDARY_PRIORITY - #define bktSECONDARY_PRIORITY ( configMAX_PRIORITIES - 4 ) -#endif - -#ifndef bktBLOCK_TIME_TASK_STACK_SIZE - #define bktBLOCK_TIME_TASK_STACK_SIZE configMINIMAL_STACK_SIZE -#endif - -/* Task behaviour. */ -#define bktQUEUE_LENGTH ( 5 ) -#define bktSHORT_WAIT pdMS_TO_TICKS( ( TickType_t ) 20 ) -#define bktPRIMARY_BLOCK_TIME ( 10 ) -#define bktALLOWABLE_MARGIN ( 15 ) -#define bktTIME_TO_BLOCK ( 175 ) -#define bktDONT_BLOCK ( ( TickType_t ) 0 ) -#define bktRUN_INDICATOR ( ( UBaseType_t ) 0x55 ) - -/* In case the demo does not have software timers enabled, as this file uses - * the configTIMER_TASK_PRIORITY setting. */ -#ifndef configTIMER_TASK_PRIORITY - #define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) -#endif - -/*-----------------------------------------------------------*/ - -/* - * The two test tasks. Their behaviour is commented within the functions. - */ -static void vPrimaryBlockTimeTestTask( void * pvParameters ); -static void vSecondaryBlockTimeTestTask( void * pvParameters ); - -/* - * Very basic tests to verify the block times are as expected. - */ -static void prvBasicDelayTests( void ); - -/*-----------------------------------------------------------*/ - -/* The queue on which the tasks block. */ -static QueueHandle_t xTestQueue; - -/* Handle to the secondary task is required by the primary task for calls - * to vTaskSuspend/Resume(). */ -static TaskHandle_t xSecondary; - -/* Used to ensure that tasks are still executing without error. */ -static volatile BaseType_t xPrimaryCycles = 0, xSecondaryCycles = 0; -static volatile BaseType_t xErrorOccurred = pdFALSE; - -/* Provides a simple mechanism for the primary task to know when the - * secondary task has executed. */ -static volatile UBaseType_t xRunIndicator; - -/*-----------------------------------------------------------*/ - -void vCreateBlockTimeTasks( void ) -{ - /* Create the queue on which the two tasks block. */ - xTestQueue = xQueueCreate( bktQUEUE_LENGTH, sizeof( BaseType_t ) ); - - if( xTestQueue != 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( xTestQueue, "Block_Time_Queue" ); - - /* Create the two test tasks. */ - xTaskCreate( vPrimaryBlockTimeTestTask, "BTest1", bktBLOCK_TIME_TASK_STACK_SIZE, NULL, bktPRIMARY_PRIORITY, NULL ); - xTaskCreate( vSecondaryBlockTimeTestTask, "BTest2", bktBLOCK_TIME_TASK_STACK_SIZE, NULL, bktSECONDARY_PRIORITY, &xSecondary ); - } -} -/*-----------------------------------------------------------*/ - -static void vPrimaryBlockTimeTestTask( void * pvParameters ) -{ - BaseType_t xItem, xData; - TickType_t xTimeWhenBlocking; - TickType_t xTimeToBlock, xBlockedTime; - - ( void ) pvParameters; - - for( ; ; ) - { - /********************************************************************* - * Test 0 - * - * Basic vTaskDelay() and vTaskDelayUntil() tests. */ - prvBasicDelayTests(); - - /********************************************************************* - * Test 1 - * - * Simple block time wakeup test on queue receives. */ - for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) - { - /* The queue is empty. Attempt to read from the queue using a block - * time. When we wake, ensure the delta in time is as expected. */ - xTimeToBlock = ( TickType_t ) ( bktPRIMARY_BLOCK_TIME << xItem ); - - xTimeWhenBlocking = xTaskGetTickCount(); - - /* We should unblock after xTimeToBlock having not received - * anything on the queue. */ - if( xQueueReceive( xTestQueue, &xData, xTimeToBlock ) != errQUEUE_EMPTY ) - { - xErrorOccurred = __LINE__; - } - - /* How long were we blocked for? */ - xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking; - - if( xBlockedTime < xTimeToBlock ) - { - /* Should not have blocked for less than we requested. */ - xErrorOccurred = __LINE__; - } - - if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) ) - { - /* Should not have blocked for longer than we requested, - * although we would not necessarily run as soon as we were - * unblocked so a margin is allowed. */ - xErrorOccurred = __LINE__; - } - } - - /********************************************************************* - * Test 2 - * - * Simple block time wakeup test on queue sends. - * - * First fill the queue. It should be empty so all sends should pass. */ - for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) - { - if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS ) - { - xErrorOccurred = __LINE__; - } - - #if configUSE_PREEMPTION == 0 - taskYIELD(); - #endif - } - - for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) - { - /* The queue is full. Attempt to write to the queue using a block - * time. When we wake, ensure the delta in time is as expected. */ - xTimeToBlock = ( TickType_t ) ( bktPRIMARY_BLOCK_TIME << xItem ); - - xTimeWhenBlocking = xTaskGetTickCount(); - - /* We should unblock after xTimeToBlock having not received - * anything on the queue. */ - if( xQueueSend( xTestQueue, &xItem, xTimeToBlock ) != errQUEUE_FULL ) - { - xErrorOccurred = __LINE__; - } - - /* How long were we blocked for? */ - xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking; - - if( xBlockedTime < xTimeToBlock ) - { - /* Should not have blocked for less than we requested. */ - xErrorOccurred = __LINE__; - } - - if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) ) - { - /* Should not have blocked for longer than we requested, - * although we would not necessarily run as soon as we were - * unblocked so a margin is allowed. */ - xErrorOccurred = __LINE__; - } - } - - /********************************************************************* - * Test 3 - * - * Wake the other task, it will block attempting to post to the queue. - * When we read from the queue the other task will wake, but before it - * can run we will post to the queue again. When the other task runs it - * will find the queue still full, even though it was woken. It should - * recognise that its block time has not expired and return to block for - * the remains of its block time. - * - * Wake the other task so it blocks attempting to post to the already - * full queue. */ - xRunIndicator = 0; - vTaskResume( xSecondary ); - - /* We need to wait a little to ensure the other task executes. */ - while( xRunIndicator != bktRUN_INDICATOR ) - { - /* The other task has not yet executed. */ - vTaskDelay( bktSHORT_WAIT ); - } - - /* Make sure the other task is blocked on the queue. */ - vTaskDelay( bktSHORT_WAIT ); - xRunIndicator = 0; - - for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) - { - /* Now when we make space on the queue the other task should wake - * but not execute as this task has higher priority. */ - if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS ) - { - xErrorOccurred = __LINE__; - } - - /* Now fill the queue again before the other task gets a chance to - * execute. If the other task had executed we would find the queue - * full ourselves, and the other task have set xRunIndicator. */ - if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS ) - { - xErrorOccurred = __LINE__; - } - - if( xRunIndicator == bktRUN_INDICATOR ) - { - /* The other task should not have executed. */ - xErrorOccurred = __LINE__; - } - - /* Raise the priority of the other task so it executes and blocks - * on the queue again. */ - vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 ); - - /* The other task should now have re-blocked without exiting the - * queue function. */ - if( xRunIndicator == bktRUN_INDICATOR ) - { - /* The other task should not have executed outside of the - * queue function. */ - xErrorOccurred = __LINE__; - } - - /* Set the priority back down. */ - vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY ); - } - - /* Let the other task timeout. When it unblocks it will check that it - * unblocked at the correct time, then suspend itself. */ - while( xRunIndicator != bktRUN_INDICATOR ) - { - vTaskDelay( bktSHORT_WAIT ); - } - - vTaskDelay( bktSHORT_WAIT ); - xRunIndicator = 0; - - /********************************************************************* - * Test 4 - * - * As per test 3 - but with the send and receive the other way around. - * The other task blocks attempting to read from the queue. - * - * Empty the queue. We should find that it is full. */ - for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) - { - if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS ) - { - xErrorOccurred = __LINE__; - } - } - - /* Wake the other task so it blocks attempting to read from the - * already empty queue. */ - vTaskResume( xSecondary ); - - /* We need to wait a little to ensure the other task executes. */ - while( xRunIndicator != bktRUN_INDICATOR ) - { - vTaskDelay( bktSHORT_WAIT ); - } - - vTaskDelay( bktSHORT_WAIT ); - xRunIndicator = 0; - - for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) - { - /* Now when we place an item on the queue the other task should - * wake but not execute as this task has higher priority. */ - if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS ) - { - xErrorOccurred = __LINE__; - } - - /* Now empty the queue again before the other task gets a chance to - * execute. If the other task had executed we would find the queue - * empty ourselves, and the other task would be suspended. */ - if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS ) - { - xErrorOccurred = __LINE__; - } - - if( xRunIndicator == bktRUN_INDICATOR ) - { - /* The other task should not have executed. */ - xErrorOccurred = __LINE__; - } - - /* Raise the priority of the other task so it executes and blocks - * on the queue again. */ - vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 ); - - /* The other task should now have re-blocked without exiting the - * queue function. */ - if( xRunIndicator == bktRUN_INDICATOR ) - { - /* The other task should not have executed outside of the - * queue function. */ - xErrorOccurred = __LINE__; - } - - vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY ); - } - - /* Let the other task timeout. When it unblocks it will check that it - * unblocked at the correct time, then suspend itself. */ - while( xRunIndicator != bktRUN_INDICATOR ) - { - vTaskDelay( bktSHORT_WAIT ); - } - - vTaskDelay( bktSHORT_WAIT ); - - xPrimaryCycles++; - } -} -/*-----------------------------------------------------------*/ - -static void vSecondaryBlockTimeTestTask( void * pvParameters ) -{ - TickType_t xTimeWhenBlocking, xBlockedTime; - BaseType_t xData; - - ( void ) pvParameters; - - for( ; ; ) - { - /********************************************************************* - * Test 0, 1 and 2 - * - * This task does not participate in these tests. */ - vTaskSuspend( NULL ); - - /********************************************************************* - * Test 3 - * - * The first thing we do is attempt to read from the queue. It should be - * full so we block. Note the time before we block so we can check the - * wake time is as per that expected. */ - xTimeWhenBlocking = xTaskGetTickCount(); - - /* We should unblock after bktTIME_TO_BLOCK having not sent anything to - * the queue. */ - xData = 0; - xRunIndicator = bktRUN_INDICATOR; - - if( xQueueSend( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_FULL ) - { - xErrorOccurred = __LINE__; - } - - /* How long were we inside the send function? */ - xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking; - - /* We should not have blocked for less time than bktTIME_TO_BLOCK. */ - if( xBlockedTime < bktTIME_TO_BLOCK ) - { - xErrorOccurred = __LINE__; - } - - /* We should of not blocked for much longer than bktALLOWABLE_MARGIN - * either. A margin is permitted as we would not necessarily run as - * soon as we unblocked. */ - if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) ) - { - xErrorOccurred = __LINE__; - } - - /* Suspend ready for test 3. */ - xRunIndicator = bktRUN_INDICATOR; - vTaskSuspend( NULL ); - - /********************************************************************* - * Test 4 - * - * As per test three, but with the send and receive reversed. */ - xTimeWhenBlocking = xTaskGetTickCount(); - - /* We should unblock after bktTIME_TO_BLOCK having not received - * anything on the queue. */ - xRunIndicator = bktRUN_INDICATOR; - - if( xQueueReceive( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_EMPTY ) - { - xErrorOccurred = __LINE__; - } - - xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking; - - /* We should not have blocked for less time than bktTIME_TO_BLOCK. */ - if( xBlockedTime < bktTIME_TO_BLOCK ) - { - xErrorOccurred = __LINE__; - } - - /* We should of not blocked for much longer than bktALLOWABLE_MARGIN - * either. A margin is permitted as we would not necessarily run as soon - * as we unblocked. */ - if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) ) - { - xErrorOccurred = __LINE__; - } - - xRunIndicator = bktRUN_INDICATOR; - - xSecondaryCycles++; - } -} -/*-----------------------------------------------------------*/ - -static void prvBasicDelayTests( void ) -{ - TickType_t xPreTime, xPostTime, x, xLastUnblockTime, xExpectedUnblockTime; - const TickType_t xPeriod = 75, xCycles = 5, xAllowableMargin = ( bktALLOWABLE_MARGIN >> 1 ), xHalfPeriod = xPeriod / ( TickType_t ) 2; - BaseType_t xDidBlock; - - /* Temporarily increase priority so the timing is more accurate, but not so - * high as to disrupt the timer tests. */ - vTaskPrioritySet( NULL, configTIMER_TASK_PRIORITY - 1 ); - - /* Crude check to too see that vTaskDelay() blocks for the expected - * period. */ - xPreTime = xTaskGetTickCount(); - vTaskDelay( bktTIME_TO_BLOCK ); - xPostTime = xTaskGetTickCount(); - - /* The priority is higher, so the allowable margin is halved when compared - * to the other tests in this file. */ - if( ( xPostTime - xPreTime ) > ( bktTIME_TO_BLOCK + xAllowableMargin ) ) - { - xErrorOccurred = __LINE__; - } - - /* Now crude tests to check the vTaskDelayUntil() functionality. */ - xPostTime = xTaskGetTickCount(); - xLastUnblockTime = xPostTime; - - for( x = 0; x < xCycles; x++ ) - { - /* Calculate the next expected unblock time from the time taken before - * this loop was entered. */ - xExpectedUnblockTime = xPostTime + ( x * xPeriod ); - - vTaskDelayUntil( &xLastUnblockTime, xPeriod ); - - if( ( xTaskGetTickCount() - xExpectedUnblockTime ) > ( bktTIME_TO_BLOCK + xAllowableMargin ) ) - { - xErrorOccurred = __LINE__; - } - - xPrimaryCycles++; - } - - /* Crude tests for return value of xTaskDelayUntil(). First a standard block - * should return that the task does block. */ - xDidBlock = xTaskDelayUntil( &xLastUnblockTime, xPeriod ); - - if( xDidBlock != pdTRUE ) - { - xErrorOccurred = __LINE__; - } - - /* Now delay a few ticks so repeating the above block period will not block for - * the full amount of time, but will still block. */ - vTaskDelay( xHalfPeriod ); - xDidBlock = xTaskDelayUntil( &xLastUnblockTime, xPeriod ); - - if( xDidBlock != pdTRUE ) - { - xErrorOccurred = __LINE__; - } - - /* This time block for longer than xPeriod before calling xTaskDelayUntil() so - * the call to xTaskDelayUntil() should not block. */ - vTaskDelay( xPeriod ); - xDidBlock = xTaskDelayUntil( &xLastUnblockTime, xPeriod ); - - if( xDidBlock != pdFALSE ) - { - xErrorOccurred = __LINE__; - } - - /* Catch up. */ - xDidBlock = xTaskDelayUntil( &xLastUnblockTime, xPeriod ); - - if( xDidBlock != pdTRUE ) - { - xErrorOccurred = __LINE__; - } - - /* Again block for slightly longer than a period so ensure the time is in the - * past next time xTaskDelayUntil() gets called. */ - vTaskDelay( xPeriod + xAllowableMargin ); - xDidBlock = xTaskDelayUntil( &xLastUnblockTime, xPeriod ); - - if( xDidBlock != pdFALSE ) - { - xErrorOccurred = __LINE__; - } - - /* Reset to the original task priority ready for the other tests. */ - vTaskPrioritySet( NULL, bktPRIMARY_PRIORITY ); -} -/*-----------------------------------------------------------*/ - -BaseType_t xAreBlockTimeTestTasksStillRunning( void ) -{ - static BaseType_t xLastPrimaryCycleCount = 0, xLastSecondaryCycleCount = 0; - BaseType_t xReturn = pdPASS; - - /* Have both tasks performed at least one cycle since this function was - * last called? */ - if( xPrimaryCycles == xLastPrimaryCycleCount ) - { - xReturn = pdFAIL; - } - - if( xSecondaryCycles == xLastSecondaryCycleCount ) - { - xReturn = pdFAIL; - } - - if( xErrorOccurred != pdFALSE ) - { - xReturn = pdFAIL; - } - - xLastSecondaryCycleCount = xSecondaryCycles; - xLastPrimaryCycleCount = xPrimaryCycles; - - return xReturn; -} +/* + * 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 + * + */ + +/* + * This file contains some test scenarios that ensure tasks do not exit queue + * send or receive functions prematurely. A description of the tests is + * included within the code. + */ + +/* Kernel includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +/* Demo includes. */ +#include "blocktim.h" + +/* Task priorities and stack sizes. Allow these to be overridden. */ +#ifndef bktPRIMARY_PRIORITY + #define bktPRIMARY_PRIORITY ( configMAX_PRIORITIES - 3 ) +#endif + +#ifndef bktSECONDARY_PRIORITY + #define bktSECONDARY_PRIORITY ( configMAX_PRIORITIES - 4 ) +#endif + +#ifndef bktBLOCK_TIME_TASK_STACK_SIZE + #define bktBLOCK_TIME_TASK_STACK_SIZE configMINIMAL_STACK_SIZE +#endif + +/* Task behaviour. */ +#define bktQUEUE_LENGTH ( 5 ) +#define bktSHORT_WAIT pdMS_TO_TICKS( ( TickType_t ) 20 ) +#define bktPRIMARY_BLOCK_TIME ( 10 ) +#define bktALLOWABLE_MARGIN ( 15 ) +#define bktTIME_TO_BLOCK ( 175 ) +#define bktDONT_BLOCK ( ( TickType_t ) 0 ) +#define bktRUN_INDICATOR ( ( UBaseType_t ) 0x55 ) + +/* In case the demo does not have software timers enabled, as this file uses + * the configTIMER_TASK_PRIORITY setting. */ +#ifndef configTIMER_TASK_PRIORITY + #define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) +#endif + +/*-----------------------------------------------------------*/ + +/* + * The two test tasks. Their behaviour is commented within the functions. + */ +static void vPrimaryBlockTimeTestTask( void * pvParameters ); +static void vSecondaryBlockTimeTestTask( void * pvParameters ); + +/* + * Very basic tests to verify the block times are as expected. + */ +static void prvBasicDelayTests( void ); + +/*-----------------------------------------------------------*/ + +/* The queue on which the tasks block. */ +static QueueHandle_t xTestQueue; + +/* Handle to the secondary task is required by the primary task for calls + * to vTaskSuspend/Resume(). */ +static TaskHandle_t xSecondary; + +/* Used to ensure that tasks are still executing without error. */ +static volatile BaseType_t xPrimaryCycles = 0, xSecondaryCycles = 0; +static volatile BaseType_t xErrorOccurred = pdFALSE; + +/* Provides a simple mechanism for the primary task to know when the + * secondary task has executed. */ +static volatile UBaseType_t xRunIndicator; + +/*-----------------------------------------------------------*/ + +void vCreateBlockTimeTasks( void ) +{ + /* Create the queue on which the two tasks block. */ + xTestQueue = xQueueCreate( bktQUEUE_LENGTH, sizeof( BaseType_t ) ); + + if( xTestQueue != 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( xTestQueue, "Block_Time_Queue" ); + + /* Create the two test tasks. */ + xTaskCreate( vPrimaryBlockTimeTestTask, "BTest1", bktBLOCK_TIME_TASK_STACK_SIZE, NULL, bktPRIMARY_PRIORITY, NULL ); + xTaskCreate( vSecondaryBlockTimeTestTask, "BTest2", bktBLOCK_TIME_TASK_STACK_SIZE, NULL, bktSECONDARY_PRIORITY, &xSecondary ); + } +} +/*-----------------------------------------------------------*/ + +static void vPrimaryBlockTimeTestTask( void * pvParameters ) +{ + BaseType_t xItem, xData; + TickType_t xTimeWhenBlocking; + TickType_t xTimeToBlock, xBlockedTime; + + ( void ) pvParameters; + + for( ; ; ) + { + /********************************************************************* + * Test 0 + * + * Basic vTaskDelay() and vTaskDelayUntil() tests. */ + prvBasicDelayTests(); + + /********************************************************************* + * Test 1 + * + * Simple block time wakeup test on queue receives. */ + for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) + { + /* The queue is empty. Attempt to read from the queue using a block + * time. When we wake, ensure the delta in time is as expected. */ + xTimeToBlock = ( TickType_t ) ( bktPRIMARY_BLOCK_TIME << xItem ); + + xTimeWhenBlocking = xTaskGetTickCount(); + + /* We should unblock after xTimeToBlock having not received + * anything on the queue. */ + if( xQueueReceive( xTestQueue, &xData, xTimeToBlock ) != errQUEUE_EMPTY ) + { + xErrorOccurred = __LINE__; + } + + /* How long were we blocked for? */ + xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking; + + if( xBlockedTime < xTimeToBlock ) + { + /* Should not have blocked for less than we requested. */ + xErrorOccurred = __LINE__; + } + + if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) ) + { + /* Should not have blocked for longer than we requested, + * although we would not necessarily run as soon as we were + * unblocked so a margin is allowed. */ + xErrorOccurred = __LINE__; + } + } + + /********************************************************************* + * Test 2 + * + * Simple block time wakeup test on queue sends. + * + * First fill the queue. It should be empty so all sends should pass. */ + for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) + { + if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS ) + { + xErrorOccurred = __LINE__; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + } + + for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) + { + /* The queue is full. Attempt to write to the queue using a block + * time. When we wake, ensure the delta in time is as expected. */ + xTimeToBlock = ( TickType_t ) ( bktPRIMARY_BLOCK_TIME << xItem ); + + xTimeWhenBlocking = xTaskGetTickCount(); + + /* We should unblock after xTimeToBlock having not received + * anything on the queue. */ + if( xQueueSend( xTestQueue, &xItem, xTimeToBlock ) != errQUEUE_FULL ) + { + xErrorOccurred = __LINE__; + } + + /* How long were we blocked for? */ + xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking; + + if( xBlockedTime < xTimeToBlock ) + { + /* Should not have blocked for less than we requested. */ + xErrorOccurred = __LINE__; + } + + if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) ) + { + /* Should not have blocked for longer than we requested, + * although we would not necessarily run as soon as we were + * unblocked so a margin is allowed. */ + xErrorOccurred = __LINE__; + } + } + + /********************************************************************* + * Test 3 + * + * Wake the other task, it will block attempting to post to the queue. + * When we read from the queue the other task will wake, but before it + * can run we will post to the queue again. When the other task runs it + * will find the queue still full, even though it was woken. It should + * recognise that its block time has not expired and return to block for + * the remains of its block time. + * + * Wake the other task so it blocks attempting to post to the already + * full queue. */ + xRunIndicator = 0; + vTaskResume( xSecondary ); + + /* We need to wait a little to ensure the other task executes. */ + while( xRunIndicator != bktRUN_INDICATOR ) + { + /* The other task has not yet executed. */ + vTaskDelay( bktSHORT_WAIT ); + } + + /* Make sure the other task is blocked on the queue. */ + vTaskDelay( bktSHORT_WAIT ); + xRunIndicator = 0; + + for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) + { + /* Now when we make space on the queue the other task should wake + * but not execute as this task has higher priority. */ + if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS ) + { + xErrorOccurred = __LINE__; + } + + /* Now fill the queue again before the other task gets a chance to + * execute. If the other task had executed we would find the queue + * full ourselves, and the other task have set xRunIndicator. */ + if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS ) + { + xErrorOccurred = __LINE__; + } + + if( xRunIndicator == bktRUN_INDICATOR ) + { + /* The other task should not have executed. */ + xErrorOccurred = __LINE__; + } + + /* Raise the priority of the other task so it executes and blocks + * on the queue again. */ + vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 ); + + /* The other task should now have re-blocked without exiting the + * queue function. */ + if( xRunIndicator == bktRUN_INDICATOR ) + { + /* The other task should not have executed outside of the + * queue function. */ + xErrorOccurred = __LINE__; + } + + /* Set the priority back down. */ + vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY ); + } + + /* Let the other task timeout. When it unblocks it will check that it + * unblocked at the correct time, then suspend itself. */ + while( xRunIndicator != bktRUN_INDICATOR ) + { + vTaskDelay( bktSHORT_WAIT ); + } + + vTaskDelay( bktSHORT_WAIT ); + xRunIndicator = 0; + + /********************************************************************* + * Test 4 + * + * As per test 3 - but with the send and receive the other way around. + * The other task blocks attempting to read from the queue. + * + * Empty the queue. We should find that it is full. */ + for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) + { + if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS ) + { + xErrorOccurred = __LINE__; + } + } + + /* Wake the other task so it blocks attempting to read from the + * already empty queue. */ + vTaskResume( xSecondary ); + + /* We need to wait a little to ensure the other task executes. */ + while( xRunIndicator != bktRUN_INDICATOR ) + { + vTaskDelay( bktSHORT_WAIT ); + } + + vTaskDelay( bktSHORT_WAIT ); + xRunIndicator = 0; + + for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) + { + /* Now when we place an item on the queue the other task should + * wake but not execute as this task has higher priority. */ + if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS ) + { + xErrorOccurred = __LINE__; + } + + /* Now empty the queue again before the other task gets a chance to + * execute. If the other task had executed we would find the queue + * empty ourselves, and the other task would be suspended. */ + if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS ) + { + xErrorOccurred = __LINE__; + } + + if( xRunIndicator == bktRUN_INDICATOR ) + { + /* The other task should not have executed. */ + xErrorOccurred = __LINE__; + } + + /* Raise the priority of the other task so it executes and blocks + * on the queue again. */ + vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 ); + + /* The other task should now have re-blocked without exiting the + * queue function. */ + if( xRunIndicator == bktRUN_INDICATOR ) + { + /* The other task should not have executed outside of the + * queue function. */ + xErrorOccurred = __LINE__; + } + + vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY ); + } + + /* Let the other task timeout. When it unblocks it will check that it + * unblocked at the correct time, then suspend itself. */ + while( xRunIndicator != bktRUN_INDICATOR ) + { + vTaskDelay( bktSHORT_WAIT ); + } + + vTaskDelay( bktSHORT_WAIT ); + + xPrimaryCycles++; + } +} +/*-----------------------------------------------------------*/ + +static void vSecondaryBlockTimeTestTask( void * pvParameters ) +{ + TickType_t xTimeWhenBlocking, xBlockedTime; + BaseType_t xData; + + ( void ) pvParameters; + + for( ; ; ) + { + /********************************************************************* + * Test 0, 1 and 2 + * + * This task does not participate in these tests. */ + vTaskSuspend( NULL ); + + /********************************************************************* + * Test 3 + * + * The first thing we do is attempt to read from the queue. It should be + * full so we block. Note the time before we block so we can check the + * wake time is as per that expected. */ + xTimeWhenBlocking = xTaskGetTickCount(); + + /* We should unblock after bktTIME_TO_BLOCK having not sent anything to + * the queue. */ + xData = 0; + xRunIndicator = bktRUN_INDICATOR; + + if( xQueueSend( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_FULL ) + { + xErrorOccurred = __LINE__; + } + + /* How long were we inside the send function? */ + xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking; + + /* We should not have blocked for less time than bktTIME_TO_BLOCK. */ + if( xBlockedTime < bktTIME_TO_BLOCK ) + { + xErrorOccurred = __LINE__; + } + + /* We should of not blocked for much longer than bktALLOWABLE_MARGIN + * either. A margin is permitted as we would not necessarily run as + * soon as we unblocked. */ + if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) ) + { + xErrorOccurred = __LINE__; + } + + /* Suspend ready for test 3. */ + xRunIndicator = bktRUN_INDICATOR; + vTaskSuspend( NULL ); + + /********************************************************************* + * Test 4 + * + * As per test three, but with the send and receive reversed. */ + xTimeWhenBlocking = xTaskGetTickCount(); + + /* We should unblock after bktTIME_TO_BLOCK having not received + * anything on the queue. */ + xRunIndicator = bktRUN_INDICATOR; + + if( xQueueReceive( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_EMPTY ) + { + xErrorOccurred = __LINE__; + } + + xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking; + + /* We should not have blocked for less time than bktTIME_TO_BLOCK. */ + if( xBlockedTime < bktTIME_TO_BLOCK ) + { + xErrorOccurred = __LINE__; + } + + /* We should of not blocked for much longer than bktALLOWABLE_MARGIN + * either. A margin is permitted as we would not necessarily run as soon + * as we unblocked. */ + if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) ) + { + xErrorOccurred = __LINE__; + } + + xRunIndicator = bktRUN_INDICATOR; + + xSecondaryCycles++; + } +} +/*-----------------------------------------------------------*/ + +static void prvBasicDelayTests( void ) +{ + TickType_t xPreTime, xPostTime, x, xLastUnblockTime, xExpectedUnblockTime; + const TickType_t xPeriod = 75, xCycles = 5, xAllowableMargin = ( bktALLOWABLE_MARGIN >> 1 ), xHalfPeriod = xPeriod / ( TickType_t ) 2; + BaseType_t xDidBlock; + + /* Temporarily increase priority so the timing is more accurate, but not so + * high as to disrupt the timer tests. */ + vTaskPrioritySet( NULL, configTIMER_TASK_PRIORITY - 1 ); + + /* Crude check to too see that vTaskDelay() blocks for the expected + * period. */ + xPreTime = xTaskGetTickCount(); + vTaskDelay( bktTIME_TO_BLOCK ); + xPostTime = xTaskGetTickCount(); + + /* The priority is higher, so the allowable margin is halved when compared + * to the other tests in this file. */ + if( ( xPostTime - xPreTime ) > ( bktTIME_TO_BLOCK + xAllowableMargin ) ) + { + xErrorOccurred = __LINE__; + } + + /* Now crude tests to check the vTaskDelayUntil() functionality. */ + xPostTime = xTaskGetTickCount(); + xLastUnblockTime = xPostTime; + + for( x = 0; x < xCycles; x++ ) + { + /* Calculate the next expected unblock time from the time taken before + * this loop was entered. */ + xExpectedUnblockTime = xPostTime + ( x * xPeriod ); + + vTaskDelayUntil( &xLastUnblockTime, xPeriod ); + + if( ( xTaskGetTickCount() - xExpectedUnblockTime ) > ( bktTIME_TO_BLOCK + xAllowableMargin ) ) + { + xErrorOccurred = __LINE__; + } + + xPrimaryCycles++; + } + + /* Crude tests for return value of xTaskDelayUntil(). First a standard block + * should return that the task does block. */ + xDidBlock = xTaskDelayUntil( &xLastUnblockTime, xPeriod ); + + if( xDidBlock != pdTRUE ) + { + xErrorOccurred = __LINE__; + } + + /* Now delay a few ticks so repeating the above block period will not block for + * the full amount of time, but will still block. */ + vTaskDelay( xHalfPeriod ); + xDidBlock = xTaskDelayUntil( &xLastUnblockTime, xPeriod ); + + if( xDidBlock != pdTRUE ) + { + xErrorOccurred = __LINE__; + } + + /* This time block for longer than xPeriod before calling xTaskDelayUntil() so + * the call to xTaskDelayUntil() should not block. */ + vTaskDelay( xPeriod ); + xDidBlock = xTaskDelayUntil( &xLastUnblockTime, xPeriod ); + + if( xDidBlock != pdFALSE ) + { + xErrorOccurred = __LINE__; + } + + /* Catch up. */ + xDidBlock = xTaskDelayUntil( &xLastUnblockTime, xPeriod ); + + if( xDidBlock != pdTRUE ) + { + xErrorOccurred = __LINE__; + } + + /* Again block for slightly longer than a period so ensure the time is in the + * past next time xTaskDelayUntil() gets called. */ + vTaskDelay( xPeriod + xAllowableMargin ); + xDidBlock = xTaskDelayUntil( &xLastUnblockTime, xPeriod ); + + if( xDidBlock != pdFALSE ) + { + xErrorOccurred = __LINE__; + } + + /* Reset to the original task priority ready for the other tests. */ + vTaskPrioritySet( NULL, bktPRIMARY_PRIORITY ); +} +/*-----------------------------------------------------------*/ + +BaseType_t xAreBlockTimeTestTasksStillRunning( void ) +{ + static BaseType_t xLastPrimaryCycleCount = 0, xLastSecondaryCycleCount = 0; + BaseType_t xReturn = pdPASS; + + /* Have both tasks performed at least one cycle since this function was + * last called? */ + if( xPrimaryCycles == xLastPrimaryCycleCount ) + { + xReturn = pdFAIL; + } + + if( xSecondaryCycles == xLastSecondaryCycleCount ) + { + xReturn = pdFAIL; + } + + if( xErrorOccurred != pdFALSE ) + { + xReturn = pdFAIL; + } + + xLastSecondaryCycleCount = xSecondaryCycles; + xLastPrimaryCycleCount = xPrimaryCycles; + + return xReturn; +} diff --git a/FreeRTOS/Demo/Common/Minimal/recmutex.c b/FreeRTOS/Demo/Common/Minimal/recmutex.c index a03c4f91d25..39bf9a4b0cd 100644 --- a/FreeRTOS/Demo/Common/Minimal/recmutex.c +++ b/FreeRTOS/Demo/Common/Minimal/recmutex.c @@ -1,411 +1,411 @@ -/* - * 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 - * - */ - -/* - * The tasks defined on this page demonstrate the use of recursive mutexes. - * - * For recursive mutex functionality the created mutex should be created using - * xSemaphoreCreateRecursiveMutex(), then be manipulated - * using the xSemaphoreTakeRecursive() and xSemaphoreGiveRecursive() API - * functions. - * - * This demo creates three tasks all of which access the same recursive mutex: - * - * prvRecursiveMutexControllingTask() has the highest priority so executes - * first and grabs the mutex. It then performs some recursive accesses - - * between each of which it sleeps for a short period to let the lower - * priority tasks execute. When it has completed its demo functionality - * it gives the mutex back before suspending itself. - * - * prvRecursiveMutexBlockingTask() attempts to access the mutex by performing - * a blocking 'take'. The blocking task has a lower priority than the - * controlling task so by the time it executes the mutex has already been - * taken by the controlling task, causing the blocking task to block. It - * does not unblock until the controlling task has given the mutex back, - * and it does not actually run until the controlling task has suspended - * itself (due to the relative priorities). When it eventually does obtain - * the mutex all it does is give the mutex back prior to also suspending - * itself. At this point both the controlling task and the blocking task are - * suspended. - * - * prvRecursiveMutexPollingTask() runs at the idle priority. It spins round - * a tight loop attempting to obtain the mutex with a non-blocking call. As - * the lowest priority task it will not successfully obtain the mutex until - * both the controlling and blocking tasks are suspended. Once it eventually - * does obtain the mutex it first unsuspends both the controlling task and - * blocking task prior to giving the mutex back - resulting in the polling - * task temporarily inheriting the controlling tasks priority. - */ - -/* Scheduler include files. */ -#include "FreeRTOS.h" -#include "task.h" -#include "semphr.h" - -/* Demo app include files. */ -#include "recmutex.h" - -/* Priorities assigned to the three tasks. recmuCONTROLLING_TASK_PRIORITY can - * be overridden by a definition in FreeRTOSConfig.h. */ -#ifndef recmuCONTROLLING_TASK_PRIORITY - #define recmuCONTROLLING_TASK_PRIORITY ( tskIDLE_PRIORITY + 2 ) -#endif -#define recmuBLOCKING_TASK_PRIORITY ( tskIDLE_PRIORITY + 1 ) -#define recmuPOLLING_TASK_PRIORITY ( tskIDLE_PRIORITY + 0 ) - -/* The recursive call depth. */ -#define recmuMAX_COUNT ( 10 ) - -/* Misc. */ -#define recmuSHORT_DELAY ( pdMS_TO_TICKS( 20 ) ) -#define recmuNO_DELAY ( ( TickType_t ) 0 ) -#define recmu15ms_DELAY ( pdMS_TO_TICKS( 15 ) ) - -#ifndef recmuRECURSIVE_MUTEX_TEST_TASK_STACK_SIZE - #define recmuRECURSIVE_MUTEX_TEST_TASK_STACK_SIZE configMINIMAL_STACK_SIZE -#endif - -/* The three tasks as described at the top of this file. */ -static void prvRecursiveMutexControllingTask( void * pvParameters ); -static void prvRecursiveMutexBlockingTask( void * pvParameters ); -static void prvRecursiveMutexPollingTask( void * pvParameters ); - -/* The mutex used by the demo. */ -static SemaphoreHandle_t xMutex; - -/* Variables used to detect and latch errors. */ -static volatile BaseType_t xErrorOccurred = pdFALSE, xControllingIsSuspended = pdFALSE, xBlockingIsSuspended = pdFALSE; -static volatile UBaseType_t uxControllingCycles = 0, uxBlockingCycles = 0, uxPollingCycles = 0; - -/* Handles of the two higher priority tasks, required so they can be resumed - * (unsuspended). */ -static TaskHandle_t xControllingTaskHandle, xBlockingTaskHandle; - -/*-----------------------------------------------------------*/ - -void vStartRecursiveMutexTasks( void ) -{ - /* Just creates the mutex and the three tasks. */ - - xMutex = xSemaphoreCreateRecursiveMutex(); - - 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 mutex 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, "Recursive_Mutex" ); - - xTaskCreate( prvRecursiveMutexControllingTask, "Rec1", recmuRECURSIVE_MUTEX_TEST_TASK_STACK_SIZE, NULL, recmuCONTROLLING_TASK_PRIORITY, &xControllingTaskHandle ); - xTaskCreate( prvRecursiveMutexBlockingTask, "Rec2", recmuRECURSIVE_MUTEX_TEST_TASK_STACK_SIZE, NULL, recmuBLOCKING_TASK_PRIORITY, &xBlockingTaskHandle ); - xTaskCreate( prvRecursiveMutexPollingTask, "Rec3", recmuRECURSIVE_MUTEX_TEST_TASK_STACK_SIZE, NULL, recmuPOLLING_TASK_PRIORITY, NULL ); - } -} -/*-----------------------------------------------------------*/ - -static void prvRecursiveMutexControllingTask( void * pvParameters ) -{ - UBaseType_t ux; - - /* Just to remove compiler warning. */ - ( void ) pvParameters; - - for( ; ; ) - { - /* Should not be able to 'give' the mutex, as we have not yet 'taken' - * it. The first time through, the mutex will not have been used yet, - * subsequent times through, at this point the mutex will be held by the - * polling task. */ - if( xSemaphoreGiveRecursive( xMutex ) == pdPASS ) - { - xErrorOccurred = __LINE__; - } - - for( ux = 0; ux < recmuMAX_COUNT; ux++ ) - { - /* We should now be able to take the mutex as many times as - * we like. - * - * The first time through the mutex will be immediately available, on - * subsequent times through the mutex will be held by the polling task - * at this point and this Take will cause the polling task to inherit - * the priority of this task. In this case the block time must be - * long enough to ensure the polling task will execute again before the - * block time expires. If the block time does expire then the error - * flag will be set here. */ - if( xSemaphoreTakeRecursive( xMutex, recmu15ms_DELAY ) != pdPASS ) - { - xErrorOccurred = __LINE__; - } - - /* Ensure the other task attempting to access the mutex (and the - * other demo tasks) are able to execute to ensure they either block - * (where a block time is specified) or return an error (where no - * block time is specified) as the mutex is held by this task. */ - vTaskDelay( recmuSHORT_DELAY ); - } - - /* For each time we took the mutex, give it back. */ - for( ux = 0; ux < recmuMAX_COUNT; ux++ ) - { - /* Ensure the other task attempting to access the mutex (and the - * other demo tasks) are able to execute. */ - vTaskDelay( recmuSHORT_DELAY ); - - /* We should now be able to give the mutex as many times as we - * took it. When the mutex is available again the Blocking task - * should be unblocked but not run because it has a lower priority - * than this task. The polling task should also not run at this point - * as it too has a lower priority than this task. */ - if( xSemaphoreGiveRecursive( xMutex ) != pdPASS ) - { - xErrorOccurred = __LINE__; - } - - #if ( configUSE_PREEMPTION == 0 ) - taskYIELD(); - #endif - } - - /* Having given it back the same number of times as it was taken, we - * should no longer be the mutex owner, so the next give should fail. */ - if( xSemaphoreGiveRecursive( xMutex ) == pdPASS ) - { - xErrorOccurred = __LINE__; - } - - /* Keep count of the number of cycles this task has performed so a - * stall can be detected. */ - uxControllingCycles++; - - /* Suspend ourselves so the blocking task can execute. */ - xControllingIsSuspended = pdTRUE; - vTaskSuspend( NULL ); - xControllingIsSuspended = pdFALSE; - } -} -/*-----------------------------------------------------------*/ - -static void prvRecursiveMutexBlockingTask( void * pvParameters ) -{ - /* Just to remove compiler warning. */ - ( void ) pvParameters; - - for( ; ; ) - { - /* This task will run while the controlling task is blocked, and the - * controlling task will block only once it has the mutex - therefore - * this call should block until the controlling task has given up the - * mutex, and not actually execute past this call until the controlling - * task is suspended. portMAX_DELAY - 1 is used instead of portMAX_DELAY - * to ensure the task's state is reported as Blocked and not Suspended in - * a later call to configASSERT() (within the polling task). */ - if( xSemaphoreTakeRecursive( xMutex, ( portMAX_DELAY - 1 ) ) == pdPASS ) - { - if( xControllingIsSuspended != pdTRUE ) - { - /* Did not expect to execute until the controlling task was - * suspended. */ - xErrorOccurred = __LINE__; - } - else - { - /* Give the mutex back before suspending ourselves to allow - * the polling task to obtain the mutex. */ - if( xSemaphoreGiveRecursive( xMutex ) != pdPASS ) - { - xErrorOccurred = __LINE__; - } - - xBlockingIsSuspended = pdTRUE; - vTaskSuspend( NULL ); - xBlockingIsSuspended = pdFALSE; - } - } - else - { - /* We should not leave the xSemaphoreTakeRecursive() function - * until the mutex was obtained. */ - xErrorOccurred = __LINE__; - } - - /* The controlling and blocking tasks should be in lock step. */ - if( uxControllingCycles != ( UBaseType_t ) ( uxBlockingCycles + 1 ) ) - { - xErrorOccurred = __LINE__; - } - - /* Keep count of the number of cycles this task has performed so a - * stall can be detected. */ - uxBlockingCycles++; - } -} -/*-----------------------------------------------------------*/ - -static void prvRecursiveMutexPollingTask( void * pvParameters ) -{ - /* Just to remove compiler warning. */ - ( void ) pvParameters; - - for( ; ; ) - { - /* 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 ) - { - #if ( INCLUDE_eTaskGetState == 1 ) - { - configASSERT( eTaskGetState( xControllingTaskHandle ) == eSuspended ); - configASSERT( eTaskGetState( xBlockingTaskHandle ) == eSuspended ); - } - #endif /* INCLUDE_eTaskGetState */ - - /* Is the blocking task suspended? */ - if( ( xBlockingIsSuspended != pdTRUE ) || ( xControllingIsSuspended != pdTRUE ) ) - { - xErrorOccurred = __LINE__; - } - else - { - /* Keep count of the number of cycles this task has performed - * so a stall can be detected. */ - uxPollingCycles++; - - /* We can resume the other tasks here even though they have a - * higher priority than the polling task. When they execute they - * will attempt to obtain the mutex but fail because the polling - * task is still the mutex holder. The polling task (this task) - * will then inherit the higher priority. The Blocking task will - * block indefinitely when it attempts to obtain the mutex, the - * Controlling task will only block for a fixed period and an - * error will be latched if the polling task has not returned the - * mutex by the time this fixed period has expired. */ - vTaskResume( xBlockingTaskHandle ); - #if ( configUSE_PREEMPTION == 0 ) - taskYIELD(); - #endif - - vTaskResume( xControllingTaskHandle ); - #if ( configUSE_PREEMPTION == 0 ) - taskYIELD(); - #endif - - /* The other two tasks should now have executed and no longer - * be suspended. */ - if( ( xBlockingIsSuspended == pdTRUE ) || ( xControllingIsSuspended == pdTRUE ) ) - { - xErrorOccurred = __LINE__; - } - - #if ( INCLUDE_uxTaskPriorityGet == 1 ) - { - /* Check priority inherited. */ - configASSERT( uxTaskPriorityGet( NULL ) == recmuCONTROLLING_TASK_PRIORITY ); - } - #endif /* INCLUDE_uxTaskPriorityGet */ - - #if ( INCLUDE_eTaskGetState == 1 ) - { - configASSERT( eTaskGetState( xControllingTaskHandle ) == eBlocked ); - configASSERT( eTaskGetState( xBlockingTaskHandle ) == eBlocked ); - } - #endif /* INCLUDE_eTaskGetState */ - - /* Release the mutex, disinheriting the higher priority again. */ - if( xSemaphoreGiveRecursive( xMutex ) != pdPASS ) - { - xErrorOccurred = __LINE__; - } - - #if ( INCLUDE_uxTaskPriorityGet == 1 ) - { - /* Check priority disinherited. */ - configASSERT( uxTaskPriorityGet( NULL ) == recmuPOLLING_TASK_PRIORITY ); - } - #endif /* INCLUDE_uxTaskPriorityGet */ - } - } - - #if configUSE_PREEMPTION == 0 - { - taskYIELD(); - } - #endif - } -} -/*-----------------------------------------------------------*/ - -/* This is called to check that all the created tasks are still running. */ -BaseType_t xAreRecursiveMutexTasksStillRunning( void ) -{ - BaseType_t xReturn; - static UBaseType_t uxLastControllingCycles = 0, uxLastBlockingCycles = 0, uxLastPollingCycles = 0; - - /* Is the controlling task still cycling? */ - if( uxLastControllingCycles == uxControllingCycles ) - { - xErrorOccurred = __LINE__; - } - else - { - uxLastControllingCycles = uxControllingCycles; - } - - /* Is the blocking task still cycling? */ - if( uxLastBlockingCycles == uxBlockingCycles ) - { - xErrorOccurred = __LINE__; - } - else - { - uxLastBlockingCycles = uxBlockingCycles; - } - - /* Is the polling task still cycling? */ - if( uxLastPollingCycles == uxPollingCycles ) - { - xErrorOccurred = __LINE__; - } - else - { - uxLastPollingCycles = uxPollingCycles; - } - - if( xErrorOccurred != pdFALSE ) - { - xReturn = pdFAIL; - } - else - { - xReturn = pdPASS; - } - - return xReturn; -} +/* + * 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 + * + */ + +/* + * The tasks defined on this page demonstrate the use of recursive mutexes. + * + * For recursive mutex functionality the created mutex should be created using + * xSemaphoreCreateRecursiveMutex(), then be manipulated + * using the xSemaphoreTakeRecursive() and xSemaphoreGiveRecursive() API + * functions. + * + * This demo creates three tasks all of which access the same recursive mutex: + * + * prvRecursiveMutexControllingTask() has the highest priority so executes + * first and grabs the mutex. It then performs some recursive accesses - + * between each of which it sleeps for a short period to let the lower + * priority tasks execute. When it has completed its demo functionality + * it gives the mutex back before suspending itself. + * + * prvRecursiveMutexBlockingTask() attempts to access the mutex by performing + * a blocking 'take'. The blocking task has a lower priority than the + * controlling task so by the time it executes the mutex has already been + * taken by the controlling task, causing the blocking task to block. It + * does not unblock until the controlling task has given the mutex back, + * and it does not actually run until the controlling task has suspended + * itself (due to the relative priorities). When it eventually does obtain + * the mutex all it does is give the mutex back prior to also suspending + * itself. At this point both the controlling task and the blocking task are + * suspended. + * + * prvRecursiveMutexPollingTask() runs at the idle priority. It spins round + * a tight loop attempting to obtain the mutex with a non-blocking call. As + * the lowest priority task it will not successfully obtain the mutex until + * both the controlling and blocking tasks are suspended. Once it eventually + * does obtain the mutex it first unsuspends both the controlling task and + * blocking task prior to giving the mutex back - resulting in the polling + * task temporarily inheriting the controlling tasks priority. + */ + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" + +/* Demo app include files. */ +#include "recmutex.h" + +/* Priorities assigned to the three tasks. recmuCONTROLLING_TASK_PRIORITY can + * be overridden by a definition in FreeRTOSConfig.h. */ +#ifndef recmuCONTROLLING_TASK_PRIORITY + #define recmuCONTROLLING_TASK_PRIORITY ( tskIDLE_PRIORITY + 2 ) +#endif +#define recmuBLOCKING_TASK_PRIORITY ( tskIDLE_PRIORITY + 1 ) +#define recmuPOLLING_TASK_PRIORITY ( tskIDLE_PRIORITY + 0 ) + +/* The recursive call depth. */ +#define recmuMAX_COUNT ( 10 ) + +/* Misc. */ +#define recmuSHORT_DELAY ( pdMS_TO_TICKS( 20 ) ) +#define recmuNO_DELAY ( ( TickType_t ) 0 ) +#define recmu15ms_DELAY ( pdMS_TO_TICKS( 15 ) ) + +#ifndef recmuRECURSIVE_MUTEX_TEST_TASK_STACK_SIZE + #define recmuRECURSIVE_MUTEX_TEST_TASK_STACK_SIZE configMINIMAL_STACK_SIZE +#endif + +/* The three tasks as described at the top of this file. */ +static void prvRecursiveMutexControllingTask( void * pvParameters ); +static void prvRecursiveMutexBlockingTask( void * pvParameters ); +static void prvRecursiveMutexPollingTask( void * pvParameters ); + +/* The mutex used by the demo. */ +static SemaphoreHandle_t xMutex; + +/* Variables used to detect and latch errors. */ +static volatile BaseType_t xErrorOccurred = pdFALSE, xControllingIsSuspended = pdFALSE, xBlockingIsSuspended = pdFALSE; +static volatile UBaseType_t uxControllingCycles = 0, uxBlockingCycles = 0, uxPollingCycles = 0; + +/* Handles of the two higher priority tasks, required so they can be resumed + * (unsuspended). */ +static TaskHandle_t xControllingTaskHandle, xBlockingTaskHandle; + +/*-----------------------------------------------------------*/ + +void vStartRecursiveMutexTasks( void ) +{ + /* Just creates the mutex and the three tasks. */ + + xMutex = xSemaphoreCreateRecursiveMutex(); + + 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 mutex 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, "Recursive_Mutex" ); + + xTaskCreate( prvRecursiveMutexControllingTask, "Rec1", recmuRECURSIVE_MUTEX_TEST_TASK_STACK_SIZE, NULL, recmuCONTROLLING_TASK_PRIORITY, &xControllingTaskHandle ); + xTaskCreate( prvRecursiveMutexBlockingTask, "Rec2", recmuRECURSIVE_MUTEX_TEST_TASK_STACK_SIZE, NULL, recmuBLOCKING_TASK_PRIORITY, &xBlockingTaskHandle ); + xTaskCreate( prvRecursiveMutexPollingTask, "Rec3", recmuRECURSIVE_MUTEX_TEST_TASK_STACK_SIZE, NULL, recmuPOLLING_TASK_PRIORITY, NULL ); + } +} +/*-----------------------------------------------------------*/ + +static void prvRecursiveMutexControllingTask( void * pvParameters ) +{ + UBaseType_t ux; + + /* Just to remove compiler warning. */ + ( void ) pvParameters; + + for( ; ; ) + { + /* Should not be able to 'give' the mutex, as we have not yet 'taken' + * it. The first time through, the mutex will not have been used yet, + * subsequent times through, at this point the mutex will be held by the + * polling task. */ + if( xSemaphoreGiveRecursive( xMutex ) == pdPASS ) + { + xErrorOccurred = __LINE__; + } + + for( ux = 0; ux < recmuMAX_COUNT; ux++ ) + { + /* We should now be able to take the mutex as many times as + * we like. + * + * The first time through the mutex will be immediately available, on + * subsequent times through the mutex will be held by the polling task + * at this point and this Take will cause the polling task to inherit + * the priority of this task. In this case the block time must be + * long enough to ensure the polling task will execute again before the + * block time expires. If the block time does expire then the error + * flag will be set here. */ + if( xSemaphoreTakeRecursive( xMutex, recmu15ms_DELAY ) != pdPASS ) + { + xErrorOccurred = __LINE__; + } + + /* Ensure the other task attempting to access the mutex (and the + * other demo tasks) are able to execute to ensure they either block + * (where a block time is specified) or return an error (where no + * block time is specified) as the mutex is held by this task. */ + vTaskDelay( recmuSHORT_DELAY ); + } + + /* For each time we took the mutex, give it back. */ + for( ux = 0; ux < recmuMAX_COUNT; ux++ ) + { + /* Ensure the other task attempting to access the mutex (and the + * other demo tasks) are able to execute. */ + vTaskDelay( recmuSHORT_DELAY ); + + /* We should now be able to give the mutex as many times as we + * took it. When the mutex is available again the Blocking task + * should be unblocked but not run because it has a lower priority + * than this task. The polling task should also not run at this point + * as it too has a lower priority than this task. */ + if( xSemaphoreGiveRecursive( xMutex ) != pdPASS ) + { + xErrorOccurred = __LINE__; + } + + #if ( configUSE_PREEMPTION == 0 ) + taskYIELD(); + #endif + } + + /* Having given it back the same number of times as it was taken, we + * should no longer be the mutex owner, so the next give should fail. */ + if( xSemaphoreGiveRecursive( xMutex ) == pdPASS ) + { + xErrorOccurred = __LINE__; + } + + /* Keep count of the number of cycles this task has performed so a + * stall can be detected. */ + uxControllingCycles++; + + /* Suspend ourselves so the blocking task can execute. */ + xControllingIsSuspended = pdTRUE; + vTaskSuspend( NULL ); + xControllingIsSuspended = pdFALSE; + } +} +/*-----------------------------------------------------------*/ + +static void prvRecursiveMutexBlockingTask( void * pvParameters ) +{ + /* Just to remove compiler warning. */ + ( void ) pvParameters; + + for( ; ; ) + { + /* This task will run while the controlling task is blocked, and the + * controlling task will block only once it has the mutex - therefore + * this call should block until the controlling task has given up the + * mutex, and not actually execute past this call until the controlling + * task is suspended. portMAX_DELAY - 1 is used instead of portMAX_DELAY + * to ensure the task's state is reported as Blocked and not Suspended in + * a later call to configASSERT() (within the polling task). */ + if( xSemaphoreTakeRecursive( xMutex, ( portMAX_DELAY - 1 ) ) == pdPASS ) + { + if( xControllingIsSuspended != pdTRUE ) + { + /* Did not expect to execute until the controlling task was + * suspended. */ + xErrorOccurred = __LINE__; + } + else + { + /* Give the mutex back before suspending ourselves to allow + * the polling task to obtain the mutex. */ + if( xSemaphoreGiveRecursive( xMutex ) != pdPASS ) + { + xErrorOccurred = __LINE__; + } + + xBlockingIsSuspended = pdTRUE; + vTaskSuspend( NULL ); + xBlockingIsSuspended = pdFALSE; + } + } + else + { + /* We should not leave the xSemaphoreTakeRecursive() function + * until the mutex was obtained. */ + xErrorOccurred = __LINE__; + } + + /* The controlling and blocking tasks should be in lock step. */ + if( uxControllingCycles != ( UBaseType_t ) ( uxBlockingCycles + 1 ) ) + { + xErrorOccurred = __LINE__; + } + + /* Keep count of the number of cycles this task has performed so a + * stall can be detected. */ + uxBlockingCycles++; + } +} +/*-----------------------------------------------------------*/ + +static void prvRecursiveMutexPollingTask( void * pvParameters ) +{ + /* Just to remove compiler warning. */ + ( void ) pvParameters; + + for( ; ; ) + { + /* 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 ) + { + #if ( INCLUDE_eTaskGetState == 1 ) + { + configASSERT( eTaskGetState( xControllingTaskHandle ) == eSuspended ); + configASSERT( eTaskGetState( xBlockingTaskHandle ) == eSuspended ); + } + #endif /* INCLUDE_eTaskGetState */ + + /* Is the blocking task suspended? */ + if( ( xBlockingIsSuspended != pdTRUE ) || ( xControllingIsSuspended != pdTRUE ) ) + { + xErrorOccurred = __LINE__; + } + else + { + /* Keep count of the number of cycles this task has performed + * so a stall can be detected. */ + uxPollingCycles++; + + /* We can resume the other tasks here even though they have a + * higher priority than the polling task. When they execute they + * will attempt to obtain the mutex but fail because the polling + * task is still the mutex holder. The polling task (this task) + * will then inherit the higher priority. The Blocking task will + * block indefinitely when it attempts to obtain the mutex, the + * Controlling task will only block for a fixed period and an + * error will be latched if the polling task has not returned the + * mutex by the time this fixed period has expired. */ + vTaskResume( xBlockingTaskHandle ); + #if ( configUSE_PREEMPTION == 0 ) + taskYIELD(); + #endif + + vTaskResume( xControllingTaskHandle ); + #if ( configUSE_PREEMPTION == 0 ) + taskYIELD(); + #endif + + /* The other two tasks should now have executed and no longer + * be suspended. */ + if( ( xBlockingIsSuspended == pdTRUE ) || ( xControllingIsSuspended == pdTRUE ) ) + { + xErrorOccurred = __LINE__; + } + + #if ( INCLUDE_uxTaskPriorityGet == 1 ) + { + /* Check priority inherited. */ + configASSERT( uxTaskPriorityGet( NULL ) == recmuCONTROLLING_TASK_PRIORITY ); + } + #endif /* INCLUDE_uxTaskPriorityGet */ + + #if ( INCLUDE_eTaskGetState == 1 ) + { + configASSERT( eTaskGetState( xControllingTaskHandle ) == eBlocked ); + configASSERT( eTaskGetState( xBlockingTaskHandle ) == eBlocked ); + } + #endif /* INCLUDE_eTaskGetState */ + + /* Release the mutex, disinheriting the higher priority again. */ + if( xSemaphoreGiveRecursive( xMutex ) != pdPASS ) + { + xErrorOccurred = __LINE__; + } + + #if ( INCLUDE_uxTaskPriorityGet == 1 ) + { + /* Check priority disinherited. */ + configASSERT( uxTaskPriorityGet( NULL ) == recmuPOLLING_TASK_PRIORITY ); + } + #endif /* INCLUDE_uxTaskPriorityGet */ + } + } + + #if configUSE_PREEMPTION == 0 + { + taskYIELD(); + } + #endif + } +} +/*-----------------------------------------------------------*/ + +/* This is called to check that all the created tasks are still running. */ +BaseType_t xAreRecursiveMutexTasksStillRunning( void ) +{ + BaseType_t xReturn; + static UBaseType_t uxLastControllingCycles = 0, uxLastBlockingCycles = 0, uxLastPollingCycles = 0; + + /* Is the controlling task still cycling? */ + if( uxLastControllingCycles == uxControllingCycles ) + { + xErrorOccurred = __LINE__; + } + else + { + uxLastControllingCycles = uxControllingCycles; + } + + /* Is the blocking task still cycling? */ + if( uxLastBlockingCycles == uxBlockingCycles ) + { + xErrorOccurred = __LINE__; + } + else + { + uxLastBlockingCycles = uxBlockingCycles; + } + + /* Is the polling task still cycling? */ + if( uxLastPollingCycles == uxPollingCycles ) + { + xErrorOccurred = __LINE__; + } + else + { + uxLastPollingCycles = uxPollingCycles; + } + + if( xErrorOccurred != pdFALSE ) + { + xReturn = pdFAIL; + } + else + { + xReturn = pdPASS; + } + + return xReturn; +}