Jump to page: 1 2
Thread overview
Passing shared delegates
Jan 11, 2013
Martin Drasar
Jan 11, 2013
mist
Jan 14, 2013
Martin Drašar
Jan 14, 2013
Maxim Fomin
Jan 14, 2013
Martin Drasar
Jan 14, 2013
mist
Jan 16, 2013
Martin Drasar
Jan 16, 2013
Maxim Fomin
Jan 17, 2013
Martin Drasar
Jan 17, 2013
Maxim Fomin
Jan 17, 2013
Martin Drasar
January 11, 2013
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
January 11, 2013
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.
January 14, 2013
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
January 14, 2013
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.
January 14, 2013
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
January 14, 2013
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 :)

January 16, 2013
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
January 16, 2013
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);
}
January 17, 2013
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
January 17, 2013
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.
« First   ‹ Prev
1 2