/*\r
- FreeRTOS.org V4.0.3 - Copyright (C) 2003-2006 Richard Barry.\r
+ FreeRTOS.org V4.2.0 - Copyright (C) 2003-2007 Richard Barry.\r
\r
This file is part of the FreeRTOS.org distribution.\r
\r
\r
+ Added the queue functions that can be used from co-routines.\r
\r
+Changes from V4.0.5\r
+\r
+ + Added a loop within xQueueSend() and xQueueReceive() to prevent the\r
+ functions exiting when a block time remains and the function has\r
+ not completed.\r
+\r
+Changes from V4.1.2:\r
+\r
+ + BUG FIX: Removed the call to prvIsQueueEmpty from within xQueueCRReceive\r
+ as it exited with interrupts enabled. Thanks Paul Katz.\r
+\r
+Changes from V4.1.3:\r
+\r
+ + Modified xQueueSend() and xQueueReceive() to handle the (very unlikely) \r
+ case whereby a task unblocking due to a temporal event can remove/send an \r
+ item from/to a queue when a higher priority task is still blocked on the \r
+ queue. This modification is a result of the SafeRTOS testing.\r
*/\r
\r
#include <stdlib.h>\r
\r
/* Constants used with the cRxLock and cTxLock structure members. */\r
#define queueUNLOCKED ( ( signed portBASE_TYPE ) -1 )\r
+#define queueERRONEOUS_UNBLOCK ( -1 )\r
\r
/*\r
* Definition of the queue used by the scheduler.\r
* to indicate that a task may require unblocking. When the queue in unlocked\r
* these lock counts are inspected, and the appropriate action taken.\r
*/\r
-static signed portBASE_TYPE prvUnlockQueue( xQueueHandle pxQueue );\r
+static void prvUnlockQueue( xQueueHandle pxQueue );\r
\r
/*\r
* Uses a critical section to determine if there is any data in a queue.\r
\r
signed portBASE_TYPE xQueueSend( xQueueHandle pxQueue, const void *pvItemToQueue, portTickType xTicksToWait )\r
{\r
-signed portBASE_TYPE xReturn;\r
+signed portBASE_TYPE xReturn = pdPASS;\r
+xTimeOutType xTimeOut;\r
\r
/* Make sure other tasks do not access the queue. */\r
vTaskSuspendAll();\r
\r
+ /* Capture the current time status for future reference. */\r
+ vTaskSetTimeOutState( &xTimeOut );\r
+\r
/* It is important that this is the only thread/ISR that modifies the\r
ready or delayed lists until xTaskResumeAll() is called. Places where\r
the ready/delayed lists are modified include:\r
*/\r
\r
/* If the queue is already full we may have to block. */\r
- if( prvIsQueueFull( pxQueue ) )\r
+ do\r
{\r
- /* The queue is full - do we want to block or just leave without\r
- posting? */\r
- if( xTicksToWait > ( portTickType ) 0 )\r
+ if( prvIsQueueFull( pxQueue ) )\r
{\r
- /* We are going to place ourselves on the xTasksWaitingToSend event\r
- list, and will get woken should the delay expire, or space become\r
- available on the queue.\r
- \r
- As detailed above we do not require mutual exclusion on the event\r
- list as nothing else can modify it or the ready lists while we\r
- have the scheduler suspended and queue locked.\r
- \r
- It is possible that an ISR has removed data from the queue since we\r
- checked if any was available. If this is the case then the data\r
- will have been copied from the queue, and the queue variables\r
- updated, but the event list will not yet have been checked to see if\r
- anything is waiting as the queue is locked. */\r
- vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );\r
-\r
- /* Force a context switch now as we are blocked. We can do\r
- this from within a critical section as the task we are\r
- switching to has its own context. When we return here (i.e. we\r
- unblock) we will leave the critical section as normal.\r
+ /* The queue is full - do we want to block or just leave without\r
+ posting? */\r
+ if( xTicksToWait > ( portTickType ) 0 )\r
+ {\r
+ /* We are going to place ourselves on the xTasksWaitingToSend event\r
+ list, and will get woken should the delay expire, or space become\r
+ available on the queue.\r
+ \r
+ As detailed above we do not require mutual exclusion on the event\r
+ list as nothing else can modify it or the ready lists while we\r
+ have the scheduler suspended and queue locked.\r
+ \r
+ It is possible that an ISR has removed data from the queue since we\r
+ checked if any was available. If this is the case then the data\r
+ will have been copied from the queue, and the queue variables\r
+ updated, but the event list will not yet have been checked to see if\r
+ anything is waiting as the queue is locked. */\r
+ vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );\r
+ \r
+ /* Force a context switch now as we are blocked. We can do\r
+ this from within a critical section as the task we are\r
+ switching to has its own context. When we return here (i.e. we\r
+ unblock) we will leave the critical section as normal.\r
+ \r
+ It is possible that an ISR has caused an event on an unrelated and\r
+ unlocked queue. If this was the case then the event list for that\r
+ queue will have been updated but the ready lists left unchanged -\r
+ instead the readied task will have been added to the pending ready\r
+ list. */\r
+ taskENTER_CRITICAL();\r
+ {\r
+ /* We can safely unlock the queue and scheduler here as\r
+ interrupts are disabled. We must not yield with anything\r
+ locked, but we can yield from within a critical section.\r
+ \r
+ Tasks that have been placed on the pending ready list cannot\r
+ be tasks that are waiting for events on this queue. See\r
+ in comment xTaskRemoveFromEventList(). */\r
+ prvUnlockQueue( pxQueue );\r
+ \r
+ /* Resuming the scheduler may cause a yield. If so then there\r
+ is no point yielding again here. */\r
+ if( !xTaskResumeAll() )\r
+ {\r
+ taskYIELD();\r
+ }\r
+\r
+ /* We want to check to see if the queue is still full\r
+ before leaving the critical section. This is to prevent\r
+ this task placing an item into the queue due to an\r
+ interrupt making space on the queue between critical\r
+ sections (when there might be a higher priority task\r
+ blocked on the queue that cannot run yet because the\r
+ scheduler gets suspended). */\r
+ if( pxQueue->uxMessagesWaiting == pxQueue->uxLength )\r
+ {\r
+ /* We unblocked but there is no space in the queue,\r
+ we probably timed out. */\r
+ xReturn = errQUEUE_FULL;\r
+ }\r
+ \r
+ /* Before leaving the critical section we have to ensure\r
+ exclusive access again. */\r
+ vTaskSuspendAll();\r
+ prvLockQueue( pxQueue ); \r
+ }\r
+ taskEXIT_CRITICAL();\r
+ }\r
+ }\r
\r
- It is possible that an ISR has caused an event on an unrelated and\r
- unlocked queue. If this was the case then the event list for that\r
- queue will have been updated but the ready lists left unchanged -\r
- instead the readied task will have been added to the pending ready\r
- list. */\r
+ /* If xReturn is errQUEUE_FULL then we unblocked when the queue\r
+ was still full. Don't check it again now as it is possible that\r
+ an interrupt has removed an item from the queue since we left the\r
+ critical section and we don't want to write to the queue in case\r
+ there is a task of higher priority blocked waiting for space to\r
+ be available on the queue. If this is the case the higher priority\r
+ task will execute when the scheduler is unsupended. */\r
+ if( xReturn != errQUEUE_FULL )\r
+ {\r
+ /* When we are here it is possible that we unblocked as space became\r
+ available on the queue. It is also possible that an ISR posted to the\r
+ queue since we left the critical section, so it may be that again there\r
+ is no space. This would only happen if a task and ISR post onto the\r
+ same queue. */\r
taskENTER_CRITICAL();\r
{\r
- /* We can safely unlock the queue and scheduler here as\r
- interrupts are disabled. We must not yield with anything\r
- locked, but we can yield from within a critical section.\r
- \r
- Tasks that have been placed on the pending ready list cannot\r
- be tasks that are waiting for events on this queue. See\r
- in comment xTaskRemoveFromEventList(). */\r
- prvUnlockQueue( pxQueue );\r
-\r
- /* Resuming the scheduler may cause a yield. If so then there\r
- is no point yielding again here. */\r
- if( !xTaskResumeAll() )\r
+ if( pxQueue->uxMessagesWaiting < pxQueue->uxLength )\r
{\r
- taskYIELD();\r
+ /* There is room in the queue, copy the data into the queue. */ \r
+ prvCopyQueueData( pxQueue, pvItemToQueue ); \r
+ xReturn = pdPASS;\r
+ \r
+ /* Update the TxLock count so prvUnlockQueue knows to check for\r
+ tasks waiting for data to become available in the queue. */\r
+ ++( pxQueue->xTxLock );\r
+ }\r
+ else\r
+ {\r
+ xReturn = errQUEUE_FULL;\r
}\r
-\r
- /* Before leaving the critical section we have to ensure\r
- exclusive access again. */\r
- vTaskSuspendAll();\r
- prvLockQueue( pxQueue ); \r
}\r
taskEXIT_CRITICAL();\r
}\r
- }\r
- \r
- /* When we are here it is possible that we unblocked as space became\r
- available on the queue. It is also possible that an ISR posted to the\r
- queue since we left the critical section, so it may be that again there\r
- is no space. This would only happen if a task and ISR post onto the\r
- same queue. */\r
- taskENTER_CRITICAL();\r
- {\r
- if( pxQueue->uxMessagesWaiting < pxQueue->uxLength )\r
- {\r
- /* There is room in the queue, copy the data into the queue. */ \r
- prvCopyQueueData( pxQueue, pvItemToQueue ); \r
- xReturn = pdPASS;\r
\r
- /* Update the TxLock count so prvUnlockQueue knows to check for\r
- tasks waiting for data to become available in the queue. */\r
- ++( pxQueue->xTxLock );\r
- }\r
- else\r
+ if( xReturn == errQUEUE_FULL )\r
{\r
- xReturn = errQUEUE_FULL;\r
+ if( xTicksToWait > 0 )\r
+ {\r
+ if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )\r
+ {\r
+ xReturn = queueERRONEOUS_UNBLOCK;\r
+ }\r
+ }\r
}\r
}\r
- taskEXIT_CRITICAL();\r
+ while( xReturn == queueERRONEOUS_UNBLOCK );\r
\r
- /* We no longer require exclusive access to the queue. prvUnlockQueue\r
- will remove any tasks suspended on a receive if either this function\r
- or an ISR has posted onto the queue. */\r
- if( prvUnlockQueue( pxQueue ) )\r
- {\r
- /* Resume the scheduler - making ready any tasks that were woken\r
- by an event while the scheduler was locked. Resuming the\r
- scheduler may cause a yield, in which case there is no point\r
- yielding again here. */\r
- if( !xTaskResumeAll() )\r
- {\r
- taskYIELD();\r
- }\r
- }\r
- else\r
- {\r
- /* Resume the scheduler - making ready any tasks that were woken\r
- by an event while the scheduler was locked. */\r
- xTaskResumeAll();\r
- }\r
+ prvUnlockQueue( pxQueue );\r
+ xTaskResumeAll();\r
\r
return xReturn;\r
}\r
\r
signed portBASE_TYPE xQueueReceive( xQueueHandle pxQueue, void *pvBuffer, portTickType xTicksToWait )\r
{\r
-signed portBASE_TYPE xReturn;\r
+signed portBASE_TYPE xReturn = pdTRUE;\r
+xTimeOutType xTimeOut;\r
\r
/* This function is very similar to xQueueSend(). See comments within\r
xQueueSend() for a more detailed explanation.\r
Make sure other tasks do not access the queue. */\r
vTaskSuspendAll();\r
\r
+ /* Capture the current time status for future reference. */\r
+ vTaskSetTimeOutState( &xTimeOut );\r
+\r
/* Make sure interrupts do not access the queue. */\r
prvLockQueue( pxQueue );\r
\r
- /* If there are no messages in the queue we may have to block. */\r
- if( prvIsQueueEmpty( pxQueue ) )\r
+ do\r
{\r
- /* There are no messages in the queue, do we want to block or just\r
- leave with nothing? */ \r
- if( xTicksToWait > ( portTickType ) 0 )\r
+ /* If there are no messages in the queue we may have to block. */\r
+ if( prvIsQueueEmpty( pxQueue ) )\r
+ {\r
+ /* There are no messages in the queue, do we want to block or just\r
+ leave with nothing? */ \r
+ if( xTicksToWait > ( portTickType ) 0 )\r
+ {\r
+ vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );\r
+ taskENTER_CRITICAL();\r
+ {\r
+ prvUnlockQueue( pxQueue );\r
+ if( !xTaskResumeAll() )\r
+ {\r
+ taskYIELD();\r
+ }\r
+\r
+ if( pxQueue->uxMessagesWaiting == ( unsigned portBASE_TYPE ) 0 )\r
+ {\r
+ /* We unblocked but the queue is empty. We probably\r
+ timed out. */\r
+ xReturn = errQUEUE_EMPTY;\r
+ }\r
+ \r
+ vTaskSuspendAll();\r
+ prvLockQueue( pxQueue );\r
+ }\r
+ taskEXIT_CRITICAL();\r
+ }\r
+ }\r
+ \r
+ if( xReturn != errQUEUE_EMPTY )\r
{\r
- vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );\r
taskENTER_CRITICAL();\r
{\r
- prvUnlockQueue( pxQueue );\r
- if( !xTaskResumeAll() )\r
+ if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 )\r
+ {\r
+ pxQueue->pcReadFrom += pxQueue->uxItemSize;\r
+ if( pxQueue->pcReadFrom >= pxQueue->pcTail )\r
+ {\r
+ pxQueue->pcReadFrom = pxQueue->pcHead;\r
+ }\r
+ --( pxQueue->uxMessagesWaiting );\r
+ memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->pcReadFrom, ( unsigned ) pxQueue->uxItemSize );\r
+ \r
+ /* Increment the lock count so prvUnlockQueue knows to check for\r
+ tasks waiting for space to become available on the queue. */\r
+ ++( pxQueue->xRxLock );\r
+ xReturn = pdPASS;\r
+ }\r
+ else\r
{\r
- taskYIELD();\r
+ xReturn = errQUEUE_EMPTY;\r
}\r
-\r
- vTaskSuspendAll();\r
- prvLockQueue( pxQueue );\r
}\r
taskEXIT_CRITICAL();\r
}\r
- }\r
\r
- taskENTER_CRITICAL();\r
- {\r
- if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 )\r
+ if( xReturn == errQUEUE_EMPTY )\r
{\r
- pxQueue->pcReadFrom += pxQueue->uxItemSize;\r
- if( pxQueue->pcReadFrom >= pxQueue->pcTail )\r
+ if( xTicksToWait > 0 )\r
{\r
- pxQueue->pcReadFrom = pxQueue->pcHead;\r
+ if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )\r
+ {\r
+ xReturn = queueERRONEOUS_UNBLOCK;\r
+ }\r
}\r
- --( pxQueue->uxMessagesWaiting );\r
- memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->pcReadFrom, ( unsigned ) pxQueue->uxItemSize );\r
-\r
- /* Increment the lock count so prvUnlockQueue knows to check for\r
- tasks waiting for space to become available on the queue. */\r
- ++( pxQueue->xRxLock );\r
- xReturn = pdPASS;\r
}\r
- else\r
- {\r
- xReturn = pdFAIL;\r
- }\r
- }\r
- taskEXIT_CRITICAL();\r
+ } while( xReturn == queueERRONEOUS_UNBLOCK );\r
\r
/* We no longer require exclusive access to the queue. */\r
- if( prvUnlockQueue( pxQueue ) )\r
- {\r
- if( !xTaskResumeAll() )\r
- {\r
- taskYIELD();\r
- }\r
- }\r
- else\r
- {\r
- xTaskResumeAll();\r
- }\r
+ prvUnlockQueue( pxQueue );\r
+ xTaskResumeAll();\r
\r
return xReturn;\r
}\r
}\r
/*-----------------------------------------------------------*/\r
\r
-static signed portBASE_TYPE prvUnlockQueue( xQueueHandle pxQueue )\r
+static void prvUnlockQueue( xQueueHandle pxQueue )\r
{\r
-signed portBASE_TYPE xYieldRequired = pdFALSE;\r
-\r
/* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. */\r
\r
/* The lock counts contains the number of extra data items placed or\r
{\r
/* The task waiting has a higher priority so record that a\r
context switch is required. */\r
- xYieldRequired = pdTRUE;\r
+ vTaskMissedYield();\r
}\r
} \r
}\r
{\r
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )\r
{\r
- xYieldRequired = pdTRUE;\r
+ vTaskMissedYield();\r
}\r
} \r
}\r
}\r
taskEXIT_CRITICAL();\r
-\r
- return xYieldRequired;\r
}\r
/*-----------------------------------------------------------*/\r
\r
between the check to see if the queue is empty and blocking on the queue. */\r
portDISABLE_INTERRUPTS();\r
{\r
- if( prvIsQueueEmpty( pxQueue ) )\r
+ if( pxQueue->uxMessagesWaiting == ( unsigned portBASE_TYPE ) 0 )\r
{\r
/* There are no messages in the queue, do we want to block or just\r
leave with nothing? */ \r