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