signed portBASE_TYPE xQueueCRSend( xQueueHandle pxQueue, const void *pvItemToQueue, portTickType xTicksToWait ) { signed portBASE_TYPE xReturn; /* If the queue is already full we may have to block. A critical section is required to prevent an interrupt removing something from the queue between the check to see if the queue is full and blocking on the queue. */ portDISABLE_INTERRUPTS(); { if( prvIsQueueFull( pxQueue ) ) { /* The queue is full - do we want to block or just leave without posting? */ if( xTicksToWait > ( portTickType ) 0 ) { /* As this is called from a coroutine we cannot block directly, but return indicating that we need to block. */ vCoRoutineAddToDelayedList( xTicksToWait, &( pxQueue->xTasksWaitingToSend ) ); portENABLE_INTERRUPTS(); return errQUEUE_BLOCKED; } else { portENABLE_INTERRUPTS(); return errQUEUE_FULL; } } } portENABLE_INTERRUPTS(); portNOP(); portDISABLE_INTERRUPTS(); { if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) { /* There is room in the queue, copy the data into the queue. */ prvCopyQueueData( pxQueue, pvItemToQueue ); xReturn = pdPASS; /* Were any co-routines waiting for data to become available? */ if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) ) { /* In this instance the co-routine could be placed directly into the ready list as we are within a critical section. Instead the same pending ready list mechansim is used as if the event were caused from within an interrupt. */ if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) { /* The co-routine waiting has a higher priority so record that a yield might be appropriate. */ xReturn = errQUEUE_YIELD; } } } else { xReturn = errQUEUE_FULL; } } portENABLE_INTERRUPTS(); return xReturn; }
signed portBASE_TYPE xQueueAltGenericSend( xQueueHandle pxQueue, const void * const pvItemToQueue, portTickType xTicksToWait, portBASE_TYPE xCopyPosition ) { signed portBASE_TYPE xReturn = pdPASS; xTimeOutType xTimeOut; /* The source code that implements the alternative (Alt) API is simpler because it makes more use of critical sections. This is the approach taken by many other RTOSes, but FreeRTOS.org has the preferred fully featured API too. The fully featured API has more complex code that takes longer to execute, but makes less use of critical sections. */ 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 ) { portENTER_CRITICAL(); { if( xReturn == pdPASS ) { /* This is the first time through - capture the time inside the critical section 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 ); /* This will exit the critical section, then re-enter when the task next runs. */ taskYIELD(); } } } portEXIT_CRITICAL(); } /* 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 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; }