www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Passing shared delegates

reply Martin Drasar <drasar ics.muni.cz> writes:
Hi,

when trying to compile this code:

 module main;
 
 class A
 {
   shared void foo() {}
 }
 
 class B
 {
   void bar(shared void delegate() f) {}
 }
 
 void main()
 {
   auto a = new A();
   auto b = new B();
 
   b.bar(&a.foo);
 }
I get this error:
 main.d(18): Error: cannot implicitly convert expression (&a.foo) of type void
delegate() shared to shared(void delegate())
I have searched the bugzilla and newsgroups and found various threads related to shared and delegate passing and the most recent was about casting it all away and then back in when needed. Is this the only possible way to do it? In my program I want to pass delegates to a running thread using std.concurrency. I can do it by casting the delegate (or rather a structure containing the delegate together with other stuff) to shared, send it, cast it away after I receive it and then work with it. But that does not feel right... What exactly happens when I cast to and from shared? Does the object get copied, moved, or any other magic? Is it costly operation? Thanks for any hints, Martin
Jan 11 2013
parent reply "mist" <none none.none> writes:
Do not have time to test code right now but first guess it is 
related to parsing differences for delegates and usual functions. 
Delegates can have shared/const applied to both delegate type 
itself and context of underlying function. Those are different 
beasts and no wonder type system complains.

You may need to try something like "void delegate() shared f" if 
you want delegate type to match method one.
Jan 11 2013
parent reply =?UTF-8?B?TWFydGluIERyYcWhYXI=?= <drasar ics.muni.cz> writes:
Dne 11.1.2013 23:26, mist napsal(a):
 Do not have time to test code right now but first guess it is related to
 parsing differences for delegates and usual functions. Delegates can
 have shared/const applied to both delegate type itself and context of
 underlying function. Those are different beasts and no wonder type
 system complains.

 You may need to try something like "void delegate() shared f" if you
 want delegate type to match method one.
Hi mist, that was the first thing I tried, but it resulted in a completely different error:
 class B
 {
   void bar(void delegate() shared f) {}
 }
 Error: const/immutable/shared/inout attributes are only valid for non-static
member functions
Martin
Jan 13 2013
parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Monday, 14 January 2013 at 07:20:17 UTC, Martin DraĊĦar wrote:
 Dne 11.1.2013 23:26, mist napsal(a):
 Do not have time to test code right now but first guess it is 
 related to
 parsing differences for delegates and usual functions. 
 Delegates can
 have shared/const applied to both delegate type itself and 
 context of
 underlying function. Those are different beasts and no wonder 
 type
 system complains.

 You may need to try something like "void delegate() shared f" 
 if you
 want delegate type to match method one.
Hi mist, that was the first thing I tried, but it resulted in a completely different error:
 class B
 {
  void bar(void delegate() shared f) {}
 }
 Error: const/immutable/shared/inout attributes are only valid 
 for non-static member functions
Martin
Which compiler version do you use? It compiles on 2.061. In case of applying attributes to functions, mostly it is irrelevant whether it stands first or last. So, void foo() shared {} and shared void foo() {} in A class are equivalent. However in case of bar parameter you are qualifying not a function, but object, so shared before return type of delegate applies to object, like (shared (int i)). Shared after delegate applies to delegate type, not object itself.
Jan 13 2013
parent reply Martin Drasar <drasar ics.muni.cz> writes:
On 14.1.2013 8:56, Maxim Fomin wrote:
 Which compiler version do you use? It compiles on 2.061.
It was 2.060. It compiles now on 2.061. Great!
 In case of applying attributes to functions, mostly it is irrelevant
 whether it stands first or last. So,
 
 void foo() shared {}
 
 and
 
 shared void foo()  {}
 
 in A class are equivalent. However in case of bar parameter you are
 qualifying not a function, but object, so shared before return type of
 delegate applies to object, like (shared (int i)). Shared after delegate
 applies to delegate type, not object itself.
Thanks for clarification. Martin
Jan 14 2013
parent reply "mist" <none none.none> writes:
On Monday, 14 January 2013 at 10:13:16 UTC, Martin Drasar wrote:
 On 14.1.2013 8:56, Maxim Fomin wrote:
 Which compiler version do you use? It compiles on 2.061.
It was 2.060. It compiles now on 2.061. Great!
Yes, it was a known bug in pre-2.061 Big relief to have it working :)
Jan 14 2013
parent reply Martin Drasar <drasar ics.muni.cz> writes:
Okay, I have hit another thing when dealing with shared delegates.

Consider this code:

 alias void delegate (B b) shared Callback;
 
 class A
 {
   private B _b;
 
   this (B b)
   {
     _b = b;
   }
 
   void callback (B b) shared
   {
     b.execute(&callback);
     //_b.execute(&callback); <-- Uncomment this for error
   }
 }
 
 class B
 {
   void execute (Callback c)
   {
     c(this);
   }
 }
 
 void main()
 {
   auto b = new B();
   auto a = new A(b);
 
   b.execute(&a.callback);
 }
If I uncomment that line I get this error:
 Error: function main.B.execute (void delegate(B b) shared c) is not callable
