/* * 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 #include /* 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 ) */