4

I have a question about the code segment bellow. It is possible to have a result [0, 1, 0] for the result (this is test executed with JCStress). So how this can happen? I think that the data write (data = 1) should be executed before write to guard2 in Actor2 (guard2 = 1). Am I right? I ask, because a lot of times I've read that instructions arround volatiles are not reordered. Moreover according to this: http://tutorials.jenkov.com/java-concurrency/volatile.html it is written the following:

The reading and writing instructions of volatile variables cannot be reordered by the JVM (the JVM may reorder instructions for performance reasons as long as the JVM detects no change in program behaviour from the reordering). Instructions before and after can be reordered, but the volatile read or write cannot be mixed with these instructions. Whatever instructions follow a read or write of a volatile variable are guaranteed to happen after the read or write.

So if we cannot reorder

  public class DoubleVolatileTest {

      volatile int guard1 = 0;
      int data = 0;
      volatile int guard2 = 0;

      @Actor
      public void actor1() {
          guard2 = 1;
          data = 1;
          guard1 = 1;
      }

      @Actor
      public void actor2(III_Result r) {
          r.r1 = guard1;
          r.r2 = data;
          r.r3 = guard2;
      }

  }

Thanks in advance!

luk2302
  • 55,258
  • 23
  • 97
  • 137
DPM
  • 1,540
  • 2
  • 18
  • 40
  • Have you experienced `[0, 1, 0]`? Or are you asking out of curiosity? – luk2302 Nov 04 '17 at 15:03
  • 1
    When I ran this test, there are executions which have this result. I don't have a practical problem, I just want to understand how this is possible from theoretical point of view, because concurrency is interesting topic for me. Thanks for your attention! – DPM Nov 04 '17 at 15:28
  • Interesting case - I'm able to reproduce. I think a minimal example can be obtained by removing `guard1`. In that case, the interesting result is `[1, 0]`. – Duarte Meneses Nov 05 '17 at 15:19
  • I think `data = 1` can be reordered with `guard2=1`. Then all reads of `guards2` will be at least up to date with the values in the other thread updating the value of guard2. I think that the compiler thing that you don't care about the updated value of data (since it is written after the writing of volatile). So data can be written nefore written to guard2. – DPM Nov 07 '17 at 16:58
  • @DuarteMeneses added an answer to explain it hopefully – Eugene Jun 04 '19 at 13:19
  • @luk2302 it is more than OK to see those values btw... – Eugene Jun 04 '19 at 13:20

1 Answers1

1

First of all, this:

The reading and writing instructions of volatile variables cannot be reordered by the JVM...

means that volatile themselves can not be re-ordered (volatile with volatile that is); but with the caveat that

the JVM may reorder instructions for performance reasons as long as the JVM detects no change in program behaviour from the reordering.

In general, reasoning about re-orderings (that could or not be done) by a JVM is incorrect (I've read that instructions around volatiles are not reordered ...). Re-ordering/barriers/etc are not part of the JLS; instead it acts on the premises of happens-before rules and that is the only thing you should care about.

Your example, can indeed be simplified as said in the comments to:

@Outcome(id = "0, 0", expect = Expect.ACCEPTABLE, desc = "don't care about this one")
@Outcome(id = "1, 0", expect = Expect.ACCEPTABLE_INTERESTING, desc = "the one we care about")
@Outcome(id = "1, 1", expect = Expect.ACCEPTABLE, desc = "don't care about this one")
@Outcome(id = "0, 1", expect = Expect.ACCEPTABLE, desc = "don't care about this one")
@JCStressTest
@State
public class VolatileTest {


    private volatile int guard = 0;
    private int x = 0;


    @Actor
    void writeActor() {
        guard = 1; // volatile store

        // your reasoning is that these two operations should be re-ordered
        // unfortunately, this is not correct.

        x = 1; // plain store
    }

    @Actor
    void readActor(II_Result r) {

        r.r1 = x; // plain store
        r.r2 = guard; // plain store

    }
}

Running this will result in 1, 0 indeed, which means that x = 1 was indeed re-ordered with guard = 1; actually in reality many more other things could have happened (but for simplicity we call them re-orderings, even though this is not the only reason why you could observe [1, 0]).

In JLS terms : you have not established any happens before between these operations (like a typical volatile store/volatile load) - as such those operations are free to float around. And that could be the end of the answer, pretty much. A little bit broader explanation would be that volatile (since you used it), is said:

A write to a volatile field happens-before every subsequent read of that same field.

You are not doing any reads of the volatile guard, so nothing is guaranteed. Another way to explain it would be this excellent article or even this one. But even if you did read guard, still nothing is guaranteed about re-orderings because the way your code is set-up.


volatile only works correctly when there are pairs of its usage, that is Thread1 does a write to a volatile field - Thread2 observes the write. In this case everything that was done before the write in program order will be seen by Thread2 (obviously after it has seen that written value). Or in code:

 public class VolatileTest {

    private volatile int guard = 0;
    private int x = 0;


    @Actor
    void writeActor() {

        // store comes "before" the store to volatile
        // as opposed to the previous example
        x = 1; // plain store
        guard = 1; // volatile store
    }

    @Actor
    void readActor(II_Result r) {

        r.r1 = guard; // plain store
        r.r2 = x; // plain store

    }
}

You are now guaranteed by the JLS that if you see guard to be 1, you will also observe x to be 1 (x = 1 can not be re-ordered below guard = 1 this time). As such, 1, 0 now is illegal and thus never seen in the output.

Eugene
  • 117,005
  • 15
  • 201
  • 306