Rendezvous: Difference between revisions
Content added Content deleted
m (→{{header|Phix}}: syntax coloured, marked p2js incompatible) |
Thundergnat (talk | contribs) m (syntax highlighting fixup automation) |
||
Line 33: | Line 33: | ||
=={{header|Ada}}== |
=={{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: |
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: |
||
< |
<syntaxhighlight lang="ada">select |
||
Server.Wake_Up (Parameters); |
Server.Wake_Up (Parameters); |
||
or delay 5.0; |
or delay 5.0; |
||
-- No response, try something else |
-- No response, try something else |
||
... |
... |
||
end select;</ |
end select;</syntaxhighlight> |
||
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: |
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: |
||
< |
<syntaxhighlight lang="ada">select |
||
accept Wake_Up (Parameters : Work_Item) do |
accept Wake_Up (Parameters : Work_Item) do |
||
Current_Work_Item := Parameters; |
Current_Work_Item := Parameters; |
||
Line 47: | Line 47: | ||
or accept Shut_Down; |
or accept Shut_Down; |
||
exit; -- Shut down requested |
exit; -- Shut down requested |
||
end select;</ |
end select;</syntaxhighlight> |
||
Entry points in the selective accept can be guarded by Boolean expressions which close the entry point when the expression yield false. |
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. |
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=== |
===The task=== |
||
< |
<syntaxhighlight lang="ada">with Ada.Text_IO; use Ada.Text_IO; |
||
procedure Rendezvous is |
procedure Rendezvous is |
||
Line 128: | Line 128: | ||
begin |
begin |
||
null; |
null; |
||
end Rendezvous;</ |
end Rendezvous;</syntaxhighlight> |
||
Sample output: |
Sample output: |
||
<pre> |
<pre> |
||
Line 145: | Line 145: | ||
=={{header|AutoHotkey}}== |
=={{header|AutoHotkey}}== |
||
< |
<syntaxhighlight lang="autohotkey">OnMessage(0x4a, "PrintMonitor") |
||
SetTimer, print2, 400 |
SetTimer, print2, 400 |
||
Line 221: | Line 221: | ||
Else |
Else |
||
Return -1 |
Return -1 |
||
}</ |
}</syntaxhighlight> |
||
=={{header|C}}== |
=={{header|C}}== |
||
Line 228: | Line 228: | ||
{{libheader|pthread}} |
{{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()''). |
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"> |
|||
<lang C> |
|||
#include <stdlib.h> |
#include <stdlib.h> |
||
#include <stdio.h> |
#include <stdio.h> |
||
Line 459: | Line 459: | ||
return 0; |
return 0; |
||
} |
} |
||
</syntaxhighlight> |
|||
</lang> |
|||
=== OpenMP implementation === |
=== 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. |
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 <unistd.h> |
||
#include <omp.h> |
#include <omp.h> |
||
Line 534: | Line 534: | ||
return 0; |
return 0; |
||
}</ |
}</syntaxhighlight> |
||
=={{header|D}}== |
=={{header|D}}== |
||
< |
<syntaxhighlight lang="d">import std.stdio, std.array, std.datetime, std.exception, |
||
std.concurrency, core.thread, core.atomic; |
std.concurrency, core.thread, core.atomic; |
||
Line 641: | Line 641: | ||
spawn(&humptyDumptyTask, rendezvous); |
spawn(&humptyDumptyTask, rendezvous); |
||
spawn(&motherGooseTask, rendezvous); |
spawn(&motherGooseTask, rendezvous); |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>main: Humpty Dumpty sat on a wall. |
<pre>main: Humpty Dumpty sat on a wall. |
||
Line 667: | Line 667: | ||
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). |
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"> |
|||
<lang Erlang> |
|||
-module( rendezvous ). |
-module( rendezvous ). |
||
Line 754: | Line 754: | ||
printer_monitor_reserve( ok, _Reserve_pid, _Line, Pid ) -> Pid ! {printer, ok}; |
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}. |
printer_monitor_reserve( out_of_ink, Reserve_pid, Line, Pid ) -> Reserve_pid ! {print, Line, Pid}. |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
{{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. |
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. |
||
Line 782: | Line 782: | ||
It is possible to extract the boilerplate code into a reusable helper class which should be considered when using active objects a lot. |
It is possible to extract the boilerplate code into a reusable helper class which should be considered when using active objects a lot. |
||
< |
<syntaxhighlight lang="fsharp">open System |
||
type PrinterCommand = Print of string |
type PrinterCommand = Print of string |
||
Line 857: | Line 857: | ||
)).Start() |
)).Start() |
||
Console.ReadLine() |> ignore</ |
Console.ReadLine() |> ignore</syntaxhighlight> |
||
Example output: |
Example output: |
||
Line 873: | Line 873: | ||
=={{header|Go}}== |
=={{header|Go}}== |
||
< |
<syntaxhighlight lang="go">package main |
||
import ( |
import ( |
||
Line 1,017: | Line 1,017: | ||
} |
} |
||
busy.Done() |
busy.Done() |
||
}</ |
}</syntaxhighlight> |
||
Output: |
Output: |
||
<pre> |
<pre> |
||
Line 1,035: | Line 1,035: | ||
=={{header|Julia}}== |
=={{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. |
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} |
inputpath::Channel{String} |
||
errorpath::Channel{String} |
errorpath::Channel{String} |
||
Line 1,109: | Line 1,109: | ||
schedulework([humptydumpty, oldmothergoose]) |
schedulework([humptydumpty, oldmothergoose]) |
||
</ |
</syntaxhighlight>{{output}}<pre> |
||
Humpty Dumpty sat on a wall. |
Humpty Dumpty sat on a wall. |
||
Humpty Dumpty had a great fall. |
Humpty Dumpty had a great fall. |
||
Line 1,126: | Line 1,126: | ||
=={{header|Nim}}== |
=={{header|Nim}}== |
||
{{trans|Python}} |
{{trans|Python}} |
||
< |
<syntaxhighlight lang="nim">import asyncdispatch, options, strutils |
||
type |
type |
||
Printer = ref object |
Printer = ref object |
||
Line 1,178: | Line 1,178: | ||
await mainPrinter.print(gooseLines) and mainPrinter.print(humptyLines) |
await mainPrinter.print(gooseLines) and mainPrinter.print(humptyLines) |
||
waitFor main()</ |
waitFor main()</syntaxhighlight> |
||
{{output}} |
{{output}} |
||
<pre>1:Old Mother Goose, |
<pre>1:Old Mother Goose, |
||
Line 1,196: | Line 1,196: | ||
First a simple printer class whose definition is completely orthogonal to multithreading issues: |
First a simple printer class whose definition is completely orthogonal to multithreading issues: |
||
< |
<syntaxhighlight lang="oz">declare |
||
class Printer |
class Printer |
||
attr ink:5 |
attr ink:5 |
||
Line 1,223: | Line 1,223: | ||
end |
end |
||
end |
end |
||
end</ |
end</syntaxhighlight> |
||
Note how requeuing the task simply becomes delegation to a different object. |
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): |
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): |
||
< |
<syntaxhighlight lang="oz"> fun {NewActiveSync Class Init} |
||
Obj = {New Class Init} |
Obj = {New Class Init} |
||
MsgPort |
MsgPort |
||
Line 1,247: | Line 1,247: | ||
{Wait Sync} |
{Wait Sync} |
||
end |
end |
||
end</ |
end</syntaxhighlight> |
||
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]. |
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 1,253: | Line 1,253: | ||
With this new abstraction we can create the two printers and execute both print tasks in their own thread: |
With this new abstraction we can create the two printers and execute both print tasks in their own thread: |
||
< |
<syntaxhighlight lang="oz"> Main = {NewActiveSync Printer init(id:1 backup:Reserve)} |
||
Reserve = {NewActiveSync Printer init(id:2)} |
Reserve = {NewActiveSync Printer init(id:2)} |
||
in |
in |
||
Line 1,282: | Line 1,282: | ||
{System.showInfo " Mother Goose out of ink!"} |
{System.showInfo " Mother Goose out of ink!"} |
||
end |
end |
||
end</ |
end</syntaxhighlight> |
||
Example output: |
Example output: |
||
Line 1,316: | Line 1,316: | ||
=={{header|Phix}}== |
=={{header|Phix}}== |
||
Phix has no rendezvous mechanism, the following achieves something similar using a simple mutex. |
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;">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;">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> |
||
Line 1,362: | Line 1,362: | ||
<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;">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> |
<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}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 1,380: | Line 1,380: | ||
=={{header|PicoLisp}}== |
=={{header|PicoLisp}}== |
||
Rendezvous can be implemented in PicoLisp via the following function: |
Rendezvous can be implemented in PicoLisp via the following function: |
||
< |
<syntaxhighlight lang="picolisp">(de rendezvous (Pid . Exe) |
||
(when |
(when |
||
(catch '(NIL) |
(catch '(NIL) |
||
(tell Pid 'setq 'Rendezvous (lit (eval Exe))) |
(tell Pid 'setq 'Rendezvous (lit (eval Exe))) |
||
NIL ) |
NIL ) |
||
(tell Pid 'quit @) ) ) # Raise caught error in caller</ |
(tell Pid 'quit @) ) ) # Raise caught error in caller</syntaxhighlight> |
||
The caller invokes it in the callee via the |
The caller invokes it in the callee via the |
||
'[http://software-lab.de/doc/refT.html#tell tell]' interprocess communication, |
'[http://software-lab.de/doc/refT.html#tell tell]' interprocess communication, |
||
Line 1,392: | Line 1,392: | ||
Use case task: |
Use case task: |
||
< |
<syntaxhighlight lang="picolisp">(de printLine (Str) |
||
(cond |
(cond |
||
((gt0 *Ink) (prinl *ID ": " Str) (dec '*Ink)) |
((gt0 *Ink) (prinl *ID ": " Str) (dec '*Ink)) |
||
Line 1,447: | Line 1,447: | ||
# Prepare to terminate all processes upon exit |
# Prepare to terminate all processes upon exit |
||
(push '*Bye '(tell 'bye))</ |
(push '*Bye '(tell 'bye))</syntaxhighlight> |
||
Output: |
Output: |
||
<pre>1: Old Mother Goose |
<pre>1: Old Mother Goose |
||
Line 1,463: | Line 1,463: | ||
=={{header|Python}}== |
=={{header|Python}}== |
||
{{works with|Python|3.7}} |
{{works with|Python|3.7}} |
||
< |
<syntaxhighlight lang="python">"""An approximation of the rendezvous pattern found in Ada using asyncio.""" |
||
from __future__ import annotations |
from __future__ import annotations |
||
Line 1,540: | Line 1,540: | ||
if __name__ == "__main__": |
if __name__ == "__main__": |
||
asyncio.run(main(), debug=True)</ |
asyncio.run(main(), debug=True)</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 1,557: | Line 1,557: | ||
=={{header|Racket}}== |
=={{header|Racket}}== |
||
<syntaxhighlight lang="racket"> |
|||
<lang Racket> |
|||
#lang racket |
#lang racket |
||
Line 1,619: | Line 1,619: | ||
(for ([l humpty]) (send main l)) |
(for ([l humpty]) (send main l)) |
||
(for ([l goose]) (send main l)) |
(for ([l goose]) (send main l)) |
||
</syntaxhighlight> |
|||
</lang> |
|||
Output: |
Output: |
||
<syntaxhighlight lang="racket"> |
|||
<lang Racket> |
|||
main:Humpty Dumpty sat on a wall. |
main:Humpty Dumpty sat on a wall. |
||
main:Humpty Dumpty had a great fall. |
main:Humpty Dumpty had a great fall. |
||
Line 1,634: | Line 1,634: | ||
reserve:And caught the goose soon, |
reserve:And caught the goose soon, |
||
uncaught exception: 'out-of-ink |
uncaught exception: 'out-of-ink |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Raku}}== |
=={{header|Raku}}== |
||
Line 1,642: | Line 1,642: | ||
{{works with|Rakudo|2016.08}} |
{{works with|Rakudo|2016.08}} |
||
<lang |
<syntaxhighlight lang="raku" line>class X::OutOfInk is Exception { |
||
method message() { "Printer out of ink" } |
method message() { "Printer out of ink" } |
||
} |
} |
||
Line 1,693: | Line 1,693: | ||
And mounting its back, |
And mounting its back, |
||
Flew up to the moon. |
Flew up to the moon. |
||
END</ |
END</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 1,715: | Line 1,715: | ||
<br> |
<br> |
||
{{works with|Tcl|8.6}} |
{{works with|Tcl|8.6}} |
||
< |
<syntaxhighlight lang="tcl">package require Tcl 8.6 |
||
package require Thread |
package require Thread |
||
Line 1,871: | Line 1,871: | ||
# Wait enough time for the example to run and then finish |
# Wait enough time for the example to run and then finish |
||
after 1000 |
after 1000 |
||
thread::broadcast thread::exit</ |
thread::broadcast thread::exit</syntaxhighlight> |
||
=={{header|Wren}}== |
=={{header|Wren}}== |
||
This uses fibers, which are always synchronous in Wren, to simulate the rendezvous mechanism. |
This uses fibers, which are always synchronous in Wren, to simulate the rendezvous mechanism. |
||
< |
<syntaxhighlight lang="ecmascript">class Printer { |
||
construct new(id, ink) { |
construct new(id, ink) { |
||
_id = id |
_id = id |
||
Line 1,946: | Line 1,946: | ||
} |
} |
||
if (tasks.all { |task| task.isDone }) return |
if (tasks.all { |task| task.isDone }) return |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 1,966: | Line 1,966: | ||
{{trans|D}} |
{{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. |
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"; |
const TEXT="Out of ink"; |
||
text=TEXT; // rename IOError to OutOfInk for this first/mother class |
text=TEXT; // rename IOError to OutOfInk for this first/mother class |
||
Line 1,999: | Line 1,999: | ||
} |
} |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
< |
<syntaxhighlight lang="zkl">fcn printTask(taskNm,rendezvous,lines){ |
||
try{ foreach line in (vm.arglist[2,*]){ rendezvous.print(line); } } |
try{ foreach line in (vm.arglist[2,*]){ rendezvous.print(line); } } |
||
catch{ println(taskNm," caught ",__exception); } // and thread quits trying to print |
catch{ println(taskNm," caught ",__exception); } // and thread quits trying to print |
||
Line 2,019: | Line 2,019: | ||
"And mounting its back,", "Flew up to the moon." |
"And mounting its back,", "Flew up to the moon." |
||
) |
) |
||
}</ |
}</syntaxhighlight> |
||
< |
<syntaxhighlight lang="zkl">rendezvous:=RendezvousPrinter(Printer("main",5), Printer("reserve",5)); |
||
humptyDumptyTask.launch(rendezvous); |
humptyDumptyTask.launch(rendezvous); |
||
motherGooseTask.launch(rendezvous); |
motherGooseTask.launch(rendezvous); |
||
Atomic.waitFor(fcn{ (not vm.numThreads) }); // wait for threads to finish</ |
Atomic.waitFor(fcn{ (not vm.numThreads) }); // wait for threads to finish</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |