Demonstrate the “rendezvous” communications technique by implementing a printer monitor.

import std.stdio, std.array, std.datetime, std.exception,
       std.concurrency, core.thread, core.atomic;
 
final class OutOfInk: Exception {
    this() pure nothrow {
        super("Out of ink.");
    }
}
 
struct Printer {
    string id;
    size_t ink;
 
    void printIt(in string line) {
        enforce(ink != 0, new OutOfInk);
        writefln("%s: %s", id, line);
        ink--;
    }
}
 
/// Treats shared lvalue as if it is thread-local.
ref assumeThreadLocal(T)(ref shared T what) pure nothrow {
    return *cast(T*)&what;
}
 
struct RendezvousPrinter {
    Printer[] printers;
 
    void print(const(string)[] lines) shared {
        OutOfInk savedException;
 
        // Lightweight mutual exclusion
        // using shared atomic bool.
        shared static bool mutex;
 
        void lock() {
            while (!cas(&mutex, false, true))
                Thread.sleep(1.hnsecs);
        }
 
        void unlock() nothrow {
            assert(mutex.atomicLoad!(MemoryOrder.acq));
            mutex.atomicStore!(MemoryOrder.rel)(false);
        }
 
        while (!lines.empty) {
            if (printers.empty) {
                // No more printers to try.
                assert(savedException !is null);
                throw savedException;
            }
 
            try {
                {
                    lock;
                    scope(exit) unlock;
                    printers.front.assumeThreadLocal
                        .printIt(lines.front);
                }
                lines.popFront;
 
                // Increase the chance of interleaved output.
                Thread.sleep(10.msecs);
            } catch (OutOfInk exc) {
                savedException = exc;
 
                // Switch to the next printer.
                lock;
                scope(exit) unlock;
                printers.assumeThreadLocal.popFront;
            }
        }
    }
}
 
void humptyDumptyTask(shared ref RendezvousPrinter rendezvous) {
    const 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(shared ref RendezvousPrinter rendezvous) {
    const 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() {
    shared rendezvous = RendezvousPrinter
        ([Printer("main", 5), Printer("reserve", 5)]);
 
    spawn(&humptyDumptyTask, rendezvous);
    spawn(&motherGooseTask, rendezvous);
}