View mode: basic / threaded / horizontal-split · Log in · Help
August 01, 2010
Shared
I've reread the relevant TDPL chapter and I still don't quite understand the
following:

1.  What is shared?  Is it simply a piece of syntactic salt to make it hard to
share data across threads by accident, or is there more to it?

2.  Is it fully or mostly implemented?
August 01, 2010
Re: Shared
== Quote from dsimcha (dsimcha@yahoo.com)'s article
> I've reread the relevant TDPL chapter and I still don't quite understand the
> following:
> 1.  What is shared?  Is it simply a piece of syntactic salt to make it hard to
> share data across threads by accident, or is there more to it?
> 2.  Is it fully or mostly implemented?

Sorry, accidentally submitted the post before I was done.

3.  How does casting to and from shared work?  Under what circumstances can
unshared data be cast to shared?  Under what circumstances can shared data
implicitly be cast to unshared?
August 02, 2010
Re: Shared
On 02/08/10 02:16, dsimcha wrote:
> == Quote from dsimcha (dsimcha@yahoo.com)'s article
>    
>> I've reread the relevant TDPL chapter and I still don't quite understand the
>> following:
>> 1.  What is shared?  Is it simply a piece of syntactic salt to make it hard to
>> share data across threads by accident, or is there more to it?
>> 2.  Is it fully or mostly implemented?
>>      
> Sorry, accidentally submitted the post before I was done.
>
> 3.  How does casting to and from shared work?  Under what circumstances can
> unshared data be cast to shared?  Under what circumstances can shared data
> implicitly be cast to unshared?
>    

I too have had a lot of trouble using shared, but I am currently giving 
it another serious try.

My observations so far are that the compiler's handling of it is a bit 
buggy, but that it seems to more-or-less work, and will be usable when 
the necessary library code is updated to use it - specifically Mutex, 
Condition and concurrency's Mailbox.

You are not supposed to need to routinely cast to-and-from shared. Value 
and immutable types should implicitly convert to/from shared, and 
synchronized types should implicitly convert to shared. So for example, 
a string is ok because it is a value and a pointer to immutable data.

I have found the following approach to work ok:

import std.stdio;
import std.traits;
import std.conv;


synchronized class Channel(T) if (!hasAliasing!T)
{
    void add(T t) { }
}

alias Channel!string MyChannel1;
alias shared MyChannel1 MyChannel;

void main() {
    auto channel = new MyChannel();

    channel.add("hello");
}


The key trick for me was to use an alias to wrap the shared up with the 
data type. For some reason the compiler didn't like it when I used a 
templated type, but the second layer of aliases placated it - hopefully 
that is a bug that will be fixed soon.

I hope that helps.

-- 
Graham St Jack
August 02, 2010
Re: Shared
On Sun, 01 Aug 2010 12:46:58 -0400, dsimcha <dsimcha@yahoo.com> wrote:

> == Quote from dsimcha (dsimcha@yahoo.com)'s article
>> I've reread the relevant TDPL chapter and I still don't quite  
>> understand the
>> following:
>> 1.  What is shared?  Is it simply a piece of syntactic salt to make it  
>> hard to
>> share data across threads by accident, or is there more to it?
>> 2.  Is it fully or mostly implemented?
>
> Sorry, accidentally submitted the post before I was done.
>
> 3.  How does casting to and from shared work?  Under what circumstances  
> can
> unshared data be cast to shared?  Under what circumstances can shared  
> data
> implicitly be cast to unshared?

Let me preface this by saying I don't actually use shared on a daily  
basis, but I'll try and respond how I think it works:

1. It indicates to the compiler that multiple threads have direct access  
to the data.  But more importantly, the *lack* of shared means that  
exactly one thread has direct access to the data.  I see shared not as  
great a feature as unshared is.  For an example of optimizations that can  
be had with unshared data, see the LRU cache for lock-free array appending.

I think in terms of technical details, reading from/writing to a shared  
piece of data requires either a lock, or the compiler will insert a memory  
barrier around the write to ensure the write is atomic as long as the data  
written is small enough (I'm very fuzzy on these details, I use the term  
memory barrier in complete ignorance).  Declaring a global variable shared  
also makes it a true global (not thread-local).  I don't know what was  
decided on for shared classes/structs, I vaguely remember that the  
consensus was to require declaring the entire class shared at class  
definition, but I could be wrong.

2. I do not think it's fully implemented, but I think the intention is  
that it's fully implemented, so submit bugs against what it doesn't do if  
you find any.

3. *NO* implicit casting of shared<->unshared is allowed for references.   
To do so would violate the shared transitivity.  It is ok to copy  
value-types to/from shared.  Think of the relationship between unshared  
and shared in the same way as the relationship between mutable and  
immutable.

I think you can cast shared to unshared if you *know* that no other thread  
will be able to access the data pointed at by the shared value.  For  
instance, you can never take a reference to a shared global and cast that  
reference to unshared, because globals are always available to all threads.

You can cast unshared to shared if you know that you have no other  
unshared references to the same data in your local thread.  This one can  
be much easier to prove.

Neither cast is statically checked or verified, it's up to you as the  
programmer to ensure these properties.

-Steve
Top | Discussion index | About this forum | D home