queue.c

来自「ARM7(LPC2131) + FreeRTOS, 基于 Codesourcer」· C语言 代码 · 共 1,323 行 · 第 1/3 页

C
1,323
字号
				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();
					}

					/* We want to check to see if the queue is still full
					before leaving the critical section.  This is to prevent
					this task placing an item into the queue due to an
					interrupt making space on the queue between critical
					sections (when there might be a higher priority task
					blocked on the queue that cannot run yet because the
					scheduler gets suspended). */
					if( pxQueue->uxMessagesWaiting == pxQueue->uxLength )
					{
						/* We unblocked but there is no space in the queue,
						we probably timed out. */
						xReturn = errQUEUE_FULL;
					}
	
					/* Before leaving the critical section we have to ensure
					exclusive access again. */
					vTaskSuspendAll();
					prvLockQueue( pxQueue );				
				}
				taskEXIT_CRITICAL();
			}
		}
			
		/* If xReturn is errQUEUE_FULL then we unblocked when the queue
		was still full.  Don't check it again now as it is possible that
		an interrupt has removed an item from the queue since we left the
		critical section and we don't want to write to the queue in case
		there is a task of higher priority blocked waiting for space to
		be available on the queue.  If this is the case the higher priority
		task will execute when the scheduler is unsupended. */
		if( xReturn != errQUEUE_FULL )
		{
			/* 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. */			
					prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );
					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();
		}

		if( xReturn == errQUEUE_FULL )
		{
			if( xTicksToWait > 0 )
			{
				if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
				{
					xReturn = queueERRONEOUS_UNBLOCK;
				}
			}
		}
	}
	while( xReturn == queueERRONEOUS_UNBLOCK );

	prvUnlockQueue( pxQueue );
	xTaskResumeAll();

	return xReturn;
}
/*-----------------------------------------------------------*/

#if configUSE_ALTERNATIVE_API == 1

	signed portBASE_TYPE xQueueAltGenericSend( xQueueHandle pxQueue, const void * const pvItemToQueue, portTickType xTicksToWait, portBASE_TYPE xCopyPosition )
	{
	signed portBASE_TYPE xReturn;
	xTimeOutType xTimeOut;

		/* The source code that implements the alternative (Alt) API is much 
		simpler	because it executes everything from within a critical section.  
		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 much less use of 
		critical sections.  Therefore the alternative API sacrifices interrupt 
		responsiveness to gain execution speed, whereas the fully featured API
		sacrifices execution speed to ensure better interrupt responsiveness.  */

		taskENTER_CRITICAL();
		{
			/* Capture the current time status for future reference. */
			vTaskSetTimeOutState( &xTimeOut );

			/* If the queue is already full we may have to block. */
			do
			{
				if( pxQueue->uxMessagesWaiting == pxQueue->uxLength )
				{
					/* 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. */
						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. */
						taskYIELD();
					}
				}
					
				if( pxQueue->uxMessagesWaiting < pxQueue->uxLength )
				{
					/* There is room in the queue, copy the data into the queue. */			
					prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );
					xReturn = pdPASS;

					if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) )
					{
						if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
						{
							/* The task waiting has a higher priority. */
							taskYIELD();
						}
					}			
				}
				else
				{
					xReturn = errQUEUE_FULL;

					if( xTicksToWait > 0 )
					{					
						if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
						{
							/* Another task must have accessed the queue between 
							this task unblocking and actually executing. */
							xReturn = queueERRONEOUS_UNBLOCK;
						}
					}
				}
			}
			while( xReturn == queueERRONEOUS_UNBLOCK );
		}
		taskEXIT_CRITICAL();

		return xReturn;
	}

#endif /* configUSE_ALTERNATIVE_API */
/*-----------------------------------------------------------*/

