[dmd-concurrency] What is protected by synchronization?

Robert Jacques sandford at jhu.edu
Sun Jan 31 14:35:44 PST 2010


On Sun, 31 Jan 2010 16:33:12 -0500, Kevin Bealer <kevinbealer at gmail.com>  
wrote:

> On Sun, Jan 31, 2010 at 4:09 PM, Robert Jacques <sandford at jhu.edu> wrote:
>
>> On Sun, 31 Jan 2010 13:52:59 -0500, Kevin Bealer <kevinbealer at gmail.com>
>> wrote:
>>
>>> On Sun, Jan 31, 2010 at 7:51 AM, Michel Fortin  
>>> <michel.fortin at michelf.com
>>> >wrote:
>>> ...
>>>
>>>
>>>> Something 'unique' is accessible through only one reference in the  
>>>> whole
>>>> program, it is guarantied to have no aliasing. With 'lent' you can  
>>>> lend a
>>>> unique reference to a function: you know once it returns your  
>>>> reference
>>>> is
>>>> still unique. As long as this 'unique' property holds, you can safely
>>>> *move*
>>>> the reference between threads. As long as the unique reference is kept
>>>> thread-local you can access it without atomic operations or locks.
>>>> 'unique'
>>>> can be seen as a temporary state as you can safely move it to  
>>>> immutable,
>>>> mutable, shared, etc.
>>>>
>>>> Andrei wants to implement unique as a template, but it'll be very  
>>>> limited
>>>> without 'lent'. If I remember well, there's no general Unique  
>>>> template,
>>>> just
>>>> a UniqueArray for arrays of primitive types. Arrays of structs would
>>>> allow
>>>> taking the address of struct members and create aliasing.
>>>>
>>>>
>>> I want to point out that this is only true if there is a memory  
>>> barrier at
>>> the point of lending.  If object X belongs uniquely to thread 1, and it
>>> gets
>>> lent to thread 2, a memory barrier should happen at this point.  There  
>>> are
>>> several cases where you can get hurt if this is not done:
>>>
>>> 1. The object might have been owned by thread 2, then given to thread  
>>> 1,
>>> which modified it, then returned to thread 2.  In this case, the second
>>> transfer is problematic because there might be a cached copy of the
>>> original
>>> in thread 2's L1 or L2 cache.
>>>
>>> 2. The object might have been in the same cache line with another  
>>> object
>>> that was modified, causing unintentional caching, and thus  
>>> snapshotting a
>>> previous version of the object's state.
>>>
>>> 3. Some other code might have fiddled with the object even though it is
>>> 'unique' -- for example, the global garbage collector might have been  
>>> run
>>> by
>>> thread 2, causing thread 2 to "see" a bunch of things that thread 1  
>>> thinks
>>> it uniquely owns.  This is going to be garbage collector design  
>>> dependent,
>>> e.g. the GC might always do a memory barrier just before returning  
>>> control
>>> to the user, in which case, no problem.
>>>
>>> The unique concept is a good thing but care must be taken -- I think
>>> Andrei's template version of the unique concept should be perfectly  
>>> safe
>>> due
>>> to the fact that it does a copy.  It would be more efficient if a  
>>> memory
>>> barrier could be used but that doesn't help with the analytical problem
>>> (making sure the old thread doesn't have a pointer stuck somewhere.)
>>>
>>> I just wanted to knock down what I think is a dangerous idea that some
>>> people might have inferred from the discussion on 'unique' -- that  
>>> thread
>>> X
>>> cannot have cached something if it was never 'seen' by thread X before.
>>>  For
>>> that matter, I'm not sure but without hard thread affinity, the CPU  
>>> might
>>> have a cached version of something even if it the 'thread' never saw it
>>> because the last thread to run there saw it.  (Or does thread switching
>>> always incur a memory barrier -- I don't know much about how that is
>>> usually
>>> done.)
>>>
>>> Kevin
>>>
>>
>> This is a good point, but this has already been discussed and resolved  
>> in
>> previous discussions. In essence, the all the ways for a unique object  
>> to
>> move between threads involve memory barriers of their own which in turn
>> protects the publication safety of both unique and immutable objects.  
>> As for
>> the cache line issues, these are generally know as false sharing, and  
>> while
>> they can be a major performance issue, they do not effect program
>> correctness. Also, the problem with Andrei's library type is that it's
>> shallow and not transitive. Lastly, lent doesn't refer to passing things
>> between threads. It is like const, a super-type which allows a function  
>> to
>> take a shared, local, owned or unique object in an agnostic manner, by
>> providing the guarantee that it won't squirrel away (escape) it  
>> somewhere.
>>
>>
> Okay, I guess I missed or skimmed those discussions.  Thanks for the
> details.
>
> Kevin

No problem. Those discussions were on the regular newsgroup a while ago  
and on Bartosz's blog.


More information about the dmd-concurrency mailing list