[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