A Type System for Unstructured Locking that Guarantees Deadlock Freedom without Imposing a Lock Ordering
Deadlocks occur in concurrent programs as a consequence of cyclic resource acquisition between threads. In this paper we present a novel type system that guarantees deadlock freedom for a language with references, unstructured locking primitives, and locks which are implicitly associated with references. The proposed type system does not impose a strict lock acquisition order and thus increases programming language expressiveness.
💡 Research Summary
The paper addresses the classic problem of deadlocks in concurrent programs, which arise when threads acquire resources in a cyclic manner. Traditional static approaches to guarantee deadlock freedom fall into three categories: prevention, detection/recovery, and avoidance. Most type‑based solutions belong to the prevention category and enforce a global lock acquisition order. While sound, this restriction rejects many correct programs, especially those that acquire locks in different orders across threads.
Boudol’s earlier work introduced a deadlock‑avoidance type system that annotates each lock operation with a “future lockset”—the set of locks that will be needed later. At runtime, a lock is granted only if its future lockset does not intersect with locks currently held by other threads. This approach allows arbitrary lock ordering but is limited to lexically scoped locks and does not handle functions, conditionals, or recursion properly.
The authors propose a new type and effect system that extends Boudol’s idea to a language with references, mutable cells, explicit allocation/deallocation, and unstructured (non‑lexical) locks. The key technical contribution is the notion of continuation effects: instead of treating effects as unordered sets, they are ordered sequences of lock (+) and unlock (−) actions. Each lock or unlock expression carries a continuation effect that describes the exact order of future lock operations after the current one.
During type checking, the system computes for every expression both a type and a continuation effect. For a lock γ e construct, the effect of the body e is appended to the current effect, and the lock is allowed only if the “future lockset” derived from the continuation does not intersect with locks already held. The runtime checks the same condition, guaranteeing that a thread never enters a state where a circular wait could form.
To make the approach practical, the authors solve three major challenges:
-
Interprocedural Effects – When a function is called, its body may contain lock/unlock pairs that match with operations in the caller after the call returns. The system computes a function’s effect intraprocedurally and annotates each call site with the continuation effect of the surrounding context, allowing the runtime to correctly calculate future locksets across function boundaries.
-
Conditionals – In an
if‑then‑elseboth branches may acquire different locks. The type system requires that the overall effect of the conditional be the same regardless of which branch is taken. This is achieved by ensuring that after cancelling matching+and−actions, each lock appears the same number of times in both branches. The combined effect is then attached to the conditional expression. -
Recursive Functions – Recursive definitions can cause the effect of the body to be strictly larger than the declared effect of the function. The authors introduce a summary of the body’s effect and require that this summary equal the declared effect. Summarization flattens repeated lock/unlock pairs and merges branches, preventing unbounded growth of effects while preserving the information needed for future lockset calculation.
The language syntax (Figure 3) includes variables, lambda abstractions, let‑bindings for allocation, explicit lock/unlock, a pop γ e construct used during evaluation, and a if construct. Regions (memory cells) are associated with capabilities (refCount, lockCount), similar to fractional permissions, enabling precise reasoning about aliasing and ownership transfer between threads.
The formalism defines typing rules that propagate both types and continuation effects, and a small‑step operational semantics that respects capabilities. The authors prove preservation (types and effects are maintained across reduction) and progress (well‑typed programs do not get stuck). Crucially, they prove a deadlock‑freedom theorem: at any execution point the future lockset of a pending lock operation is disjoint from the set of locks currently held by other threads, thus eliminating the possibility of a circular wait.
Two illustrative examples demonstrate the expressive power of the system. The first shows two parallel threads acquiring locks x and y in opposite order; the system accepts the program, whereas order‑based systems reject it. The second example involves a higher‑order function that updates three shared variables with a complex lock/unlock pattern. After substituting the same variable for two parameters, the program would be rejected by a naïve extension of Boudol’s system, but the continuation‑effect approach correctly types both the generic and instantiated versions.
In conclusion, the paper presents a robust static‑dynamic hybrid mechanism that guarantees deadlock freedom without imposing a global lock ordering, while supporting unstructured locks, conditionals, and recursion. The authors suggest future work on automated effect inference, integration with real‑world languages such as C/pthreads, and extensions to richer memory models.
Comments & Academic Discussion
Loading comments...
Leave a Comment