First draft of translation document

Brian Goetz brian.goetz at oracle.com
Wed May 19 10:08:38 PDT 2010


>>> void f() {
>>>    shared String s = "hello";
>>>    Utils.perhapsRunInAnotherThread(#() {
>>>      System.out.println(s); // may or may not prints null
>>>    });
>>> }
>>>
>> The initialization of the variable s "happens before" the thread is
>> even created, so there is no race condition.  This program always
>> prints "hello".  This is no different than if s had been a field.
>>
>
> I am not a concurrency guru but I think you are wrong.
> The field s of the Frame can be printed by another thread before it is
> initialization.
> The local variable s will be correctly initialized but not the
> corresponding field in Frame.

Since 'shared' does not yet exist in the language, we don't know what its 
semantics are.  Therefore Neal cannot be wrong!  (He also cannot be right.)

But lets imagine there was such a language feature, and it had a 
specification.  Maybe shared variables are implicitly volatile; maybe shared 
variables can be declared explicitly volatile in addition to shared.

Reasoning about either case is not so easy.  We probably have to assume that 
Utils is written such that the closure is not passed from thread to thread via 
a data race; that there has to be a happens-before between some point inside 
perhapsRunInAnotherThread (call it X) and the execution of the closure body in 
the other thread (call it Y).

With this assumption, in both cases there is no data race between the 
initialization and the use, since the initialization happens-before X (and by 
transitivity, it happens-before the use at Y).

If the assumption fails to hold, making s volatile will generate a 
happens-before ordering even if the closure is passed via a data race, but 
calls into question other aspects of the closure's internal state (such as the 
frame pointer.)

The same argument applies in reverse:

  void f() {
     shared String s;
     Utils.perhapsRunInAnotherThread(#() {
       s = "blarg";
     });
     System.out.println(s);
  }

Where we now need a happens-before between the write in the other thread and 
the read in the calling thread.  Note that this is totally outside the 
semantics of closures; it is about the behavior of Utils.  So now we find 
ourselves reasoning about the JMM characteristics of the dispatch logic in 
Utils, which is of course opaque and nearly always underspecified.

Note also that the JSR-292 EG may want to make stronger representations about 
whether the various MH combinators behave effectively like setting final 
fields in a constructor, or whether the internal state in the combinator nodes 
are ordinary nonvolatile mutable state.



More information about the lambda-dev mailing list