Thread overview | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
January 19, 2010 [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?
--
Michel Fortin
michel.fortin at michelf.com
http://michelf.com/
|
January 19, 2010 [dmd-concurrency] Defining shared delegates | ||||
---|---|---|---|---|
| ||||
Posted in reply to Michel Fortin |
----- 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
|
January 19, 2010 [dmd-concurrency] Defining shared delegates | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steve Schveighoffer | 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]; } } |
January 19, 2010 [dmd-concurrency] Defining shared delegates | ||||
---|---|---|---|---|
| ||||
Posted in reply to Michel Fortin | Michel Fortin wrote:
> - If the data carried by the delegate is thread-local, in whole or in part,
> then the delegate is thread-local.
I haven't thought much about shared delegates, but on the face of it this doesn't work. A delegate may increment an ostensibly thread-local variable and return it. Then, the delegate may be passed to several threads. Race ensues.
Andrei
|
January 19, 2010 [dmd-concurrency] Defining shared delegates | ||||
---|---|---|---|---|
| ||||
Posted in reply to Robert Jacques | One suggestion about shared delegates: should we take the executive decision to not define them at all? There's nothing you can do with delegates that you can't do with classes. Although generally delegates do justify their existence because they're more comfy than e.g. local classes, it doesn't strike me as obvious we'll need comparable convenience when dealing with shared classes. Andrei |
January 19, 2010 [dmd-concurrency] Defining shared delegates | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | Le 2010-01-19 ? 15:18, Andrei Alexandrescu a ?crit : > Michel Fortin wrote: >> - If the data carried by the delegate is thread-local, in whole or in part, then the delegate is thread-local. > > I haven't thought much about shared delegates, but on the face of it this doesn't work. A delegate may increment an ostensibly thread-local variable and return it. Then, the delegate may be passed to several threads. Race ensues. Are you reading things in reverse? What you quoted says that if the delegate references a thread-local variable it can't be shared. If it can't be shared it can't be sent to other threads. -- Michel Fortin michel.fortin at michelf.com http://michelf.com/ |
January 19, 2010 [dmd-concurrency] Defining shared delegates | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Tue, 19 Jan 2010 15:20:07 -0500, Andrei Alexandrescu <andrei at erdani.com> wrote:
> One suggestion about shared delegates: should we take the executive decision to not define them at all? There's nothing you can do with delegates that you can't do with classes. Although generally delegates do justify their existence because they're more comfy than e.g. local classes, it doesn't strike me as obvious we'll need comparable convenience when dealing with shared classes.
>
> Andrei
I can express pretty much all of my parallel research algorithms safely using const delegates, I can't express any of them safely using a shared class: I'd have to cast/cowboy everything. I'd think this will be true of most OpenMP / data parallel algorithms. And I think not supporting data parallel algorithms cleanly would be just as bad as not supporting clean message passing.
|
January 19, 2010 [dmd-concurrency] Defining shared delegates | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | Le 2010-01-19 ? 15:20, Andrei Alexandrescu a ?crit : > One suggestion about shared delegates: should we take the executive decision to not define them at all? There's nothing you can do with delegates that you can't do with classes. Although generally delegates do justify their existence because they're more comfy than e.g. local classes, it doesn't strike me as obvious we'll need comparable convenience when dealing with shared classes. The reason I wrote all this is because I think delegates can be much easier to work with than classes. That's what I tried to demonstrate. That's because delegates completely encapsulate the "sharedness" of what they touch, which means that contrary to other types you can implicitly convert a shared delegate to a thread-local one. This should make them much easier to share than most other types, including classes. -- Michel Fortin michel.fortin at michelf.com http://michelf.com/ |
January 19, 2010 [dmd-concurrency] Defining shared delegates | ||||
---|---|---|---|---|
| ||||
Posted in reply to Michel Fortin | Michel Fortin wrote: > Le 2010-01-19 ? 15:20, Andrei Alexandrescu a ?crit : > >> One suggestion about shared delegates: should we take the executive decision to not define them at all? There's nothing you can do with delegates that you can't do with classes. Although generally delegates do justify their existence because they're more comfy than e.g. local classes, it doesn't strike me as obvious we'll need comparable convenience when dealing with shared classes. > > The reason I wrote all this is because I think delegates can be much easier to work with than classes. That's what I tried to demonstrate. > > That's because delegates completely encapsulate the "sharedness" of what they touch, which means that contrary to other types you can implicitly convert a shared delegate to a thread-local one. This should make them much easier to share than most other types, including classes. I may be missing something, but passing a delegate object across threads begets arbitrary sharing of that delegate's state across threads - because the delegate can do anything to its data. In fact, many functional languages define poor man's classes as delegates that dispatch the action depending on arguments. There are examples in e.g. SICP (http://mitpress.mit.edu/sicp/). Andrei |
January 19, 2010 [dmd-concurrency] Defining shared delegates | ||||
---|---|---|---|---|
| ||||
Posted in reply to Michel Fortin | Michel Fortin wrote:
> Le 2010-01-19 ? 15:18, Andrei Alexandrescu a ?crit :
>
>> Michel Fortin wrote:
>>> - If the data carried by the delegate is thread-local, in whole or in part, then the delegate is thread-local.
>> I haven't thought much about shared delegates, but on the face of it this doesn't work. A delegate may increment an ostensibly thread-local variable and return it. Then, the delegate may be passed to several threads. Race ensues.
>
> Are you reading things in reverse?
>
> What you quoted says that if the delegate references a thread-local variable it can't be shared. If it can't be shared it can't be sent to other threads.
I guess I'm misunderstanding what's going on.
Andrei
|
Copyright © 1999-2021 by the D Language Foundation