Rendezvous: Difference between revisions

47,181 bytes added ,  3 months ago
m
→‎{{header|Wren}}: Changed to Wren S/H
({{omit from|PARI/GP}})
m (→‎{{header|Wren}}: Changed to Wren S/H)
 
(37 intermediate revisions by 18 users not shown)
Line 33:
=={{header|Ada}}==
Ada has integrated [http://www.iuma.ulpgc.es/users/jmiranda/gnat-rts/node20.htm rendezvous support]. The caller calls to a rendezvous using the name of the task suffixed by the entry point name and the parameters. An entry point can be called using timed entry call statement which allow limit waiting time:
<langsyntaxhighlight lang="ada">select
Server.Wake_Up (Parameters);
or delay 5.0;
-- No response, try something else
...
end select;</langsyntaxhighlight>
The task accepts a rendezvous using accept statement. The statement can contain body which implements the rendezvous. When several rendezvous need to be accepted a selective accept statement can be used. For example:
<langsyntaxhighlight lang="ada">select
accept Wake_Up (Parameters : Work_Item) do
Current_Work_Item := Parameters;
Line 47:
or accept Shut_Down;
exit; -- Shut down requested
end select;</langsyntaxhighlight>
Entry points in the selective accept can be guarded by Boolean expressions which close the entry point when the expression yield false.
 
A task may requeue rendezvous request from the body of an accept statement to an entry point of the same or another task if the parameter profile of the entry point is compatible. The requeue statement may contain clause '''with abort'' which allows the caller to abort the request when it waits for other task to accept it. Without the clause the request is protected from abortion. This might be useful when the first task initiates processing of the request and the side effect of this action need to be removed when processing is completed.
===The task===
<langsyntaxhighlight lang="ada">with Ada.Text_IO; use Ada.Text_IO;
 
procedure Rendezvous is
Line 128:
begin
null;
end Rendezvous;</langsyntaxhighlight>
Sample output:
<pre>
Line 143:
Mother Goose out of ink!
</pre>
 
=={{header|AutoHotkey}}==
<langsyntaxhighlight AutoHotkeylang="autohotkey">OnMessage(0x4a, "PrintMonitor")
SetTimer, print2, 400
 
Line 220 ⟶ 221:
Else
Return -1
}</langsyntaxhighlight>
 
=={{header|C}}==
=== Pthreads implementation ===
{{works with|gcc|10.1.0}}
{{libheader|pthread}}
This uses POSIX threads to implement a subset of the Ada functionality and primarily focuses on the synchronization aspect. C does not have exceptions, so return values are used to signal errors. Multiple threads can enter a rendezvous at once, and a single thread can accept them. No attempt is made to implement selective accept statements or timeouts (though pthreads does have ''pthread_cond_timedwait()'').
<syntaxhighlight lang="c">
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
 
/* The language task, implemented with pthreads for POSIX systems. */
 
/* Each rendezvous_t will be accepted by a single thread, and entered
* by one or more threads. accept_func() only returns an integer and
* is always run within the entering thread's context to simplify
* handling the arguments and return value. This somewhat unlike an
* Ada rendezvous and is a subset of the Ada rendezvous functionality.
* Ada's in and out parameters can be simulated via the void pointer
* passed to accept_func() to update variables owned by both the
* entering and accepting threads, if a suitable struct with pointers
* to those variables is used. */
typedef struct rendezvous {
pthread_mutex_t lock; /* A mutex/lock to use with the CVs. */
pthread_cond_t cv_entering; /* Signaled when a thread enters. */
pthread_cond_t cv_accepting; /* Signaled when accepting thread is ready. */
pthread_cond_t cv_done; /* Signaled when accept_func() finishes. */
int (*accept_func)(void*); /* The function to run when accepted. */
int entering; /* Number of threads trying to enter. */
int accepting; /* True if the accepting thread is ready. */
int done; /* True if accept_func() is done. */
} rendezvous_t;
 
/* Static initialization for rendezvous_t. */
#define RENDEZVOUS_INITILIZER(accept_function) { \
.lock = PTHREAD_MUTEX_INITIALIZER, \
.cv_entering = PTHREAD_COND_INITIALIZER, \
.cv_accepting = PTHREAD_COND_INITIALIZER, \
.cv_done = PTHREAD_COND_INITIALIZER, \
.accept_func = accept_function, \
.entering = 0, \
.accepting = 0, \
.done = 0, \
}
 
int enter_rendezvous(rendezvous_t *rv, void* data)
{
/* Arguments are passed in and out of the rendezvous via
* (void*)data, and the accept_func() return value is copied and
* returned to the caller (entering thread). A data struct with
* pointers to variables in both the entering and accepting
* threads can be used to simulate Ada's in and out parameters, if
* needed. */
pthread_mutex_lock(&rv->lock);
 
rv->entering++;
pthread_cond_signal(&rv->cv_entering);
 
while (!rv->accepting) {
/* Nothing is accepting yet, keep waiting. pthreads will
* queue all waiting entries. The loop is needed to handle
* both race conditions and spurious wakeups. */
pthread_cond_wait(&rv->cv_accepting, &rv->lock);
}
 
/* Call accept_func() and copy the return value before leaving
* the mutex. */
int ret = rv->accept_func(data);
 
/* This signal is needed so that the accepting thread will wait
* for the rendezvous to finish before trying to accept again. */
rv->done = 1;
pthread_cond_signal(&rv->cv_done);
 
rv->entering--;
rv->accepting = 0;
pthread_mutex_unlock(&rv->lock);
 
return ret;
}
 
void accept_rendezvous(rendezvous_t *rv)
{
/* This accept function does not take in or return parameters.
* That is handled on the entry side. This is only for
* synchronization. */
pthread_mutex_lock(&rv->lock);
rv->accepting = 1;
 
while (!rv->entering) {
/* Nothing to accept yet, keep waiting. */
pthread_cond_wait(&rv->cv_entering, &rv->lock);
}
 
pthread_cond_signal(&rv->cv_accepting);
 
while (!rv->done) {
/* Wait for accept_func() to finish. */
pthread_cond_wait(&rv->cv_done, &rv->lock);
}
rv->done = 0;
 
rv->accepting = 0;
pthread_mutex_unlock(&rv->lock);
}
 
/* The printer use case task implemented using the above rendezvous
* implementation. Since C doesn't have exceptions, return values are
* used to signal out of ink errors. */
 
typedef struct printer {
rendezvous_t rv;
struct printer *backup;
int id;
int remaining_lines;
} printer_t;
 
typedef struct print_args {
struct printer *printer;
const char* line;
} print_args_t;
 
int print_line(printer_t *printer, const char* line) {
print_args_t args;
args.printer = printer;
args.line = line;
return enter_rendezvous(&printer->rv, &args);
}
 
int accept_print(void* data) {
/* This is called within the rendezvous, so everything is locked
* and okay to modify. */
print_args_t *args = (print_args_t*)data;
printer_t *printer = args->printer;
const char* line = args->line;
 
if (printer->remaining_lines) {
/* Print the line, character by character. */
printf("%d: ", printer->id);
while (*line != '\0') {
putchar(*line++);
}
putchar('\n');
printer->remaining_lines--;
return 1;
}
else if (printer->backup) {
/* "Requeue" this rendezvous with the backup printer. */
return print_line(printer->backup, line);
}
else {
/* Out of ink, and no backup available. */
return -1;
}
}
 
printer_t backup_printer = {
.rv = RENDEZVOUS_INITILIZER(accept_print),
.backup = NULL,
.id = 2,
.remaining_lines = 5,
};
 
printer_t main_printer = {
.rv = RENDEZVOUS_INITILIZER(accept_print),
.backup = &backup_printer,
.id = 1,
.remaining_lines = 5,
};
 
void* printer_thread(void* thread_data) {
printer_t *printer = (printer_t*) thread_data;
while (1) {
accept_rendezvous(&printer->rv);
}
}
 
typedef struct poem {
char* name;
char* lines[];
} poem_t;
 
poem_t humpty_dumpty = {
.name = "Humpty Dumpty",
.lines = {
"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.",
""
},
};
 
poem_t mother_goose = {
.name = "Mother Goose",
.lines = {
"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.",
""
},
};
 
void* poem_thread(void* thread_data) {
poem_t *poem = (poem_t*)thread_data;
 
for (unsigned i = 0; poem->lines[i] != ""; i++) {
int ret = print_line(&main_printer, poem->lines[i]);
if (ret < 0) {
printf(" %s out of ink!\n", poem->name);
exit(1);
}
}
return NULL;
}
 
int main(void)
{
pthread_t threads[4];
 
pthread_create(&threads[0], NULL, poem_thread, &humpty_dumpty);
pthread_create(&threads[1], NULL, poem_thread, &mother_goose);
pthread_create(&threads[2], NULL, printer_thread, &main_printer);
pthread_create(&threads[3], NULL, printer_thread, &backup_printer);
 
pthread_join(threads[0], NULL);
pthread_join(threads[1], NULL);
pthread_cancel(threads[2]);
pthread_cancel(threads[3]);
 
return 0;
}
</syntaxhighlight>
 
=== OpenMP implementation ===
Basically just synched threads doing printing: since task didn't ask for service type or resource enumeration, and "message passing is stupid" (c.f. talk), the guarding thread is no more than a glorified mutex, hence completely cut out, leaving the threads directly check ink and do print.
<syntaxhighlight lang="c">#include <stdio.h>
#include <unistd.h>
#include <omp.h>
 
typedef struct printer printer;
struct printer { int id, ink; };
printer pnt_main = { 1, 5 };
printer pnt_backup = { 2, 5 };
 
int print(const char * text, const char **error)
{
#pragma omp critical
{
printer *p = &pnt_main;
if (!p->ink) p = &pnt_backup;
if (!p->ink)
*error = "Out of ink";
else {
*error = 0;
p->ink--;
printf("%d | ", p->id, p->ink);
while (*text != '\0') {
putchar(*(text++));
fflush(stdout);
usleep(30000);
}
putchar('\n');
}
}
return 0 != *error;
}
 
const char *humpty[] = {
"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."
};
 
const char *goose[] = {
"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."
};
 
int main()
{
int i, j, len;
const char *msg, **text;
 
omp_set_num_threads(2);
 
#pragma omp parallel for private(text, msg, len, j)
for (i = 0; i < 2; i++) {
text = i ? goose : humpty;
len = (i ? sizeof(goose) : sizeof(humpty) ) / sizeof(const char*);
for (j = 0; j < len; j++) {
usleep(100000);
if (print(text[j], &msg)) {
fprintf(stderr, "Error: %s\n", msg);
break;
}
}
}
 
return 0;
}</syntaxhighlight>
 
=={{header|D}}==
<syntaxhighlight lang="d">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);
}</syntaxhighlight>
{{out}}
<pre>main: Humpty Dumpty sat on a wall.
main: Old Mother Goose,
main: When she wanted to wander,
main: Humpty Dumpty had a great fall.
main: Would ride through the air,
reserve: On a very fine gander.
reserve: All the king's horses and all the king's men,
reserve: Jack's mother came in,
reserve: And caught the goose soon,
reserve: Couldn't put Humpty together again.
rendezvous2.OutOfInk @rendezvous2.d(6): Out of ink.
----------------
0x0040609F
0x004021FC
0x0040243A
0x00405F47
0x00412D55
0x00433A48
0x77BB1603 in RtlInitializeExceptionChain
0x77BB15D6 in RtlInitializeExceptionChain</pre>
 
=={{header|Erlang}}==
There is no rendezvous in Erlang. To fulfil the task description I have implemented rendezvous with message passing (which is in Erlang). Doing these printers directly with message passing would have been simpler (in Erlang).
 
<syntaxhighlight lang="erlang">
-module( rendezvous ).
 
-export( [task/0] ).
 
task() ->
Printer_pid = erlang:spawn( fun() -> printer(1, 5) end ),
Reserve_printer_pid = erlang:spawn( fun() -> printer(2, 5) end ),
Monitor_pid = erlang:spawn( fun() -> printer_monitor(Printer_pid, Reserve_printer_pid) end ),
erlang:spawn( fun() -> print(Monitor_pid, humpty_dumpty()) end ),
erlang:spawn( fun() -> print(Monitor_pid, mother_goose()) end ).
 
humpty_dumpty() ->
["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."].
 
mother_goose() ->
["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."].
 
print( Pid, Lines ) ->
io:fwrite( "Print ~p started~n", [erlang:self()] ),
print( Pid, Lines, infinity ).
 
print( _Pid, [], _Timeout ) -> ok;
print( Pid, [Line | T], Timeout ) ->
print_line( Pid, Line, Timeout ),
print_line_done(),
print( Pid, T, Timeout ).
 
print_line( Pid, Line, Timeout ) ->
Pid ! {print, Line, erlang:self()},
receive
{print, started} -> ok
after Timeout -> erlang:throw( timeout )
end.
 
print_line_done() ->
receive
{printer, ok} -> ok;
{printer, out_of_ink} -> erlang:throw( out_of_ink )
end.
 
printer( N, 0 ) ->
receive
{print, _Line, Pid} -> Pid ! {printer, out_of_ink}
end,
printer( N, 0 );
printer( N, Ink ) ->
receive
{print, Line, Pid} ->
Pid ! {printer, ok},
io:fwrite( "~p: ", [N] ),
[io:fwrite("~c", [X]) || X <- Line],
io:nl()
end,
printer( N, Ink - 1 ).
 
printer_monitor( Printer, Reserve ) ->
{Line, Pid} = printer_monitor_get_line(),
Result = printer_monitor_print_line( Printer, Line ),
printer_monitor_reserve( Result, Reserve, Line, Pid ),
printer_monitor( Printer, Reserve ).
 
printer_monitor_get_line() ->
receive
{print, Line, Pid} ->
Pid ! {print, started},
{Line, Pid}
end.
 
printer_monitor_print_line( Printer_pid, Line ) ->
Printer_pid ! {print, Line, erlang:self()},
receive
{printer, Result} -> Result
end.
 
printer_monitor_reserve( ok, _Reserve_pid, _Line, Pid ) -> Pid ! {printer, ok};
printer_monitor_reserve( out_of_ink, Reserve_pid, Line, Pid ) -> Reserve_pid ! {print, Line, Pid}.
</syntaxhighlight>
{{out}}
The first printouts are there to show the identity of the processes that print. It makes it easier to match the exception to one of them and not to some other process.
<pre>
53> rendezvous:task().
Print <0.251.0> started
Print <0.252.0> started
1: Humpty Dumpty sat on a wall.
1: Old Mother Goose,
1: Humpty Dumpty had a great fall.
1: When she wanted to wander,
1: All the king's horses and all the king's men,
2: Would ride through the air,
2: Couldn't put Humpty together again.
2: On a very fine gander.
2: Jack's mother came in,
2: And caught the goose soon,
 
=ERROR REPORT==== 22-Sep-2013::12:09:56 ===
Error in process <0.252.0> with exit value: {{nocatch,out_of_ink},[{rendezvous,print_line_done,0,[{file,"rendezvous.erl"},{line,48}]},{rendezvous,print,3,[{file,"rendezvous.erl"},{line,35}]}]}
</pre>
 
=={{header|F_Sharp|F#}}==
 
Line 227 ⟶ 782:
It is possible to extract the boilerplate code into a reusable helper class which should be considered when using active objects a lot.
 
<langsyntaxhighlight lang="fsharp">open System
 
type PrinterCommand = Print of string
Line 302 ⟶ 857:
)).Start()
 
Console.ReadLine() |> ignore</langsyntaxhighlight>
 
Example output:
Line 316 ⟶ 871:
2: And mounting its back,
Mother Goose out of ink!</pre>
 
 
=={{header|Go}}==
<syntaxhighlight lang="go">package main
Pretty sure printing could be done better ways in Go, but I stayed as close to the task description as I could.
 
Back-to-back synchronous send and receive, for example, achieves the rendezvous property of one task suspending while the other executes. Passing an interface means only a ''reference'' to data is passed, the underlying data is not passed on a channel or moved in any way. rSync and it's rendezvous method are defined in a way that is completely general and not specific to this task description. The only nod to exception handling here is the rendezvous response being an interface, which can reference a value or object of any type.
<lang go>package main
 
import (
"errors"
"fmt"
"strings"
Line 354 ⟶ 906:
}
 
// printer is a type representing an abstraction of a physical printer.
func newPrinter(ink int) func(string) string {
// It is a type defintion for a function that takes a string to print
return func(line string) string {
// and returns an error value, (hopefully usually nil, meaning no error.)
type printer func(string) error
 
// newPrinter is a constructor. The parameter is a quantity of ink. It
// returns a printer object encapsulating the ink quantity.
// Note that this is not creating the monitor, only the object serving as
// a physical printer by writing to standard output.
func newPrinter(ink int) printer {
return func(line string) error {
if ink == 0 {
return "out of ink"eOutOfInk
}
for _, c := range line {
fmt.Printf("%c", c)
}
fmt.Println("")
ink--
return ""nil
}
}
 
var eOutOfInk = errors.New("out of ink")
 
// For the language task, rSync is a type used to approximate the Ada
// rendezvous mechanism that includes the caller waiting for completion
// of the callee. For this use case, we signal completion with an error
// value as a response. Exceptions are not idiomatic in Go and there is
// no attempt here to model the Ada exception mechanism. Instead, it is
// idomatic in Go to return error values. Sending an error value on a
// channel works well here to signal completion. Go unbuffered channels
// provide synchronous rendezvous, but call and response takes two channels,
// which are bundled together here in a struct. The channel types are chosen
// to mirror the parameter and return types of "type printer" defined above.
// The channel types here, string and error are both "reference types"
// in Go terminology. That is, they are small things containing pointers
// to the actual data. Sending one on a channel does not involve copying,
// or much less marshalling string data.
type rSync struct {
call chan interface{}string
response chan interface{}error
}
 
// "rendezvous Print" requested by use case task.
func (r *rSync) rendezvous(data interface{}) interface{} {
// For the language task though, it is implemented here as a method on
r.call <- data
// rSync that sends its argument on rSync.call and returns the result
return <-r.response
// received from rSync.response. Each channel operation is synchronous.
// The two operations back to back approximate the Ada rendezvous.
func (r *rSync) print(data string) error {
r.call <- data // blocks until data is accepted on channel
return <-r.response // blocks until response is received
}
 
// monitor is run as a goroutine. It encapsulates the printer passed to it.
func startMonitor(printer func(string) string, reservePrinter *rSync) *rSync {
// Print requests are received through the rSync object "entry," named entry
r := &rSync{make(chan interface{}), make(chan interface{})}
// here to correspond to the Ada concept of an entry point.
go monitor(printer, r, reservePrinter)
func monitor(hardPrint printer, entry, reserve *rSync) {
return r
}
 
func monitor(print func(string) string, entry, reserve *rSync) {
for {
// The monitor goroutine will block here waiting for a "call"
// to its "entry point."
data := <-entry.call
// Assuming the call came from a goroutine calling rSync.print,
err := print(data.(string))
// that goroutine is now blocked, waiting for this one to send
switch {
case// erra == "":response.
 
entry.response <- nil
// attempt output
case err == "out of ink" && reserve != nil:
switch err := entry.response <- reserve.rendezvoushardPrint(data); {
 
// consider return value from attempt
case err == nil:
entry.response <- nil // no problems
 
case err == eOutOfInk && reserve != nil:
// Requeue to "entry point" of reserve printer monitor.
// Caller stays blocked, and now this goroutine blocks until
// it gets a response from the reserve printer monitor.
// It then transparently relays the response to the caller.
entry.response <- reserve.print(data)
 
default:
entry.response <- err // return failure
}
// The response is away. Loop, and so immediately block again.
}
}
 
// startMonitor can be seen as an rSync constructor. It also
func writer(printer *rSync, id, text string, busy *sync.WaitGroup) {
// of course, starts the monitor for which the rSync serves as entry point.
for _, line := range strings.Split(text, "\n", -1) {
// Further to the langauge task, note that the channels created here are
if e := printer.rendezvous(line); e != nil {
// unbuffered. There is no buffer or message box to hold channel data.
fmt.Println("**** print job", id, "terminated:", e, "****")
// A sender will block waiting for a receiver to accept data synchronously.
func startMonitor(p printer, reservePrinter *rSync) *rSync {
entry := &rSync{make(chan string), make(chan error)}
go monitor(p, entry, reservePrinter)
return entry
}
 
// Two writer tasks are started as goroutines by main. They run concurrently
// and compete for printers as resources. Note the call to "rendezvous Print"
// as requested in the use case task and compare the syntax,
// Here: printMonitor.print(line);
// Ada solution: Main.Print ("string literal");
func writer(printMonitor *rSync, id, text string, busy *sync.WaitGroup) {
for _, line := range strings.Split(text, "\n") {
if err := printMonitor.print(line); err != nil {
fmt.Printf("**** writer task %q terminated: %v ****\n", id, err)
break
}
}
busy.Done()
}</langsyntaxhighlight>
Output:
<pre>
Old Mother Goose,
Humpty Dumpty sat on a wall.
Old Mother Goose,
Humpty Dumpty had a great fall.
When she wanted to wander,
Line 420 ⟶ 1,030:
Jack's mother came in,
And caught the goose soon,
**** printwriter jobtask "mg" terminated: out of ink ****
</pre>
 
=={{header|Julia}}==
Julia has coroutines started with the @async macro and Channels, which can be used for interprocess communication, such as passing lines to and errors from a printing routine.
<syntaxhighlight lang="julia">mutable struct Printer
inputpath::Channel{String}
errorpath::Channel{String}
inkremaining::Int32
reserve::Printer
name::String
function Printer(ch1, ch2, ink, name)
this = new()
this.inputpath = ch1
this.errorpath = ch2
this.inkremaining = ink
this.name = name
this.reserve = this
this
end
end
 
function lineprintertask(printer)
while true
line = take!(printer.inputpath)
linesprinted = 0
if(printer.inkremaining < 1)
if(printer.reserve == printer)
put!(printer.errorpath, "Error: printer $(printer.name) out of ink")
else
put!(printer.reserve.inputpath, line)
end
else
println(line)
printer.inkremaining -= 1
end
end
end
 
function schedulework(poems)
printerclose(printer) = (close(printer.inputpath); close(printer.errorpath))
reserveprinter = Printer(Channel{String}(1), Channel{String}(10), 5, "Reserve")
mainprinter = Printer(Channel{String}(1), Channel{String}(10), 5, "Main")
mainprinter.reserve = reserveprinter
@async(lineprintertask(mainprinter))
@async(lineprintertask(reserveprinter))
printers = [mainprinter, reserveprinter]
activeprinter = 1
@sync(
for poem in poems
activeprinter = (activeprinter % length(printers)) + 1
@async(
for line in poem
put!(printers[activeprinter].inputpath, line)
end)
end)
for p in printers
while isready(p.errorpath)
println(take!(p.errorpath))
end
printerclose(p)
end
end
 
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."]
 
const oldmothergoose = ["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."]
 
schedulework([humptydumpty, oldmothergoose])
</syntaxhighlight>{{output}}<pre>
Humpty Dumpty sat on a wall.
Humpty Dumpty had a great fall.
Old Mother Goose,
All the king's horses and all the king's men,
When she wanted to wander,
Couldn't put Humpty together again.
Would ride through the air,
On a very fine gander.
Jack's mother came in,
And caught the goose soon,
Error: printer Reserve out of ink
Error: printer Reserve out of ink
</pre>
 
=={{header|Nim}}==
{{trans|Python}}
<syntaxhighlight lang="nim">import asyncdispatch, options, strutils
type
Printer = ref object
inkLevel, id: int
backup: Option[Printer]
OutOfInkException = object of IOError
proc print(p: Printer, line: string){.async.} =
if p.inkLevel <= 0:
if p.backup.isNone():
raise newException(OutOfInkException, "out of ink")
else:
await p.backup.get().print(line)
else:
p.inkLevel-=1
stdout.writeLine("$1:$2".format(p.id, line))
await sleepAsync(100)
proc newPrinter(inkLevel, id: int, backup: Option[Printer]): Printer =
new(result)
result.inkLevel = inkLevel
result.id = id
result.backup = backup
 
proc print(p: Printer, msg: seq[string]){.async.} =
for line in msg:
try:
await p.print(line)
except OutOfInkException as e:
echo("out of ink")
break
const
humptyLines = @[
"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.",
]
gooseLines = @[
"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.",
]
proc main(){.async.} =
var
reservePrinter = newPrinter(5, 2, none(Printer))
mainPrinter = newPrinter(5, 1, some(reservePrinter))
await mainPrinter.print(gooseLines) and mainPrinter.print(humptyLines)
 
waitFor main()</syntaxhighlight>
{{output}}
<pre>1:Old Mother Goose,
1:Humpty Dumpty sat on a wall.
1:When she wanted to wander,
1:Humpty Dumpty had a great fall.
1:Would ride through the air,
2:All the king's horses and all the king's men,
2:On a very fine gander.
2:Couldn't put Humpty together again.
2:Jack's mother came in,
2:And caught the goose soon,
out of ink</pre>
 
=={{header|Oz}}==
Line 427 ⟶ 1,196:
 
First a simple printer class whose definition is completely orthogonal to multithreading issues:
<langsyntaxhighlight lang="oz">declare
class Printer
attr ink:5
Line 454 ⟶ 1,223:
end
end
end</langsyntaxhighlight>
Note how requeuing the task simply becomes delegation to a different object.
 
Active object are not a predefined abstraction in Oz. But due to Oz' first-class object messages, we can easily define it using ports and streams (many-to-one message passing):
<langsyntaxhighlight lang="oz"> fun {NewActiveSync Class Init}
Obj = {New Class Init}
MsgPort
Line 478 ⟶ 1,247:
{Wait Sync}
end
end</langsyntaxhighlight>
This functions takes a class and an initialization message and returns a procedure. When called, this procedure will send messages to the new object in a new thread and then wait for the <code>Sync</code> variable to become bound. Exceptions are propagated using [http://www.mozart-oz.org/home/doc/base/node13.html#label696 failed values].
 
Line 484 ⟶ 1,253:
 
With this new abstraction we can create the two printers and execute both print tasks in their own thread:
<langsyntaxhighlight lang="oz"> Main = {NewActiveSync Printer init(id:1 backup:Reserve)}
Reserve = {NewActiveSync Printer init(id:2)}
in
Line 513 ⟶ 1,282:
{System.showInfo " Mother Goose out of ink!"}
end
end</langsyntaxhighlight>
 
Example output:
Line 544 ⟶ 1,313:
Like in the rendezvous mechanism, parameters are not marshalled. This is because sharing immutable data between threads is safe.
In contrast to ADA, the parameters are buffered until the printer becomes ready. But with a synchronous communication mechanism, this should not cause problems.
 
=={{header|Phix}}==
Phix has no rendezvous mechanism, the following achieves something similar using a simple mutex.
<!--<syntaxhighlight lang="phix">(notonline)-->
<span style="color: #008080;">without</span> <span style="color: #008080;">js</span> <span style="color: #000080;font-style:italic;">-- (threads)</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">print_cs</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">init_cs</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">enum</span> <span style="color: #000000;">NAME</span><span style="color: #0000FF;">,</span><span style="color: #000000;">INK</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">printers</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{{</span><span style="color: #008000;">"main"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5</span><span style="color: #0000FF;">},</span>
<span style="color: #0000FF;">{</span><span style="color: #008000;">"reserve"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5</span><span style="color: #0000FF;">}}</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">printer</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">name</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">sequence</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">try</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #7060A8;">enter_cs</span><span style="color: #0000FF;">(</span><span style="color: #000000;">print_cs</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">p</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">printers</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">printers</span><span style="color: #0000FF;">[</span><span style="color: #000000;">p</span><span style="color: #0000FF;">][</span><span style="color: #000000;">INK</span><span style="color: #0000FF;">]!=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">printers</span><span style="color: #0000FF;">[</span><span style="color: #000000;">p</span><span style="color: #0000FF;">][</span><span style="color: #000000;">INK</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">-=</span> <span style="color: #000000;">1</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%s/%s: %s\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">name</span><span style="color: #0000FF;">,</span><span style="color: #000000;">printers</span><span style="color: #0000FF;">[</span><span style="color: #000000;">p</span><span style="color: #0000FF;">][</span><span style="color: #000000;">NAME</span><span style="color: #0000FF;">],</span><span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]})</span>
<span style="color: #008080;">exit</span>
<span style="color: #008080;">elsif</span> <span style="color: #000000;">p</span><span style="color: #0000FF;">=</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">printers</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">throw</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"out of ink"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #7060A8;">leave_cs</span><span style="color: #0000FF;">(</span><span style="color: #000000;">print_cs</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #7060A8;">exit_thread</span><span style="color: #0000FF;">(</span><span style="color: #000000;">0</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">catch</span> <span style="color: #000000;">e</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"exception(%s): %s\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">name</span><span style="color: #0000FF;">,</span><span style="color: #000000;">e</span><span style="color: #0000FF;">[</span><span style="color: #004600;">E_USER</span><span style="color: #0000FF;">]})</span>
<span style="color: #7060A8;">leave_cs</span><span style="color: #0000FF;">(</span><span style="color: #000000;">print_cs</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">exit_thread</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">try</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">hd</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #008000;">"Humpty Dumpty sat on a wall."</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"Humpty Dumpty had a great fall."</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"All the king's horses and all the king's men"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"Couldn't put Humpty together again."</span><span style="color: #0000FF;">},</span>
<span style="color: #000000;">mg</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #008000;">"Old Mother Goose"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"When she wanted to wander,"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"Would ride through the air"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"On a very fine gander."</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"Jack's mother came in,"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"And caught the goose soon,"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"And mounting its back,"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"Flew up to the moon."</span><span style="color: #0000FF;">}</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">hThreads</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #7060A8;">create_thread</span><span style="color: #0000FF;">(</span><span style="color: #000000;">printer</span><span style="color: #0000FF;">,{</span><span style="color: #008000;">"hd"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">hd</span><span style="color: #0000FF;">}),</span>
<span style="color: #7060A8;">create_thread</span><span style="color: #0000FF;">(</span><span style="color: #000000;">printer</span><span style="color: #0000FF;">,{</span><span style="color: #008000;">"mg"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">mg</span><span style="color: #0000FF;">})}</span>
<span style="color: #7060A8;">wait_thread</span><span style="color: #0000FF;">(</span><span style="color: #000000;">hThreads</span><span style="color: #0000FF;">)</span>
<!--</syntaxhighlight>-->
{{out}}
<pre>
hd/main: Humpty Dumpty sat on a wall.
mg/main: Old Mother Goose
mg/main: When she wanted to wander,
hd/main: Humpty Dumpty had a great fall.
hd/main: All the king's horses and all the king's men
hd/reserve: Couldn't put Humpty together again.
mg/reserve: Would ride through the air
mg/reserve: On a very fine gander.
mg/reserve: Jack's mother came in,
mg/reserve: And caught the goose soon,
exception(mg): out of ink
</pre>
 
=={{header|PicoLisp}}==
Rendezvous can be implemented in PicoLisp via the following function:
<langsyntaxhighlight PicoLisplang="picolisp">(de rendezvous (Pid . Exe)
(when
(catch '(NIL)
(tell Pid 'setq 'Rendezvous (lit (eval Exe)))
NIL )
(tell Pid 'quit @) ) ) # Raise caught error in caller</langsyntaxhighlight>
The caller invokes it in the callee via the
'[http://software-lab.de/doc/refT.html#tell tell]' interprocess communication,
Line 559 ⟶ 1,392:
 
Use case task:
<langsyntaxhighlight PicoLisplang="picolisp">(de printLine (Str)
(cond
((gt0 *Ink) (prinl *ID ": " Str) (dec '*Ink))
Line 614 ⟶ 1,447:
 
# Prepare to terminate all processes upon exit
(push '*Bye '(tell 'bye))</langsyntaxhighlight>
Output:
<pre>1: Old Mother Goose
Line 627 ⟶ 1,460:
2: And mounting its back,
Humpty Dumpty: Out of Ink!</pre>
 
=={{header|Python}}==
{{works with|Python|3.7}}
<syntaxhighlight lang="python">"""An approximation of the rendezvous pattern found in Ada using asyncio."""
from __future__ import annotations
 
import asyncio
import sys
 
from typing import Optional
from typing import TextIO
 
 
class OutOfInkError(Exception):
"""Exception raised when a printer is out of ink."""
 
 
class Printer:
def __init__(self, name: str, backup: Optional[Printer]):
self.name = name
self.backup = backup
 
self.ink_level: int = 5
self.output_stream: TextIO = sys.stdout
 
async def print(self, msg):
if self.ink_level <= 0:
if self.backup:
await self.backup.print(msg)
else:
raise OutOfInkError(self.name)
else:
self.ink_level -= 1
self.output_stream.write(f"({self.name}): {msg}\n")
 
 
async def main():
reserve = Printer("reserve", None)
main = Printer("main", reserve)
 
humpty_lines = [
"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.",
]
 
goose_lines = [
"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.",
]
 
async def print_humpty():
for line in humpty_lines:
try:
task = asyncio.Task(main.print(line))
await task
except OutOfInkError:
print("\t Humpty Dumpty out of ink!")
break
 
async def print_goose():
for line in goose_lines:
try:
task = asyncio.Task(main.print(line))
await task
except OutOfInkError:
print("\t Mother Goose out of ink!")
break
 
await asyncio.gather(print_goose(), print_humpty())
 
 
if __name__ == "__main__":
asyncio.run(main(), debug=True)</syntaxhighlight>
 
{{out}}
<pre>
(main): Old Mother Goose,
(main): Humpty Dumpty sat on a wall.
(main): When she wanted to wander,
(main): Humpty Dumpty had a great fall.
(main): Would ride through the air,
(reserve): All the king's horses and all the king's men,
(reserve): On a very fine gander.
(reserve): Couldn't put Humpty together again.
(reserve): Jack's mother came in,
(reserve): And caught the goose soon,
Mother Goose out of ink!</pre>
 
=={{header|Racket}}==
<syntaxhighlight lang="racket">
#lang racket
 
;;; Rendezvous primitives implemented in terms of synchronous channels.
(define (send ch msg)
(define handshake (make-channel))
(channel-put ch (list msg handshake))
(channel-get handshake)
(void))
 
(define (receive ch action)
(match-define (list msg handshake) (channel-get ch))
(action msg)
(channel-put handshake 'done))
 
;;; A printer receives a line of text, then
;;; - prints it (still ink left)
;;; - sends it to the backup printer (if present)
;;; - raises exception (if no ink and no backup)
(define (printer id ink backup)
(define (on-line-received line)
(cond
[(and (= ink 0) (not backup)) (raise 'out-of-ink)]
[(= ink 0) (send backup line)]
[else (display (~a id ":"))
(for ([c line]) (display c))
(newline)]))
(define ch (make-channel))
(thread
(λ ()
(let loop ()
(receive ch on-line-received)
(set! ink (max 0 (- ink 1)))
(loop))))
ch)
 
;;; Setup two printers
 
(define reserve (printer "reserve" 5 #f))
(define main (printer "main" 5 reserve))
 
;;; Two stories
 
(define humpty
'("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."))
 
(define goose
'("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."))
 
;;; Print the stories
(for ([l humpty]) (send main l))
(for ([l goose]) (send main l))
</syntaxhighlight>
 
Output:
<syntaxhighlight lang="racket">
main:Humpty Dumpty sat on a wall.
main:Humpty Dumpty had a great fall.
main:All the king's horses and all the king's men,
main:Couldn't put Humpty together again.
main:Old Mother Goose,
reserve:When she wanted to wander,
reserve:Would ride through the air,
reserve:On a very fine gander.
reserve:Jack's mother came in,
reserve:And caught the goose soon,
uncaught exception: 'out-of-ink
</syntaxhighlight>
 
=={{header|Raku}}==
(formerly Perl 6)
 
Raku has no built-in support for rendezvous. Simulated using message passing and a lock. May be slightly bogus.
 
{{works with|Rakudo|2016.08}}
<syntaxhighlight lang="raku" line>class X::OutOfInk is Exception {
method message() { "Printer out of ink" }
}
 
class Printer {
has Str $.id;
has Int $.ink = 5;
has Lock $!lock .= new;
has ::?CLASS $.fallback;
method print ($line) {
$!lock.protect: {
if $!ink { say "$!id: $line"; $!ink-- }
elsif $!fallback { $!fallback.print: $line }
else { die X::OutOfInk.new }
}
}
}
 
my $printer =
Printer.new: id => 'main', fallback =>
Printer.new: id => 'reserve';
 
sub client ($id, @lines) {
start {
for @lines {
$printer.print: $_;
CATCH {
when X::OutOfInk { note "<$id stops for lack of ink>"; exit }
}
}
note "<$id is done>";
}
}
 
await
client('Humpty', q:to/END/.lines),
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.
END
client('Goose', q:to/END/.lines);
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.
END</syntaxhighlight>
 
{{out}}
<pre>
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.
<Humpty is done>
reserve: Jack's mother came in,
reserve: And caught the goose soon,
<Goose stops for lack of ink>
</pre>
 
=={{header|Tcl}}==
Line 632 ⟶ 1,715:
<br>
{{works with|Tcl|8.6}}
<langsyntaxhighlight lang="tcl">package require Tcl 8.6
package require Thread
 
Line 788 ⟶ 1,871:
# Wait enough time for the example to run and then finish
after 1000
thread::broadcast thread::exit</langsyntaxhighlight>
 
=={{header|Wren}}==
This uses fibers, which are always synchronous in Wren, to simulate the rendezvous mechanism.
<syntaxhighlight lang="wren">class Printer {
construct new(id, ink) {
_id = id
_ink = ink
}
ink { _ink }
ink=(v) { _ink = v }
 
print(text) {
System.write("%(_id): ")
for (c in text) System.write(c)
System.print()
_ink = _ink - 1
}
}
 
var ptrMain = Printer.new("Main ", 5)
var ptrReserve = Printer.new("Reserve", 5)
 
var hd = [
"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."
]
 
var mg = [
"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."
]
 
var task = Fn.new { |name|
var lines = (name == "Humpty Dumpty") ? hd : mg
for (line in lines) {
if (ptrMain.ink > 0) {
ptrMain.print(line)
Fiber.yield()
} else if (ptrReserve.ink > 0) {
ptrReserve.print(line)
Fiber.yield()
} else {
Fiber.abort("ERROR : Reserve printer ran out of ink in %(name) task.")
}
}
}
 
var rhymes = ["Humpty Dumpty", "Mother Goose"]
var tasks = List.filled(2, null)
for (i in 0..1) {
tasks[i] = Fiber.new(task)
tasks[i].call(rhymes[i])
}
 
while (true) {
for (i in 0..1) {
if (!tasks[i].isDone) {
var error = tasks[i].try()
if (error) {
System.print(error)
return
}
}
}
if (tasks.all { |task| task.isDone }) return
}</syntaxhighlight>
 
{{out}}
<pre>
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,
ERROR : Reserve printer ran out of ink in Mother Goose task.
</pre>
 
=={{header|zkl}}==
{{trans|D}}
It is unfortunate the critical section is so long but there are several intertwined objects that can only be changed as a unit.
<syntaxhighlight lang="zkl">class OutOfInk(Exception.IOError){
const TEXT="Out of ink";
text=TEXT; // rename IOError to OutOfInk for this first/mother class
fcn init{ IOError.init(TEXT) } // this renames instances
}
class Printer{
var id, ink;
fcn init(_id,_ink){ id,ink=vm.arglist }
fcn print(line){
if(not ink) throw(OutOfInk);
println("%s: %s".fmt(id,line));
Atomic.sleep((0.0).random(0.01)); // don't let one thread dominate
ink-=1;
}
}
class RendezvousPrinter{ // the choke point between printers and tasks
var printers=Thread.List(); // a thread safe list
fcn init(_printers){ printers.extend(vm.arglist) }
fcn print(line){ // caller waits for print job to finish
var lines=Thread.List(); // fcn local [static] variable, the print queue
lines.write(line); // thread safe, stalls when full
// lines is racy - other threads are modifing it, length is suspect here
while(True){ // this thread can print that threads job
critical{ // creates a [global] mutex, automatically unlocks on exception
if(not printers) throw(OutOfInk); // No more printers to try
if(not lines) break; // only remove jobs in this serialized section
try{
printers[0].print(lines[0]); // can throw
lines.del(0); // successful print, remove job from queue
}catch(OutOfInk){ printers.del(0) } // Switch to the next printer
}
}
}
}</syntaxhighlight>
<syntaxhighlight lang="zkl">fcn printTask(taskNm,rendezvous,lines){
try{ foreach line in (vm.arglist[2,*]){ rendezvous.print(line); } }
catch{ println(taskNm," caught ",__exception); } // and thread quits trying to print
}
fcn humptyDumptyTask(rendezvous){ // a thread
printTask("humptyDumptyTask",rendezvous,
"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."
)
}
fcn motherGooseTask(rendezvous){ // a thread
printTask("motherGooseTask",rendezvous,
"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."
)
}</syntaxhighlight>
<syntaxhighlight lang="zkl">rendezvous:=RendezvousPrinter(Printer("main",5), Printer("reserve",5));
humptyDumptyTask.launch(rendezvous);
motherGooseTask.launch(rendezvous);
 
Atomic.waitFor(fcn{ (not vm.numThreads) }); // wait for threads to finish</syntaxhighlight>
{{out}}
<pre>
main: Humpty Dumpty sat on a wall.
main: Old Mother Goose,
main: Humpty Dumpty had a great fall.
main: All the king's horses and all the king's men,
main: When she wanted to wander,
reserve: Would ride through the air,
reserve: On a very fine gander.
reserve: Jack's mother came in,
reserve: And caught the goose soon,
reserve: And mounting its back,
motherGooseTask caught OutOfInk(Out of ink)
humptyDumptyTask caught OutOfInk(Out of ink)
</pre>
 
 
{{omit from|ACL2}}
{{omit from|E}} <!-- Not in the spirit of E concurrency; would be unenlightening to implement -->
{{omit from|JavaScript}} <!-- As yet, no multitasking unless you count setTimeout or multiple pages -->
{{omit from|ML/I}}
{{omit from|Maxima}}
{{omit from|PARI/GP|No concurrency or access to devices}}
{{omit from|Retro}} <!-- no concurrency in the standard implementations -->
Line 798 ⟶ 2,053:
==See also==
* [[wp:Rendezvous|Rendezvous]] on Wikipedia.
* [http://www.facebook.com/pages/Rendezvous/5922599377 Rendezvous] electronic duo.
9,476

edits