/* | |
* FreeRTOS Kernel V10.3.0 | |
* 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. | |
* | |
* http://www.FreeRTOS.org | |
* http://aws.amazon.com/freertos | |
* | |
* 1 tab == 4 spaces! | |
*/ | |
/* | |
* Tests the behaviour when data is peeked from a queue when there are | |
* multiple tasks blocked on the queue. | |
*/ | |
#include <stdlib.h> | |
/* Scheduler include files. */ | |
#include "FreeRTOS.h" | |
#include "task.h" | |
#include "queue.h" | |
#include "semphr.h" | |
/* Demo program include files. */ | |
#include "QPeek.h" | |
#define qpeekQUEUE_LENGTH ( 5 ) | |
#define qpeekNO_BLOCK ( 0 ) | |
#define qpeekSHORT_DELAY ( 10 ) | |
#define qpeekLOW_PRIORITY ( tskIDLE_PRIORITY + 0 ) | |
#define qpeekMEDIUM_PRIORITY ( tskIDLE_PRIORITY + 1 ) | |
#define qpeekHIGH_PRIORITY ( tskIDLE_PRIORITY + 2 ) | |
#define qpeekHIGHEST_PRIORITY ( tskIDLE_PRIORITY + 3 ) | |
/*-----------------------------------------------------------*/ | |
/* | |
* The following three tasks are used to demonstrate the peeking behaviour. | |
* Each task is given a different priority to demonstrate the order in which | |
* tasks are woken as data is peeked from a queue. | |
*/ | |
static void prvLowPriorityPeekTask( void *pvParameters ); | |
static void prvMediumPriorityPeekTask( void *pvParameters ); | |
static void prvHighPriorityPeekTask( void *pvParameters ); | |
static void prvHighestPriorityPeekTask( 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; | |
/* Counter that is incremented on each cycle of a test. This is used to | |
detect a stalled task - a test that is no longer running. */ | |
static volatile uint32_t ulLoopCounter = 0; | |
/* Handles to the test tasks. */ | |
TaskHandle_t xMediumPriorityTask, xHighPriorityTask, xHighestPriorityTask; | |
/*-----------------------------------------------------------*/ | |
void vStartQueuePeekTasks( void ) | |
{ | |
QueueHandle_t xQueue; | |
/* Create the queue that we are going to use for the test/demo. */ | |
xQueue = xQueueCreate( qpeekQUEUE_LENGTH, sizeof( uint32_t ) ); | |
if( xQueue != NULL ) | |
{ | |
/* vQueueAddToRegistry() adds the queue to the queue registry, if one is | |
in use. The queue registry is provided as a means for kernel aware | |
debuggers to locate queues and has no purpose if a kernel aware debugger | |
is not being used. The call to vQueueAddToRegistry() will be removed | |
by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is | |
defined to be less than 1. */ | |
vQueueAddToRegistry( xQueue, "QPeek_Test_Queue" ); | |
/* Create the demo tasks and pass it the queue just created. We are | |
passing the queue handle by value so it does not matter that it is declared | |
on the stack here. */ | |
xTaskCreate( prvLowPriorityPeekTask, "PeekL", configMINIMAL_STACK_SIZE, ( void * ) xQueue, qpeekLOW_PRIORITY, NULL ); | |
xTaskCreate( prvMediumPriorityPeekTask, "PeekM", configMINIMAL_STACK_SIZE, ( void * ) xQueue, qpeekMEDIUM_PRIORITY, &xMediumPriorityTask ); | |
xTaskCreate( prvHighPriorityPeekTask, "PeekH1", configMINIMAL_STACK_SIZE, ( void * ) xQueue, qpeekHIGH_PRIORITY, &xHighPriorityTask ); | |
xTaskCreate( prvHighestPriorityPeekTask, "PeekH2", configMINIMAL_STACK_SIZE, ( void * ) xQueue, qpeekHIGHEST_PRIORITY, &xHighestPriorityTask ); | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvHighestPriorityPeekTask( void *pvParameters ) | |
{ | |
QueueHandle_t xQueue = ( QueueHandle_t ) pvParameters; | |
uint32_t ulValue; | |
#ifdef USE_STDIO | |
{ | |
void vPrintDisplayMessage( const char * const * ppcMessageToSend ); | |
const char * const pcTaskStartMsg = "Queue peek test started.\r\n"; | |
/* Queue a message for printing to say the task has started. */ | |
vPrintDisplayMessage( &pcTaskStartMsg ); | |
} | |
#endif | |
for( ;; ) | |
{ | |
/* Try peeking from the queue. The queue should be empty so we will | |
block, allowing the high priority task to execute. */ | |
if( xQueuePeek( xQueue, &ulValue, portMAX_DELAY ) != pdPASS ) | |
{ | |
/* We expected to have received something by the time we unblock. */ | |
xErrorDetected = pdTRUE; | |
} | |
/* When we reach here the high and medium priority tasks should still | |
be blocked on the queue. We unblocked because the low priority task | |
wrote a value to the queue, which we should have peeked. Peeking the | |
data (rather than receiving it) will leave the data on the queue, so | |
the high priority task should then have also been unblocked, but not | |
yet executed. */ | |
if( ulValue != 0x11223344 ) | |
{ | |
/* We did not receive the expected value. */ | |
xErrorDetected = pdTRUE; | |
} | |
if( uxQueueMessagesWaiting( xQueue ) != 1 ) | |
{ | |
/* The message should have been left on the queue. */ | |
xErrorDetected = pdTRUE; | |
} | |
/* Now we are going to actually receive the data, so when the high | |
priority task runs it will find the queue empty and return to the | |
blocked state. */ | |
ulValue = 0; | |
if( xQueueReceive( xQueue, &ulValue, qpeekNO_BLOCK ) != pdPASS ) | |
{ | |
/* We expected to receive the value. */ | |
xErrorDetected = pdTRUE; | |
} | |
if( ulValue != 0x11223344 ) | |
{ | |
/* We did not receive the expected value - which should have been | |
the same value as was peeked. */ | |
xErrorDetected = pdTRUE; | |
} | |
/* Now we will block again as the queue is once more empty. The low | |
priority task can then execute again. */ | |
if( xQueuePeek( xQueue, &ulValue, portMAX_DELAY ) != pdPASS ) | |
{ | |
/* We expected to have received something by the time we unblock. */ | |
xErrorDetected = pdTRUE; | |
} | |
/* When we get here the low priority task should have again written to the | |
queue. */ | |
if( ulValue != 0x01234567 ) | |
{ | |
/* We did not receive the expected value. */ | |
xErrorDetected = pdTRUE; | |
} | |
if( uxQueueMessagesWaiting( xQueue ) != 1 ) | |
{ | |
/* The message should have been left on the queue. */ | |
xErrorDetected = pdTRUE; | |
} | |
/* We only peeked the data, so suspending ourselves now should enable | |
the high priority task to also peek the data. The high priority task | |
will have been unblocked when we peeked the data as we left the data | |
in the queue. */ | |
vTaskSuspend( NULL ); | |
/* This time we are going to do the same as the above test, but the | |
high priority task is going to receive the data, rather than peek it. | |
This means that the medium priority task should never peek the value. */ | |
if( xQueuePeek( xQueue, &ulValue, portMAX_DELAY ) != pdPASS ) | |
{ | |
xErrorDetected = pdTRUE; | |
} | |
if( ulValue != 0xaabbaabb ) | |
{ | |
xErrorDetected = pdTRUE; | |
} | |
vTaskSuspend( NULL ); | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvHighPriorityPeekTask( void *pvParameters ) | |
{ | |
QueueHandle_t xQueue = ( QueueHandle_t ) pvParameters; | |
uint32_t ulValue; | |
for( ;; ) | |
{ | |
/* Try peeking from the queue. The queue should be empty so we will | |
block, allowing the medium priority task to execute. Both the high | |
and highest priority tasks will then be blocked on the queue. */ | |
if( xQueuePeek( xQueue, &ulValue, portMAX_DELAY ) != pdPASS ) | |
{ | |
/* We expected to have received something by the time we unblock. */ | |
xErrorDetected = pdTRUE; | |
} | |
/* When we get here the highest priority task should have peeked the data | |
(unblocking this task) then suspended (allowing this task to also peek | |
the data). */ | |
if( ulValue != 0x01234567 ) | |
{ | |
/* We did not receive the expected value. */ | |
xErrorDetected = pdTRUE; | |
} | |
if( uxQueueMessagesWaiting( xQueue ) != 1 ) | |
{ | |
/* The message should have been left on the queue. */ | |
xErrorDetected = pdTRUE; | |
} | |
/* We only peeked the data, so suspending ourselves now should enable | |
the medium priority task to also peek the data. The medium priority task | |
will have been unblocked when we peeked the data as we left the data | |
in the queue. */ | |
vTaskSuspend( NULL ); | |
/* This time we are going actually receive the value, so the medium | |
priority task will never peek the data - we removed it from the queue. */ | |
if( xQueueReceive( xQueue, &ulValue, portMAX_DELAY ) != pdPASS ) | |
{ | |
xErrorDetected = pdTRUE; | |
} | |
if( ulValue != 0xaabbaabb ) | |
{ | |
xErrorDetected = pdTRUE; | |
} | |
vTaskSuspend( NULL ); | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvMediumPriorityPeekTask( void *pvParameters ) | |
{ | |
QueueHandle_t xQueue = ( QueueHandle_t ) pvParameters; | |
uint32_t ulValue; | |
for( ;; ) | |
{ | |
/* Try peeking from the queue. The queue should be empty so we will | |
block, allowing the low priority task to execute. The highest, high | |
and medium priority tasks will then all be blocked on the queue. */ | |
if( xQueuePeek( xQueue, &ulValue, portMAX_DELAY ) != pdPASS ) | |
{ | |
/* We expected to have received something by the time we unblock. */ | |
xErrorDetected = pdTRUE; | |
} | |
/* When we get here the high priority task should have peeked the data | |
(unblocking this task) then suspended (allowing this task to also peek | |
the data). */ | |
if( ulValue != 0x01234567 ) | |
{ | |
/* We did not receive the expected value. */ | |
xErrorDetected = pdTRUE; | |
} | |
if( uxQueueMessagesWaiting( xQueue ) != 1 ) | |
{ | |
/* The message should have been left on the queue. */ | |
xErrorDetected = pdTRUE; | |
} | |
/* Just so we know the test is still running. */ | |
ulLoopCounter++; | |
/* Now we can suspend ourselves so the low priority task can execute | |
again. */ | |
vTaskSuspend( NULL ); | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvLowPriorityPeekTask( void *pvParameters ) | |
{ | |
QueueHandle_t xQueue = ( QueueHandle_t ) pvParameters; | |
uint32_t ulValue; | |
for( ;; ) | |
{ | |
/* Write some data to the queue. This should unblock the highest | |
priority task that is waiting to peek data from the queue. */ | |
ulValue = 0x11223344; | |
if( xQueueSendToBack( xQueue, &ulValue, qpeekNO_BLOCK ) != pdPASS ) | |
{ | |
/* We were expecting the queue to be empty so we should not of | |
had a problem writing to the queue. */ | |
xErrorDetected = pdTRUE; | |
} | |
#if configUSE_PREEMPTION == 0 | |
taskYIELD(); | |
#endif | |
/* By the time we get here the data should have been removed from | |
the queue. */ | |
if( uxQueueMessagesWaiting( xQueue ) != 0 ) | |
{ | |
xErrorDetected = pdTRUE; | |
} | |
/* Write another value to the queue, again waking the highest priority | |
task that is blocked on the queue. */ | |
ulValue = 0x01234567; | |
if( xQueueSendToBack( xQueue, &ulValue, qpeekNO_BLOCK ) != pdPASS ) | |
{ | |
/* We were expecting the queue to be empty so we should not of | |
had a problem writing to the queue. */ | |
xErrorDetected = pdTRUE; | |
} | |
#if configUSE_PREEMPTION == 0 | |
taskYIELD(); | |
#endif | |
/* All the other tasks should now have successfully peeked the data. | |
The data is still in the queue so we should be able to receive it. */ | |
ulValue = 0; | |
if( xQueueReceive( xQueue, &ulValue, qpeekNO_BLOCK ) != pdPASS ) | |
{ | |
/* We expected to receive the data. */ | |
xErrorDetected = pdTRUE; | |
} | |
if( ulValue != 0x01234567 ) | |
{ | |
/* We did not receive the expected value. */ | |
} | |
/* Lets just delay a while as this is an intensive test as we don't | |
want to starve other tests of processing time. */ | |
vTaskDelay( qpeekSHORT_DELAY ); | |
/* Unsuspend the other tasks so we can repeat the test - this time | |
however not all the other tasks will peek the data as the high | |
priority task is actually going to remove it from the queue. Send | |
to front is used just to be different. As the queue is empty it | |
makes no difference to the result. */ | |
vTaskResume( xMediumPriorityTask ); | |
vTaskResume( xHighPriorityTask ); | |
vTaskResume( xHighestPriorityTask ); | |
#if( configUSE_PREEMPTION == 0 ) | |
taskYIELD(); | |
#endif | |
ulValue = 0xaabbaabb; | |
if( xQueueSendToFront( xQueue, &ulValue, qpeekNO_BLOCK ) != pdPASS ) | |
{ | |
/* We were expecting the queue to be empty so we should not of | |
had a problem writing to the queue. */ | |
xErrorDetected = pdTRUE; | |
} | |
#if configUSE_PREEMPTION == 0 | |
taskYIELD(); | |
#endif | |
/* This time we should find that the queue is empty. The high priority | |
task actually removed the data rather than just peeking it. */ | |
if( xQueuePeek( xQueue, &ulValue, qpeekNO_BLOCK ) != errQUEUE_EMPTY ) | |
{ | |
/* We expected to receive the data. */ | |
xErrorDetected = pdTRUE; | |
} | |
/* Unsuspend the highest and high priority tasks so we can go back | |
and repeat the whole thing. The medium priority task should not be | |
suspended as it was not able to peek the data in this last case. */ | |
vTaskResume( xHighPriorityTask ); | |
vTaskResume( xHighestPriorityTask ); | |
/* Lets just delay a while as this is an intensive test as we don't | |
want to starve other tests of processing time. */ | |
vTaskDelay( qpeekSHORT_DELAY ); | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
/* This is called to check that all the created tasks are still running. */ | |
BaseType_t xAreQueuePeekTasksStillRunning( void ) | |
{ | |
static uint32_t ulLastLoopCounter = 0; | |
/* If the demo task is still running then we expect the loopcounter to | |
have incremented since this function was last called. */ | |
if( ulLastLoopCounter == ulLoopCounter ) | |
{ | |
xErrorDetected = pdTRUE; | |
} | |
ulLastLoopCounter = ulLoopCounter; | |
/* Errors detected in the task itself will have latched xErrorDetected | |
to true. */ | |
return ( BaseType_t ) !xErrorDetected; | |
} | |