Jump to page: 1 2
Thread overview
Sending an immutable object to a thread
Jul 18, 2015
Frank Pagliughi
Jul 18, 2015
Frank Pagliughi
Jul 19, 2015
Ali Çehreli
Jul 19, 2015
Frank Pagliughi
Jul 19, 2015
rsw0x
Jul 21, 2015
Frank Pagliughi
Jul 21, 2015
rsw0x
Jul 21, 2015
rsw0x
Jul 22, 2015
Marc Schütz
Jul 22, 2015
rsw0x
Jul 22, 2015
Frank Pagliughi
Jul 22, 2015
rsw0x
Jul 23, 2015
Marc Schütz
Jul 23, 2015
Frank Pagliughi
Jul 24, 2015
Ali Çehreli
Jul 24, 2015
Frank Pagliughi
Jul 24, 2015
anonymous
Jul 24, 2015
Frank Pagliughi
Jul 25, 2015
anonymous
July 18, 2015
Hey All,

I'm trying to send immutable class objects to a thread, and am having trouble if the object is one of several variables sent to the thread. For example, I have a "Message" class:

    class Message { ... }

and I create an immutable object from it, and send it to another thread:

    auto msg = immutable Message(...);

    Tid tid = spawn(&threadFunc);
    send(tid, thisTid(), msg);

I then attempt to receive it in the threadFunc like:

    receive(
        (Tid cli, immutable Message msg) {
            int retCode = do_something_with(msg);
            send(cli, retCode);
        }
    );

I get compilation errors about the inability of building the tuple, like:
/usr/include/dmd/phobos/std/variant.d(346): Error: cannot modify struct *zat Tuple!(Tid, immutable(Message)) with immutable members
/usr/include/dmd/phobos/std/variant.d(657): Error: template instance std.variant.VariantN!32LU.VariantN.handler!(Tuple!(Tid, immutable(Message))) error instantiating
/usr/include/dmd/phobos/std/variant.d(580):        instantiated from here: opAssign!(Tuple!(Tid, immutable(Message)))
/usr/include/dmd/phobos/std/concurrency.d(124):        instantiated from here: __ctor!(Tuple!(Tid, immutable(Message)))
/usr/include/dmd/phobos/std/concurrency.d(628):        instantiated from here: __ctor!(Tid, immutable(Message))
/usr/include/dmd/phobos/std/concurrency.d(618):        ... (1 instantiations, -v to show) ...
/usr/include/dmd/phobos/std/concurrency.d(594):        instantiated from here: _send!(Tid, immutable(Message))
MsgTest.d(92):        instantiated from here: send!(Tid, immutable(Message))

I tried various combinations of using Rebindable, but couldn't get anything to compile.

Thanks.
July 18, 2015
OK, I found a couple of solutions, though if anyone can tell me something better, I would love to hear it.

By making an alias to a rebindable reference, the receive() was able to create the tuple. So I renamed the class "MessageType":

    class MessageType { ... };

and then made a "Message" an immutable one of these:

    alias immutable(MessageType) Message;

and finally made a "VarMessage" as a rebindable Message (thus, a mutable reference to an immutable object):

    alias Rebindable!(Message) VarMessage;

[I will likely rethink these names, but anyway... ]

Now I can send a reference to an immutable object across threads. The receiver wants the VarMessage:

    receive(
        (Tid cli, VarMessage msg) {
            int retVal = do_something_with(msg);
            send(cli, retVal);
        }
    );


and a few different things work to send the object:

    auto msg = new Message(...);
    send(tid, thisTid(), VarMessage(msg));

or:
    send(tid, thisTid(), rebindable(msg));

or:
    VarMessage vmsg = new Message(...);
    send(tid, thisTid(), vmsg);


A second way that seems plausible is to just make the message a var type using a struct and then send a copy to the thread. This seems viable since the vast bulk of the message is a string payload, and thus the size of the struct is pretty small.
July 19, 2015
It is a pitty that although Variant is the default message type in concurrency, it still has issues:


https://issues.dlang.org/buglist.cgi?quicksearch=variant%20concurrency&list_id=202195

It looks like passing a pointer to an immutable(Message) works as well:

import std.stdio;
import std.concurrency;

class Message
{
    int i;

    this(int i) immutable
    {
        this.i = i;
    }
}

int do_something_with(immutable(Message) msg)
{
    writefln("msg.i is %s", msg.i);
    return 0;
}

void threadFunc()
{
    receive((Tid cli, immutable(Message) *msg) {
            int retCode = do_something_with(*msg);
            send(cli, retCode);
        });

    ownerTid.send(42);
}


void main()
{
    auto msg = new immutable(Message)(100);

    Tid tid = spawn(&threadFunc);
    send(tid, thisTid(), &msg);    // <-- POINTER
    receiveOnly!int();
}

Ali

July 19, 2015
> It looks like passing a pointer to an immutable(Message) works as well:

Oh, yes, pointer. Ha! I didn't even think of that. Thanks.

I'm not familiar with how garbage collection works in D. If the initial reference goes out of scope, and you just have a pointer - in another thread, no less - then are you still guaranteed that the object will not disappear while the pointer exists?

Like if I did something akin to:

// ...like before...

void send_msg(Tid tid, int n)
{
    auto msg = new immutable(Message)(n);
    send(tid, thisTid(), &msg);
}

void main()
{
    Tid tid = spawn(&threadFunc);
    send_msg(tid, 100);
    receiveOnly!int();
}