using argument types (void delegate(B b) shared) shared
This error probably appears because typeof b is B and typeof _b is shared(B). My question is - why? Is it a bug or does the shared delegate made it shared and it is ok? And what to do with it? I can cast away shared of _b, but is it a correct and clean way? Thanks for your input. Martin
Jan 16 2013
parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Wednesday, 16 January 2013 at 20:05:40 UTC, Martin Drasar 
wrote:
 Okay, I have hit another thing when dealing with shared 
 delegates.

 If I uncomment that line I get this error:

 Error: function main.B.execute (void delegate(B b) shared c) 
 is not callable using argument types (void delegate(B b) 
 shared) shared
This error probably appears because typeof b is B and typeof _b is shared(B). My question is - why? Is it a bug or does the shared delegate made it shared and it is ok? And what to do with it? I can cast away shared of _b, but is it a correct and clean way? Thanks for your input. Martin
Yes, it happens so (shared function made it a member). Casting away shared is UB but it can be done if your are sure. Consider rewriting the code and eliminating unnecessary shareds and please stop attaching "> " to each line of code. To workaround add another method: void callback(B b) { _b.execute(&callback); }
Jan 16 2013
parent reply Martin Drasar <drasar ics.muni.cz> writes:
On 16.1.2013 22:53, Maxim Fomin wrote:
 Yes, it happens so (shared function made it a member). Casting away
 shared is UB but it can be done if your are sure.
Ok. But that leaves me with an unanswered question from one of my previous posts. What happens when you cast from and to shared? Is there any moving in memory from TLS and back? Or does it just access the memory as if it were in shared space? I have tried to compare addresses of b and _b, but writeln refuses to display an address of shared and assert does not help me either. But using the inspection capabilities of VisualD I found out that they seem to have the same address.
 Consider rewriting the code and eliminating unnecessary shareds and
I am afraid that the original code has as little shareds as possible. But I have an idea how to rewrite it to avoid needing to access _b.
 please stop attaching "> " to each line of code.
Sorry about that. It is a habit that prevents Thunderbird from linewrapping a code. If you have Thunderbird on receiving end then it is no problem - you can copy the quoted code without those '>'. But I understand that with other clients it might be irritating.
 To workaround add another method:
 
 void callback(B b)
 {
     _b.execute(&callback);
 }
Yes, that could help. But as I wrote before, I will rewrite it to avoid this issue. Thanks, Martin
Jan 17 2013
parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Thursday, 17 January 2013 at 08:46:22 UTC, Martin Drasar wrote:

 Ok. But that leaves me with an unanswered question from one of 
 my
 previous posts.
Casting away shared in undefined behavior. Although it may be not written explicitly in dlang.org, once D will have a standard like C or C++, it will be name like so. In practice this means that behavior of program is uncertain and may result in many consequences. In this case content of arrays may be any of 1,2,3,4,5,6. import std.concurrency : spawn; import std.stdio : writeln; shared int[] arr1; shared int[] arr2; static this() { arr1.length = arr2.length = 100; foreach(ref e; arr1) { e = 1; } foreach(ref e; arr2) { e = 2; } } void thread1() { int[] arr_1 = cast(int[])arr1; int[] arr_2 = cast(int[])arr2; arr_2[] = 3; arr_1[] = 4; } void thread2() { int[] arr_1 = cast(int[])arr1; int[] arr_2 = cast(int[])arr2; arr_2[] = 5; arr_1[] = 6; } void main() { spawn(&thread1); spawn(&thread2); writeln(arr1); writeln(arr2); } Note, if you mark functions as safe, the code will not compile, because throwing shared is not allowed in D safe code.
 What happens when you cast from and to shared? Is there any 
 moving in
 memory from TLS and back? Or does it just access the memory as 
 if it
 were in shared space?
It is implementation specific, but I guess nothing is moved, just a variable is reinterpreted.
 I have tried to compare addresses of b and _b, but writeln 
 refuses to
 display an address of shared and assert does not help me 
 either. But
 using the inspection capabilities of VisualD I found out that 
 they seem
 to have the same address.


 Thanks,
 Martin
import core.stdc.stdio : printf; import std.concurrency; int a; __gshared int b; shared int c; void thread() { printf("%p=%d\n%p=%d\n%p=%d\n", &a, a, &b, b, &c, c); } void main() { c = a = 2; spawn(&thread); printf("%p=%d\n%p=%d\n%p=%d\n", &a, a, &b, b, &c, c); } You can compile this code and look at addresses and assembly if you are interested in implementation details.
Jan 17 2013
parent Martin Drasar <drasar ics.muni.cz> writes:
On 17.1.2013 12:56, Maxim Fomin wrote:
 Casting away shared in undefined behavior. Although it may be not
 written explicitly in dlang.org, once D will have a standard like C or
 C++, it will be name like so.
 
 In practice this means that behavior of program is uncertain and may
 result in many consequences. In this case content of arrays may be any
 of 1,2,3,4,5,6.
 ... snip ...
 Note, if you mark functions as  safe, the code will not compile, because
 throwing shared is not allowed in D safe code.
I know about some peculiarities of shared (this thread http://forum.dlang.org/thread/k7orpj$1tt5$1 digitalmars.com was a good eye-opener) and tend to avoid it as much as possible and solve my problems with message passing concurrency, however there are some times when I had to do some casting and just be careful.
 What happens when you cast from and to shared? Is there any moving in
 memory from TLS and back? Or does it just access the memory as if it
 were in shared space?
It is implementation specific, but I guess nothing is moved, just a variable is reinterpreted.
Ok, good.
 ... snip ...
 You can compile this code and look at addresses and assembly if you are
 interested in implementation details.
Good ol' printf. I should have thought about it. Thanks. Martin
Jan 17 2013