reading a reference to an object and reading the object’s fields under JMM

64 Views Asked by At

This post was raised after reading: https://shipilev.net/blog/2016/close-encounters-of-jmm-kind/#pitfall-semi-sync

class Box {
  int x;
  public Box(int v) {
    x = v;
  }
}

class RacyBoxy {
  Box box;

  public synchronized void set(Box v) {
    box = v;
  }

  public Box get() {
    return box;
  }
}

and test:

@JCStressTest
@State
public class SynchronizedPublish {
  RacyBoxy boxie = new RacyBoxy();

  @Actor
  void actor() {
    boxie.set(new Box(42)); // set is synchronized
  }

  @Actor
  void observer(IntResult1 r) {
    Box t = boxie.get(); // get is not synchronized
    if (t != null) {
      r.r1 = t.x;
    } else {
      r.r1 = -1;
    }
  }
}

The author says that it is possible that r.r1 == 0. And I agree with that. But, I am confused with an explanation:

The actual failure comes from the fact that reading a reference to an object and reading the object’s fields are distinct under the memory model.

I agree that

reading a reference to an object and reading the object’s fields are distinct under the memory model but, I don't see how it has an influence on result.

Please help me understand it.

P.S. If someone is confused about @Actor. It just means: run in a thread.

2

There are 2 best solutions below

2
On BEST ANSWER

I think it adresses a common miconception of people that read code with regards to sequential consitency. The fact that the reference to an instance is available in one thread, does not imply that its constructor is set. In other words: reading an instance is a different operation than reading an instance's field. Many people assume that once they can observe an instance, it requires the constructor to be run but due to the missing read synchronization, this is not true for the above example.

0
On

Ill just slightly augment the accepted answer here - without some barriers there are absolutely no guarantees that once you see a reference (think some threads can get a hold of a reference) - all the fields from that constructor are initialized. I actually answered sort of this already some time ago to one of your questions if I'm not mistaken.

There are two barriers inserted after the constructor that has final fields LoadLoad and LoadStore; it you think about their names - you will notice that no operation after the constructor can be re-ordered with one inside it:

Load -> Load (no Load can be re-ordered with a previous Load)
Load -> Store (no Store can be re-ordered with a previous Load)

Also note that it would be impossible for you to break that under the current x86 memory model - as it is a (too?) strong memory model; and as such these barriers are free on x86 - they are not inserted at all, because the operations are not re-ordered.