Search code examples
javaconcurrencyjava-memory-model

Why JMM produces (0, 0) even though it is considered a forbidden result


I am checking some statements from JMM and I wrote a JCS test like this:

@JCStressTest
@State
@Outcome(expect = ACCEPTABLE,  desc = "ACCEPTABLE")
public class ConcurrencyTest {
    private final int a = 1;
    private final int b = 2;

    public ConcurrencyTest instance;
    
    @Actor
    public void actor1() {
        instance = new ConcurrencyTest();
    }

    @Actor
    public void actor2(II_Result result) {
        ConcurrencyTest c = instance;
        if (c != null) {
            result.r1 = c.a;
            result.r2 = c.b;
        }
    }
}

After running this test, I see the following results:

(0, 0) (1, 2)

Although the JMM explicitly states that the result (0, 0) is forbidden, why is this happening?


Solution

  • Let's change the code a little bit to begin with:

    @JCStressTest
    @State
    @Outcome(id = "0, 0", expect = Expect.FORBIDDEN)
    @Outcome(id = "1, 2", expect = Expect.ACCEPTABLE)
    @Outcome(id = "-1, -1", expect = Expect.ACCEPTABLE)
    public class ConcurrencyTest {
    
        private final int a = 1;
        private final int b = 2;
    
        public ConcurrencyTest instance;
    
        @Actor
        public void actor1() {
            instance = new ConcurrencyTest();
        }
    
        @Actor
        public void actor2(II_Result result) {
            ConcurrencyTest c = instance;
            if (c != null) {
                result.r1 = c.a;
                result.r2 = c.b;
            } else {                   // <-- this is what you care about
                result.r1 = -1;
                result.r2 = -1;
            }
        }
    }
    

    Where do you think that the values from @Outcome(id = "0, 0") are coming from? These are the ones you set in II_Result that holds two ints, that have a default value of 0.

    As such, when c == null (meaning that actor1 has not run), that if (c != null) { ... will not be entered. So, in your code, you would do nothing : resulting in those default values of r1 and r2 being zero. You should take care of this default cases, via a simple else, like I did.