Fall 2003 CS 263 Course Project Survey : Race Detection and Atomicity Checking

Concurrent programs are hard to write and debug because of the inherent concurrency and indeterminism. Bugs in these programs are often hard to find in the testing process. When they do show up later in production systems, they are not easy to pinpoint because a lot of them are hard to reproduce. Therefore, automated tools that are able to find these bugs are of great value in relieving programmers from identifying these bugs manually. In this paper we survey some of the recent work in this area, focusing on static language-based tools that do not actually run the application. Static tools have the benefit of considering different execution paths more exhaustively and can potentially be sound, i.e. being able to prove the bug-freeness of programs. Apart from static tools, other kinds of concurrency bug finding tools include dynamic tools and post-mortem tools. Dynamic tools, e.g. Eraser [8], tracks program execution and report potential concurrency bugs if the program does not follow certain concurrency control disciplines (e.g. locking). These tools often produce relevant results with low false positive rate. But they cannot possibly cover all execution paths and therefore cannot be sound. Post mortem tools detect bugs by analyzing execution traces collected during execution. Although they introduces less overhead than dynamic tools, they still suffer the same limitations. Concurrency bugs can be of several forms. The most extensively studied one is race conditions. Races occur when shared variables are accessed concurrently by multiple threads of control, at least one of them doing a write, without proper synchronization. The final values of the variables will depend on the interleaving of the threads and thus normal assumptions of serial programming, essentially that a variable will not change value if the current thread does not change it, do not hold any more. If the programmer make use of these assumptions, insidious bugs are introduced. However, it is important to note that not all races cause bugs. Races can be benign if, e.g. only reads of a single variable are done without synchronization, or if only increment and decrement are done to a particular shared variable and hardware guarantees atomicity of these operations. Races are actually only part of the reasons for so-called atomicity bugs [4]. In general, atomicity bugs happen when the serial assumptions that the correctness of the program is based on is broken. These assumptions can be broken even if no races exist. For example, lock(l); t = s; unlock(l); t++; lock(l); s = t; unlock(l) will not be correct if the intended functionality is to increment shared variable s, protected by lock l. It is easy to see that the result may be wrong if more than one of these operations are interleaved. Static tools for finding concurrency bugs include at least the following kinds, ordered roughly from lightweight ones to heavyweight ones in terms of analysis complexity. 1) Type systems, e.g. rccjava [3] and Java atomicity types[4]. They work by extending the type system of the language with atomicity-related properties like thread-local, shared, “protected by lock l”, etc. Code blocks or functions/methods also have concurrency properties defined so that the tools can