Strange behaviour after portEND_SWITCHING_ISR

anonymous wrote on Monday, March 12, 2012:

Hello. I am using GCC   STM32F103VE.
I came across some odd behaviour of my firmware after calling portEND_SWITCHING_ISR - there are two tasks, one for processing data, low priority, and another for delayed interrupt handling, high priority.
So, high priority task is waiting for counting semaphore, xSemaphoreTake(IRQSemph, portMAX_DELAY);

ISR text is provided below:

void EXTI15_10_IRQHandler() //IRQ
portBASE_TYPE xHigherPriorityTaskWoken;
xSemaphoreGiveFromISR(IRQSemph, &xHigherPriorityTaskWoken);

When entering ISR first time both tasks are blocked, awaiting different events. So, It works great, context is switched to High Priority task, which is unblocked by IRQSemph counting semaphore. But while processing this task, I suppose, this interrupt is being called once more.

Here is my question: due to mistype, I always passed TRUE to portEND_SWITCHING_ISR, causing context switch even when I shouldn’t.

But this caused odd effect - high priority task never unblocked.
This seems wrong to me - even after unexpected context switch - there are no more candidates for running, low priority task is blocked all the time, then why sheduler does not return to High Priority task, why is High Priority task still awaiting semaphore, when semaphore is already being given?
I checked return value of xSemaphoreGiveFromISR, it retrurns success.
When I fixed code and started passing *real* xHigherPriorityTaskWoken value instead of 1, it began to work.

rtel wrote on Monday, March 12, 2012:

In your code, you have forgotten to initialise the xHigherPriorityTaskWoken.  It needs to be initialised to pdFALSE on interrupt entry.

The behaviour you observed is the expected behaviour.  If giving the semaphore has the potential to unblock a semaphore then the xHigherPriorityTaskWoken is checked, and if it is found to be false, the task is unblocked and xHigherPriorityTaskWoken is set to pdTRUE.  If xHigherPriorityTaskWoken was already pdTRUE, then it is assumed that a task has already been unblocked, and no action is taken.

This prevents multiple tasks being unblocked by a single semaphore give, as only one task will process the semaphore, leaving the other unblocked tasks with nothing to do but return to the Blocked state.


anonymous wrote on Monday, March 12, 2012:

Thanks alot, now it is clear!

richard_damon wrote on Monday, March 12, 2012:

One question, if an interrupt does two operations that unblock tasks (say different queues), and happens to unblock the lower priority (but still above the current running priority) first, then when does the higher one wake?

Or, do you need a separate flag for each queue and test if any of them got set?

rtel wrote on Monday, March 12, 2012:

If you want a task to unblock more than one task from one queue then reset xHigherPriorityTaskWoken to pdFALSE in between each semaphore give.  In any case - the task that is unblocked first will always be the highest priority task that was blocked on the semaphore as tasks are unblocked in priority order.

If you have a single interrupt operating on multiple queues and/or semaphores, and you wish one task to be unblocked from each queue/semaphore that is processes, then use a separate flag for each, and OR the flag values when they are passed into portEND_SWITCHING_ISR()/portYIELD_FROM_ISR().


paulgcoleman wrote on Tuesday, March 13, 2012:

So is the following not correct?

portBASE_TYPE higherPriorityTaskWoken = pdFALSE;

if(some event)
xSemaphoreGiveFromISR(semA, &higherPriorityTaskWoken);

if(some other event)
xSemaphoreGiveFromISR(semB, &higherPriorityTaskWoken);


Should both semaphore gives use different flags?

Thanks, Paul.

rtel wrote on Tuesday, March 13, 2012:

Hang on!  Looking at the code I think my reply above is not correct.  The higher priority task woken flag being pdTRUE should not effect whether any further tasks are unblocked or not.  If more than one task is unblocked then any unblocked tasks that find the reason they were unblocked no longer exists (the semaphore or queue has been processed by a different task) will simply put themselves back to sleep again for the remains of the block task.

Sorry for any confusion caused!