Concurrency verification proves correctness properties of multi-threaded and distributed programs — systems where multiple threads of execution interact through shared state or message passing. The core difficulty is that concurrent programs have exponentially many possible interleavings of thread actions, and bugs may manifest only under specific rare schedules. Key approaches include concurrent separation logic (each thread owns a portion of the heap, with explicit transfer of ownership), rely-guarantee reasoning (each thread specifies what it assumes about other threads and guarantees about itself), and model checking with partial-order reduction (exploring representative interleavings rather than all of them).
Sequential programs execute one step at a time in a deterministic order. Concurrent programs execute multiple threads simultaneously, and the order in which their steps interleave depends on the scheduler — which is typically nondeterministic. This means a concurrent program does not have one behavior but an exponentially large set of possible behaviors, one for each possible interleaving. Concurrency verification must prove that the desired property holds across ALL possible interleavings, making it fundamentally harder than sequential verification.
Concurrent separation logic (CSL), introduced by O'Hearn (2004), extends separation logic to multi-threaded programs. The key idea is ownership: each thread owns a portion of the heap, described by its separation logic assertion. The separating conjunction P * Q guarantees disjointness, preventing two threads from simultaneously accessing the same memory. Shared resources (protected by locks, for instance) are governed by resource invariants: when a thread acquires a lock, it gains ownership of the associated heap region (the invariant transfers from the lock to the thread); when it releases the lock, ownership transfers back. This discipline ensures that shared memory is accessed under mutual exclusion, preventing data races by construction.
Rely-guarantee reasoning, introduced by Jones (1983), takes a complementary approach. Instead of partitioning the heap, it partitions the behavior: each thread specifies a rely condition R (an assumption about how other threads may modify shared state) and a guarantee condition G (a promise about how this thread modifies shared state). The thread is verified under the assumption that R holds at every interference point, and the proof obligation includes showing that G holds for every step the thread takes. Compositionality is established by checking that each thread's guarantee implies the other threads' rely conditions — the system is consistent if all such implications hold.
Model checking approaches concurrency verification differently: instead of compositional proofs, it exhaustively explores the state space of the concurrent system. The challenge is the state explosion from interleaving: n threads with k steps each produce up to (nk)!/(k!)^n interleavings. Partial-order reduction mitigates this by recognizing that many interleavings are equivalent — if two thread actions are independent (they access different memory), their order does not matter, and only one ordering needs to be explored. This can reduce the state space by orders of magnitude. Tools like SPIN and Java Pathfinder apply these reductions to verify concurrent programs.
In practice, the most effective approach combines multiple techniques. Thread-modular analysis (a blend of abstract interpretation and rely-guarantee) verifies each thread independently with sound abstractions of other threads' behavior. Linearizability proofs verify that a concurrent data structure appears to execute operations atomically even though they overlap in time — the gold standard for concurrent data structure correctness. Rust's type system enforces a form of concurrent separation logic at the type level: the borrow checker prevents shared mutable access, and ownership transfer (via channels or Arc) is explicit. Each approach tackles a different aspect of the concurrency verification challenge, and real-world verification often combines deductive, model-checking, and type-based techniques.
No topics depend on this one yet.