From 3b02b4c8f8150709865f4d7b4f71174f2cddff2a Mon Sep 17 00:00:00 2001
From: Richard Barry <ribarry@amazon.com>
Date: Thu, 27 Jun 2013 14:25:17 +0000
Subject: [PATCH] Add xQueueOverwriteFromISR() and update the QueueOverwrite.c
 to demonstrate its use.

---
 FreeRTOS/Demo/Common/Minimal/QueueOverwrite.c | 93 ++++++++++++++++---
 FreeRTOS/Demo/Common/include/QueueOverwrite.h |  1 +
 FreeRTOS/Demo/WIN32-MSVC/main.c               |  3 +
 FreeRTOS/Source/include/queue.h               | 78 ++++++++++++++++
 FreeRTOS/Source/queue.c                       |  3 +-
 5 files changed, 165 insertions(+), 13 deletions(-)

diff --git a/FreeRTOS/Demo/Common/Minimal/QueueOverwrite.c b/FreeRTOS/Demo/Common/Minimal/QueueOverwrite.c
index e554201bd..e33dcdbff 100644
--- a/FreeRTOS/Demo/Common/Minimal/QueueOverwrite.c
+++ b/FreeRTOS/Demo/Common/Minimal/QueueOverwrite.c
@@ -98,18 +98,33 @@ static void prvQueueOverwriteTask( void *pvParameters );
 prvQueueOverwriteTask() has not found any errors. */
 static unsigned long ulLoopCounter = 0;
 
