[concurrency-interest] "Volatile-like" guarantees

Brian Goetz brian at briangoetz.com
Wed Feb 2 22:32:37 EST 2011


I think you've got some things backwards in Foo.  You want to swap the 
statements in setF, and swap the Wf assignment.  Similarly, you want to 
swap the statements in getF:

     class Foo {
         private volatile int v;
         private int f;

         void setF(int x) {
             f = x;    // Wf
             v = 0;    // Wv
         }

         int getF() {
             int y = v;    // Rv
             return f;     // Rf
         }
     }

Now, calls to setF happen-before calls to getF, which is the ordering 
you want to expose.  But additionally, you no longer have a data race on 
f; the write to f really does happen-before the read of f in another thread:

   Program Order rule: write to f HB write to v
   Volatile rule: write of v HB subsequent read of v
   Program order rule: read of v HB read of f
   Transitivity: write to f HB read of f

The tricky part here is the meaning of subsequent.  Synchronization 
operations (lock/unlock, read/write volatile) are totally ordered.  So 
it makes sense to say "subsequent read of v".  Does it make sense to say 
"subsequent call to getF"?  I say yes; there is a 1:1 relationship 
between setf and Wf, as well as getF and Rf, so you can align the calls 
to getF/setF in the synchronization order.

Your question about compilers eliding the "stupid" write to v is a fair 
one.  But compilers are carefully trained to not be overeager on 
eliminating volatile writes, for this reason.

Cheers,
-Brian

On 2/2/2011 2:20 PM, Niko Matsakis wrote:
> Hello everyone,
>
> I have a question about the Java Memory Model. I am trying to decide if
> it is possible to use a dummy volatile field, like the field v below, to
> get the same guarantees as an actual volatile field. In other words, is
> the class Foo shown here equivalent to a class where the the field f is
> declared volatile:
>> class Foo {
>> private volatile int v;
>> private int f;
>>
>> void setF(int x) {
>> v = 0; // Wv
>> x = f; // Wf
>> }
>>
>> int getF() {
>> int x = f; // Rf
>> int y = v; // Rv
>> return x;
>> }
>> }
> In particular, would code using the accessors shown above still be
> sequentially consistent, as it would be if "f" were volatile?
>
> Clearly, the program is not data-race free as defined by the JMM,
> because the write Wf does not happen before Rf. Nonetheless, I believe
> there is a guarantee very much like the one that volatile offers, which
> I will call "volatile-like":
>> If Rf sees the value written by Wf, then no subsequent read will see a
>> write that was overwritten before the write Wv occurred. More
>> formally, no read r that happens after Rv, which includes all reads in
>> the thread calling getF(), will see a write w that happens before Wv
>> where there exists another write w' such that w -> w' -> Wv.
> An informal argument can be found below [1]. I believe that this
> "volatile-like" guarantee implies sequential consistency, in the same
> way that the volatile guarantee would (argument [2] below). However, I
> ALSO believe that the JMM is fairly subtle, and I may well be missing
> something. :) Hence my e-mail to this list.
>
> One particular concern I have is the possibility that a compiler might
> observe that all the writes to the volatile field "v" are of the same
> value, 0, and therefore eliminated the field. (A similar example appears
> in the JMM paper I have been consulting). I believe however that this
> ought to be illegal by the JMM, as "v" is volatile and therefore induces
> happens-before relations in addition to carrying a value.
>
> I thank all of you in advance, both for taking the time to read this
> e-mail and for any insight you may provide.
>
>
> regards,
> Niko
>
> -----
>
> [1] The argument that Rf/Wf have a "volatile-like" relationship is as
> follows:
>
> First, the only important cases are those where Rf sees the write Wf.
> This is because the "volatile-like" guarantees described above only
> concern what happens when Rf sees the write Wf. As the volatile accesses
> Wv and Rv are both synchronization actions, one must happen before the
> other in any particular execution. If Rv happens before Wv, then at the
> time when field f is read, Wf cannot be visible, because Rf -> Rv -> Wv
> -> Wf. Therefore, Rf cannot see the write Wf, and this case is not
> important.
>
> If Wv happens before Rv, then when Rf executes, it is possible that Wf
> has already been committed. In that case, Rf may legally see the value
> written by Wf, as it would not violate happens-before consistency (Rf
> does not happen before Wf, nor is there a write w such that Wf -> w ->
> Rf). However, those same happens-before consistency requirements,
> combined with the happens-before relation from Wv to Rv, guarantee that
> any reads that happen after Rv will not see overwritten writes that
> happen before Wv.
>
> [2] A rather loose argument for sequential consistency is that if there
> were two instances of this class Foo f1 and f2, and thread T1 performed
> { f1.setF(w1); f2.setF(w2); } where another thread T2 performed { int r2
> = f2.getF(); int r1 = f1.getF(); }, it should be impossible for T2 to
> observe w2 but not w1. Why? Because the write of w1 happens before the
> write of w2 (presumably overwriting the value written by some previous
> write w). Therefore, by the "volatile-like" guarantee on f2.f, the
> subsequent read of f1.f cannot see the write w, as w -> write of w1 ->
> write of w2. A better argument would probably construct a sequentially
> consistent equivalent to any schedule, but I haven't tried writing such
> a thing out yet.
> _______________________________________________
> Concurrency-interest mailing list
> Concurrency-interest at cs.oswego.edu
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest


More information about the Concurrency-interest mailing list