priority inversion (and priority inheritance)
Picture three people and one shared tool, say a single ladder. A low-priority worker borrows the ladder. Before returning it, an urgent high-priority worker arrives and needs that same ladder — so they must wait for the low worker to finish. So far that is just sharing. But now a flood of medium-priority workers keeps grabbing the low worker, pushing them aside to do other jobs, so the low worker never gets to finish and return the ladder. The urgent worker is stuck waiting, blocked indirectly by tasks LESS important than itself. That upside-down situation is priority inversion.
Precisely: priority inversion happens when a high-priority task is blocked waiting for a resource (typically a mutex) held by a low-priority task, and the low-priority task is itself prevented from running — and thus from releasing the lock — because medium-priority tasks keep preempting it. The high task is effectively waiting on the medium tasks, even though it outranks them, which violates the whole point of priorities. In the bounded case the high task waits only as long as the low task needs to finish its critical section; the dangerous case is UNBOUNDED priority inversion, where unrelated medium tasks can extend the wait indefinitely. The classic real-world example is NASA's 1997 Mars Pathfinder rover, which kept resetting because a high-priority task was blocked this way. The standard fix is priority inheritance: while a low-priority task holds a lock that a high-priority task wants, the low task TEMPORARILY inherits the high task's priority, so medium tasks can no longer preempt it; once it releases the lock, it drops back to its own priority. An alternative is the priority ceiling protocol, where a mutex carries a priority ceiling and a task is raised to it on acquisition.
It matters because priority inversion can silently destroy the timing guarantees of a real-time system — a high-priority deadline gets missed even though the CPU was 'available', and it is intermittent and hard to reproduce. The honest points: priority inheritance bounds the inversion (limiting it to the lengths of the relevant critical sections) but does NOT eliminate all blocking, so you still want short critical sections; many RTOS mutexes offer priority inheritance as an option you must turn on; and inheritance solves only the mutex-induced case, not delays from, say, a long ISR or a non-prioritized resource. Enabling inheritance and keeping locked sections tiny are the practical defenses.
Tasks (H high, M medium, L low) sharing one mutex: 1. L takes the mutex, starts its critical section. 2. H wakes, wants the mutex -> blocks, waiting on L. 3. M wakes and preempts L (M > L). L can't finish. 4. H is now waiting on M indirectly: UNBOUNDED inversion. Fix - priority inheritance: At step 2, L is boosted to H's priority, so M can't preempt L; L finishes fast, releases the mutex, then drops back; H runs.
Without inheritance, medium tasks can starve the low lock-holder forever, blocking the high task. Inheritance boosts the holder so it finishes fast.
Priority inheritance bounds priority inversion but does not remove all blocking — keep critical sections short anyway. Many RTOS mutexes only inherit if you enable that option, and inheritance does nothing for delays caused by long ISRs.