#if configUSE_ALTERNATIVE_API == 1

	signed portBASE_TYPE xQueueAltGenericReceive( xQueueHandle pxQueue, const void * const pvBuffer, portTickType xTicksToWait, portBASE_TYPE xJustPeeking )
	{
	signed portBASE_TYPE xReturn = pdTRUE;
	xTimeOutType xTimeOut;
	signed portCHAR *pcOriginalReadPosition;

		/* The source code that implements the alternative (Alt) API is much 
		simpler	because it executes everything from within a critical section.  
		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 much less use of 
		critical sections.  Therefore the alternative API sacrifices interrupt 
		responsiveness to gain execution speed, whereas the fully featured API
		sacrifices execution speed to ensure better interrupt responsiveness.  */

		taskENTER_CRITICAL();
		{
			/* Capture the current time status for future reference. */
			vTaskSetTimeOutState( &xTimeOut );

			do
			{
				/* If there are no messages in the queue we may have to block. */
				if( pxQueue->uxMessagesWaiting == ( unsigned portBASE_TYPE ) 0 )
				{
					/* There are no messages in the queue, do we want to block or just
					leave with nothing? */			
					if( xTicksToWait > ( portTickType ) 0 )
					{
						#if ( configUSE_MUTEXES == 1 )
						{
							if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
							{
								vTaskPriorityInherit( ( void * const ) pxQueue->pxMutexHolder );
							}
						}
						#endif
						
						vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
						taskYIELD();
					}
				}
			
				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 )
					{
						/* 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 ) ) )
						{
							if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
							{
								/* The task waiting has a higher priority. */
								taskYIELD();
							}
						}
					}
					else
					{
						/* We are not removing the data, so reset our read
						pointer. */
						pxQueue->pcReadFrom = pcOriginalReadPosition;
					}
					
					xReturn = pdPASS;					
				}
				else
				{
					xReturn = errQUEUE_EMPTY;

					if( xTicksToWait > 0 )
					{
						if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
						{
							xReturn = queueERRONEOUS_UNBLOCK;
						}
					}
				}

			} while( xReturn == queueERRONEOUS_UNBLOCK );
		}
		taskEXIT_CRITICAL();

		return xReturn;
	}

#endif /* configUSE_ALTERNATIVE_API */
/*-----------------------------------------------------------*/

signed portBASE_TYPE xQueueGenericSendFromISR( xQueueHandle pxQueue, const void * const pvItemToQueue, signed portBASE_TYPE xTaskPreviouslyWoken, portBASE_TYPE xCopyPosition )
{
	/* 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
	queue read, instead we return a flag to say whether a context switch is
	required or not (i.e. has a task with a higher priority than us been woken
	by this	post). */
	if( pxQueue->uxMessagesWaiting < pxQueue->uxLength )
	{
		prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );

		/* If the queue is locked we do not alter the event list.  This will
		be done when the queue is unlocked later. */
		if( pxQueue->xTxLock == queueUNLOCKED )
		{
			/* We only want to wake one task per ISR, so check that a task has
			not already been woken. */
			if( !xTaskPreviouslyWoken )		
			{
				if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) )
				{
					if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
					{
						/* The task waiting has a higher priority so record that a
						context	switch is required. */
						return pdTRUE;
					}
				}
			}
		}
		else
		{
			/* Increment the lock count so the task that unlocks the queue
			knows that data was posted while it was locked. */
			++( pxQueue->xTxLock );
		}
	}

	return xTaskPreviouslyWoken;
}
/*-----------------------------------------------------------*/

signed portBASE_TYPE xQueueGenericReceive( xQueueHandle pxQueue, const void * const pvBuffer, portTickType xTicksToWait, portBASE_TYPE xJustPeeking )
{
signed portBASE_TYPE xReturn = pdTRUE;
xTimeOutType xTimeOut;
signed portCHAR *pcOriginalReadPosition;

	/* This function is very similar to xQueueGenericSend().  See comments
	within xQueueGenericSend() 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 )
			{
				#if ( configUSE_MUTEXES == 1 )
				{
					if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
					{
						portENTER_CRITICAL();
							vTaskPriorityInherit( ( void * const ) pxQueue->pxMutexHolder );
						portEXIT_CRITICAL();
					}
				}
				#endif
				
				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 )
				{
					/* Remember our read position in case we are just peeking. */
					pcOriginalReadPosition = pxQueue->pcReadFrom;

					prvCopyDataFromQueue( pxQueue, pvBuffer );

					if( xJustPeeking == pdFALSE )
					{
						/* We are actually removing data. */
						--( pxQueue->uxMessagesWaiting );
							
						/* Increment the lock count so prvUnlockQueue knows to check for
						tasks waiting for space to become available on the queue. */
						++( pxQueue->xRxLock );
						
						#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
					}
					else
					{
						/* We are not removing the data, so reset our read
						pointer. */
						pxQueue->pcReadFrom = pcOriginalReadPosition;

						/* The data is being left in the queue, so increment the
						lock count so prvUnlockQueue knows to check for other
						tasks waiting for the data to be available. */
						++( pxQueue->xTxLock );						
					}
					
					xReturn = pdPASS;					

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?