February 25, 2014
Is someone willing to write a D entry for this?

http://rosettacode.org/wiki/Rendezvous

Bye,
bearophile
February 26, 2014
On 02/25/2014 03:48 PM, bearophile wrote:
> Is someone willing to write a D entry for this?
>
> http://rosettacode.org/wiki/Rendezvous
>
> Bye,
> bearophile

I think the following satisfies the requirements. Improve at will! :p

import std.stdio;
import std.exception;
import std.array;
import std.concurrency;
import std.datetime;
import core.thread;

class OutOfInk : Exception
{
    this()
    {
        super("Out of ink.");
    }
}

struct Printer
{
    string id;
    size_t ink;

    void print(string line)
    {
        enforce(ink != 0, new OutOfInk);
        writefln("%s: %s", id, line);
        --ink;
    }
}

struct PrinterRendezvous
{
    Printer[] printers;

    void print(string[] lines) shared
    {
        Exception savedException;

        while (true) {
            if (lines.empty) {
                break;
            }

            if (printers.empty) {
                // No more printers to try
                assert(savedException !is null);
                throw savedException;
            }

            try {
                synchronized {
                    (cast(Printer)printers.front).print(lines.front);
                }
                lines.popFront();

                // Increase the chance of interleaved output
                Thread.sleep(10.msecs);

            } catch (OutOfInk exc) {
                savedException = exc;

                synchronized {
                    // Switch to the next printer
                    printers = printers[1..$];
                }
            }
        }
    }
}

void humptyDumptyTask(ref shared(PrinterRendezvous) rendezvous)
{
    auto humptyDumpty = [ "Humpty Dumpty sat on a wall.",
                          "Humpty Dumpty had a great fall.",
                          "All the king's horses and all the king's men,",
                          "Couldn't put Humpty together again.", ];

    rendezvous.print(humptyDumpty);
}

void motherGooseTask(ref shared(PrinterRendezvous) rendezvous)
{
    auto motherGoose = [ "Old Mother Goose,",
                         "When she wanted to wander,",
                         "Would ride through the air,",
                         "On a very fine gander.",
                         "Jack's mother came in,",
                         "And caught the goose soon,",
                         "And mounting its back,",
                         "Flew up to the moon.", ];

    rendezvous.print(motherGoose);
}

void main()
{
    auto rendezvous = shared(PrinterRendezvous)([ Printer("main", 5),
                                                  Printer("reserve", 5) ]);

    spawn(&humptyDumptyTask, rendezvous);
    spawn(&motherGooseTask, rendezvous);
}

Sample output:

main: Humpty Dumpty sat on a wall.
main: Old Mother Goose,
main: Humpty Dumpty had a great fall.
main: When she wanted to wander,
main: All the king's horses and all the king's men,
reserve: Would ride through the air,
reserve: Couldn't put Humpty together again.
reserve: On a very fine gander.
reserve: Jack's mother came in,
reserve: And caught the goose soon,
deneme.OutOfInk@deneme.d([...]): Out of ink.

Ali

February 26, 2014
Ali Çehreli:

>Improve at will! :p

I will mostly just uniform its formatting to all the other Rosettacode entries, shorten the lines to 72 chars, etc.


>                 synchronized {
>                     // Switch to the next printer
>                     printers = printers[1..$];
>                 }

This doesn't work:

printers.popFront();


