RFR(M): 8029101: PPC64 (part 211): ordering of Independent Reads of Independent Writes

Aleksey Shipilev aleksey.shipilev at oracle.com
Tue Nov 26 04:06:44 PST 2013


On 11/26/2013 05:12 AM, Vitaly Davidovich wrote:
> Volatile fields written in constructor aren't guaranteed by JMM to occur
> before the reference is assigned; this guarantee only applies to final
> fields and StoreStore (in JMM parlance) before ref assignment should be
> sufficient.  Of course making the implementation stronger is probably ok,
> but just wanted to point that out as there seem to be changes aimed at
> this, as you say.
> 
> The reason volatile is excluded is because the ref assignment following the
> volatile store is a plain one, and plain stores can move before volatile
> stores (but not the other way, of course).  So only final fields get this
> special treatment; if someone is unsafely publishing a ref and assumes the
> volatile store in constructor is sufficient, they're mistaken :).

That's a popular misconception. The rule of thumb, however, is: "you can
replace finals with volatiles and have the same publishing guarantees".

This particular PPC failure was discussed with Goetz and Doug somewhat a
year ago, and the result of that discussion is sealed within the
jcstress tests, e.g.:
 http://hg.openjdk.java.net/code-tools/jcstress/file/tip/tests-custom/src/main/java/org/openjdk/jcstress/tests/init/primitives/volatiles/LongVolatileTest.java

The final fields indeed have the special treatment in spec. However, the
volatile fields have effectively the same guarantees when initialized in
constructor, because there is *no* valid execution that might expose the
default value for the volatile field.

Suppose you have the class:

   class A {
      volatile int f;
      A() {
         f = 42;
      }
   }

...and this test case (note it deliberately omits NPE cases to make
reasoning simpler):

         T1       |      T2
   ---------------|----------------
    a = new A();  |   r1 = a.f;

The only allowed value for r1 is {42}. The sketch for the proof follows.
There are only two possible juxtapositions on volatile ops in the trace:

a) the one that reads (r1=0):

   vread(a.f, 0)
              \----so---> vstore(a.f, 42)

b) the one that reads (r1=42):

   vstore(a.f, 42)
              \----so---> vread(a.f, 42)

Now, we can extend that skeleton with the program order, as follows in
T1 and T2, yielding two traces:

a) one that reads (r1=0):

 read(a)
    \--po--> vread (a.f, 0)
                  \---so---> vstore(a.f, 42)
                                  \---po---> store (a)

b) one that reads (r1=42):

   vstore (a.f, 42)
     \       \-----------so------------------> vread(a.f, 42)
      \                        read(a) ----po----^
       \----po--->store(a)

Now, notice that trace (a) not well-formed, because we read(a) before
store(a). The trace (b) is well-formed. Hence, the only valid execution
yields r1=42. Q.E.D.

-Aleksey.


More information about the ppc-aix-port-dev mailing list