To run the analysis, you can use plain `infer` (to run RacerD along with other
analyses that are run by default) or `infer --racerd-only` (to run only RacerD).
For example, the command `infer --racerd-only -- javac File.java` will run
RacerD on File.java.
## Background
RacerD statically analyzes Java code to detect potential concurrency bugs. This
analysis does not attempt to prove the absence of concurrency issues, rather, it
searches for a high-confidence class of data races. At the moment RacerD
concentrates on race conditions between methods in a class that is itself
intended to be thread safe. A race condition occurs when there are two
concurrent accesses to a class member variable that are not separated by mutual
exclusion, and at least one of the accesses is a write. Mutual exclusion can be
ensured by synchronization primitives such as locks, or by knowledge that both
accesses occur on the same thread.
## Triggering the analysis
RacerD doesn't try to check _all_ code for concurrency issues; it only looks at
code that it believes can run in a concurrent context. There are two signals
that RacerD looks for: (1) Explicitly annotating a class/method with
`@ThreadSafe` and (2) using a lock via the `synchronized` keyword. In both
cases, RacerD will look for concurrency issues in the code containing the signal
and all of its dependencies. In particular, it will report races between any
non-`private` methods of the same class that can peform conflicting accesses.
Annotating a class/interface with `@ThreadSafe` also triggers checking for all
of the subclasses of the class/implementations of the interface.
## Warnings
Let's take a look at the different types of concurrency issues that RacerD
flags. Two of the warning types are data races (`Unprotected write` and
`Read/write race`), and the third warning type encourages adding `@ThreadSafe`
annotations to interfaces to trigger additional checking.
### Unprotected write
RacerD will report an unprotected write when one or more writes can run in
parallel without synchronization. These come in two flavors: (1) a self-race (a
write-write race that occurs due to a method running in parallel with itself)
and (2) two conflicting writes to the same location. Here's an example of the
self-race flavor:
```
@ThreadSafe
public class Dinner {
private int mTemperature;
public void makeDinner() {
boilWater();
}
private void boilWater() {
mTemperature = 100; // unprotected write.
}
}
```
The class `Dinner` will generate the following report on the public method
`makeDinner()`:
`There may be a Thread Safety Violation: makeDinner() indirectly writes to mTemperature outside of synchronization.`
This warning can be fixed by synchronizing the access to `mTemperature`, making
`mTemperature``volatile`, marking `makeDinner` as `@VisibleForTesting`, or
suppressing the warning by annotating the `Dinner` class or `makeDinner` method
with `@ThreadSafe(enableChecks = false)`.
### Read/Write Race
We sometimes need to protect read accesses as well as writes. Consider the
following class with unsynchronized methods.
```
@ThreadSafe
public class Account {
int mBalance = 0;
public void deposit(int amount) {
if (amount > 0) {
mBalance += amount;
}
}
public int withdraw(int amount){
if (amount >= 0 && mBalance - amount >= 0) {
mBalance -= amount;
return mBalance;
} else {
return 0;
}
}
}
```
If you run the `withdraw()` method in parallel with itself or with `deposit()`
you can get unexpected results here. For instance, if the stored balance is 11
and you run `withdraw(10)` in parallel with itself you can get a negative
balance. Furthermore, if you synchronize only the write statement
`mBalance -= amount`, then you can still get this bad result. The reason is that
there is a read/write race between the boolean condition
`mBalance - amount >= 0` and the writes. RacerD will duly warn
`Read/Write race. Public method int Account.withdraw(int) reads from field Account.mBalance. Potentially races with writes in methods void Account.deposit(int), int Account.withdraw(int)`
on the line with this boolean condition.
A solution to the threading problem here is to make both methods `synchronized`
to wrap both read and write accesses, or to use an `AtomicInteger` for
`mBalance` rather than an ordinary `int`.
### Interface not thread-safe
In the following code, RacerD will report an `Interface not thread-safe` warning
on the call to `i.bar()`:
```
interface I {
void bar();
}
@ThreadSafe
class C {
void foo(I i) {
i.bar(); // RacerD warns here
}
}
```
The way to fix this warning is to add a `@ThreadSafe` annotation to the
interface `I`, which will enforce the thread-safety of each of the
implementations of `I`.
You might wonder why it's necessary to annotate `I` -- can't RacerD just look at
all the implementations of `i` at the call site for `bar`? Although this is a
fine idea idea in principle, it's a bad idea in practice due to a (a) separate
compilation and (b) our diff-based deployment model. In the example above, the
compiler doesn't have to know about all implementations (or indeed, any
implementations) of `I` at the time it compiles this code, so there's no
guarantee that RacerD will know about or be able to check all implementations of
`I`. That's (a). For (b), say that we check that all implementations of `I` are
thread-safe at the time this code is written, but we don't add the annotation.
If someone else comes along and adds a new implementation of `I` that is not
thread-safe, RacerD will have no way of knowing that this will cause a potential
bug in `foo`. But if `I` is annotated, RacerD will enforce that all new
implementations of `I` are thread-safe, and `foo` will remain bug-free.
## Annotations to help RacerD understand your code
Getting started with RacerD doesn't require any annotations at all -- RacerD
will look at your usage of locks and figure out what data is not guarded
consistently. But increasing the coverage and signal-to-noise ratio may require
adding `@ThreadSafe` annotations along with some of the other annotations
described below. Most of annotations described below can be used via the Maven
An important feature of RacerD is that it finds races by analyzing not just one
file or class, but by looking at memory accesses that occur after going through
several procedure calls. It handles this even between classes and between files.
Here is a very basic example
```
@ThreadSafe
class A{
void m1(B bb) {
bb.meth_write();
}
}
class B{
Integer x;
void meth_write() {
x = 88;
}
}
```
Class `B` is not annotated `@ThreadSafe` and does not have any locks, so RacerD
does not directly look for threading issues there. However, method `m1()` in
class `A` has a potential self-race, if it is run in parallel with itself and
the same argument for each call. RacerD discovers this.
```
InterProc.java:17: error: THREAD_SAFETY_VIOLATION
Unprotected write. Non-private method `A.m1` indirectly writes to field `&this.B.x` outside of synchronization.
Reporting because the current class is annotated `@ThreadSafe`, so we assume that this method can run in
parallel with other non-private methods in the class (incuding itself).
15.
16. void m1(B bb) {
17. > bb.meth_write();
18. }
19. }
```
RacerD does this sort of reasoning using what is known as a _compositional
inteprocedural analysis_. There, each method is analyzed independently of its
context to produce a summary of the behaviour of the procedure. In this case the
summaries for `m1()' and`meth()' include information as follows.
```
Procedure: void A.m1(B)
Accesses: { Unprotected({ 1 }) -> { Write to &bb.B.x at void B.meth_write() at line 17 } }
Procedure: void B.meth_write()
Accesses { Unprotected({ 0 }) -> { Write to &this.B.x at at line 25 } }
```
The descriptions here are cryptic and do not include all the information in the
summaries, but the main point is that you can use RacerD to look for races in
codebases where the mutations done by threads might occur only after a chain of
procedure calls.
## <a name="context"></a> Context and Selected Related Work
Reasoning about concurrency divides into bug detection and proving absence of
bugs. RacerD is on the detection side of reasoning.
The rapid growth in the number of interleavings is problematic for tools that
attempt exhaustive exploration. With just 150 instructions for two threads, the
number 10^88 of interleavings is more that the estimated number of atoms in the
known universe.
[There has been important work which uses various techniques to attempt to reduce the number of interleavings](https://en.wikipedia.org/wiki/Partial_order_reduction)
while still in principle covering all possibilities, but scale is still a
challenge. Note that RacerD is not exhaustive: it has false negatives (missed
bugs). But in compensation it is fast, and effective (it finds bugs in
practice).
Static analysis for concurrency has attracted a lot of attention from
researchers, but difficulties with scalability and precision have meant that
previous techniques have had little industrial impact. Automatic static race
detection itself has seen significant work. The most advanced approaches,
exemplified by the [Chord](http://www.cis.upenn.edu/~mhnaik/pubs/pldi06.pdf)
tool, often use a whole-program analysis paired with a sophisticated alias
analysis, two features we have consciously avoided. Generally speaking, the
leading research tools can be more precise, but RacerD is faster and can operate
without the whole program: we have opted to go for speed in a way that enables
industrial deployment on a large, rapidly changing codebase, while trying to use
as simple techniques as possible to cover many (not all) of the patterns covered
by slower but precise research tools.
An industrial static analysis tool from
[Contemplate](http://homepages.inf.ed.ac.uk/dts/pub/avocs2015.pdf) also targets
@ThreadSafe annotations, but limits the amount of inter-procedural reasoning:
“This analysis is interprocedural, but to keep the overall analysis scalable,
only calls to private and protected methods on the same class are followed”.
RacerD does deep, cross-file and cross-class inter-procedural reasoning, and yet
still scales; the inter-class capability was one of the first requests from
Facebook engineers.
[A separate blog post looked at 100 recent data race fixes](https://code.facebook.com/posts/1537144479682247/finding-inter-procedural-bugs-at-scale-with-infer-static-analyzer/)
in Infer's deployment in various bug categories, and for data races observed
that 53 of them were inter-file (and thus involving multiple classes).
[See above](#interprocedural-reasoning) for an example of RacerD's interprocedural
capabilities.
One reaction to the challenge of developing effective static race detectors has
been to ask the programmer to do more work to help the analyzer. Examples of