+/* Set to pdFALSE if an error is discovered by the
+vQueueOverwritePeriodicISRDemo() function. */
+static portBASE_TYPE xISRTestStatus = pdPASS;
+
+/* The queue that is accessed from the ISR.  The queue accessed by the task is
+created inside the task itself. */
+static xQueueHandle xISRQueue = NULL;
+
 /*-----------------------------------------------------------*/
 
 void vStartQueueOverwriteTask( unsigned portBASE_TYPE uxPriority )
 {
-	/* Create the test task. */
+const unsigned portBASE_TYPE uxQueueLength = 1;
+
+	/* Create the queue used by the ISR.  xQueueOverwriteFromISR() should only
+	be used on queues that have a length of 1. */
+	xISRQueue = xQueueCreate( uxQueueLength, ( unsigned portBASE_TYPE ) sizeof( unsigned long ) );
+
+	/* Create the test task.  The queue used by the test task is created inside
+	the task itself. */
 	xTaskCreate( prvQueueOverwriteTask, ( signed char * ) "QOver", configMINIMAL_STACK_SIZE, NULL, uxPriority, ( xTaskHandle * ) NULL );
 }
 /*-----------------------------------------------------------*/
 
 static void prvQueueOverwriteTask( void *pvParameters )
 {
-xQueueHandle xQueue;
+xQueueHandle xTaskQueue;
 const unsigned portBASE_TYPE uxQueueLength = 1;
 unsigned long ulValue, ulStatus = pdPASS, x;
 
@@ -118,18 +133,18 @@ unsigned long ulValue, ulStatus = pdPASS, x;
 
 	/* Create the queue.  xQueueOverwrite() should only be used on queues that
 	have a length of 1. */
-	xQueue = xQueueCreate( uxQueueLength, ( unsigned portBASE_TYPE ) sizeof( unsigned long ) );
-	configASSERT( xQueue );
+	xTaskQueue = xQueueCreate( uxQueueLength, ( unsigned portBASE_TYPE ) sizeof( unsigned long ) );
+	configASSERT( xTaskQueue );
 
 	for( ;; )
 	{
 		/* The queue is empty.  Writing to the queue then reading from the queue
 		should return the item written. */
 		ulValue = 10;
-		xQueueOverwrite( xQueue, &ulValue );
+		xQueueOverwrite( xTaskQueue, &ulValue );
 
 		ulValue = 0;
-		xQueueReceive( xQueue, &ulValue, qoDONT_BLOCK );
+		xQueueReceive( xTaskQueue, &ulValue, qoDONT_BLOCK );
 
 		if( ulValue != 10 )
 		{
@@ -141,27 +156,27 @@ unsigned long ulValue, ulStatus = pdPASS, x;
 		for( x = 0; x < qoLOOPS; x++ )
 		{
 			/* Write to the queue. */
-			xQueueOverwrite( xQueue, &x );
+			xQueueOverwrite( xTaskQueue, &x );
 
 			/* Check the value in the queue is that written, even though the
 			queue was not necessarily empty. */
-			xQueuePeek( xQueue, &ulValue, qoDONT_BLOCK );
+			xQueuePeek( xTaskQueue, &ulValue, qoDONT_BLOCK );
 			if( ulValue != x )
 			{
 				ulStatus = pdFAIL;
 			}
 
 			/* There should always be one item in the queue. */
-			if( uxQueueMessagesWaiting( xQueue ) != uxQueueLength )
+			if( uxQueueMessagesWaiting( xTaskQueue ) != uxQueueLength )
 			{
 				ulStatus = pdFAIL;
 			}
 		}
 
 		/* Empty the queue again. */
-		xQueueReceive( xQueue, &ulValue, qoDONT_BLOCK );
+		xQueueReceive( xTaskQueue, &ulValue, qoDONT_BLOCK );
 
-		if( uxQueueMessagesWaiting( xQueue ) != 0 )
+		if( uxQueueMessagesWaiting( xTaskQueue ) != 0 )
 		{
 			ulStatus = pdFAIL;
 		}
@@ -180,7 +195,11 @@ portBASE_TYPE xIsQueueOverwriteTaskStillRunning( void )
 {
 portBASE_TYPE xReturn;
 
-	if( ulLoopCounter > 0 )
+	if( xISRTestStatus != pdPASS )
+	{
+		xReturn = pdFAIL;
+	}
+	else if( ulLoopCounter > 0 )
 	{
 		xReturn = pdPASS;
 	}
@@ -194,4 +213,54 @@ portBASE_TYPE xReturn;
 
 	return xReturn;
 }
+/*-----------------------------------------------------------*/
+
+void vQueueOverwritePeriodicISRDemo( void )
+{
+static unsigned long ulCallCount = 0;
+const unsigned long ulTx1 = 10UL, ulTx2 = 20UL, ulNumberOfSwitchCases = 3UL;
+unsigned long ulRx;
+
+	/* This function should be called from an interrupt, such as the tick hook
+	function vApplicationTickHook(). */
+
+	configASSERT( xISRQueue );
+
+	switch( ulCallCount )
+	{
+		case 0:
+			/* The queue is empty.  Write ulTx1 to the queue.  In this demo the
+			last parameter is not used because there are no tasks blocked on
+			this queue. */
+			xQueueOverwriteFromISR( xISRQueue, &ulTx1, NULL );
+			break;
+
+		case 1:
+			/* The queue already holds ulTx1.  Overwrite the value in the queue
+			with ulTx2. */
+			xQueueOverwriteFromISR( xISRQueue, &ulTx2, NULL );
+			break;
+
+		case 2:
+			/* Read from the queue to empty the queue again.  The value read
+			should be ulTx2. */
+			xQueueReceiveFromISR( xISRQueue, &ulRx, NULL );
+
+			if( ulRx != ulTx2 )
+			{
+				xISRTestStatus = pdFAIL;
+			}
+			break;
+	}
+
+	/* Run the next case in the switch statement above next time this function
+	is called. */
+	ulCallCount++;
+
+	if( ulCallCount >= ulNumberOfSwitchCases )
+	{
+		/* Go back to the start. */
+		ulCallCount = 0;
+	}
+}
 
diff --git a/FreeRTOS/Demo/Common/include/QueueOverwrite.h b/FreeRTOS/Demo/Common/include/QueueOverwrite.h
index c4fba5ffb..fc1231229 100644
--- a/FreeRTOS/Demo/Common/include/QueueOverwrite.h
+++ b/FreeRTOS/Demo/Common/include/QueueOverwrite.h
@@ -77,6 +77,7 @@
 
 void vStartQueueOverwriteTask( unsigned portBASE_TYPE uxPriority );
 portBASE_TYPE xIsQueueOverwriteTaskStillRunning( void );
+void vQueueOverwritePeriodicISRDemo( void );
 
 #endif /* QUEUE_OVERWRITE_H */
 
diff --git a/FreeRTOS/Demo/WIN32-MSVC/main.c b/FreeRTOS/Demo/WIN32-MSVC/main.c
index 0dc4dcd45..b57938c7b 100644
--- a/FreeRTOS/Demo/WIN32-MSVC/main.c
+++ b/FreeRTOS/Demo/WIN32-MSVC/main.c
@@ -411,6 +411,9 @@ void vApplicationTickHook( void )
 	can be called from an ISR. */
 	vTimerPeriodicISRTests();
 
+	/* Call the periodic queue overwrite from ISR demo. */
+	vQueueOverwritePeriodicISRDemo();
+
 	/* Write to a queue that is in use as part of the queue set demo to 
 	demonstrate using queue sets from an ISR. */
 	vQueueSetAccessQueueSetFromISR();
diff --git a/FreeRTOS/Source/include/queue.h b/FreeRTOS/Source/include/queue.h
index f81a91fae..915e2ab99 100644
--- a/FreeRTOS/Source/include/queue.h
+++ b/FreeRTOS/Source/include/queue.h
@@ -1053,6 +1053,84 @@ void vQueueDelete( xQueueHandle xQueue ) PRIVILEGED_FUNCTION;
  */
 #define xQueueSendToBackFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK )
 
+/**
+ * queue. h
+ * <pre>
+ portBASE_TYPE xQueueOverwriteFromISR(
+							  xQueueHandle xQueue,
+							  const void * pvItemToQueue,
+							  portBASE_TYPE *pxHigherPriorityTaskWoken
+						 );
+ * </pre>
+ *
+ * A version of xQueueOverwrite() that can be used from an interrupt service
+ * routine (ISR).
+ *
+ * Only for use with queues that can hold a single item - so the queue is either
+ * empty or full.
+ *
+ * Post an item on a queue.  If the queue is already full then overwrite the
+ * value held in the queue.  The item is queued by copy, not by reference.
+ *
+ * @param xQueue The handle to the queue on which the item is to be posted.
+ *
+ * @param pvItemToQueue A pointer to the item that is to be placed on the
+ * queue.  The size of the items the queue will hold was defined when the
+ * queue was created, so this many bytes will be copied from pvItemToQueue
+ * into the queue storage area.
+ *
+ * @param pxHigherPriorityTaskWoken xQueueOverwriteFromISR() will set
+ * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task
+ * to unblock, and the unblocked task has a priority higher than the currently
+ * running task.  If xQueueSendFromISR() sets this value to pdTRUE then
+ * a context switch should be requested before the interrupt is exited.
+ *
+ * @return xQueueOverwriteFromISR() is a macro that calls 
+ * xQueueGenericSendFromISR(), and therefore has the same return values as 
+ * xQueueSendToFrontFromISR().  However, as xQueueOverwriteFromISR() will write 
+ * to the queue even when the queue is full pdPASS will be returned in all cases 
+ * (errQUEUE_FULL will never be returned).
+ *
+ * Example usage:
+   <pre>
+
+ xQueueHandle xQueue;
+ 
+ void vFunction( void *pvParameters )
+ {
+ 	// Create a queue to hold one unsigned long value.  It is strongly
+	// recommended *not* to use xQueueOverwrite() on queues that can
+	// contain more than one value, and doing so will trigger an assertion
+	// if configASSERT() is defined.
+	xQueue = xQueueCreate( 1, sizeof( unsigned long ) );
+}
+
+void vAnInterruptHandler( void )
+{
+// xHigherPriorityTaskWoken must be set to pdFALSE before it is used.
+portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
+unsigned long ulVarToSend, ulValReceived;
+
+	// Write the value 10 to the queue using xQueueOverwriteFromISR().
+	ulVarToSend = 10;
+	xQueueOverwriteFromISR( xQueue, &ulVarToSend, &xHigherPriorityTaskWoken );
+
+	// The queue is full, but calling xQueueOverwriteFromISR() again will still
+	// pass because the value held in the queue will be overwritten with the
+	// new value.
+	ulVarToSend = 100;
+	xQueueOverwrite( xQueue, &ulVarToSend, &xHigherPriorityTaskWoken );
+
+	// Reading from the queue will now return 100.
+
+	// ...
+}
+ </pre>
+ * \defgroup xQueueOverwriteFromISR xQueueOverwriteFromISR
+ * \ingroup QueueManagement
+ */
+#define xQueueOverwriteFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueOVERWRITE )
+
 /**
  * queue. h
  * <pre>
diff --git a/FreeRTOS/Source/queue.c b/FreeRTOS/Source/queue.c
index cd1c8545e..4488169ea 100644
--- a/FreeRTOS/Source/queue.c
+++ b/FreeRTOS/Source/queue.c
@@ -939,6 +939,7 @@ xQUEUE *pxQueue;
 	pxQueue = ( xQUEUE * ) xQueue;
 	configASSERT( pxQueue );
 	configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( unsigned portBASE_TYPE ) 0U ) ) );
+	configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) );
 
 	/* Similar to xQueueGenericSend, except we don't block if there is no room
 	in the queue.  Also we don't directly wake a task that was blocked on a
@@ -947,7 +948,7 @@ xQUEUE *pxQueue;
 	by this	post). */
 	uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
 	{
-		if( pxQueue->uxMessagesWaiting < pxQueue->uxLength )
+		if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
 		{
 			traceQUEUE_SEND_FROM_ISR( pxQueue );