Operating Systems

Mutexes

A mutex is like a lock with one key. Only its owner may enter the protected critical section; other threads wait until the key becomes available.

A simulator showing three threads acquiring a mutex and waiting in a queue

Use each thread's button to call lock() or unlock()

Critical section

When one thread holds the mutex, another thread's lock() joins the queue. Once the owner unlocks, the first waiting thread acquires it in this simulation.

A mutex has an owner

Only the thread that locks a mutex should unlock it. Unlocking from another thread violates the mutex's ownership rules; behavior depends on the implementation. A binary semaphore also has two states, but has no owner and can be signaled by another thread.

A timeline comparing priority inversion and priority inheritance

Priority inversion: high-priority H waits for low-priority L's mutex

Running (H, M, L) Blocked on mutex Ready (waiting for CPU) Running at inherited priority

Play the timeline one time unit at a time. Compare when H finishes with and without priority inheritance.

Priority inversion

Low-priority L holds a mutex that high-priority H needs, so H must wait. The problem becomes worse when unrelated medium-priority M preempts L. L cannot release the mutex while M runs, so H is delayed by M despite having a higher priority.

Priority inversion caused repeated resets on the Mars Pathfinder mission in 1997.

Priority inheritance

When H blocks on L's mutex, the operating system temporarily raises L to H's priority. M can no longer preempt L, which can finish the critical section and release the mutex. L then returns to its original priority.

In FreeRTOS, xSemaphoreCreateMutex() creates a mutex with priority inheritance, whereas xSemaphoreCreateBinary() creates a binary semaphore without it. This distinction matters when protecting shared resources.