[dmd-concurrency] draft 8: the final countdown

Michel Fortin michel.fortin at michelf.com
Wed Feb 10 13:49:11 PST 2010


Le 2010-02-10 à 15:29, Andrei Alexandrescu a écrit :

> Michel Fortin wrote:
>> Le 2010-02-10 à 14:08, Andrei Alexandrescu a écrit :
>>> Nonono. What should happen is no escape of *field* addresses
>>> because those are exposed to races when accessed naively.
>>> Synchronized methods do NOT assumes that the *indirections*
>>> starting from fields are locked/protected/non-shared.
>>> If I have a pointer to int as a field:
>>> - inside the sychronized method, the pointer itself is protected by
>>> the lock and can be considered not shared. Escaping THE ADDRESS OF
>>> that pointer would create races because it breaks the assumption
>>> that only the method messes with it
>>> - the int pointed to by that field is STILL considered shared by
>>> the method AND by the rest of the world so there's never the risk
>>> of an undue race. The method can escape it all it wants.
>> I understand quite well your intent. I just disagree with it.
>> My point is that it will happen quite often that the compiler's
>> assumption (that synchronization does not apply beyond indirections)
>> isn't adequate: when you need to use an internal array as a buffer,
>> or when you need some other hidden data structure. I understand that
>> in those cases you need to cast your way around, fine. It looks
>> crippled, but let's say that's okay.
>> What is much less shiny is that in those cases where you need a cast
>> to do the right thing, the compiler will also let you do the wrong
>> thing (let a value escape) without a cast. For instance, in your
>> example of "casting away shared" with a 'List!double' member, nothing
>> prevents a function from escaping a reference to the list as a
>> shared(List!double), but doing so will lead to races.
> 
> There are no races because the List!double type is either thread-unaware in which case there is next to nothing you can do with, or is thread-aware in which case it knows how to fend for itself.

Are you saying you cannot access public members of a thread-unware object? This would lead to a race in this situation. Same with a struct, or any primitive type. As soon as you read or write a variable from outside the lock you have a race.


>> Instead of having T* members implicitly converted to shared(T)*, the
>> compiler could just prevent any access to non-shared data through an
>> indirection. So you'd still need a cast to do the right thing, but
>> the compiler won't let you do the wrong thing by accident.
> 
> With the no escape rule you cripple people who rent; without it I cripple people who own. Most importantly, I allow escaping of members of class type which I think is an important case.

Not at all. All you have to do is enforce the no-escpae rule for T* but not for shared(T)*. The 'shared' qualifier in a synchronized class context has the implicit meaning of 'not-owned'. If you want to give a reference to someone, all you have to do is declare your members explicitly as shared(T)*, and then give a reference to the shared part. Done. Implicitly changing T* to shared(T)* is what is breaking the safeties.


-- 
Michel Fortin
michel.fortin at michelf.com
http://michelf.com/





More information about the dmd-concurrency mailing list