Priority inheritance is a mutex protocol in which a low-priority task temporarily receives the priority of the highest-priority task blocked waiting for a mutex it holds, lasting until the mutex is released. It is a common mechanism used to bound the duration of priority inversion, though specific boost and deboost rules can vary across RTOS implementations.
In practice
Priority inversion occurs when a high-priority task is blocked on a mutex held by a low-priority task, and a medium-priority task preempts the low-priority task, preventing it from releasing the mutex. Without mitigation, the high-priority task can be delayed indefinitely. Priority inheritance directly attacks this by boosting the holder's priority so it can run and finish its critical section sooner, though the exact reduction in delay still depends on preemption rules and whether unrelated higher-priority work intervenes before the mutex is released.
Many commercial and open-source RTOSes used in embedded work -- including FreeRTOS (via its mutex type, as opposed to binary semaphores), ThreadX, Zephyr, embOS, and RTEMS -- offer mutexes with priority inheritance, though support is often limited to specific mutex types or configurations. The feature is typically opt-in per mutex type: in FreeRTOS, for example, xSemaphoreCreateMutex() provides inheritance while xSemaphoreCreateBinary() does not. Choosing the wrong primitive is a common source of latent priority inversion bugs.
Priority inheritance has important limitations. It handles only direct blocking chains: task A holds mutex 1 and waits on mutex 2, which is held by task B. Chained or nested inheritance is handled correctly by most mature RTOS implementations, but the logic becomes complex and is worth verifying against your specific RTOS documentation. Priority inheritance does not eliminate inversion -- it reduces the window of inversion, though the actual bound can exceed the critical section duration if the boosted task is preempted by unrelated higher-priority work or if nested locks and implementation-specific effects are involved -- and it does not prevent deadlock. For hard real-time systems with strict deadline guarantees, the Priority Ceiling Protocol (PCP) or Immediate Ceiling Priority Protocol (ICPP) offer stronger, more analyzable guarantees.
A practical pitfall is that priority inheritance operates only on mutex objects tracked by the RTOS scheduler. Shared resources protected by disabling interrupts or by spinlocks are invisible to the scheduler, so inheritance cannot apply to them. On Cortex-M targets where critical sections are sometimes implemented with PRIMASK or BASEPRI manipulation rather than RTOS mutexes, no inheritance occurs regardless of RTOS configuration.
Frequently asked
What is the difference between priority inheritance and priority ceiling?
Priority inheritance boosts a
mutex holder's priority reactively -- only when a higher-priority task actually blocks on the mutex -- and only to the level of the blocked waiter. The Immediate Ceiling Priority Protocol (ICPP), a variant of the classic Priority Ceiling Protocol (PCP), assigns a fixed ceiling priority to each mutex and boosts any task that locks it to that ceiling immediately, before any other task blocks; classic PCP differs in that the boost occurs only when a conflict is detected rather than unconditionally at lock acquisition. The ceiling approach prevents inversion and avoids nested blocking entirely, at the cost of requiring the ceiling value to be set correctly at design time.
Does priority inheritance prevent deadlock?
No. Priority inheritance only addresses
priority inversion. If two tasks each hold one
mutex and attempt to acquire the other,
deadlock still occurs. Deadlock prevention requires design-level measures such as enforcing a consistent lock-ordering convention or using a deadlock-detecting RTOS feature.
Why does FreeRTOS require using xSemaphoreCreateMutex() rather than xSemaphoreCreateBinary() to get priority inheritance?
FreeRTOS binary
semaphores are signaling primitives with no concept of ownership: any task can give them. Because the
scheduler does not track which task 'holds' a binary semaphore, there is no holder whose priority can be raised.
Mutexes created with xSemaphoreCreateMutex() record the owning task, enabling the scheduler to apply inheritance when a higher-priority task blocks on them.
Does priority inheritance work across chained blocking (task A waits on task B which waits on task C)?
Many mature RTOS implementations propagate the inheritance boost transitively through blocking chains, but this is an implementation-specific quality. Verify your RTOS documentation -- some lightweight implementations only perform single-level inheritance, which is insufficient for multi-level chains.
Can priority inheritance be used with interrupt-based critical sections on Cortex-M?
No. Raising or lowering PRIMASK/BASEPRI to protect a
critical section is invisible to the RTOS
scheduler. Only
mutex objects managed by the scheduler are eligible for inheritance. If a shared resource is guarded by
interrupt masking rather than a mutex,
priority inversion between tasks at that resource cannot be mitigated by inheritance.
Differentiators vs similar concepts
Priority inheritance is often confused with the Priority Ceiling Protocol (PCP) and its variant ICPP. Inheritance is reactive: the boost happens only after a higher-priority task blocks. Ceiling protocols are proactive: the boost happens at lock acquisition regardless of whether any higher-priority task is waiting. PCP and ICPP differ from each other -- ICPP boosts unconditionally at lock time, while classic PCP conditionally boosts based on conflict detection -- but both are more analyzable for hard real-time scheduling than priority inheritance. Priority inheritance is easier to configure and sufficient for many soft real-time designs.