Mutex/Semaphore using task/interrupt with different priority

il-mix wrote on Wednesday, May 10, 2017:

Hi, everyone!

I have several task running and some ISR from GPIO. All these task/isr will use a printf function, and it is quite likely that output will be messed up by concurrent call to it (output of merged strings from different tasks).
To prevent this behavior, I tried using mutex/semaphore/critical sections, awfully without luck. Or better, with some luck (no more merged strings), but when an ISR kicks in, everything locks; the ISR hangs waiting for the mutex.
I think I have to better understand how things are heandled by FreeRTOS.

Here is an example of the code/flow:

int main()
  gPrintfSemaphore = xSemaphoreCreteBinary(); // or xSemaphoreCreteMutex()
  xTaskCreate(myTask, "my_task1", DEFAULT_STACK_SIZE, 1, NULL);
  xTaskCreate(myTask, "my_task2", DEFAULT_STACK_SIZE, 2, NULL);
  [enable some interrupts]

void myTask(void* handle)

void GPIO_ISR()
  myPrintf("GPIO ISR\n");

void myPrintf(const char* msg)
  xSemaphoreTake(gPrintfSemaphore, portMAX_DELAY(); // or taskENTER_CRITICAL()
  [call actual printf]
  // without semaphore/mutex control, it is likely that printf outputs something like "helhellolo\n\n"
  // tried taskYIELD() here, but no luck (actually, locks at first printf...)

Thanks for your support!

Actually, I’m not really using printf, but I send couple of serial message data, that needs to be correctly decode on the other side (if two messages got merged, it becomes garbage). This is also an example of a more generic critical section that need to be accessed by a single task/ISR at a time.

richard_damon wrote on Wednesday, May 10, 2017:

Using printf in an ISR is a VERY bad idea, it is bg and slow which is exactly what you don’t want in an ISR. Printf likely uses polling to output the data, which is awful in an ISR.

I use something sort of similar, it is more of a putstr than printf which is a lot lighter weight, and queues the data to my own serial driver. To allow it to be used from an ISR, I have a …FromISR version. There is still the possibility of the ISR breaking up the task level print, but tasks can’t interrupt each other.

il-mix wrote on Wednesday, May 10, 2017:

Hi Richard,

thanks for your answer. I’ve just edited my question. The “printf” is not the real scenario (altought quite similar, since I send some bytes (putc()) via serial port). Still, the lock problem is real.

rtel wrote on Wednesday, May 10, 2017:

You can’t use mutexes from an ISR. That is because mutexes priority
inheritance mechanism associates a mutex with a task (the holding task,
and tasks attempting to take a mutex that is not available).

il-mix wrote on Wednesday, May 10, 2017:

I’ve read the mutex priority inheritance feature in xSemaphoreCreateBinary() documentation. That’s why I’ve tested with binary semaphores, too. Still the lock happens when ISR tries to take the semaphore.

rtel wrote on Wednesday, May 10, 2017:

Are you using the ‘FromISR’ version of the semaphore take function? If
not you are probably halting in a configASSERT().

richard_damon wrote on Thursday, May 11, 2017:

You just CAN’T use a semaphore/mutex to fully syncronize an ISR access. The fundamental issue is that if the resourse is in use when the ISR is entered, there is no way for the ISR to “block” to wait for it to be free. The best you could do is have the ISR check if it could take the semaphore (check the return value), and then you have the big decision of what to in the ISR if the resource you want to use is busy.

For simple, quick to access resources, like a variable, you can use a critical section to block out the ISR from getting in while you are updating things, this doesn’t work so well for a slow operation (which likely needs the interrupts) like serial I/O.

You basic problem is you are trying to do something in the ISR that really doesn’t belong there, so you really need to figurre out what you really want to do.

glenenglish wrote on Thursday, May 11, 2017:

Hi Massimo
I think you are going about this the wrong way.

ISRs should be used to do only things that MUST be done immediately, or as a method of unblocking threads on IO events.
If you MUST read or write a buffer in a hurry, then fire off a YIELDfromISR() and the very next thing that will execute will be the thread that reads or writes a buffer from IO .

be sure to read

I try and keep ISRs to five or six sequential lines of code.
If you need to write a string to a serial port based on a, say a GPIO interrupt trigger,

## - and have it go to the task on returning from interrupt
## -

Then, the task can WAIT on the task notification, and then go about its business without holding up anyone else. if it must hold up, you can use an ISR notification from the serial port that it wants another char etc etc etc etc or you can Yield on a timer and come back later and poll.

this method is about as fast as it gets.

(Richard BTW portYieldfromISR() it is not easily found or indexed in the standard FreeRTOS doco)

if you need to guard multi accessed global variables , in the TASKS you can use critical sections / disable interrupts etc.

But never do this inside interrupts. The Critical Sections are in the tasks so the interrupts (if they touch those global variables like incrementing a counter or setting a flag) are clean.

sometimes you do not need a critical section- it depends how atomic the global variable access is, but in that case you better know what you are doing… (cache, instruction order, whether an instruction will complete before interrupt service etc) .

there is also

but I have never used it,

while you are there , read

it’s just like waitformultiple objects()

il-mix wrote on Friday, May 12, 2017:

Thanks to everyone for the useful hints and tech explaination.
I think I’ve placed too much emphasis in the ISR vs. mutex issue. I used the ISR example since it was the one that will lead to a predictable/repeatable lock. My bad.
Actually, most of my ISR drive a semaphore that wake up a task. And now I can confirm that this is the right (maybe not best, since TaskNotify will be lighter) way to do it, given your answers.

Getting back to the (real) topic, how to correctly “protect” access of a critical section from concurrent access by 2 tasks with different priority?
Using an example similar to the one in my first post, let’s say that Task1 (high priority) and Task2 (low priority) want to printf something, what happens when Task2 acquire the mutex, then Task1 tries to acquire it before release?

I’ve noticed a couple of lock in my system in such scenario. Actually, I can confirm 100% that the problem was really due to mutex management. Happened a while ago, and now that I reproduced it with the ISR, I thought the problem source was the same (that’s why I put emphasis in the ISR vs. mutex issue).


richard_damon wrote on Friday, May 12, 2017:

A mutex is exactly the thing that should handle this. When task1 attempts to aquire the mutex when it is already held by task2, iit will block until task 2 releases it. If task1 has a higher priority than task2 using a mutex instead of a semaphore says that task2 will be given a temporary boost in priority to avoid priorrity inversion (a task in priority between task1 and task2 holding up task2 by using a lot of CPU time).

One thing you do need to watch out for is if you have two mutexes, and task1 first takes mutex1, then task2 takes mutex2, then task1 tries too take mutex2 (and blocks), and then task2 tries to take mutex1, it will block and you will be in a deadlock. To avoid this, you either need to put tiimeouts on the takes and abort the operation to kill the deadlock, or there needs to be an order to your mutexs. If multiple tasks end up taking two mutexe concurrently, they all must take them in the same order).

il-mix wrote on Friday, May 12, 2017:

Hi Richard,

thanks for your answer.
So I’ll go for mutex, again. I don’t have multiple mutexes that can cause deadlock (or so it seems…). If I got stuck again I’ll go deep and see if it really is a lock/unlock problem. Will report in a while on the status of the issue.