Analyze and Fix Race Conditions

Analyze Race Conditions

vmlens shows you all detected race conditions in the Race Conditions, Deadlocks View. To analyze a race condition expand the race condition, to see the stack traces of the reading and writing threads:

export race conditions

The example above shows you a race at field bounds of class sun.reflect.generics.reflectiveObjects.TypeVariableImpl. Thread-0 is reading and Thread-3 is writing to this field. Let us look at the source code by using open declaration on the method getBounds:

export race conditions

This shows us the following source code:

package sun.reflect.generics.reflectiveObjects;
// import statements omitted
public class TypeVariableImpl
    extends LazyReflectiveObjectGenerator implements TypeVariable {
    // upper bounds - evaluated lazily
    private Type[] bounds;
    public Type[] getBounds() {
        // lazily initialize bounds if necessary
        if (bounds == null) {
            FieldTypeSignature[] fts = getBoundASTs(); // get AST
            // allocate result array; note that
            // keeping ts and bounds separate helps with threads
            Type[] ts = new Type[fts.length];
            // iterate over bound trees, reifying each in turn
            for ( int j = 0; j  < fts.length; j++) {
                Reifier r = getReifier();
                fts[j].accept(r);
                ts[j] = r.getResult();
            }
            // cache result
            bounds = ts;
            // could throw away bound ASTs here; thread safety?
        }
        return bounds.clone(); // return cached bounds
    }
    // other fields and methods omitted
}

The class TypeVariableImpl modifies the not volatile field bounds without synchronization In line 15 and 30 the field bounds is read and in line 27 it is written. If the code is executed in the given order everything is o.k. But if some component re-orders the statements, the array is not completely initialized. In pseudo code the method getBounds looks like this:

if instance variable bounds is  null
{
    set local variable ts to new Array
    initialize the array
    set instance variable bounds to the local variable ts 
}
return instance variable bounds.clone

If the statements get reordered another thread sees an uninitialised array:

Thread A set local variable ts to new Array
Thread A set instance variable bounds to the local variable ts
Thread B if instance variable bounds is null
Thread B Thread B return instance variable bounds.clone // the array is not yet completely initialized

One such component is the cache system of the CPU. ARM compatible processors like in smart phones or the Raspberry Pi reorder reads and writes to improve performance, leading to a scenario as described above.

Read here more about the consequences of this race on a Raspberry Pi.

Fix The Race Condition

To fix the race condition you can either declare the method getBounds as synchronized or declare the field bounds as volatile. Using synchronized makes sure that the field bounds only gets assigned once. Using volatile on the other hand is faster.