NOTE Hello r/rust ! Thank you for the interest in this work and for the delightful conversation ❤️
I hate deadlocks. Maybe you do too. Back at Fission, whenever someone would suggest a mutex we’d start a chant of “I say mutex, you say deadlock: Mutex! DEADLOCK! Mutex! DEADLOCK!“. Deadlocks lurk — perfectly invisible in code review, happy to pass CI a thousand times, then lock your system up at 3am under a request pattern that no one anticipated.
They have their own tradeoffs, but I miss TVar s from Haskell. AFAICT there’s no way to do proper TVar s in languages that have no way to enforce purity. We “should” prefer lock-free programming, but mutexes are very common in the Rust standard style. I often hear that actors eliminate deadlocks, but as someone who’s written her fair share of Elixir, this is 100% a lie (though they are less frequent).
Rust famously catches data races at compile time, but for deadlocks? You get a Mutex , a pat on the back, and “good luck, babe”. There are some tools that help analyze your code that are fairly good, but I want feedback during development. I’ve been thinking about a better approach to this problem for a while, looked at a bunch of other attempts and have come up with what I hope is a decent ergonomic balance that covers many common use cases in Rust: surelock , a deadlock-freedom library. If your code compiles, it doesn’t deadlock. No Result , no Option , no runtime panic on the lock path. Every acquisition is either proven safe by the compiler or rejected at build time1.
WARNING This is a pre-release. I think the core design is solid, but I wouldn’t be surprised if there are rough edges. Feedback and contributions welcome!
Deadlocks happen when all four Coffman Conditions occur simultaneously Surelock breaks one of them — circular wait — using two complementary mechanisms Same-level locks are acquired atomically in a deterministic order ( LockSet ) Cross-level locks are acquired incrementally with compile-time ordering enforcement ( Level<N> ) The entire public API is safe — unsafe is confined to the raw mutex internals no_std compatible, zero required runtime dependencies2
Why Not Just Be Careful?
The honest answer is that being careful doesn’t scale. It’s easy to shoot yourself in the foot while composing code. But wait, isn’t this the kind of thing that Rust us supposed to help us with? The borrow checker catches data races at compile time — why shouldn’t we expect the same for deadlocks?
The fundamental problem is well-understood. Coffman et al. identified four necessary conditions for deadlock back in 1971:
Condition Meaning Mutual exclusion At least one resource is held exclusively Hold and wait A thread holds one lock while waiting for another No preemption Locks can’t be forcibly taken away Circular wait A cycle exists in the wait-for graph
... continue reading