[dmd-concurrency] Defining shared delegates

Robert Jacques sandford at jhu.edu
Tue Jan 19 12:13:09 PST 2010


On Tue, 19 Jan 2010 14:43:32 -0500, Steve Schveighoffer  
<schveiguy at yahoo.com> wrote:
> ----- Original Message ----
>> From: Michel Fortin <michel.fortin at michelf.com>
>> To: Discuss the concurrency model(s) for D  
>> <dmd-concurrency at puremagic.com>
>> Sent: Tue, January 19, 2010 2:27:28 PM
>> Subject: [dmd-concurrency] Defining shared delegates
>>
>> I think we should define a shared delegate as a delegate you can pass  
>> to other
>> threads, and which other threads can call.
>>
>> The delegate contains a data pointer and a function pointer. The  
>> function's code
>> is always immutable so there is never a problem sharing it. The data it  
>> carries
>> can be part thread-local, part shared, part immutable. So here are the  
>> proposed
>> rules:
>>
>> - If the data carried by the delegate is thread-local, in whole or in  
>> part,
>>   then the delegate is thread-local.
>> - If the data is all shared or immutable, then the delegate can be  
>> shared.
>>
>> I believe all delegates should be shared when they refer only to  
>> immutable and
>> shared data, and a shared delegate should implicitly convert to a  
>> thread-local
>> delegate when necessary.
>>
>> Does it make sense that shared delegates convert implicitly to  
>> thread-local
>> ones? This might look a little suspicious at first because you can't do  
>> that
>> with other types, but with a delegate the data is private to only the  
>> associated
>> code, and the associated code already knows whether the data is really  
>> shared or
>> not, it is not relying on the caller's knowledge to call the right  
>> function like
>> elsewhere. The result is that we can safely discard shared from the  
>> type the
>> caller sees as it has no consequence.
>>
>> So this should be an error (and already is) because the delegate is  
>> accessing
>> thread-local, mutable variables a and b:
>>
>>     alias shared int delegate() SampleSharedDelegate;
>>
>>     SampleSharedDelegate func1() {
>>         int a, b;
>>         return { return a + b; }; // error: cannot make delegate shared
>>     }
>>
>> This should work since a and b are shared:
>>
>>     SampleSharedDelegate func2() {
>>         shared int a, b;
>>         return { return a + b; }; // ok, delegate can be shared
>>     }
>>
>> This too should work because a and b are immutable:
>>
>>     SampleSharedDelegate func3() {
>>         immutable int a, b;
>>         return { return a + b; }; // ok, delegate can be shared
>>     }
>>
>> Does all this makes sense?
>
> What about this?
>
> SampleSharedDelegate func4() {
>    int x, y;
>    immutable int a, b;
>    return { return a + b; };
> }
>
> The stack frame contains both thread-local and immutable variables, but  
> the delegate only accesses the immutable ones.  Should this be allowed?   
> A more explicit case:
>
> class X
> {
>    shared int a, b;
>    int x, y;
>
>    int foo() { return a + b; }
> }
>
> should foo be allowed to be a shared delegate?  What if the compiler  
> only can see the signature?
>
> I think shared delegates should only be callable on shared  
> classes/structs, or else you should have to mark the inner function  
> shared:
>
> SampleSharedDelegate func4()
> {
>   shared int a, b;
>   int x, y;
>
>   shared int foo() { return a + b; }
>   //shared int foo2() { return x + y; } // fails to compile, shared  
> inner function using unshared members of the stack frame.
>   int foo3() { return x + y; }
>   return &foo; // ok
>   return &foo3; // not ok, it was not marked as shared.
> }
>
> -Steve

I agree that named functions should require the correct signatures.  
However, anonymous delegates should default to the most restrictive type.  
One of the major use cases I see is:

{
     shared int x, y;
     Task!int foo = x + y;
     writeln("do stuff");
     writeln("Results: ", foo.value);
}

Also, there is a need for const delegates as well, i.e. delegates which  
are allowed to read/write shared/immutable data (like shared delegate, but  
may also read local data. The use case for this is parallel foreach and  
other fork/join models:

{
     /// Create two tasks to fill the data arrays wait until finished;
     auto results = parallelTasks(
         take(100,Random(unpredictableSeed) ),
         take(100,Random(unpredictableSeed) )
     );

     auto x = results[0];
     auto y = results[1];
     auto z = x.dup;

     /// Perform a parallel foreach across
     foreach(i, ref v; parallel(z) ) {
         z = x[i] + y[i];
     }
}



 


More information about the dmd-concurrency mailing list