embedded software boot camp

RTOS Myth #3: Mutexes are Needed at the Task Level

Wednesday, March 12th, 2008 by Michael Barr

The Myth: Mutexes are a useful intertask synchronization primitive, which you should expect to use frequently.

The Truth: Mutexes are a necessary feature of all real-time operating systems. However, best practice is to use them only inside functions that must be reentrant. That is, you should use mutexes only inside functions that are or could be called by two or more tasks.

Mutexes, as their name suggests, enforce mutual exclusion and thus eliminate race conditions between tasks. A mutex can be used to protect a global data structure accessed by two or more tasks. However, doing this properly requires that each user task have the actual address of the global data and allows for bugs in the failure of one task to acquire and release the associated mutex properly.

It is preferable always to abstract or encapsulate said global data structure as an object, which can only be read or written through a set of reentrant function calls. That way, both the address (and possibly internal format) of the data structure can be hidden and the mutex calls can be ensured to be coded correctly.

Note that mutexes are also necessary inside functions that control hardware through I/O registers, which are effectively global data structures.

Of course, a priority inheritance capability should be present within the mutex API (put there by the RTOS vendor) to ensure that priority inversions cannot occur.

Go back to RTOS Myth #2 or forward to RTOS Myth #4.

Tags: ,

One Response to “RTOS Myth #3: Mutexes are Needed at the Task Level”

  1. Dan says:

    Nice article… the last paragraph really resonated with me:Note that mutexes are also necessary inside functions that control hardware through I/O registers, which are effectively global data structures.Just got done debugging a client’s project/product that we took over after client was left high & dry by another firm. One problem they were having was intermittent interrupt problems.Turns out the interrupt enable register was originally being accessed by a single task which handled the ethernet interface (and the only interrupt that was enabled/configured once the kernel was running.)Later a 2nd task was added to handle a serial interface, and under the right conditions, the tasks were clobbering each others’ attempts to modify the IER.In other words, one task was just doing: IER |= foo1;and the other: IER |= foo2;and it’s funny, the same comment above each one, something to the effect of: // Fold in foo bit to enable interrupt; // NOTE: use logical OR, not assignment, to make sure we don’t alter the other bits in the IERWell, yes, use BITWISE OR, but on top of that…. protect the shared data [hardware register]!!!I’m not perfect, we’ve all made mistakes, but this just struck me as someone who develops in the kiddie pool, as opposed to having a deep understanding of the architecture of the whole system, how multi-tasking & pre-emption work, etc…There were a host of other problems as well, probably not too surprising based on this example…Guess I should be happy, things like this keep me employed.It’s funny, I’ve been doing this for over 15 years, and I realize how much I learned in my first job by working with an outstanding engineering team (educated as EE, but was doing firmware/kernel/driver development since day 1 of employment.)

Leave a Reply