Master the fundamental concepts of threads & concurrency through this focused micro-challenge.
You have read the whole brief, and the concepts above stay free on every task. Writing and running the code needs a plan.
Three hints are available for this task, revealed one at a time inside the code workspace so you can struggle productively before seeing them.
Every task includes starter code, theory, and hidden tests so you can implement and verify locally in the browser.
How it worksDeadlock needs four conditions simultaneously: mutual exclusion, hold-and-wait, no preemption, and circular wait. In practice you diagnose it by finding a cycle in the wait-for graph: A waits for B's lock, B waits for C, C waits for A.
Impose a global order on mutex acquisition:
L2 then L1 if rank(L1) < rank(L2)For example, threads locking account_mutex before log_mutex never deadlock with threads following the same order, even when transferring funds and logging.
The dining philosophers deadlock Dijkstra formalized in 1965 has a very real modern descendant: database engines like MySQL's InnoDB detect exactly this kind of lock-ordering cycle at runtime and kill one transaction to break it, because the alternative is an indefinite hang. The global lock-ordering fix you implement here is the same discipline the Linux kernel's lockdep validator enforces to catch AB-BA deadlocks before they ship.
Before you call the implementation done, walk failure modes on purpose. Test empty structures, single-element edge cases, maximum concurrency, and errno paths that must not crash the program. OS code usually fails in production when happy-path tests pass but invariants break under contention or memory pressure.
Keep structures small and name fields after kernel counterparts when possible. That lets you read man pages and kernel source side by side while you work. Print observable events during development; remove noisy logs once tests pass reliably.
You will reproduce a two-thread deadlock, capture stack traces or logs, then fix it with ordered locking. The task asks you to document the cycle before and after the fix so the prevention strategy is explicit.
Two tools diagnose lock deadlocks. The first is a wait-for graph: a thread waits for a mutex, the mutex is held by a thread, and so on. A cycle in that graph is a deadlock that has already happened. The second is lockdep, the Linux kernel's lock validator. It remembers every "held X, then took Y" order it has ever seen, and warns about an inversion before it deadlocks, the first time a thread takes the locks the other way round. Build both, driven by a sequence of lock operations.
cLoading…
Threads and mutexes are created by name the first time they are mentioned, even by an operation that is then refused.
EDEADLK, because mutexes are not recursive.EPERM (not the owner)). The mutex passes straight to the first waiter, if there is one.H -> M. If M can already reach H through recorded edges (a path of any length), warn once for that pair of locks.T is blocked, or T is blocked forever (deadlocked).cLoading…
The cycle is listed starting from the thread that just blocked. state lists mutexes in order of first use. The summary is always printed last. Any other operation prints unknown operation.
Input:
cLoading…
Output:
cLoading…
Hidden tests cover a three-thread, three-lock cycle whose lockdep warning needs a transitive path, FIFO hand-off to several waiters, relocking, unlocking someone else's mutex, operations on blocked but not deadlocked threads, and unknown operations.