Do I know that the message object won't be garbage collected before the thread finishes with it? (I realize this is a very artificial example, but something like this could happen in a bigger library).

Frank
July 19, 2015
On Sunday, 19 July 2015 at 17:04:07 UTC, Frank Pagliughi wrote:
>> [...]
>
> Oh, yes, pointer. Ha! I didn't even think of that. Thanks.
>
> I'm not familiar with how garbage collection works in D. If the initial reference goes out of scope, and you just have a pointer - in another thread, no less - then are you still guaranteed that the object will not disappear while the pointer exists?
>
> [...]

a pointer to a pointer(or in this case, a reference) does not keep it alive.
July 21, 2015
On Sunday, 19 July 2015 at 17:12:07 UTC, rsw0x wrote:
>
> a pointer to a pointer(or in this case, a reference) does not keep it alive.

Interesting. If you de-reference the pointer and assign it back, do you get back the keep-alive? Like, in the receiving thread:

void threadFunc()
{
    receive((Tid cli, immutable(Message) *m) {
        immutable(Message) msg = *m;           // <---
        int retCode = do_something_with(msg);
        send(cli, retCode);
    });
}

I assume that even if so, there is a race condition there. You would need to keep the original reference alive until at least the "msg = *m" assignment happened, right?

Or... could you tell the GC to leave the memory alone until the thread gets it?  Like in the sending thread:

  Tid tid = spawn(&threadFunc);
  auto p = cast(void*) &msg;
  GC.addRoot(p);
  GC.setAttr(p, GC.BlkAttr.NO_MOVE);
  send(tid, thisTid(), &msg);
  //...

Is that possible? Is it efficient enough to do if you're sending lots and lots of messages?

Thanks.

July 21, 2015
On Sunday, 19 July 2015 at 17:12:07 UTC, rsw0x wrote:
> On Sunday, 19 July 2015 at 17:04:07 UTC, Frank Pagliughi wrote:
>>> [...]
>>
>> Oh, yes, pointer. Ha! I didn't even think of that. Thanks.
>>
>> I'm not familiar with how garbage collection works in D. If the initial reference goes out of scope, and you just have a pointer - in another thread, no less - then are you still guaranteed that the object will not disappear while the pointer exists?
>>
>> [...]
>
> a pointer to a pointer(or in this case, a reference) does not keep it alive.

wow, I don't even remember posting this.

This is (mostly) wrong, but I'm unsure if a pointer to another pointer on the stack would correctly keep its object alive(but, I believe this would just be a bug I think,) If the pointer was pointing to a pointer on the heap, then AFAICT it would keep it alive.
July 21, 2015
On Tuesday, 21 July 2015 at 21:44:07 UTC, rsw0x wrote:
> On Sunday, 19 July 2015 at 17:12:07 UTC, rsw0x wrote:
>> [...]
>
> wow, I don't even remember posting this.
>
> This is (mostly) wrong, but I'm unsure if a pointer to another pointer on the stack would correctly keep its object alive(but, I believe this would just be a bug I think,) If the pointer was pointing to a pointer on the heap, then AFAICT it would keep it alive.

addendum:
http://dlang.org/garbage.html

>Pointers in D can be broadly divided into two categories: Those that point to garbage collected memory, and those that do not. Examples of the latter are pointers created by calls to C's malloc(), pointers received from C library routines, pointers to static data, pointers to objects on the stack, etc.

>and those that do not ... pointers to objects on the stack, etc.

I believe this implies that it would *not* keep the object alive.

Sorry for the confusion/noise.
July 22, 2015
On Tuesday, 21 July 2015 at 21:50:35 UTC, rsw0x wrote:
> On Tuesday, 21 July 2015 at 21:44:07 UTC, rsw0x wrote:
>> On Sunday, 19 July 2015 at 17:12:07 UTC, rsw0x wrote:
>>> [...]
>>
>> wow, I don't even remember posting this.
>>
>> This is (mostly) wrong, but I'm unsure if a pointer to another pointer on the stack would correctly keep its object alive(but, I believe this would just be a bug I think,) If the pointer was pointing to a pointer on the heap, then AFAICT it would keep it alive.
>
> addendum:
> http://dlang.org/garbage.html
>
>>Pointers in D can be broadly divided into two categories: Those that point to garbage collected memory, and those that do not. Examples of the latter are pointers created by calls to C's malloc(), pointers received from C library routines, pointers to static data, pointers to objects on the stack, etc.
>
>>and those that do not ... pointers to objects on the stack, etc.
>
> I believe this implies that it would *not* keep the object alive.
>
> Sorry for the confusion/noise.

But as long as the original pointer is still on the stack, that one _will_ keep the object alive. It is only a problem if all pointers to a GC managed object are stored in places the GC isn't informed about.
July 22, 2015
On Wednesday, 22 July 2015 at 09:04:49 UTC, Marc Schütz wrote:
> On Tuesday, 21 July 2015 at 21:50:35 UTC, rsw0x wrote:
>> On Tuesday, 21 July 2015 at 21:44:07 UTC, rsw0x wrote:
>>> [...]
>>
>> addendum:
>> http://dlang.org/garbage.html
>>
>>>[...]
>>
>>>[...]
>>
>> I believe this implies that it would *not* keep the object alive.
>>
>> Sorry for the confusion/noise.
>
> But as long as the original pointer is still on the stack, that one _will_ keep the object alive. It is only a problem if all pointers to a GC managed object are stored in places the GC isn't informed about.

correct, I managed to confuse myself :o)
« First   ‹ Prev
1 2