>     void print(string line)
>     {
>         enforce(ink != 0, new OutOfInk);
>         writefln("%s: %s", id, line);
>         --ink;
>     }
> }
>
> struct PrinterRendezvous
> ...
>             try {
>                 synchronized {
>                     (cast(Printer)printers.front).print(lines.front);
>                 }

It it a good idea to define Printer.print like this to remove that cast?

void print(string line) shared

Bye,
bearophile
February 26, 2014
On Wednesday, 26 February 2014 at 11:24:58 UTC, bearophile wrote:
> Ali Çehreli:

>>                synchronized {
>>                    // Switch to the next printer
>>                    printers = printers[1..$];
>>                }
>
> This doesn't work:
>
> printers.popFront();

Yes, because typeof(printers) == shared. I'm wondering why front() works.

>>            try {
>>                synchronized {
>>                    (cast(Printer)printers.front).print(lines.front);
>>                }
>
> It it a good idea to define Printer.print like this to remove that cast?
>
> void print(string line) shared

No it is not, because the implementation of print() would be invalid then:

    void print(string line) shared
    {
        enforce(ink != 0, new OutOfInk); // ink != 0 is not valid code
        writefln("%s: %s", id, line);
        --ink;                           // --ink is not valid code
    }

By declaring the method "shared" you promise that any acess to shared data in this method is safe. Using naked operators on shared scalar variables will eventually be disallowed, you'll have to explicitly use atomic operations.

You'd also have to synchronize access to id member, because it is public.  Although "immutable" means "implicitly shared", id being public means that potentionally one thread could be performing assigment to it while another one reads from it. Slices are two machine words, so atomicity of reads/writes is not implicitly guaranteed.

The cast in this case is safe and better approach because all access to "printers" is synchronized.
If/when https://d.puremagic.com/issues/show_bug.cgi?id=12133 and the corresponding pull is accepted, the cast could be replaced with

    printers.assumeLocal.front.print(lines.front);

which states the intent even more clearly, IMO.
February 26, 2014
I forgot to note that both synchronized {} blocks should also be synchronizing on the same mutex. Right now it's two different critical sections, so a race is still possible, i.e. while one thread is printing the other may be removing the first printer. Run the code several times and you'll no doubt stumble upon it.

The mutex could be emulated with a shared bool and std.atomic.cas(). That would get rid of synchronized{} blocks and would potentionally be faster.
February 26, 2014
Stanislav Blinov:

> You'd also have to synchronize access to id member,

> I forgot to note that both synchronized {} blocks should also be synchronizing on the same mutex.

> The mutex could be emulated with a shared bool and std.atomic.cas(). That would get rid of synchronized{} blocks and would potentionally be faster.

This is the current D entry:

http://rosettacode.org/wiki/Rendezvous#D

If you have bug fixes, or improvements, it's better to do them right there. Of if you don't want to register on that site, you can put the modified version in dpaste, and I'll upload it on Rosettacode.

Bye,
bearophile
February 26, 2014
On Wednesday, 26 February 2014 at 12:58:26 UTC, bearophile wrote:

> If you have bug fixes, or improvements, it's better to do them right there. Of if you don't want to register on that site, you can put the modified version in dpaste, and I'll upload it on Rosettacode.

Here are some improvements:

http://dpaste.dzfl.pl/6430488f3d07

The changes are minimal just to accomodate for simple synchronization primitive. Full-blown Mutex version would be a little more involved, and somewhat ugly, at least until the upcoming migration of Mutex et al. to 'shared' takes place.
February 26, 2014
On 02/26/2014 03:24 AM, bearophile wrote:

> Ali Çehreli:
>
>> Improve at will! :p
>
> I will mostly just uniform its formatting to all the other Rosettacode
> entries, shorten the lines to 72 chars, etc.
>
>
>>                 synchronized {
>>                     // Switch to the next printer
>>                     printers = printers[1..$];
>>                 }
>
> This doesn't work:
>
> printers.popFront();

I've noticed that too. ;) And I am not sure why the slicing syntax works because the 'printers' member is still shared then.

>>     void print(string line)
>>     {
>>         enforce(ink != 0, new OutOfInk);
>>         writefln("%s: %s", id, line);
>>         --ink;
>>     }
>> }
>>
>> struct PrinterRendezvous
>> ...
>>             try {
>>                 synchronized {
>> (cast(Printer)printers.front).print(lines.front);
>>                 }
>
> It it a good idea to define Printer.print like this to remove that cast?
>
> void print(string line) shared

I had that at one point but then I could not convince myself that Printer.print should be a shared member function. How do we know at design time that every Printer would be shared? I thought that Printer should be as simple as possible and that shared should be handled by a higher-level code. Then the code became ugly like that. :)

I need more experience. :-/

> Bye,
> bearophile

Ali

February 26, 2014
Ali Çehreli:

> And I am not sure why the slicing syntax works because the 'printers' member is still shared then.

Probably it's a known D implementation fault meant to be eventually fixed.

-------------

Stanislav Blinov:

> Here are some improvements:
> http://dpaste.dzfl.pl/6430488f3d07

Updated the site with your code (in that the "View history" shows both your names):
http://rosettacode.org/wiki/Rendezvous#D

Bye,
bearophile
February 26, 2014
On 02/26/2014 04:46 AM, Stanislav Blinov wrote:

> I forgot to note that both synchronized {} blocks should also be
> synchronizing on the same mutex.

Oh, that's a good one! :)

> Run the code several times and you'll no doubt stumble upon it.

But I had inserted that Sleep() in there. Isn't that the solution for all multi-threading problems? :p

Ali