Priorities, Nice Values, and Priority Inversion
What This Concept Is
Priority says "some processes matter more than others." Unix expresses this through:
- nice value: integer in
[-20, 19]on Linux. Lower is higher priority (less nice to others). Default0. Changed vianice(+5)orrenice. - static priority: kernel-internal field derived from nice.
- real-time priorities (separate class):
SCHED_FIFOandSCHED_RRin[1, 99], always above any normal task.
Priority inversion is the pathology where a high-priority task is indirectly blocked by a low-priority task, typically because:
- Low-priority task
Lholds a lock. - High-priority task
Hbecomes runnable, tries to acquire the same lock, blocks. - Medium-priority task
Mruns (no lock needed), preemptingL. Lcannot release the lock, soHwaits -- on a medium-priority job.
H is effectively held hostage by M, even though H should dominate M.
Why It Matters Here
Priorities seem harmless ("just set it higher"). They are not. Priority inversion has caused real failures -- most famously Mars Pathfinder in 1997, where a low-priority data-gathering task held a mutex needed by a high-priority bus task, a medium-priority task kept preempting the low one, and the watchdog reset the spacecraft repeatedly. The fix was enabling priority inheritance on the mutex.
Concrete Example
Three tasks, one mutex, Linux-like scheduling:
t=0 L (low, nice +10) acquires mutex M
t=5 H (high, nice -10) becomes runnable, tries to acquire M -> blocks
t=6 M (medium, nice 0) becomes runnable, preempts L
... M runs for 500 ms doing unrelated CPU work
t=506 M finishes; L resumes; releases mutex; H finally runs
H saw a 501 ms latency on a task that should have taken microseconds. The bug is invisible in CPU utilization metrics: CPU was always busy. It shows up only in H's observed latency.
Common Confusion / Misconception
"Raise the priority of H to make it faster." Useless: H already has the highest priority and is still blocked on the lock.
"Never use priorities." Also wrong. Audio pipelines, video encoding, networking fast-path threads, and real-time control all depend on priorities being respected. The fix is not to abandon them; it is to enable priority inheritance on any lock that crosses priority boundaries.
Priority inheritance: while L holds a lock H is waiting for, L temporarily inherits H's priority. So L preempts M, finishes its critical section, drops back to its own priority, and H proceeds.
How To Use It
When you design a system that mixes priorities:
- List every lock (mutex, spinlock, rwlock) in the code path of a high-priority task.
- For each lock, list every task that ever holds it.
- If any of those holders can be lower priority than the high-priority waiter, you have a potential inversion.
- Turn on priority inheritance (
PTHREAD_PRIO_INHERITon pthread mutexes,PI-futexunder the hood on Linux) or redesign so that the fast path does not take the shared lock.
Check Yourself
- Why does raising
H's priority not fix priority inversion? - What does priority inheritance change about
L's behavior while holding the lock? - Why does the inversion not show up in CPU utilization graphs?
Mini Drill or Application
For each scenario, say whether priority inversion is possible and how to fix it:
- A web server's accept thread is
SCHED_FIFO, its worker threads are normal, and they all share a stats mutex. - An audio callback (
SCHED_FIFO) and a logger (normal) share a ring buffer lock. - Two equal-priority tasks share a lock with a third, higher-priority task never touching that lock.