| Thread overview | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
February 07, 2012 Re: Possible to pass a member function to spawn? | ||||
|---|---|---|---|---|
| ||||
On Feb 6, 2012, at 1:38 PM, Oliver Puerto wrote:
> Hello,
>
> I'm very new to D. Just started reading "The D programming language". I should read it from beginning to end before posting questions here. I know ... But I'm just too impatient. The issue seems not to be that simple, nevertheless. The code below compiles with Visual Studio.
>
> I want to have something like my actor class that I can start running in it's own thread like in Scala or other languages that support actors. So at best, I would like to do something like this:
>
> MyActor myActor = new MyActor();
> auto tid = spawn(&start, &myActor.run());
This should work:
void runActor(shared MyActor a) { (cast(MyActor)a)).run(); }
MyActor myActor = new MyActor();
auto tid = spawn(cast(shared MyActor) myActor, &runActor);
std.concurrency really should allow unique references to a non-shared type to be passed as well, using something similar to assumeUnique.
| ||||
February 07, 2012 Re: Possible to pass a member function to spawn? | ||||
|---|---|---|---|---|
| ||||
Attachments:
| On 8 February 2012 00:33, Sean Kelly <sean@invisibleduck.org> wrote: > On Feb 6, 2012, at 1:38 PM, Oliver Puerto wrote: > > > Hello, > > > > I'm very new to D. Just started reading "The D programming language". I > should read it from beginning to end before posting questions here. I know ... But I'm just too impatient. The issue seems not to be that simple, nevertheless. The code below compiles with Visual Studio. > > > > I want to have something like my actor class that I can start running in > it's own thread like in Scala or other languages that support actors. So at best, I would like to do something like this: > > > > MyActor myActor = new MyActor(); > > auto tid = spawn(&start, &myActor.run()); > > This should work: > > void runActor(shared MyActor a) { (cast(MyActor)a)).run(); } > MyActor myActor = new MyActor(); > auto tid = spawn(cast(shared MyActor) myActor, &runActor); > See, my conclusion is, whenever using this API, you inevitably have dog ugly code. That code is barely readable through the casts... I can only draw this up to faulty API design. I understand the premise of 'shared'-ness that the API is trying to assert/guarantee, but the concept is basically broken in the language. You can't use this API at all with out these blind casts, which is, basically, a hack, and I am yet to see an example of using this API 'properly'. The casts are totally self defeating. > std.concurrency really should allow unique references to a non-shared type to be passed as well, using something similar to assumeUnique. Something like that should exist in the language (... or shared should just not be broken). Using a template like assumeUnique is no better than the ugly cast. What does it offer over a cast? | |||
February 07, 2012 Re: Possible to pass a member function to spawn? | ||||
|---|---|---|---|---|
| ||||
On Feb 7, 2012, at 3:09 PM, Manu wrote: > On 8 February 2012 00:33, Sean Kelly <sean@invisibleduck.org> wrote: On Feb 6, 2012, at 1:38 PM, Oliver Puerto wrote: > > > Hello, > > > > I'm very new to D. Just started reading "The D programming language". I should read it from beginning to end before posting questions here. I know ... But I'm just too impatient. The issue seems not to be that simple, nevertheless. The code below compiles with Visual Studio. > > > > I want to have something like my actor class that I can start running in it's own thread like in Scala or other languages that support actors. So at best, I would like to do something like this: > > > > MyActor myActor = new MyActor(); > > auto tid = spawn(&start, &myActor.run()); > > This should work: > > void runActor(shared MyActor a) { (cast(MyActor)a)).run(); } > MyActor myActor = new MyActor(); > auto tid = spawn(cast(shared MyActor) myActor, &runActor); > > See, my conclusion is, whenever using this API, you inevitably have dog ugly code. That code is barely readable through the casts... I can only draw this up to faulty API design. > I understand the premise of 'shared'-ness that the API is trying to assert/guarantee, but the concept is basically broken in the language. You can't use this API at all with out these blind casts, which is, basically, a hack, and I am yet to see an example of using this API 'properly'. The casts are totally self defeating. In this case, an alternative would be to create the actor in the spawned thread: void run() { auto a = new Actor; a.run(); } spawn(&run); Regarding shared as it applies to classes… one reason it hasn't been built out in druntime yet is because the original intent was for shared methods to basically end up with compiler-generated memory barriers all over the place and this was sufficiently unappealing that I didn't want to support it. As far as I'm aware, this is no longer planned, but the transitivity of shared can still make for some weirdness at the implementation level. For example, if I have: class Thread { pthread_t p; shared void detach() { pthread_detach(p); } } Building this yields: Error: function core.sys.posix.pthread.pthread_detach (_opaque_pthread_t*) is not callable using argument types (shared(_opaque_pthread_t*)) Error: cannot implicitly convert expression (this.p) of type shared(_opaque_pthread_t*) to _opaque_pthread_t* So I either need to speculatively update a bunch of system API calls to add a 'shared' qualifier to everything I think will always be used in a shared context, or explicitly cast away shared when actually passing these variables to their associated API routines. Neither option is appealing, so again I haven't done anything regarding shared. > std.concurrency really should allow unique references to a non-shared type to be passed as well, using something similar to assumeUnique. > > Something like that should exist in the language (... or shared should just not be broken). Using a template like assumeUnique is no better than the ugly cast. What does it offer over a cast? Only that it isn't a cast, so the receiver routine could be something like: void run(Unique!MyActor a) { a.run(); } | ||||
February 07, 2012 Re: Possible to pass a member function to spawn? | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Manu | On 02/08/2012 12:09 AM, Manu wrote: > On 8 February 2012 00:33, Sean Kelly <sean@invisibleduck.org > <mailto:sean@invisibleduck.org>> wrote: > > On Feb 6, 2012, at 1:38 PM, Oliver Puerto wrote: > > > Hello, > > > > I'm very new to D. Just started reading "The D programming > language". I should read it from beginning to end before posting > questions here. I know ... But I'm just too impatient. The issue > seems not to be that simple, nevertheless. The code below compiles > with Visual Studio. > > > > I want to have something like my actor class that I can start > running in it's own thread like in Scala or other languages that > support actors. So at best, I would like to do something like this: > > > > MyActor myActor = new MyActor(); > > auto tid = spawn(&start, &myActor.run()); > > This should work: > > void runActor(shared MyActor a) { (cast(MyActor)a)).run(); } > MyActor myActor = new MyActor(); > auto tid = spawn(cast(shared MyActor) myActor, &runActor); > > > See, my conclusion is, whenever using this API, you inevitably have dog > ugly code. If it is combined with OO. > That code is barely readable through the casts... I can only > draw this up to faulty API design. > I understand the premise of 'shared'-ness that the API is trying to > assert/guarantee, but the concept is basically broken in the language. The concept is not broken at all. There are just too few type system features to conveniently support the concept. > You can't use this API at all with out these blind casts, which is, > basically, a hack, and I am yet to see an example of using this API > 'properly'. Passing value type and/or immutable messages works well. > The casts are totally self defeating. They indicate a potentially unsafe operation. > > std.concurrency really should allow unique references to a > non-shared type to be passed as well, using something similar to > assumeUnique. > > > Something like that should exist in the language (... or shared should > just not be broken). How would you improve usability of the shared qualifier without some kind of ownership type system? > Using a template like assumeUnique is no better > than the ugly cast. What does it offer over a cast? Nothing. I prefer cast(immutable). | |||
February 07, 2012 Re: Possible to pass a member function to spawn? | ||||
|---|---|---|---|---|
| ||||
On 02/08/12 00:32, Sean Kelly wrote:
> [...] the transitivity of shared can still make for some weirdness at the implementation level. For example, if I have:
>
> class Thread {
> pthread_t p;
> shared void detach() {
> pthread_detach(p);
> }
> }
>
> Building this yields:
>
> Error: function core.sys.posix.pthread.pthread_detach (_opaque_pthread_t*) is not callable using argument types (shared(_opaque_pthread_t*))
> Error: cannot implicitly convert expression (this.p) of type shared(_opaque_pthread_t*) to _opaque_pthread_t*
>
> So I either need to speculatively update a bunch of system API calls to add a 'shared' qualifier to everything I think will always be used in a shared context, or explicitly cast away shared when actually passing these variables to their associated API routines. Neither option is appealing, so again I haven't done anything regarding shared.
An overload would work, right?
class Thread {
pthread_t p;
shared void detach() {
pthread_detach(p);
}
void detach() {
pthread_detach(p);
}
}
And, yes, without a class similar to inout, this is not really a solution.
artur
| ||||
February 08, 2012 Re: Possible to pass a member function to spawn? | ||||
|---|---|---|---|---|
| ||||
On Feb 7, 2012, at 3:55 PM, Artur Skawina wrote:
> On 02/08/12 00:32, Sean Kelly wrote:
>> [...] the transitivity of shared can still make for some weirdness at the implementation level. For example, if I have:
>>
>> class Thread {
>> pthread_t p;
>> shared void detach() {
>> pthread_detach(p);
>> }
>> }
>>
>> Building this yields:
>>
>> Error: function core.sys.posix.pthread.pthread_detach (_opaque_pthread_t*) is not callable using argument types (shared(_opaque_pthread_t*))
>> Error: cannot implicitly convert expression (this.p) of type shared(_opaque_pthread_t*) to _opaque_pthread_t*
>>
>> So I either need to speculatively update a bunch of system API calls to add a 'shared' qualifier to everything I think will always be used in a shared context, or explicitly cast away shared when actually passing these variables to their associated API routines. Neither option is appealing, so again I haven't done anything regarding shared.
>
> An overload would work, right?
>
> class Thread {
> pthread_t p;
> shared void detach() {
> pthread_detach(p);
> }
> void detach() {
> pthread_detach(p);
> }
> }
The shared call still won't work because pthread_detach accepts a pthread_t, not a shared(pthread_t). It might work if pthread_t weren't a pointer type, but that obviously can't be safely assumed. This is a tiny bit like when static arrays were changed to be passed by value. This applies to the C API as well, regardless of the fact that C has no notion of passing arrays by value. But at least in that case the type of the parameter didn't change based on the calling context.
| ||||
February 08, 2012 Re: Possible to pass a member function to spawn? | ||||
|---|---|---|---|---|
| ||||
On 02/08/12 01:41, Sean Kelly wrote: > On Feb 7, 2012, at 3:55 PM, Artur Skawina wrote: > >> On 02/08/12 00:32, Sean Kelly wrote: >>> [...] the transitivity of shared can still make for some weirdness at the implementation level. For example, if I have: >>> >>> class Thread { >>> pthread_t p; >>> shared void detach() { >>> pthread_detach(p); >>> } >>> } >>> >>> Building this yields: >>> >>> Error: function core.sys.posix.pthread.pthread_detach (_opaque_pthread_t*) is not callable using argument types (shared(_opaque_pthread_t*)) >>> Error: cannot implicitly convert expression (this.p) of type shared(_opaque_pthread_t*) to _opaque_pthread_t* >>> >>> So I either need to speculatively update a bunch of system API calls to add a 'shared' qualifier to everything I think will always be used in a shared context, or explicitly cast away shared when actually passing these variables to their associated API routines. Neither option is appealing, so again I haven't done anything regarding shared. >> >> An overload would work, right? >> >> class Thread { >> pthread_t p; >> shared void detach() { >> pthread_detach(p); >> } >> void detach() { >> pthread_detach(p); >> } >> } > > The shared call still won't work because pthread_detach accepts a pthread_t, not a shared(pthread_t). It might work if pthread_t weren't a pointer type, but that obviously can't be safely assumed. Hmm, but shouldn't the lower level thread functions use 'shared' too? "int pthread_mutex_lock(pthread_mutex_t*);" doesn't really make sense... artur | ||||
February 08, 2012 Re: Possible to pass a member function to spawn? | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Sean Kelly | On 2012-02-08 00:32, Sean Kelly wrote: > On Feb 7, 2012, at 3:09 PM, Manu wrote: > >> On 8 February 2012 00:33, Sean Kelly<sean@invisibleduck.org> wrote: >> On Feb 6, 2012, at 1:38 PM, Oliver Puerto wrote: >> >>> Hello, >>> >>> I'm very new to D. Just started reading "The D programming language". I should read it from beginning to end before posting questions here. I know ... But I'm just too impatient. The issue seems not to be that simple, nevertheless. The code below compiles with Visual Studio. >>> >>> I want to have something like my actor class that I can start running in it's own thread like in Scala or other languages that support actors. So at best, I would like to do something like this: >>> >>> MyActor myActor = new MyActor(); >>> auto tid = spawn(&start,&myActor.run()); >> >> This should work: >> >> void runActor(shared MyActor a) { (cast(MyActor)a)).run(); } >> MyActor myActor = new MyActor(); >> auto tid = spawn(cast(shared MyActor) myActor,&runActor); >> >> See, my conclusion is, whenever using this API, you inevitably have dog ugly code. That code is barely readable through the casts... I can only draw this up to faulty API design. >> I understand the premise of 'shared'-ness that the API is trying to assert/guarantee, but the concept is basically broken in the language. You can't use this API at all with out these blind casts, which is, basically, a hack, and I am yet to see an example of using this API 'properly'. The casts are totally self defeating. > > In this case, an alternative would be to create the actor in the spawned thread: > > void run() { > auto a = new Actor; > a.run(); > } > spawn(&run); > > Regarding shared as it applies to classes… one reason it hasn't been built out in druntime yet is because the original intent was for shared methods to basically end up with compiler-generated memory barriers all over the place and this was sufficiently unappealing that I didn't want to support it. As far as I'm aware, this is no longer planned, but the transitivity of shared can still make for some weirdness at the implementation level. For example, if I have: Then what's "shared" supposed to do? -- /Jacob Carlborg | |||
February 08, 2012 Re: Possible to pass a member function to spawn? | ||||
|---|---|---|---|---|
| ||||
Hi guys,
thanks for all the answers to my post. In the meanwhile I have continued playing around with my little actor "project". See below what I came up with. Actually, I like this semi object-oriented approach I found. In the beginning a spawn is done with a pointer to the dispatch function. This is still the non-OOP approach. Here, things remain as intended by the language designers in the way that threads already have a message queue and already are some kind of actor at the same time. After the spawn things become object-oriented as every message send results in a method in the actor class to be invoked. So far I'm quite happy with this.
In the main clause below I do several times a Thread.sleep. Whenever the Thread.sleep is called the actor responds to the previously sent message(s). Without the Thread.sleep they just sit in the actor and don't get executed (no expected output on the console). This is still some mystery to me...
Regards, Oliver
int main()
{
MyActor myActor = new MyActor();
auto tid = myActor.start(thisTid);
tid.send(123);
tid.send(456);
tid.send(1.0f);
Thread.sleep(dur!("seconds")( 1 ));
tid.send("hello");
Thread.sleep(dur!("seconds")( 1 ));
tid.send(thisTid, "shutdown");
receive(
(int x) { writeln("actor has shut down with return code: ", x);
});
writeln("end");
return 0;
}
----------------- AbstractActor.d -----------------
shared abstract class AbstractActor {
public static string SHUTDOWN = "shutdown";
protected bool cont = true;
Tid start(Tid ownerTid) {
return spawn(&dispatch, this);
}
void run() {
while(cont) {
act();
}
}
abstract void act();
protected bool checkShutdown(Tid sender, string msg) {
if(msg == SHUTDOWN) {
writeln("shutting down ...");
cont = false;
sender.send(0);
return true;
}
return false;
}
}
void dispatch(AbstractActor actor)
{
actor.run();
}
----------------- AbstractActor.d -----------------
shared class MyActor : AbstractActor {
void run(int i) {
writeln(i);
}
void act()
{
receive(
(int msg) { run(msg); },
(Tid sender, string msg) { checkShutdown(sender, msg); },
(Variant v) { writeln("huh?"); }
);
}
}
-------- Original-Nachricht --------
> Datum: Mon, 06 Feb 2012 22:38:03 +0100
> Von: "Oliver Puerto" <saxo123@gmx.de>
> An: digitalmars-d@puremagic.com
> Betreff: Possible to pass a member function to spawn?
> Hello,
>
> I'm very new to D. Just started reading "The D programming language". I should read it from beginning to end before posting questions here. I know ... But I'm just too impatient. The issue seems not to be that simple, nevertheless. The code below compiles with Visual Studio.
>
> I want to have something like my actor class that I can start running in it's own thread like in Scala or other languages that support actors. So at best, I would like to do something like this:
>
> MyActor myActor = new MyActor();
> auto tid = spawn(&start, &myActor.run());
>
> However, this doesn't work. So I came up with the solution below where the start function is called with myActor as an argument. The code at the end of the mail prints this to the console:
>
> MyActor 123
> tid
> Main thread received message: -1
>
> This solution works, but is really not very elegant. I'm not sure whether declaring MyActor shared is a good solution. The minimum I would like to achieve is to have the start function in the source file with the MyActor class. Also this does not compile. I played with delegates but couldn't get it to compile.
>
> I'd be thankful for any useful suggestions or hints.
>
> Regards, Oliver Plow
>
> -------------------- beginning of main.d --------------
>
> import MyActor;
>
> int main()
> {
>
> MyActor myActor = new MyActor();
> auto tid = spawn(&start, myActor);
>
> tid.send(123);
> tid.send(thisTid);
>
> receive(
> (int x) {
> writeln("Main thread received message: ", x);
> });
>
> return 0;
> }
>
>
> void start(MyActor actor)
> {
> bool cont = true;
>
> while (cont)
> {
> receive(
> (int msg) { actor.run(msg); },
> (Tid sender) { cont = false; sender.send(-1); },
> (Variant v) { writeln("huh?"); }
> );
> }
> }
>
> -------------------- end of main.d --------------
>
>
> -------------------- beginning of MyActor.d --------------
>
> shared class MyActor {
>
> void run(int i) {
> write("MyActor ");
> write(i);
> write("\n");
> }
>
> }
>
> -------------------- end of MyActor.d --------------
>
>
> --
> Empfehlen Sie GMX DSL Ihren Freunden und Bekannten und wir
> belohnen Sie mit bis zu 50,- Euro! https://freundschaftswerbung.gmx.de
--
Empfehlen Sie GMX DSL Ihren Freunden und Bekannten und wir
belohnen Sie mit bis zu 50,- Euro! https://freundschaftswerbung.gmx.de
| ||||
February 08, 2012 Re: Possible to pass a member function to spawn? | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Timon Gehr Attachments:
| On 8 February 2012 01:53, Timon Gehr <timon.gehr@gmx.ch> wrote: > On 02/08/2012 12:09 AM, Manu wrote: > >> On 8 February 2012 00:33, Sean Kelly <sean@invisibleduck.org <mailto:sean@invisibleduck.org**>> wrote: >> >> On Feb 6, 2012, at 1:38 PM, Oliver Puerto wrote: >> >> > Hello, >> > >> > I'm very new to D. Just started reading "The D programming >> language". I should read it from beginning to end before posting >> questions here. I know ... But I'm just too impatient. The issue >> seems not to be that simple, nevertheless. The code below compiles >> with Visual Studio. >> > >> > I want to have something like my actor class that I can start >> running in it's own thread like in Scala or other languages that >> support actors. So at best, I would like to do something like this: >> > >> > MyActor myActor = new MyActor(); >> > auto tid = spawn(&start, &myActor.run()); >> >> This should work: >> >> void runActor(shared MyActor a) { (cast(MyActor)a)).run(); } >> MyActor myActor = new MyActor(); >> auto tid = spawn(cast(shared MyActor) myActor, &runActor); >> >> >> See, my conclusion is, whenever using this API, you inevitably have dog ugly code. >> > > If it is combined with OO. The OO changes nothing, the casting is what I'm getting at. That code is barely readable through the casts... I can only >> draw this up to faulty API design. >> I understand the premise of 'shared'-ness that the API is trying to >> assert/guarantee, but the concept is basically broken in the language. >> > > The concept is not broken at all. There are just too few type system features to conveniently support the concept. ... I think you contradicted yourself one sentence after the other :) You can't use this API at all with out these blind casts, which is, > basically, a hack, and I am yet to see an example of using this API >> 'properly'. >> > > Passing value type and/or immutable messages works well. I guess so, but I think this would be relatively rare. Many immutable things may just be addressed directly/globally. Maybe it's just my experience of threading, but I basically never spawn a thread where I don't intend to pass some sort of 'workload' to it... I shouldn't have to jump through hoops to achieve that basic task. > The casts are totally self defeating. >> > > They indicate a potentially unsafe operation. > Sure, but what's the point of 'indicating' such a thing, when the ONLY way to deal with it, is to add ugly blind casts? You are forced to ignore the warning and just cast the 'problem' away. std.concurrency really should allow unique references to a >> non-shared type to be passed as well, using something similar to >> assumeUnique. >> >> >> Something like that should exist in the language (... or shared should >> just not be broken). >> > > How would you improve usability of the shared qualifier without some kind of ownership type system? I've thought about that... but I've got nothing. The shared concept seems basically broken to me. It's nothing more than a self-documentation keyword, with the added bonus that it breaks your code and forces casts everywhere you touch it. It creates a boundary between 2 worlds of objects. Nothing shared can be used in an un-shared environment (ie. all your code), and nothing unshared can be passed to a shared environment... and there's no implicit(/safe) transfer between the 2 worlds. The ONLY way to interact is explicit cast, which doesn't guarantee any safety, you just have to type it wherever the compile errors pop up. So what's the point? Without a way for the language to assert what transfers between worlds are safe or not, the whole premise is self defeating. Passing args by value seems to be the only solution, but that doesn't work in an OO environment. Do you have any ideas how to make it work? I get the feeling, as I say, shared is a nice idea, but it's basically broken, and the language doesn't have any mechanism to really support it. As is, it's nothing more than an info-keyword that makes your code ugly :/ Using a template like assumeUnique is no better >> than the ugly cast. What does it offer over a cast? >> > > Nothing. I prefer cast(immutable). > I'd prefer to not have shared at all in it's current state. It adds zero value that I can see, and has clear negative side effects. | |||
Copyright © 1999-2021 by the D Language Foundation
Permalink
Reply