nobody wrote on Monday, April 17, 2006:
NOT TESTED, USE AT YOUR OWN RISK.
This function xDoubleQueueWait allows you to block on two queues, or more if you wish. (I think two should be enough!)
To use it, cut & paste the patch in queue.c
Richardbarry, please give your blessing.
Ami
/*----- START xDoubleQueueWait ------*/
signed portBASE_TYPE xDoubleQueueWait(xQueueHandle pxQueue1, xQueueHandle pxQueue2, portTickType xTicksToWait )
{
signed portBASE_TYPE xReturn = 0;
xListItem LocalEventListItem1;
xListItem LocalEventListItem2;
xList LocalEventList;
void* pxOwner;
/* This function allows waiting for two queues until one of them unblock,
for example one for socket/message queue, one for serial i/o.
it returns 0 (none of them / timeout), 1 (queue1) or 2 (queue2) that can
be used in a switch block.
Design:
I create a local event list to which the task is placed on so that the
EventListItem in TCB points to this local event list. (using vTaskPlaceOnEventList)
For each queue, I insert a locally allocated event-list-item with owner set to
this task’s TCB.
Then when an event arrives to either queue, the event will resume this task
by removing it from the local event list.
Afterward we can then safely remove the two event-list-items from the two queues.
This is made possible because xTaskRemoveFromEventList contains the following two lines:
pxUnblockedTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( pxEventList );
vListRemove( &( pxUnblockedTCB->xEventListItem ) );
So it removes the item from pxUnblockedTCB->EventListItem rather than
from the pxEventList->pxHead->pxNext.
Normally the two should point to the same thing.
However (fortunately for this modification), the pxUnblockedTCB->xEventListItem is
contained in our local event-list (actually dummy event list), and not in the
event list that causes the Task to be removed.
This allows the task to subscribe to more than one event list by
allocating list items locally on stack
RichardBarry, please don’t change this nice loophole
*/
vListInitialise(&LocalEventList);
/*Make sure other tasks do not access the queue. */
vTaskSuspendAll();
/* Make sure interrupts do not access the queue. */
prvLockQueue( pxQueue1 );
prvLockQueue( pxQueue2 );
if(prvIsQueueEmpty( pxQueue1 ) && prvIsQueueEmpty( pxQueue2 ))
{
/* There are no messages in both queues, do we want to block or just
leave with nothing? */
if( xTicksToWait > ( portTickType ) 0 )
{
vTaskPlaceOnEventList( &LocalEventList , xTicksToWait );
pxOwner = listGET_OWNER_OF_HEAD_ENTRY( ( &LocalEventList ) );
listSET_LIST_ITEM_OWNER( &LocalEventListItem1, pxOwner );
listSET_LIST_ITEM_OWNER( &LocalEventListItem2, pxOwner );
vListInsert( &( pxQueue2->xTasksWaitingToReceive ), & LocalEventListItem1 );
vListInsert( &( pxQueue2->xTasksWaitingToReceive ), & LocalEventListItem2 );
taskENTER_CRITICAL();
{
prvUnlockQueue( pxQueue1 );
prvUnlockQueue( pxQueue2 );
if( !xTaskResumeAll() )
{
taskYIELD();
}
vTaskSuspendAll();
prvLockQueue( pxQueue1);
prvLockQueue( pxQueue2);
}
taskEXIT_CRITICAL();
vListRemove( &LocalEventListItem1 );
vListRemove( &LocalEventListItem2 );
xReturn = 0;
if(prvIsQueueEmpty( pxQueue1 ) != 0)
xReturn = 1;
else
if(prvIsQueueEmpty( pxQueue2 ) != 0)
xReturn = 2;
}
else
xReturn = 0;
}
else
{
if(prvIsQueueEmpty( pxQueue1 ) != 0)
xReturn = 1;
else
if(prvIsQueueEmpty( pxQueue2 ) != 0)
xReturn = 2;
}
/* We no longer require exclusive access to the queue. */
/* !! Here we must use binary OR, not logical OR since both queue must
be unlocked */
if( prvUnlockQueue( pxQueue1 ) | prvUnlockQueue( pxQueue2 ) )
{
if( !xTaskResumeAll() )
{
taskYIELD();
}
}
else
{
xTaskResumeAll();
}
return xReturn;
}
/*-----------------------------------------------------------*/