Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
	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;
	}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
0
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;
}