2 Queues messages posted from ISR

philvdm wrote on Tuesday, August 28, 2007:

I am in a situation where I have 2 pending interrupts. Each interrupt handler posts a queue msg to 2 different tasks.
The 2 second msg seems to be lost ( even though the second msg is posted to a higher priority task than the first one )

I was wondering if it is not because of line 492. What is the idea behind the comment line 490-491

Should all the tasks that received a queue msg be waken?

signed portBASE_TYPE xQueueGenericSendFromISR( xQueueHandle pxQueue, const void * const pvItemToQueue, signed portBASE_TYPE xTaskPreviouslyWoken, portBASE_TYPE xCopyPosition )
  476 {
  477     /* Similar to xQueueGenericSend, except we don’t block if there is no room
  478     in the queue.  Also we don’t directly wake a task that was blocked on a
  479     queue read, instead we return a flag to say whether a context switch is
  480     required or not (i.e. has a task with a higher priority than us been woken
  481     by this    post). */
  482     if( pxQueue->uxMessagesWaiting < pxQueue->uxLength )
  483     {
  484         prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );
  486         /* If the queue is locked we do not alter the event list.  This will
  487         be done when the queue is unlocked later. */
  488         if( pxQueue->xTxLock == queueUNLOCKED )
  489         {
  490             /* We only want to wake one task per ISR, so check that a task has
  491             not already been woken. */
  492             if( !xTaskPreviouslyWoken )
  493             {
  494                 if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) )
  495                 {
  496                     if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
  497                     {
  498                         /* The task waiting has a higher priority so record that a
  499                         context    switch is required. */
  500                         return pdTRUE;
  501                     }
  502                 }
  503             }
  504         }

rtel wrote on Tuesday, August 28, 2007:

Not sure.  The xTaskPreviouslyWoken variable in this example is intended to give flexibility in the case than an interrupt writes to a queue more than once - and that there is more than one task blocked on the queue.  In this case it might be that even though you write to the queue twice you only want one of the tasks to be unblocked - this task would then presumable consume all the queue data.  If you want as many tasks woken as there are data items written to the queue then you can hardcode the xTaskPreviouslyWoken parameter to pdFALSE when you call xQueueGenericSendFromISR(). 

In either case, the data written to the queue should not be lost, it should remain in the queue.  Could it be the case that you have a task still blocked on the queue even when data was available?  To look at this, stop on the debugger when the problem has occurred, then inspect the queue data structure.  Queue->uxMessagesWaiting will tell you how many items are in the queue, and Queue->xTasksWaitingToReceive.uxNumberOfItems will tell you how many tasks are blocked on the queue waiting for data.


philvdm wrote on Tuesday, August 28, 2007:

By passing pdFALSE to xQueueSendFromISR fixes the problem but I think there is still a problem

In my previous implementation, I posted two msg into 2 differents queues ( only one task waiting on each queue)in the ISR. Because  xTaskPreviouslyWoken was true from the first call,the highest priority task was not woken. The lowest priority task was scheduled on exit of the ISR (which is the expected behavior) but the highest priority was never scheduled again : I guess that this task stays NOT READY forever.

When is this task supposed to be scheduled ?

rtel wrote on Tuesday, August 28, 2007:

The xTaskPreviouslyWoken parameter is not intended for use across two queue, but to control the behaviour when posting multiple times onto the same queue.

If you have more than one task blocked on the same queue then the first task to be unblocked is guaranteed to be the task that has the highest priority (out of the set of tasks blocked on the queue), but in your case you have two queues.

Lets see if I understand your scenario correctly: 

+ In your case you have QueueA with a priority 1 task blocked waiting for data, and QueueB with a priority 2 task blocked waiting for data. 

+ You post to a message to QueueA first passing in xTaskPreviouslyWoken as pdFALSE.  Posting the message causes the (lower) priority 1 task to be unblocked, and xTaskPreviouslyWoken to be set to pdTRUE.

+ You then post a message onto QueueB, but passing in xTaskPreviouslyWoken this time set to pdTRUE.  You have therefore explicitly told the kernel NOT to unblock any task even if one is waiting for data.

+ The interrupt ends, and the priority 1 task runs - it was unblocked.  The priority 2 task remains in the blocked state.  This is what you requested to happen and is what has happened.  The mistake being that the xTaskPreviouslyWoken parameter has been used across two queues.

Is this correct?

Now take the case that you only have one queue again with two tasks blocked, and your interrupt writes two items to the queue.  You have two choices of what can happen:

Choice 1 - writing to the queue the first time unblocks the first task, writing to the queue the second time unblocks the second task.  This is probably the behaviour you want if each task is going to take the data off for some processing elsewhere, and each task only wants one piece of data.  This scenario is achieved by passing in pdFALSE in place of the xTaskPreviouslyWoken parametrer.

Choice 2 - writing to the queue the first time unblocks the first task, writing to the queue the second time does not unblock any further tasks.  This is probably the behaviour you want if the first task that unblocks wants to consume all the items from the queue before either blocking on the queue again (because it has emptied it) or going off to do something else.  The other task will not get data from the queue until the first task has gone away - or the first task has once again blocked on the queue AND both tasks are of equal priority.  This scenario is achieved by passing in the xTaskPreviouslyWoken parameter as described in the code of your original post.


philvdm wrote on Wednesday, August 29, 2007:

Thanks a lot for your very good explanation.

You are correct about my mistake ( I misunderstood the concept of the xTaskPreviouslyWoken). 
However I would suggest that the documentation is not very clear. And it is quite weird to have multiple tasks waiting and competing on the same queue especially on a mono-processor. Usually this is the other way around : multiple producers to a queue and one consummer.

Also I was wondering why this highest priority is actually never scheduled ever.

Other unrelated question :
-How bigger is the version 4.4.0 compared to 4.3.1 ?