Quality RTOS & Embedded Software

 Real time embedded FreeRTOS RSS feed 
Quick Start Supported MCUs PDF Books Trace Tools Ecosystem


Mutex/Semaphore using task/interrupt with different priority

Posted by il-mix on 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, "mytask1", DEFAULTSTACKSIZE, 1, NULL); xTaskCreate(myTask, "mytask2", DEFAULTSTACKSIZE, 2, NULL); [...] [enable some interrupts] }

void myTask(void* handle) { for(;;) { myPrintf("hellon"); vTaskDelay(rand()%100); } }

void GPIO_ISR() { myPrintf("GPIO ISRn"); }

void myPrintf(const char* msg) { xSemaphoreTake(gPrintfSemaphore, portMAXDELAY(); // or taskENTERCRITICAL() [call actual printf] // without semaphore/mutex control, it is likely that printf outputs something like "helhellolonn" xSemaphoreGive(gPrintfSemaphore); // tried taskYIELD() here, but no luck (actually, locks at first printf...) } ~~~

Thanks for your support! MIX

EDIT: 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.

Mutex/Semaphore using task/interrupt with different priority

Posted by richard_damon on 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.

Mutex/Semaphore using task/interrupt with different priority

Posted by il-mix on 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.

Mutex/Semaphore using task/interrupt with different priority

Posted by rtel on 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).

Mutex/Semaphore using task/interrupt with different priority

Posted by il-mix on 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.

Mutex/Semaphore using task/interrupt with different priority

Posted by rtel on May 10, 2017

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

Mutex/Semaphore using task/interrupt with different priority

Posted by richard_damon on 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.

Mutex/Semaphore using task/interrupt with different priority

Posted by glenenglish on 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 http://www.freertos.org/deferredinterruptprocessing.html

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, - write an ISR to service the GPIO interrupt (reset flags etc as required) . - notisfy the task to unblock - - http://www.freertos.org/xTaskNotifyFromISR.html ~~~

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

~~~ http://www.freertos.org/FreeRTOSSupportForumArchive/June2007/freertosportYIELDFROMISR1752080.html

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 http://www.freertos.org/taskENTERCRITICALFROMISRtaskEXITCRITICALFROM_ISR.html

but I have never used it,

while you are there , read http://www.freertos.org/Pend-on-multiple-rtos-objects.html

it's just like waitformultiple objects()

Mutex/Semaphore using task/interrupt with different priority

Posted by il-mix on 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).


Mutex/Semaphore using task/interrupt with different priority

Posted by richard_damon on 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).

Mutex/Semaphore using task/interrupt with different priority

Posted by il-mix on 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.

[ Back to the top ]    [ About FreeRTOS ]    [ Sitemap ]    [ ]

Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.

Latest News

FreeRTOS kernel V10.0.1 is available for immediate download. Now MIT licensed.

Video: Watch James Gosling & Richard Barry at re:Invent, Las Vegas 2017.

New FAQ page about the FreeRTOS kernel and Amazon FreeRTOS.

FreeRTOS Partners

ARM Connected RTOS partner for all ARM microcontroller cores

IAR Partner

Microchip Premier RTOS Partner

RTOS partner of NXP for all NXP ARM microcontrollers

STMicro RTOS partner supporting ARM7, ARM Cortex-M3, ARM Cortex-M4 and ARM Cortex-M0

Texas Instruments MCU Developer Network RTOS partner for ARM and MSP430 microcontrollers

OpenRTOS and SafeRTOS

Xilinx Microblaze and Zynq partner