Concurrent programs are well known for containing errors that are difficult to detect, reproduce, and diagnose. Deadlock is a common concurrency error, which occurs when a set of threads are blocked, due to each attempting to acquire a lock held by another. This paper presents a collection of highly scalable static and dynamic techniques for exposing potential deadlocks. The basis is a known algorithm, which, when locks are acquired in a nested fashion, captures the nesting order in a lock graph. A cycle in the graph indicates a deadlock potential. We propose three extensions to this basic algorithm to eliminate, or label as low severity, false warnings of possible deadlocks (false positives). These false positives may be due to cycles within one thread, cycles guarded by a gate lock (an enclosing lock that prevents deadlocks), and cycles involving several code fragments that cannot possibly execute in parallel. We also present a technique that combines information from multiple runs of the program into a single lock graph, to find deadlock potentials that would not be revealed by analyzing one run at a time. Finally, this paper describes the use of static analysis to automatically reduce the overhead of dynamic checking for deadlock potentials.
IntroductionConcurrent programs are well known for containing errors that are difficult to detect, reproduce, and diagnose. Some common programming errors include data races and deadlocks. A data race occurs when two or more threads concurrently access a shared variable, at least one of the accesses is a write, and no mechanism is used to enforce mutual exclusion. Data races can be avoided by proper use of locks. However, the use of locks introduces the potential for deadlocks. Two types of deadlocks, namely, resource deadlocks and communication deadlocks, are discussed in the literature [1,2]. In the case of resource deadlocks, a set of threads are deadlocked if each thread in the set is waiting to acquire a lock held by another thread in the set. In the case of communication deadlocks, threads wait for messages or signals that do not occur. In the Java** programming language, resource deadlocks result from the use of synchronized methods and synchronized statements.Communication deadlocks result from the use of the wait and notify primitives. The algorithms presented in this paper address resource deadlocks, from now on referred to as deadlocks, illustrated by example programs written in Java.Deadlocks can be analyzed using a variety of techniques, such as model checking (using algorithms that explore all possible behaviors of a program), dynamic analysis (analyzing only one or just a few executions), and static analysis (analyzing the source code without executing it). Model checking is computationally expensive and often impractical for large software applications. Static analysis can guarantee that all executions of a program are deadlock free but often yields false warnings of possible deadlocks, also called false positives or false alarms. Dynamic analysis general...