signed portBASE_TYPE xQueueReceive( xQueueHandle pxQueue, void *pvBuffer, portTickType xTicksToWait ) { signed portBASE_TYPE xReturn; /* This function is very similar to xQueueSend(). See comments within xQueueSend() for a more detailed explanation. Make sure other tasks do not access the queue. */ vTaskSuspendAll(); /* Make sure interrupts do not access the queue. */ prvLockQueue( pxQueue ); /* If there are no messages in the queue we may have to block. */ if( prvIsQueueEmpty( pxQueue ) ) { /* There are no messages in the queue, do we want to block or just leave with nothing? */ if( xTicksToWait > ( portTickType ) 0 ) { vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); taskENTER_CRITICAL(); { prvUnlockQueue( pxQueue ); if( !xTaskResumeAll() ) { taskYIELD(); } vTaskSuspendAll(); prvLockQueue( pxQueue ); } taskEXIT_CRITICAL(); } } taskENTER_CRITICAL(); { if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 ) { pxQueue->pcReadFrom += pxQueue->uxItemSize; if( pxQueue->pcReadFrom >= pxQueue->pcTail ) { pxQueue->pcReadFrom = pxQueue->pcHead; } --( pxQueue->uxMessagesWaiting ); memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); /* Increment the lock count so prvUnlockQueue knows to check for tasks waiting for space to become available on the queue. */ ++( pxQueue->xRxLock ); xReturn = pdPASS; } else { xReturn = pdFAIL; } } taskEXIT_CRITICAL(); /* We no longer require exclusive access to the queue. */ if( prvUnlockQueue( pxQueue ) ) { if( !xTaskResumeAll() ) { taskYIELD(); } } else { xTaskResumeAll(); } return xReturn; }
signed portBASE_TYPE xQueueSend( xQueueHandle pxQueue, const void *pvItemToQueue, portTickType xTicksToWait ) { signed portBASE_TYPE xReturn; /* Make sure other tasks do not access the queue. */ vTaskSuspendAll(); /* It is important that this is the only thread/ISR that modifies the ready or delayed lists until xTaskResumeAll() is called. Places where the ready/delayed lists are modified include: + vTaskDelay() - Nothing can call vTaskDelay as the scheduler is suspended, vTaskDelay() cannot be called from an ISR. + vTaskPrioritySet() - Has a critical section around the access. + vTaskSwitchContext() - This will not get executed while the scheduler is suspended. + prvCheckDelayedTasks() - This will not get executed while the scheduler is suspended. + xTaskCreate() - Has a critical section around the access. + vTaskResume() - Has a critical section around the access. + xTaskResumeAll() - Has a critical section around the access. + xTaskRemoveFromEventList - Checks to see if the scheduler is suspended. If so then the TCB being removed from the event is removed from the event and added to the xPendingReadyList. */ /* Make sure interrupts do not access the queue event list. */ prvLockQueue( pxQueue ); /* It is important that interrupts to not access the event list of the queue being modified here. Places where the event list is modified include: + xQueueSendFromISR(). This checks the lock on the queue to see if it has access. If the queue is locked then the Tx lock count is incremented to signify that a task waiting for data can be made ready once the queue lock is removed. If the queue is not locked then a task can be moved from the event list, but will not be removed from the delayed list or placed in the ready list until the scheduler is unlocked. + xQueueReceiveFromISR(). As per xQueueSendFromISR(). */ /* If the queue is already full we may have to block. */ if( prvIsQueueFull( pxQueue ) ) { /* The queue is full - do we want to block or just leave without posting? */ if( xTicksToWait > ( portTickType ) 0 ) { /* We are going to place ourselves on the xTasksWaitingToSend event list, and will get woken should the delay expire, or space become available on the queue. As detailed above we do not require mutual exclusion on the event list as nothing else can modify it or the ready lists while we have the scheduler suspended and queue locked. It is possible that an ISR has removed data from the queue since we checked if any was available. If this is the case then the data will have been copied from the queue, and the queue variables updated, but the event list will not yet have been checked to see if anything is waiting as the queue is locked. */ vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait ); /* Force a context switch now as we are blocked. We can do this from within a critical section as the task we are switching to has its own context. When we return here (i.e. we unblock) we will leave the critical section as normal. It is possible that an ISR has caused an event on an unrelated and unlocked queue. If this was the case then the event list for that queue will have been updated but the ready lists left unchanged - instead the readied task will have been added to the pending ready list. */ taskENTER_CRITICAL(); { /* We can safely unlock the queue and scheduler here as interrupts are disabled. We must not yield with anything locked, but we can yield from within a critical section. Tasks that have been placed on the pending ready list cannot be tasks that are waiting for events on this queue. See in comment xTaskRemoveFromEventList(). */ prvUnlockQueue( pxQueue ); /* Resuming the scheduler may cause a yield. If so then there is no point yielding again here. */ if( !xTaskResumeAll() ) { taskYIELD(); } /* Before leaving the critical section we have to ensure exclusive access again. */ vTaskSuspendAll(); prvLockQueue( pxQueue ); } taskEXIT_CRITICAL(); } } /* When we are here it is possible that we unblocked as space became available on the queue. It is also possible that an ISR posted to the queue since we left the critical section, so it may be that again there is no space. This would only happen if a task and ISR post onto the same queue. */ taskENTER_CRITICAL(); { if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) { /* There is room in the queue, copy the data into the queue. */ prvCopyQueueData( pxQueue, pvItemToQueue ); xReturn = pdPASS; /* Update the TxLock count so prvUnlockQueue knows to check for tasks waiting for data to become available in the queue. */ ++( pxQueue->xTxLock ); } else { xReturn = errQUEUE_FULL; } } taskEXIT_CRITICAL(); /* We no longer require exclusive access to the queue. prvUnlockQueue will remove any tasks suspended on a receive if either this function or an ISR has posted onto the queue. */ if( prvUnlockQueue( pxQueue ) ) { /* Resume the scheduler - making ready any tasks that were woken by an event while the scheduler was locked. Resuming the scheduler may cause a yield, in which case there is no point yielding again here. */ if( !xTaskResumeAll() ) { taskYIELD(); } } else { /* Resume the scheduler - making ready any tasks that were woken by an event while the scheduler was locked. */ xTaskResumeAll(); } return xReturn; }
signed portBASE_TYPE xQueueGenericSend( xQueueHandle pxQueue, const void * const pvItemToQueue, portTickType xTicksToWait, portBASE_TYPE xCopyPosition ) { signed portBASE_TYPE xReturn = pdTRUE; xTimeOutType xTimeOut; do { /* If xTicksToWait is zero then we are not going to block even if there is no room in the queue to post. */ if( xTicksToWait > ( portTickType ) 0 ) { vTaskSuspendAll(); prvLockQueue( pxQueue ); if( xReturn == pdTRUE ) { /* This is the first time through - we need to capture the time while the scheduler is locked to ensure we attempt to block at least once. */ vTaskSetTimeOutState( &xTimeOut ); } if( prvIsQueueFull( pxQueue ) ) { /* Need to call xTaskCheckForTimeout again as time could have passed since it was last called if this is not the first time around this loop. */ if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) { traceBLOCKING_ON_QUEUE_SEND( pxQueue ); vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait ); /* Unlocking the queue means queue events can effect the event list. It is possible that interrupts occurring now remove this task from the event list again - but as the scheduler is suspended the task will go onto the pending ready last instead of the actual ready list. */ prvUnlockQueue( pxQueue ); /* Resuming the scheduler will move tasks from the pending ready list into the ready list - so it is feasible that this task is already in a ready list before it yields - in which case the yield will not cause a context switch unless there is also a higher priority task in the pending ready list. */ if( !xTaskResumeAll() ) { taskYIELD(); } } else { prvUnlockQueue( pxQueue ); ( void ) xTaskResumeAll(); } } else { /* The queue was not full so we can just unlock the scheduler and queue again before carrying on. */ prvUnlockQueue( pxQueue ); ( void ) xTaskResumeAll(); } } /* Higher priority tasks and interrupts can execute during this time and could possible refill the queue - even if we unblocked because space became available. */ taskENTER_CRITICAL(); { /* Is there room on the queue now? To be running we must be the highest priority task wanting to access the queue. */ if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) { traceQUEUE_SEND( pxQueue ); prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); xReturn = pdPASS; /* If there was a task waiting for data to arrive on the queue then unblock it now. */ if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) { if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) == pdTRUE ) { /* The unblocked task has a priority higher than our own so yield immediately. */ taskYIELD(); } } } else { /* Setting xReturn to errQUEUE_FULL will force its timeout to be re-evaluated. This is necessary in case interrupts and higher priority tasks accessed the queue between this task being unblocked and subsequently attempting to write to the queue. */ xReturn = errQUEUE_FULL; } } taskEXIT_CRITICAL(); if( xReturn == errQUEUE_FULL ) { if( xTicksToWait > ( portTickType ) 0 ) { if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) { xReturn = queueERRONEOUS_UNBLOCK; } else { traceQUEUE_SEND_FAILED( pxQueue ); } } else { traceQUEUE_SEND_FAILED( pxQueue ); } } } while( xReturn == queueERRONEOUS_UNBLOCK ); return xReturn; }
signed portBASE_TYPE xQueueGenericReceive( xQueueHandle pxQueue, void * const pvBuffer, portTickType xTicksToWait, portBASE_TYPE xJustPeeking ) { signed portBASE_TYPE xReturn = pdTRUE; xTimeOutType xTimeOut; signed portCHAR *pcOriginalReadPosition; do { /* If there are no messages in the queue we may have to block. */ if( xTicksToWait > ( portTickType ) 0 ) { vTaskSuspendAll(); prvLockQueue( pxQueue ); if( xReturn == pdTRUE ) { /* This is the first time through - we need to capture the time while the scheduler is locked to ensure we attempt to block at least once. */ vTaskSetTimeOutState( &xTimeOut ); } if( prvIsQueueEmpty( pxQueue ) ) { /* Need to call xTaskCheckForTimeout again as time could have passed since it was last called if this is not the first time around this loop. */ if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) { traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ); #if ( configUSE_MUTEXES == 1 ) { if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) { portENTER_CRITICAL(); vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder ); portEXIT_CRITICAL(); } } #endif vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); prvUnlockQueue( pxQueue ); if( !xTaskResumeAll() ) { taskYIELD(); } } else { prvUnlockQueue( pxQueue ); ( void ) xTaskResumeAll(); } } else { prvUnlockQueue( pxQueue ); ( void ) xTaskResumeAll(); } } /* The two tasks are blocked on the queue, the low priority task is polling/running. */ /* An interrupt occurs here - which unblocks the HP tasks, but they do not run. */ taskENTER_CRITICAL(); { /* Because the interrupt occurred the LP task manages to grab the data as the other two tasks are not yet running. */ if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 ) { /* Remember our read position in case we are just peeking. */ pcOriginalReadPosition = pxQueue->pcReadFrom; prvCopyDataFromQueue( pxQueue, pvBuffer ); if( xJustPeeking == pdFALSE ) { traceQUEUE_RECEIVE( pxQueue ); /* We are actually removing data. */ --( pxQueue->uxMessagesWaiting ); #if ( configUSE_MUTEXES == 1 ) { if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) { /* Record the information required to implement priority inheritance should it become necessary. */ pxQueue->pxMutexHolder = xTaskGetCurrentTaskHandle(); } } #endif if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) { if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) == pdTRUE ) { taskYIELD(); } } } else { traceQUEUE_PEEK( pxQueue ); /* We are not removing the data, so reset our read pointer. */ pxQueue->pcReadFrom = pcOriginalReadPosition; /* The data is being left in the queue, so see if there are any other tasks waiting for the data. */ if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) ) { /* Tasks that are removed from the event list will get added to the pending ready list as the scheduler is still suspended. */ if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) { /* The task waiting has a higher priority than this task. */ taskYIELD(); } } } xReturn = pdPASS; } else { xReturn = errQUEUE_EMPTY; } } taskEXIT_CRITICAL(); if( xReturn == errQUEUE_EMPTY ) { if( xTicksToWait > ( portTickType ) 0 ) { if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) { xReturn = queueERRONEOUS_UNBLOCK; } else { traceQUEUE_RECEIVE_FAILED( pxQueue ); } } else { traceQUEUE_RECEIVE_FAILED( pxQueue ); } } } while( xReturn == queueERRONEOUS_UNBLOCK ); return xReturn; }
signed portBASE_TYPE xQueueReceive( xQueueHandle pxQueue, void *pvBuffer, portTickType xTicksToWait ) { signed portBASE_TYPE xReturn = pdTRUE; xTimeOutType xTimeOut; /* This function is very similar to xQueueSend(). See comments within xQueueSend() for a more detailed explanation. Make sure other tasks do not access the queue. */ vTaskSuspendAll(); /* Capture the current time status for future reference. */ vTaskSetTimeOutState( &xTimeOut ); /* Make sure interrupts do not access the queue. */ prvLockQueue( pxQueue ); do { /* If there are no messages in the queue we may have to block. */ if( prvIsQueueEmpty( pxQueue ) ) { /* There are no messages in the queue, do we want to block or just leave with nothing? */ if( xTicksToWait > ( portTickType ) 0 ) { vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); taskENTER_CRITICAL(); { prvUnlockQueue( pxQueue ); if( !xTaskResumeAll() ) { taskYIELD(); } if( pxQueue->uxMessagesWaiting == ( unsigned portBASE_TYPE ) 0 ) { /* We unblocked but the queue is empty. We probably timed out. */ xReturn = errQUEUE_EMPTY; } vTaskSuspendAll(); prvLockQueue( pxQueue ); } taskEXIT_CRITICAL(); } } if( xReturn != errQUEUE_EMPTY ) { taskENTER_CRITICAL(); { if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 ) { pxQueue->pcReadFrom += pxQueue->uxItemSize; if( pxQueue->pcReadFrom >= pxQueue->pcTail ) { pxQueue->pcReadFrom = pxQueue->pcHead; } --( pxQueue->uxMessagesWaiting ); if(pvBuffer != (void*)0UL) { memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); } /* Increment the lock count so prvUnlockQueue knows to check for tasks waiting for space to become available on the queue. */ ++( pxQueue->xRxLock ); xReturn = pdPASS; } else { xReturn = errQUEUE_EMPTY; } } taskEXIT_CRITICAL(); } if( xReturn == errQUEUE_EMPTY ) { if( xTicksToWait > 0 ) { if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) { xReturn = queueERRONEOUS_UNBLOCK; } } } } while( xReturn == queueERRONEOUS_UNBLOCK ); /* We no longer require exclusive access to the queue. */ prvUnlockQueue( pxQueue ); xTaskResumeAll(); return xReturn; }