Visual walkthrough — Priority inversion — problem and solutions (priority inheritance, priority ceiling)
Everything below lives on one kind of diagram: a timeline. Let's earn that first.
Step 1 — What a "timeline" even shows
WHAT. Before any bug, we need a language to talk about who runs when. We draw a horizontal axis for time (moving right = later) and stack three horizontal lanes, one per task. A coloured bar in a lane means that task is the one holding the CPU right now. Only one lane can be filled at any instant, because a single CPU runs exactly one task at a time.
WHY this picture and not a table. A table of "task states" hides the one thing that matters: the gap during which the most urgent task is NOT running. A timeline makes that gap a literal empty stretch you can measure with a ruler. We are going to measure that gap — so we need it visible.
PICTURE. Three lanes: H (high priority, magenta), M (medium, orange), L (low, violet). Higher lane = higher priority. Right now only L is running.

Step 2 — The lock: a door only one task fits through
WHAT. H and L both touch one shared thing (a buffer, a pipe). To keep it from being corrupted, they guard it with a mutex — call it . A mutex is a door: whoever walks in locks it; everyone else who reaches the door must wait until it is unlocked.
WHY a mutex and not just "be careful." Two tasks writing the same buffer at once produces garbage. Mutual exclusion (see Mutexes vs Semaphores) makes the rule physical: at most one task is inside 's critical section — the stretch of code between lock and unlock — at any instant.
PICTURE. L walks through the door of (locks it). While L is inside, the door shows LOCKED. The region of L's bar spent inside is its critical section, drawn hatched.

Step 3 — H arrives and hits the locked door
WHAT. While L is inside , task H becomes ready. By the scheduler rule H preempts L (magenta bar starts). H runs its own code, then reaches the shared thing and tries to lock . The door is LOCKED (L is still inside). So H cannot proceed — H blocks.
WHY this is not yet a bug. H is waiting for exactly one thing: L to finish its critical section and unlock the door. That wait is at most . A wait this short and predictable is called bounded inversion — it is unavoidable whenever two tasks share a lock, and it is acceptable.
PICTURE. H's bar starts, then hits a red wall at 's door and stops. Control has to go back to L (violet), because L is the only task that can unlock .

Step 4 — M walks in and breaks the bound
WHAT. L has resumed (it must, to release ). Now M becomes ready. M needs nothing from . But M outranks L. Scheduler rule fires: M preempts L. L freezes mid-critical-section, door still LOCKED. M runs — and M can run for a long, unspecified time.
WHY this is the disaster. Follow the dependency chain:
- H waits for L (to unlock ).
- L waits for M (to give up the CPU).
- M waits for nothing — it just runs.
So H, the most urgent task, is effectively waiting on M, a task it shares nothing with. And M's runtime has no fixed limit. The delay to H is now unbounded.
PICTURE. The orange M-bar shoves L aside. L's hatched critical section is paused (frozen, greyed). H's red wall stretches to the right with no end in sight — an arrow labelled "unbounded".

Step 5 — The fix idea: lend L your urgency (Priority Inheritance)
WHAT. Change one rule. The instant H blocks on held by L, give L H's priority — L is boosted. L keeps this borrowed rank until it unlocks , then drops back.
WHY this exact move. The bug was "M outranks L, so M preempts L." Boost L to H's level and that inequality flips: now L outranks M. When M becomes ready, the scheduler rule says the higher task runs — that is L (boosted). M cannot preempt. L races through its critical section, unlocks , drops rank, and H — first in line at the door — runs at once.
PICTURE. At H's block instant, L's violet bar changes to magenta ("boosted to H"). When M arrives (orange), it is held in the ready lane — a dashed "denied preemption" bar — while boosted-L finishes.

