Priority inversion: when a low task blocks a high one
A high task stops because of a mutex held by a low task, and meanwhile a middle task takes the CPU. A Mars lander ran into this too.
Scenario
- L (low) takes the SPI mutex
- H (high) wakes, waits for the same mutex and goes Blocked
- M (middle) wakes. It is above L, so it preempts L
- While M runs for a long time, L cannot give the mutex back, and H waits indefinitely because of M
H has nothing to do with M and is still blocked by it. In 1997 Mars Pathfinder reset repeatedly because of this problem.
The fix: priority inheritance
While H waits for a mutex that L holds, L's priority is temporarily raised to H's. Then M cannot preempt L, and L finishes quickly and returns the mutex. A FreeRTOS mutex (xSemaphoreCreateMutex) includes this feature, but a binary semaphore does not. Always use a mutex to protect a resource.
Avoiding it by design
- Keep the sections that hold a mutex short
- Give a shared resource to one owning task and have the others request through a queue
- Priority ceiling protocol: on taking a resource, rise immediately to the priority of the highest task that uses it