EBIKE-FreeRTOS/Common/Minimal/QueueSet.c
2024-04-14 18:38:39 +08:00

1161 lines
45 KiB
C

/*
* FreeRTOS V202212.00
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
/*
* Tests the use of queue sets.
*
* A receive task creates a number of queues and adds them to a queue set before
* blocking on the queue set receive. A transmit task and (optionally) an
* interrupt repeatedly unblocks the receive task by sending messages to the
* queues in a pseudo random order. The receive task removes the messages from
* the queues and flags an error if the received message does not match that
* expected. The task sends values in the range 0 to
* queuesetINITIAL_ISR_TX_VALUE, and the ISR sends value in the range
* queuesetINITIAL_ISR_TX_VALUE to ULONG_MAX.
*/
/* Standard includes. */
#include <stdlib.h>
#include <limits.h>
/* Kernel includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
/* Demo includes. */
#include "QueueSet.h"
#if ( configUSE_QUEUE_SETS == 1 ) /* Remove the tests if queue sets are not defined. */
/* The number of queues that are created and added to the queue set. */
#define queuesetNUM_QUEUES_IN_SET 3
/* The length of each created queue. */
#define queuesetQUEUE_LENGTH 3
/* Block times used in this demo. A block time or 0 means "don't block". */
#define queuesetSHORT_DELAY 200
#define queuesetDONT_BLOCK 0
/* Messages are sent in incrementing order from both a task and an interrupt.
* The task sends values in the range 0 to 0xfffe, and the interrupt sends values
* in the range of 0xffff to ULONG_MAX. */
#define queuesetINITIAL_ISR_TX_VALUE 0xffffUL
/* The priorities used in this demo. */
#define queuesetLOW_PRIORITY ( tskIDLE_PRIORITY )
#define queuesetMEDIUM_PRIORITY ( queuesetLOW_PRIORITY + 1 )
/* For test purposes the priority of the sending task is changed after every
* queuesetPRIORITY_CHANGE_LOOPS number of values are sent to a queue. */
#define queuesetPRIORITY_CHANGE_LOOPS ( ( queuesetNUM_QUEUES_IN_SET * queuesetQUEUE_LENGTH ) * 2 )
/* The ISR sends to the queue every queuesetISR_TX_PERIOD ticks. */
#define queuesetISR_TX_PERIOD ( 100UL )
/* A delay inserted when the Tx task changes its priority to be above the idle
* task priority to ensure the idle priority tasks get some CPU time before the
* next iteration of the queue set Tx task. */
#define queuesetTX_LOOP_DELAY pdMS_TO_TICKS( ( TickType_t ) 200 )
/* The allowable maximum deviation between a received value and the expected
* received value. A deviation will occur when data is received from a queue
* inside an ISR in between a task receiving from a queue and the task checking
* the received value. */
#define queuesetALLOWABLE_RX_DEVIATION 3
/* Ignore values that are at the boundaries of allowable values to make the
* testing of limits easier (don't have to deal with wrapping values). */
#define queuesetIGNORED_BOUNDARY ( queuesetALLOWABLE_RX_DEVIATION * 2 )
typedef enum
{
eEqualPriority = 0, /* Tx and Rx tasks have the same priority. */
eTxHigherPriority, /* The priority of the Tx task is above that of the Rx task. */
eTxLowerPriority /* The priority of the Tx task is below that of the Rx task. */
} eRelativePriorities;
/*
* The task that periodically sends to the queue set.
*/
static void prvQueueSetSendingTask( void * pvParameters );
/*
* The task that reads from the queue set.
*/
static void prvQueueSetReceivingTask( void * pvParameters );
/*
* Check the value received from a queue is the expected value. Some values
* originate from the send task, some values originate from the ISR, with the
* range of the value being used to distinguish between the two message
* sources.
*/
static void prvCheckReceivedValue( uint32_t ulReceived );
/*
* For purposes of test coverage, functions that read from and write to a
* queue set from an ISR respectively.
*/
static void prvReceiveFromQueueInSetFromISR( void );
static void prvSendToQueueInSetFromISR( void );
/*
* Create the queues and add them to a queue set before resuming the Tx
* task.
*/
static void prvSetupTest( void );
/*
* Checks a value received from a queue falls within the range of expected
* values.
*/
static BaseType_t prvCheckReceivedValueWithinExpectedRange( uint32_t ulReceived,
uint32_t ulExpectedReceived );
/*
* Increase test coverage by occasionally change the priorities of the two tasks
* relative to each other.
*/
static void prvChangeRelativePriorities( void );
/*
* Queue overwrites can only be performed on queues of length of one, requiring
* a special test function so a queue of length 1 can temporarily be added to a
* set.
*/
static void prvTestQueueOverwriteWithQueueSet( void );
/*
* Test the case where two queues within a set are written to with
* xQueueOverwrite().
*/
static void prvTestQueueOverwriteOnTwoQueuesInQueueSet( void );
static void prvTestQueueOverwriteFromISROnTwoQueuesInQueueSet( void );
/*
* Local pseudo random number seed and return functions. Used to avoid calls
* to the standard library.
*/
static size_t prvRand( void );
static void prvSRand( size_t uxSeed );
/*-----------------------------------------------------------*/
/* The queues that are added to the set. */
static QueueHandle_t xQueues[ queuesetNUM_QUEUES_IN_SET ] = { 0 };
/* Counts how many times each queue in the set is used to ensure all the
* queues are used. */
static uint32_t ulQueueUsedCounter[ queuesetNUM_QUEUES_IN_SET ] = { 0 };
/* The handle of the queue set to which the queues are added. */
static QueueSetHandle_t xQueueSet;
/* If the prvQueueSetReceivingTask() task has not detected any errors then
* it increments ulCycleCounter on each iteration.
* xAreQueueSetTasksStillRunning() returns pdPASS if the value of
* ulCycleCounter has changed between consecutive calls, and pdFALSE if
* ulCycleCounter has stopped incrementing (indicating an error condition). */
static volatile uint32_t ulCycleCounter = 0UL;
/* Set to pdFAIL if an error is detected by any queue set task.
* ulCycleCounter will only be incremented if xQueueSetTasksStatus equals pdPASS. */
static volatile BaseType_t xQueueSetTasksStatus = pdPASS;
/* Just a flag to let the function that writes to a queue from an ISR know that
* the queues are setup and can be used. */
static volatile BaseType_t xSetupComplete = pdFALSE;
/* The value sent to the queue from the ISR is file scope so the
* xAreQueueSetTasksStillRunning() function can check it is incrementing as
* expected. */
static volatile uint32_t ulISRTxValue = queuesetINITIAL_ISR_TX_VALUE;
/* Used by the pseudo random number generator. */
static size_t uxNextRand = 0;
/* The task handles are stored so their priorities can be changed. */
TaskHandle_t xQueueSetSendingTask, xQueueSetReceivingTask;
/*-----------------------------------------------------------*/
void vStartQueueSetTasks( void )
{
/* Create the tasks. */
xTaskCreate( prvQueueSetSendingTask, "SetTx", configMINIMAL_STACK_SIZE, NULL, queuesetMEDIUM_PRIORITY, &xQueueSetSendingTask );
if( xQueueSetSendingTask != NULL )
{
xTaskCreate( prvQueueSetReceivingTask, "SetRx", configMINIMAL_STACK_SIZE, ( void * ) xQueueSetSendingTask, queuesetMEDIUM_PRIORITY, &xQueueSetReceivingTask );
/* It is important that the sending task does not attempt to write to a
* queue before the queue has been created. It is therefore placed into
* the suspended state before the scheduler has started. It is resumed by
* the receiving task after the receiving task has created the queues and
* added the queues to the queue set. */
vTaskSuspend( xQueueSetSendingTask );
}
}
/*-----------------------------------------------------------*/
BaseType_t xAreQueueSetTasksStillRunning( void )
{
static uint32_t ulLastCycleCounter, ulLastISRTxValue = 0;
static uint32_t ulLastQueueUsedCounter[ queuesetNUM_QUEUES_IN_SET ] = { 0 };
BaseType_t xReturn = pdPASS, x;
if( ulLastCycleCounter == ulCycleCounter )
{
/* The cycle counter is no longer being incremented. Either one of the
* tasks is stalled or an error has been detected. */
xReturn = pdFAIL;
}
ulLastCycleCounter = ulCycleCounter;
/* Ensure that all the queues in the set have been used. This ensures the
* test is working as intended and guards against the rand() in the Tx task
* missing some values. */
for( x = 0; x < queuesetNUM_QUEUES_IN_SET; x++ )
{
if( ulLastQueueUsedCounter[ x ] == ulQueueUsedCounter[ x ] )
{
xReturn = pdFAIL;
}
ulLastQueueUsedCounter[ x ] = ulQueueUsedCounter[ x ];
}
/* Check the global status flag. */
if( xQueueSetTasksStatus != pdPASS )
{
xReturn = pdFAIL;
}
/* Check that the ISR is still sending values to the queues too. */
if( ulISRTxValue == ulLastISRTxValue )
{
xReturn = pdFAIL;
}
else
{
ulLastISRTxValue = ulISRTxValue;
}
return xReturn;
}
/*-----------------------------------------------------------*/
static void prvQueueSetSendingTask( void * pvParameters )
{
uint32_t ulTaskTxValue = 0;
size_t uxQueueToWriteTo;
QueueHandle_t xQueueInUse;
/* Remove compiler warning about the unused parameter. */
( void ) pvParameters;
/* Seed mini pseudo random number generator. */
prvSRand( ( size_t ) &ulTaskTxValue );
for( ; ; )
{
/* Generate the index for the queue to which a value is to be sent. */
uxQueueToWriteTo = prvRand() % queuesetNUM_QUEUES_IN_SET;
xQueueInUse = xQueues[ uxQueueToWriteTo ];
/* Note which index is being written to to ensure all the queues are
* used. */
( ulQueueUsedCounter[ uxQueueToWriteTo ] )++;
/* Send to the queue to unblock the task that is waiting for data to
* arrive on a queue within the queue set to which this queue belongs. */
if( xQueueSendToBack( xQueueInUse, &ulTaskTxValue, portMAX_DELAY ) != pdPASS )
{
/* The send should always pass as an infinite block time was
* used. */
xQueueSetTasksStatus = pdFAIL;
}
#if ( configUSE_PREEMPTION == 0 )
taskYIELD();
#endif
ulTaskTxValue++;
/* If the Tx value has reached the range used by the ISR then set it
* back to 0. */
if( ulTaskTxValue == queuesetINITIAL_ISR_TX_VALUE )
{
ulTaskTxValue = 0;
}
/* Increase test coverage by occasionally change the priorities of the
* two tasks relative to each other. */
prvChangeRelativePriorities();
}
}
/*-----------------------------------------------------------*/
static void prvChangeRelativePriorities( void )
{
static UBaseType_t ulLoops = 0;
static eRelativePriorities ePriorities = eEqualPriority;
/* Occasionally change the task priority relative to the priority of
* the receiving task. */
ulLoops++;
if( ulLoops >= queuesetPRIORITY_CHANGE_LOOPS )
{
ulLoops = 0;
switch( ePriorities )
{
case eEqualPriority:
/* Both tasks are running with medium priority. Now lower the
* priority of the receiving task so the Tx task has the higher
* relative priority. */
vTaskPrioritySet( xQueueSetReceivingTask, queuesetLOW_PRIORITY );
ePriorities = eTxHigherPriority;
break;
case eTxHigherPriority:
/* The Tx task is running with a higher priority than the Rx
* task. Switch the priorities around so the Rx task has the
* higher relative priority. */
vTaskPrioritySet( xQueueSetReceivingTask, queuesetMEDIUM_PRIORITY );
vTaskPrioritySet( xQueueSetSendingTask, queuesetLOW_PRIORITY );
ePriorities = eTxLowerPriority;
break;
case eTxLowerPriority:
/* The Tx task is running with a lower priority than the Rx
* task. Make the priorities equal again. */
vTaskPrioritySet( xQueueSetSendingTask, queuesetMEDIUM_PRIORITY );
ePriorities = eEqualPriority;
/* When both tasks are using a non-idle priority the queue set
* tasks will starve idle priority tasks of execution time - so
* relax a bit before the next iteration to minimise the impact. */
vTaskDelay( queuesetTX_LOOP_DELAY );
break;
}
}
}
/*-----------------------------------------------------------*/
static void prvQueueSetReceivingTask( void * pvParameters )
{
uint32_t ulReceived;
QueueHandle_t xActivatedQueue;
TickType_t xBlockTime;
/* Remove compiler warnings. */
( void ) pvParameters;
/* Create the queues and add them to the queue set before resuming the Tx
* task. */
prvSetupTest();
for( ; ; )
{
/* For test coverage reasons, the block time is dependent on the
* priority of this task - which changes during the test. When the task
* is at the idle priority it polls the queue set. */
if( uxTaskPriorityGet( NULL ) == tskIDLE_PRIORITY )
{
xBlockTime = 0;
}
else
{
xBlockTime = portMAX_DELAY;
}
/* Wait for a message to arrive on one of the queues in the set. */
xActivatedQueue = xQueueSelectFromSet( xQueueSet, portMAX_DELAY );
if( xActivatedQueue == NULL )
{
if( xBlockTime != 0 )
{
/* This should not happen as an infinite delay was used. */
xQueueSetTasksStatus = pdFAIL;
}
}
else
{
/* Reading from the queue should pass with a zero block time as
* this task will only run when something has been posted to a task
* in the queue set. */
if( xQueueReceive( xActivatedQueue, &ulReceived, queuesetDONT_BLOCK ) != pdPASS )
{
xQueueSetTasksStatus = pdFAIL;
}
/* Ensure the value received was the value expected. This function
* manipulates file scope data and is also called from an ISR, hence
* the critical section. */
taskENTER_CRITICAL();
{
prvCheckReceivedValue( ulReceived );
}
taskEXIT_CRITICAL();
if( xQueueSetTasksStatus == pdPASS )
{
ulCycleCounter++;
}
}
}
}
/*-----------------------------------------------------------*/
void vQueueSetAccessQueueSetFromISR( void )
{
static uint32_t ulCallCount = 0;
/* xSetupComplete is set to pdTRUE when the queues have been created and
* are available for use. */
if( xSetupComplete == pdTRUE )
{
/* It is intended that this function is called from the tick hook
* function, so each call is one tick period apart. */
ulCallCount++;
if( ulCallCount > queuesetISR_TX_PERIOD )
{
ulCallCount = 0;
/* First attempt to read from the queue set. */
prvReceiveFromQueueInSetFromISR();
/* Then write to the queue set. */
prvSendToQueueInSetFromISR();
}
}
}
/*-----------------------------------------------------------*/
static void prvCheckReceivedValue( uint32_t ulReceived )
{
static uint32_t ulExpectedReceivedFromTask = 0, ulExpectedReceivedFromISR = queuesetINITIAL_ISR_TX_VALUE;
/* Values are received in tasks and interrupts. It is likely that the
* receiving task will sometimes get preempted by the receiving interrupt
* between reading a value from the queue and calling this function. When
* that happens, if the receiving interrupt calls this function the values
* will get passed into this function slightly out of order. For that
* reason the value passed in is tested against a small range of expected
* values, rather than a single absolute value. To make the range testing
* easier values in the range limits are ignored. */
/* If the received value is equal to or greater than
* queuesetINITIAL_ISR_TX_VALUE then it was sent by an ISR. */
if( ulReceived >= queuesetINITIAL_ISR_TX_VALUE )
{
/* The value was sent from the ISR. */
if( ( ulReceived - queuesetINITIAL_ISR_TX_VALUE ) < queuesetIGNORED_BOUNDARY )
{
/* The value received is at the lower limit of the expected range.
* Don't test it and expect to receive one higher next time. */
}
else if( ( ULONG_MAX - ulReceived ) <= queuesetIGNORED_BOUNDARY )
{
/* The value received is at the higher limit of the expected range.
* Don't test it and expect to wrap soon. */
}
else
{
/* Check the value against its expected value range. */
if( prvCheckReceivedValueWithinExpectedRange( ulReceived, ulExpectedReceivedFromISR ) != pdPASS )
{
xQueueSetTasksStatus = pdFAIL;
}
}
configASSERT( xQueueSetTasksStatus );
/* It is expected to receive an incrementing number. */
ulExpectedReceivedFromISR++;
if( ulExpectedReceivedFromISR == 0 )
{
ulExpectedReceivedFromISR = queuesetINITIAL_ISR_TX_VALUE;
}
}
else
{
/* The value was sent from the Tx task. */
if( ulReceived < queuesetIGNORED_BOUNDARY )
{
/* The value received is at the lower limit of the expected range.
* Don't test it, and expect to receive one higher next time. */
}
else if( ( ( queuesetINITIAL_ISR_TX_VALUE - 1 ) - ulReceived ) <= queuesetIGNORED_BOUNDARY )
{
/* The value received is at the higher limit of the expected range.
* Don't test it and expect to wrap soon. */
}
else
{
/* Check the value against its expected value range. */
if( prvCheckReceivedValueWithinExpectedRange( ulReceived, ulExpectedReceivedFromTask ) != pdPASS )
{
xQueueSetTasksStatus = pdFAIL;
}
}
configASSERT( xQueueSetTasksStatus );
/* It is expected to receive an incrementing number. */
ulExpectedReceivedFromTask++;
if( ulExpectedReceivedFromTask >= queuesetINITIAL_ISR_TX_VALUE )
{
ulExpectedReceivedFromTask = 0;
}
}
}
/*-----------------------------------------------------------*/
static BaseType_t prvCheckReceivedValueWithinExpectedRange( uint32_t ulReceived,
uint32_t ulExpectedReceived )
{
BaseType_t xReturn = pdPASS;
if( ulReceived > ulExpectedReceived )
{
configASSERT( ( ulReceived - ulExpectedReceived ) <= queuesetALLOWABLE_RX_DEVIATION );
if( ( ulReceived - ulExpectedReceived ) > queuesetALLOWABLE_RX_DEVIATION )
{
xReturn = pdFALSE;
}
}
else
{
configASSERT( ( ulExpectedReceived - ulReceived ) <= queuesetALLOWABLE_RX_DEVIATION );
if( ( ulExpectedReceived - ulReceived ) > queuesetALLOWABLE_RX_DEVIATION )
{
xReturn = pdFALSE;
}
}
return xReturn;
}
/*-----------------------------------------------------------*/
static void prvReceiveFromQueueInSetFromISR( void )
{
QueueSetMemberHandle_t xActivatedQueue;
uint32_t ulReceived;
/* See if any of the queues in the set contain data. */
xActivatedQueue = xQueueSelectFromSetFromISR( xQueueSet );
if( xActivatedQueue != NULL )
{
/* Reading from the queue for test purposes only. */
if( xQueueReceiveFromISR( xActivatedQueue, &ulReceived, NULL ) != pdPASS )
{
/* Data should have been available as the handle was returned from
* xQueueSelectFromSetFromISR(). */
xQueueSetTasksStatus = pdFAIL;
}
/* Ensure the value received was the value expected. */
prvCheckReceivedValue( ulReceived );
}
}
/*-----------------------------------------------------------*/
static void prvSendToQueueInSetFromISR( void )
{
static BaseType_t xQueueToWriteTo = 0;
uint32_t ulTxValueSnapshot = ulISRTxValue;
if( xQueueSendFromISR( xQueues[ xQueueToWriteTo ], ( void * ) &ulTxValueSnapshot, NULL ) == pdPASS )
{
ulISRTxValue++;
/* If the Tx value has wrapped then set it back to its initial value. */
if( ulISRTxValue == 0UL )
{
ulISRTxValue = queuesetINITIAL_ISR_TX_VALUE;
}
/* Use a different queue next time. */
xQueueToWriteTo++;
if( xQueueToWriteTo >= queuesetNUM_QUEUES_IN_SET )
{
xQueueToWriteTo = 0;
}
}
}
/*-----------------------------------------------------------*/
static void prvTestQueueOverwriteWithQueueSet( void )
{
uint32_t ulValueToSend = 0, ulValueReceived = 0;
QueueHandle_t xQueueHandle = NULL, xReceivedHandle = NULL;
const UBaseType_t xLengthOfOne = ( UBaseType_t ) 1;
/* Create a queue that has a length of one - a requirement in order to call
* xQueueOverwrite. This will get deleted again when this test completes. */
xQueueHandle = xQueueCreate( xLengthOfOne, sizeof( uint32_t ) );
configASSERT( xQueueHandle );
if( xQueueHandle != NULL )
{
xQueueAddToSet( xQueueHandle, xQueueSet );
/* Add an item to the queue then ensure the queue set correctly
* indicates that one item is available, and that item is indeed the
* queue written to. */
xQueueOverwrite( xQueueHandle, ( void * ) &ulValueToSend );
if( uxQueueMessagesWaiting( xQueueSet ) != ( UBaseType_t ) 1 )
{
/* Expected one item in the queue set. */
xQueueSetTasksStatus = pdFAIL;
}
xQueuePeek( xQueueSet, &xReceivedHandle, queuesetDONT_BLOCK );
if( xReceivedHandle != xQueueHandle )
{
/* Wrote to xQueueHandle so expected xQueueHandle to be the handle
* held in the queue set. */
xQueueSetTasksStatus = pdFAIL;
}
/* Now overwrite the value in the queue and ensure the queue set state
* doesn't change as the number of items in the queues within the set have
* not changed. */
ulValueToSend++;
xQueueOverwrite( xQueueHandle, ( void * ) &ulValueToSend );
if( uxQueueMessagesWaiting( xQueueSet ) != ( UBaseType_t ) 1 )
{
/* Still expected one item in the queue set. */
xQueueSetTasksStatus = pdFAIL;
}
xReceivedHandle = xQueueSelectFromSet( xQueueSet, queuesetDONT_BLOCK );
if( xReceivedHandle != xQueueHandle )
{
/* Wrote to xQueueHandle so expected xQueueHandle to be the handle
* held in the queue set. */
xQueueSetTasksStatus = pdFAIL;
}
/* Also ensure the value received from the queue is the overwritten
* value, not the value originally written. */
xQueueReceive( xQueueHandle, &ulValueReceived, queuesetDONT_BLOCK );
if( ulValueReceived != ulValueToSend )
{
/* Unexpected value received from the queue. */
xQueueSetTasksStatus = pdFAIL;
}
/* Should be anything in the queue set now. */
if( uxQueueMessagesWaiting( xQueueSet ) != ( UBaseType_t ) 0 )
{
xQueueSetTasksStatus = pdFAIL;
}
xReceivedHandle = xQueueSelectFromSet( xQueueSet, queuesetDONT_BLOCK );
if( xReceivedHandle != NULL )
{
xQueueSetTasksStatus = pdFAIL;
}
/* Clean up. */
xQueueRemoveFromSet( xQueueHandle, xQueueSet );
vQueueDelete( xQueueHandle );
}
}
/*-----------------------------------------------------------*/
static void prvTestQueueOverwriteOnTwoQueuesInQueueSet( void )
{
uint32_t ulValueToSend1 = 1, ulValueToSend2 = 2UL, ulValueReceived = 0;
QueueHandle_t xQueueHandle1 = NULL, xQueueHandle2 = NULL, xReceivedHandle = NULL;
const UBaseType_t xLengthOfOne = ( UBaseType_t ) 1;
/* Create two queues that have a length of one - a requirement in order to call
* xQueueOverwrite. These will get deleted again when this test completes. */
xQueueHandle1 = xQueueCreate( xLengthOfOne, sizeof( uint32_t ) );
configASSERT( xQueueHandle1 );
xQueueHandle2 = xQueueCreate( xLengthOfOne, sizeof( uint32_t ) );
configASSERT( xQueueHandle2 );
if( ( xQueueHandle1 != NULL ) && ( xQueueHandle2 != NULL ) )
{
/* Add both queues to the queue set. */
xQueueAddToSet( xQueueHandle1, xQueueSet );
xQueueAddToSet( xQueueHandle2, xQueueSet );
/* Add an item using the first queue. */
xQueueOverwrite( xQueueHandle1, ( void * ) &ulValueToSend1 );
if( uxQueueMessagesWaiting( xQueueSet ) != ( UBaseType_t ) 1 )
{
/* Expected one item in the queue set. */
xQueueSetTasksStatus = pdFAIL;
}
xQueuePeek( xQueueSet, &xReceivedHandle, queuesetDONT_BLOCK );
if( xReceivedHandle != xQueueHandle1 )
{
/* Wrote to xQueueHandle so expected xQueueHandle to be the handle
* held in the queue set. */
xQueueSetTasksStatus = pdFAIL;
}
/* Next add an item to the second queue. */
xQueueOverwrite( xQueueHandle2, ( void * ) &ulValueToSend2 );
if( uxQueueMessagesWaiting( xQueueSet ) != ( UBaseType_t ) 2 )
{
/* Expected two items in the queue set. */
xQueueSetTasksStatus = pdFAIL;
}
/* The head of the queue set should not have changed though. */
xQueuePeek( xQueueSet, &xReceivedHandle, queuesetDONT_BLOCK );
if( xReceivedHandle != xQueueHandle1 )
{
/* Wrote to xQueueHandle so expected xQueueHandle to be the handle
* held in the queue set. */
xQueueSetTasksStatus = pdFAIL;
}
/* Now overwrite the value in the queue and ensure the queue set state
* doesn't change as the number of items in the queues within the set have
* not changed. NOTE: after this queue 1 should hold ulValueToSend2 and queue
* 2 should hold the value ulValueToSend1. */
xQueueOverwrite( xQueueHandle1, ( void * ) &ulValueToSend2 );
if( uxQueueMessagesWaiting( xQueueSet ) != ( UBaseType_t ) 2 )
{
/* Still expected two items in the queue set. */
xQueueSetTasksStatus = pdFAIL;
}
xQueueOverwrite( xQueueHandle2, ( void * ) &ulValueToSend1 );
if( uxQueueMessagesWaiting( xQueueSet ) != ( UBaseType_t ) 2 )
{
/* Still expected two items in the queue set. */
xQueueSetTasksStatus = pdFAIL;
}
/* Repeat the above to ensure the queue set state doesn't change. */
xQueueOverwrite( xQueueHandle1, ( void * ) &ulValueToSend2 );
if( uxQueueMessagesWaiting( xQueueSet ) != ( UBaseType_t ) 2 )
{
/* Still expected two items in the queue set. */
xQueueSetTasksStatus = pdFAIL;
}
xQueueOverwrite( xQueueHandle2, ( void * ) &ulValueToSend1 );
if( uxQueueMessagesWaiting( xQueueSet ) != ( UBaseType_t ) 2 )
{
/* Still expected two items in the queue set. */
xQueueSetTasksStatus = pdFAIL;
}
/* Now when reading from the queue set we expect the handle to the first
* queue to be received first, and for that queue to hold ulValueToSend2 as the
* originally written value was overwritten. Likewise the second handle received
* from the set should be that of the second queue, and that queue should hold
* ulValueToSend1 as the originally written value was overwritten. */
xReceivedHandle = xQueueSelectFromSet( xQueueSet, queuesetDONT_BLOCK );
if( xReceivedHandle != xQueueHandle1 )
{
/* Wrote to xQueueHandle1 first so expected that handle to be read from
* the set first. */
xQueueSetTasksStatus = pdFAIL;
}
if( uxQueueMessagesWaiting( xQueueSet ) != ( UBaseType_t ) 1 )
{
/* One value was read from the set, so now only expect a single value
* in the set. */
xQueueSetTasksStatus = pdFAIL;
}
xQueueReceive( xReceivedHandle, &ulValueReceived, queuesetDONT_BLOCK );
if( ulValueReceived != ulValueToSend2 )
{
/* Unexpected value received from the queue. ulValueToSend1 was written
* first, but then overwritten with ulValueToSend2; */
xQueueSetTasksStatus = pdFAIL;
}
xReceivedHandle = xQueueSelectFromSet( xQueueSet, queuesetDONT_BLOCK );
if( xReceivedHandle != xQueueHandle2 )
{
/* xQueueHandle1 has already been removed from the set so expect only
* xQueueHandle2 to be left. */
xQueueSetTasksStatus = pdFAIL;
}
if( uxQueueMessagesWaiting( xQueueSet ) != ( UBaseType_t ) 0 )
{
/* The last value was read from the set so don't expect any more. */
xQueueSetTasksStatus = pdFAIL;
}
xQueueReceive( xReceivedHandle, &ulValueReceived, queuesetDONT_BLOCK );
if( ulValueReceived != ulValueToSend1 )
{
/* Unexpected value received from the queue. ulValueToSend2 was written
* first, but then overwritten with ulValueToSend1. */
xQueueSetTasksStatus = pdFAIL;
}
/* Should be anything in the queue set now. */
xReceivedHandle = xQueueSelectFromSet( xQueueSet, queuesetDONT_BLOCK );
if( xReceivedHandle != NULL )
{
xQueueSetTasksStatus = pdFAIL;
}
/* Clean up. */
xQueueRemoveFromSet( xQueueHandle1, xQueueSet );
xQueueRemoveFromSet( xQueueHandle2, xQueueSet );
vQueueDelete( xQueueHandle1 );
vQueueDelete( xQueueHandle2 );
}
}
/*-----------------------------------------------------------*/
static void prvTestQueueOverwriteFromISROnTwoQueuesInQueueSet( void )
{
uint32_t ulValueToSend1 = 1, ulValueToSend2 = 2UL, ulValueReceived = 0;
QueueHandle_t xQueueHandle1 = NULL, xQueueHandle2 = NULL, xReceivedHandle = NULL;
const UBaseType_t xLengthOfOne = ( UBaseType_t ) 1;
/* Create two queues that have a length of one - a requirement in order to call
* xQueueOverwrite. These will get deleted again when this test completes. */
xQueueHandle1 = xQueueCreate( xLengthOfOne, sizeof( uint32_t ) );
configASSERT( xQueueHandle1 );
xQueueHandle2 = xQueueCreate( xLengthOfOne, sizeof( uint32_t ) );
configASSERT( xQueueHandle2 );
if( ( xQueueHandle1 != NULL ) && ( xQueueHandle2 != NULL ) )
{
/* Add both queues to the queue set. */
xQueueAddToSet( xQueueHandle1, xQueueSet );
xQueueAddToSet( xQueueHandle2, xQueueSet );
/* Add an item using the first queue using the 'FromISR' version of the
* overwrite function. */
xQueueOverwriteFromISR( xQueueHandle1, ( void * ) &ulValueToSend1, NULL );
if( uxQueueMessagesWaiting( xQueueSet ) != ( UBaseType_t ) 1 )
{
/* Expected one item in the queue set. */
xQueueSetTasksStatus = pdFAIL;
}
xQueuePeek( xQueueSet, &xReceivedHandle, queuesetDONT_BLOCK );
if( xReceivedHandle != xQueueHandle1 )
{
/* Wrote to xQueueHandle so expected xQueueHandle to be the handle
* held in the queue set. */
xQueueSetTasksStatus = pdFAIL;
}
/* Next add an item to the second queue using the 'FromISR' version of the
* overwrite function. */
xQueueOverwriteFromISR( xQueueHandle2, ( void * ) &ulValueToSend2, NULL );
if( uxQueueMessagesWaiting( xQueueSet ) != ( UBaseType_t ) 2 )
{
/* Expected two items in the queue set. */
xQueueSetTasksStatus = pdFAIL;
}
/* The head of the queue set should not have changed though. */
xQueuePeek( xQueueSet, &xReceivedHandle, queuesetDONT_BLOCK );
if( xReceivedHandle != xQueueHandle1 )
{
/* Wrote to xQueueHandle so expected xQueueHandle to be the handle
* held in the queue set. */
xQueueSetTasksStatus = pdFAIL;
}
/* Now overwrite the value in the queue and ensure the queue set state
* doesn't change as the number of items in the queues within the set have
* not changed. NOTE: after this queue 1 should hold ulValueToSend2 and queue
* 2 should hold the value ulValueToSend1. */
xQueueOverwriteFromISR( xQueueHandle1, ( void * ) &ulValueToSend2, NULL );
if( uxQueueMessagesWaiting( xQueueSet ) != ( UBaseType_t ) 2 )
{
/* Still expected two items in the queue set. */
xQueueSetTasksStatus = pdFAIL;
}
xQueueOverwriteFromISR( xQueueHandle2, ( void * ) &ulValueToSend1, NULL );
if( uxQueueMessagesWaiting( xQueueSet ) != ( UBaseType_t ) 2 )
{
/* Still expected two items in the queue set. */
xQueueSetTasksStatus = pdFAIL;
}
/* Repeat the above to ensure the queue set state doesn't change. */
xQueueOverwriteFromISR( xQueueHandle1, ( void * ) &ulValueToSend2, NULL );
if( uxQueueMessagesWaiting( xQueueSet ) != ( UBaseType_t ) 2 )
{
/* Still expected two items in the queue set. */
xQueueSetTasksStatus = pdFAIL;
}
xQueueOverwriteFromISR( xQueueHandle2, ( void * ) &ulValueToSend1, NULL );
if( uxQueueMessagesWaiting( xQueueSet ) != ( UBaseType_t ) 2 )
{
/* Still expected two items in the queue set. */
xQueueSetTasksStatus = pdFAIL;
}
/* Now when reading from the queue set we expect the handle to the first
* queue to be received first, and for that queue to hold ulValueToSend2 as the
* originally written value was overwritten. Likewise the second handle received
* from the set should be that of the second queue, and that queue should hold
* ulValueToSend1 as the originally written value was overwritten. */
xReceivedHandle = xQueueSelectFromSet( xQueueSet, queuesetDONT_BLOCK );
if( xReceivedHandle != xQueueHandle1 )
{
/* Wrote to xQueueHandle1 first so expected that handle to be read from
* the set first. */
xQueueSetTasksStatus = pdFAIL;
}
if( uxQueueMessagesWaiting( xQueueSet ) != ( UBaseType_t ) 1 )
{
/* One value was read from the set, so now only expect a single value
* in the set. */
xQueueSetTasksStatus = pdFAIL;
}
xQueueReceive( xReceivedHandle, &ulValueReceived, queuesetDONT_BLOCK );
if( ulValueReceived != ulValueToSend2 )
{
/* Unexpected value received from the queue. ulValueToSend1 was written
* first, but then overwritten with ulValueToSend2; */
xQueueSetTasksStatus = pdFAIL;
}
xReceivedHandle = xQueueSelectFromSet( xQueueSet, queuesetDONT_BLOCK );
if( xReceivedHandle != xQueueHandle2 )
{
/* xQueueHandle1 has already been removed from the set so expect only
* xQueueHandle2 to be left. */
xQueueSetTasksStatus = pdFAIL;
}
if( uxQueueMessagesWaiting( xQueueSet ) != ( UBaseType_t ) 0 )
{
/* The last value was read from the set so don't expect any more. */
xQueueSetTasksStatus = pdFAIL;
}
xQueueReceive( xReceivedHandle, &ulValueReceived, queuesetDONT_BLOCK );
if( ulValueReceived != ulValueToSend1 )
{
/* Unexpected value received from the queue. ulValueToSend2 was written
* first, but then overwritten with ulValueToSend1. */
xQueueSetTasksStatus = pdFAIL;
}
/* Should be anything in the queue set now. */
xReceivedHandle = xQueueSelectFromSet( xQueueSet, queuesetDONT_BLOCK );
if( xReceivedHandle != NULL )
{
xQueueSetTasksStatus = pdFAIL;
}
/* Clean up. */
xQueueRemoveFromSet( xQueueHandle1, xQueueSet );
xQueueRemoveFromSet( xQueueHandle2, xQueueSet );
vQueueDelete( xQueueHandle1 );
vQueueDelete( xQueueHandle2 );
}
}
/*-----------------------------------------------------------*/
static void prvSetupTest( void )
{
BaseType_t x;
uint32_t ulValueToSend = 0;
/* Ensure the queues are created and the queue set configured before the
* sending task is unsuspended.
*
* First Create the queue set such that it will be able to hold a message for
* every space in every queue in the set. */
xQueueSet = xQueueCreateSet( queuesetNUM_QUEUES_IN_SET * queuesetQUEUE_LENGTH );
for( x = 0; x < queuesetNUM_QUEUES_IN_SET; x++ )
{
/* Create the queue and add it to the set. The queue is just holding
* uint32_t value. */
xQueues[ x ] = xQueueCreate( queuesetQUEUE_LENGTH, sizeof( uint32_t ) );
configASSERT( xQueues[ x ] );
if( xQueueAddToSet( xQueues[ x ], xQueueSet ) != pdPASS )
{
xQueueSetTasksStatus = pdFAIL;
}
else
{
/* The queue has now been added to the queue set and cannot be added to
* another. */
if( xQueueAddToSet( xQueues[ x ], xQueueSet ) != pdFAIL )
{
xQueueSetTasksStatus = pdFAIL;
}
}
}
/* Attempt to remove a queue from a queue set it does not belong
* to (NULL being passed as the queue set in this case). */
if( xQueueRemoveFromSet( xQueues[ 0 ], NULL ) != pdFAIL )
{
/* It is not possible to successfully remove a queue from a queue
* set it does not belong to. */
xQueueSetTasksStatus = pdFAIL;
}
/* Attempt to remove a queue from the queue set it does belong to. */
if( xQueueRemoveFromSet( xQueues[ 0 ], xQueueSet ) != pdPASS )
{
/* It should be possible to remove the queue from the queue set it
* does belong to. */
xQueueSetTasksStatus = pdFAIL;
}
/* Add an item to the queue before attempting to add it back into the
* set. */
xQueueSend( xQueues[ 0 ], ( void * ) &ulValueToSend, 0 );
if( xQueueAddToSet( xQueues[ 0 ], xQueueSet ) != pdFAIL )
{
/* Should not be able to add a non-empty queue to a set. */
xQueueSetTasksStatus = pdFAIL;
}
/* Remove the item from the queue before adding the queue back into the
* set so the dynamic tests can begin. */
xQueueReceive( xQueues[ 0 ], &ulValueToSend, 0 );
if( xQueueAddToSet( xQueues[ 0 ], xQueueSet ) != pdPASS )
{
/* If the queue was successfully removed from the queue set then it
* should be possible to add it back in again. */
xQueueSetTasksStatus = pdFAIL;
}
/* The task that sends to the queues is not running yet, so attempting to
* read from the queue set should fail. */
if( xQueueSelectFromSet( xQueueSet, queuesetSHORT_DELAY ) != NULL )
{
xQueueSetTasksStatus = pdFAIL;
}
/* Testing the behaviour of queue sets when a queue overwrite operation is
* performed on a set member requires a special test as overwrites can only
* be performed on queues that have a length of 1. */
prvTestQueueOverwriteWithQueueSet();
/* Test the case where two queues within a set are written to with
* xQueueOverwrite(). */
prvTestQueueOverwriteOnTwoQueuesInQueueSet();
prvTestQueueOverwriteFromISROnTwoQueuesInQueueSet();
/* In case any of the above have already indicated a failure. */
configASSERT( xQueueSetTasksStatus != pdFAIL );
/* Resume the task that writes to the queues. */
vTaskResume( xQueueSetSendingTask );
/* Let the ISR access the queues also. */
xSetupComplete = pdTRUE;
}
/*-----------------------------------------------------------*/
static size_t prvRand( void )
{
uxNextRand = ( uxNextRand * ( size_t ) 1103515245 ) + ( size_t ) 12345;
return ( uxNextRand / ( size_t ) 65536 ) % ( size_t ) 32768;
}
/*-----------------------------------------------------------*/
static void prvSRand( size_t uxSeed )
{
uxNextRand = uxSeed;
}
#endif /* ( configUSE_QUEUE_SETS == 1 ) */