Step 6 — Where inheritance still bleeds: chained blocking
WHAT. Inheritance bounds the delay, but with nested locks the bound can stack. Suppose H may need and . Low holds ; Mid holds and is also waiting for . When H arrives it can be forced to wait for two critical sections in sequence — one per lock in the chain.
WHY it happens. Inheritance only reacts after a block occurs; it never prevents a task from grabbing a second lock that starts a chain. So boosts ripple through a hierarchy (this is transitive inheritance), and each nested critical section adds to H's wait. Inheritance can also still deadlock — two tasks each holding one lock and wanting the other (see Deadlock — conditions and prevention).
PICTURE. A chain: H → waits on (held by Mid) → Mid waits on (held by Low). Two hatched critical sections stack up as H's total block. A small red "⚠ still possible: deadlock" flag sits between two tasks holding-and-wanting each other's lock.

Step 7 — Put a bouncer at the door (Priority Ceiling)
WHAT. Give every lock a static number, its ceiling = the priority of the highest task that will ever lock it. New rule: a task may enter a lock only if its priority is strictly above the ceilings of all locks currently held by other tasks (the system ceiling). Otherwise it is turned away at the door — before it grabs anything.
WHY this prevents the chain, not just bounds it. In Step 6 the tangle formed because Mid was allowed to take a second lock. The ceiling is a gate-keeper checked before entry: if taking a lock could lead into a blocking chain, you are refused up front, so the chain can never assemble. The order the ceilings impose is acyclic → no deadlock, and a task can be blocked at most once, for one critical section, per activation.
PICTURE. Low holds ; system ceiling = H. M reaches 's door — the bouncer checks: "your priority (M) is not strictly above the system ceiling (H)" → DENIED. The chain of Step 6 never forms. H is blocked by only one critical section.

Step 8 — The degenerate cases (never leave a gap)
WHAT & WHY & PICTURE, one panel each:
- No shared lock. No mutex → no door → no inversion possible. Ingredient 2 missing.
- Only two priority levels (no M). Inversion is bounded automatically: H waits only , because there is no medium task to steal the CPU. The unbounded bug needs three levels.
- M needs the lock too. Then M is bound by the same door rules — it can't just barrel past; it too must wait, so it can't be the unrelated hog.
- Zero-length critical section (). Blocking : L is never caught holding the lock. The limit sanity-checks every formula: .

Recall Check the edge cases
Remove the shared lock — can inversion happen? ::: No. With no mutex there is no door to wait at; ingredient 2 is gone. With only H and L (no medium task), is inversion unbounded? ::: No, it is bounded to — you need a third (medium) level for the unbounded bug. As , what does tend to? ::: — an instant critical section can never trap a higher task.
The one-picture summary

One figure, four stacked timelines: (a) the bug — H's block runs off the chart (unbounded, orange M hogging); (b) PIP — L boosted magenta, M frozen, H's block = one ; (c) PCP — the bouncer denies M at the door, chain prevented; and the shrinking block-bar written underneath.
Recall Feynman retelling — say it like a story
One CPU, three workers: H the urgent one, M a busybody, L the slow one. L is inside a shared room (a lock). H shows up needing that room and has to wait at the door — fair enough, that's a short wait. But then M struts in; M doesn't need the room at all, yet M outranks L, so M kicks L off the CPU. Now L is stuck frozen inside the room, still holding the key, and H is stuck outside — waiting on M, a stranger who could dawdle forever. That's the unbounded bug.
Fix one (Inheritance): the moment H is stuck waiting on L, we lend L H's rank. Suddenly L outranks M, so M can't kick L off; L finishes fast, unlocks, hands the room to H. H's wait shrinks to just L's time in the room.
Fix two (Ceiling): hire a bouncer. Every room has a sign showing the rank of the fanciest guest who ever uses it. You may only enter a room if you outrank every sign currently in use. M gets turned away before it can start a nested tangle — so the mess never forms, no deadlock, and H is delayed by at most one room's worth of time.
The whole journey in three numbers: block time goes from ∞ (bug) to a sum of critical sections (inheritance) to one critical section (ceiling).