[dmd-concurrency] draft 4

Andrei Alexandrescu andrei at erdani.com
Tue Jan 12 14:16:13 PST 2010


Sean Kelly wrote:
> On Jan 12, 2010, at 12:45 AM, Andrei Alexandrescu wrote:
> 
>> To be found at the usual location:
>>
>> http://erdani.com/d/fragment.preview.pdf
>>
>> I didn't add a lot of text this time around but I do have a full example of communicating threads. Skip to the last section for explanation. I paste the code below. I think it looks pretty darn cool. Sean, please let me know if it floats your boat.
>>
>> import std.concurrency, std.stdio;
>>
>> void main() {
>>   auto low = 0, high = 1000;
>>   auto tid = spawn(&fun);
>>   foreach (i; low .. high) {
>>      writeln("Main thread: ", message, i);
>>      tid.send(thisTid, i);
> 
> I haven't been able to come up with any difference in implementation for using an interface vs. a free function that accepts an opaque type for sending messages, so the choice seems largely a matter of how you want the call to look.  I'll admit that I like "send(tid, thisTid, i)" since it matches "receive(...)" but this is a small thing really.

You're echoing my thoughts as I was writing this. I'm undecided wrt 
tid.send(...) vs. send(tid, ...). I agree it is more symmetric with 
receive. Probably I'll use the free form. Other opinions?

>>      enforce(receiveOnly!Tid() == tid);
> 
> This could be a wrapper for the usual recvmsg call, so no big deal.

Right. Note that receiveOnly is very strict: if you send anything else 
than what's expected, it raises an exception. This makes mailbox 
crowding impossible.

> What I'd like to do now that I haven't been able to because of a compiler bug is allow a timeout and catchall receiver to be supplied to the full recvmsg call:
> 
> recvmsg( after(5, { writefln( "no message received!" ); },
>                   any( (Variant v) { writefln( "got something: %s", v ); } ) );

This is cute, perhaps a bit too cute. My objections are many but minor:

* "recvmsg" is an awful name that evokes to me the name of a Cobol 
routine. It's very difficult to use in a conversation, and my accent 
doesn't help either :o). I think "receive" is a definite improvement.

* Instead of after(...) as a clause, I'd rather have a receiveTimed (or 
recvmsgtmd if you wish) that takes a timeout value and returns true or 
false. I mean I'm not sure what moving the timeout code inside the call 
to recvms... I mean receive  would gain us.

* The "any" wrapper is, I think, unnecessary. Dispatching on Variant 
will always catch everything because everything is packed in a Variant 
to start with.

So I'd rewrite your example as:

if (!receiveTimed(5000,
         (Variant v) { writefln( "got something: %s", v ); }))
{
      writefln( "no message received!" );
}

> I guess the "any" wrapper could be dropped for the catchall routine and some special casing could be done inside recvmsg:
> 
> recvmsg( (Variant v) {...} );

Ah, there we go.

> though I do kind of like that "any" (or whatever it would be called) is obvious at a glance and greppable.

Well I think understanding that messages are packed as one Variant must 
be very basic knowledge. You accept Variant => you accept anything.

>>   }
>>   // Signal the other thread
>>   tid.send(Tid(), 0);
> 
> I've considered having the sender's Tid automatically embedded in every message.  Seems like it's pretty much always wanted, though this would mean not being able to use just any old delegate for receiving messages.  ie. if you have a function already that accepts a Foo then would you want to use that directly or would it be a bother to wrap it in something to throw away the Tid?

Per Steve's point I think this has been settled.

> Finally, I'd like for recvmsg to accept either a function returning void for "accept any of this type" as well as functions returning a bool for "if returns true, the passed value was a match, if false then not" to allow dynamic pattern matching.  Seems like this should be easily possible with a static if inside the recvmsg loop, but I haven't actually tried it yet.

Sounds great! I'll add that explanation later on.

>> }
>>
>> void fun() {
>>   for (;;) {
>>      auto msg = receiveOnly!(Tid, int)();
>>      if (!msg[0]) return;
> 
> Oh, so this format returns a Tuple for multiple arguments and the value for a single argument?  The Tuple is gone by the time the user code is hit with recvmsg so it would have to rewrap it for receiveOnly, but if that's okay then this would work.  I guess the other option would be a completely separate receiveOnly call instead of a wrapper, which could eliminate the extra work.

Yah, exactly. As long as it's just an implementation matter, it could 
use a bit of a roundabout mechanism. I think it saves the user of a fair 
amount of manual unpacking.

I'm very glad to see this much shared vision!


Andrei


More information about the dmd-concurrency mailing list