Rendezvous: Difference between revisions

12,507 bytes added ,  3 months ago
m
→‎{{header|Wren}}: Changed to Wren S/H
(A Python implementation)
m (→‎{{header|Wren}}: Changed to Wren S/H)
 
(5 intermediate revisions by 4 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 145:
 
=={{header|AutoHotkey}}==
<langsyntaxhighlight AutoHotkeylang="autohotkey">OnMessage(0x4a, "PrintMonitor")
SetTimer, print2, 400
 
Line 221:
Else
Return -1
}</langsyntaxhighlight>
 
=={{header|C}}==
Line 228:
{{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">
<lang C>
#include <stdlib.h>
#include <stdio.h>
Line 459:
return 0;
}
</syntaxhighlight>
</lang>
 
=== 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.
<langsyntaxhighlight Clang="c">#include <stdio.h>
#include <unistd.h>
#include <omp.h>
Line 534:
 
return 0;
}</langsyntaxhighlight>
 
=={{header|D}}==
<langsyntaxhighlight lang="d">import std.stdio, std.array, std.datetime, std.exception,
std.concurrency, core.thread, core.atomic;
 
Line 641:
spawn(&humptyDumptyTask, rendezvous);
spawn(&motherGooseTask, rendezvous);
}</langsyntaxhighlight>
{{out}}
<pre>main: Humpty Dumpty sat on a wall.
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).
 
<syntaxhighlight lang="erlang">
<lang Erlang>
-module( rendezvous ).
 
Line 754:
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>
</lang>
{{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.
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.
 
<langsyntaxhighlight lang="fsharp">open System
 
type PrinterCommand = Print of string
Line 857:
)).Start()
 
Console.ReadLine() |> ignore</langsyntaxhighlight>
 
Example output:
Line 873:
 
=={{header|Go}}==
<langsyntaxhighlight lang="go">package main
 
import (
Line 1,017:
}
busy.Done()
}</langsyntaxhighlight>
Output:
<pre>
Line 1,035:
=={{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.
<langsyntaxhighlight lang="julia">mutable struct Printer
inputpath::Channel{String}
errorpath::Channel{String}
Line 1,109:
 
schedulework([humptydumpty, oldmothergoose])
</langsyntaxhighlight>{{output}}<pre>
Humpty Dumpty sat on a wall.
Humpty Dumpty had a great fall.
Line 1,123:
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 1,128 ⟶ 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 1,155 ⟶ 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 1,179 ⟶ 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 1,185 ⟶ 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 1,214 ⟶ 1,282:
{System.showInfo " Mother Goose out of ink!"}
end
end</langsyntaxhighlight>
 
Example output:
Line 1,248 ⟶ 1,316:
=={{header|Phix}}==
Phix has no rendezvous mechanism, the following achieves something similar using a simple mutex.
<!--<syntaxhighlight lang="phix">(notonline)-->
<lang Phix>constant print_cs = init_cs()
<span style="color: #008080;">without</span> <span style="color: #008080;">js</span> <span style="color: #000080;font-style:italic;">-- (threads)</span>
enum NAME,INK
<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>
sequence printers = {{"main",5},
<span style="color: #008080;">enum</span> <span style="color: #000000;">NAME</span><span style="color: #0000FF;">,</span><span style="color: #000000;">INK</span>
{"reserve",5}}
<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>
procedure printer(string name, sequence s)
try
<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>
for i=1 to length(s) do
<span style="color: #008080;">try</span>
enter_cs(print_cs)
<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>
for p=1 to length(printers) do
<span style="color: #7060A8;">enter_cs</span><span style="color: #0000FF;">(</span><span style="color: #000000;">print_cs</span><span style="color: #0000FF;">)</span>
if printers[p][INK]!=0 then
<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>
printers[p][INK] -= 1
<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>
printf(1,"%s/%s: %s\n",{name,printers[p][NAME],s[i]})
<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>
exit
<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>
elsif p=length(printers) then
throw("out of<span style="color: ink#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>
end if
<span style="color: #008080;">throw</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"out of ink"</span><span style="color: #0000FF;">)</span>
end for
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
leave_cs(print_cs)
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
end for
<span style="color: #7060A8;">leave_cs</span><span style="color: #0000FF;">(</span><span style="color: #000000;">print_cs</span><span style="color: #0000FF;">)</span>
exit_thread(0)
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
catch e
<span style="color: #7060A8;">exit_thread</span><span style="color: #0000FF;">(</span><span style="color: #000000;">0</span><span style="color: #0000FF;">)</span>
printf(1,"exception(%s): %s\n",{name,e[E_USER]})
<span style="color: #008080;">catch</span> <span style="color: #000000;">e</span>
leave_cs(print_cs)
<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>
exit_thread(1)
<span style="color: #7060A8;">leave_cs</span><span style="color: #0000FF;">(</span><span style="color: #000000;">print_cs</span><span style="color: #0000FF;">)</span>
end try
<span style="color: #7060A8;">exit_thread</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span>
end procedure
<span style="color: #008080;">end</span> <span style="color: #008080;">try</span>
constant r_printer = routine_id("printer")
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
 
constant hd = {"Humpty Dumpty sat on a wall.",
<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>
"Humpty Dumpty had a great fall.",
<span style="Allcolor: the#008000;">"Humpty king'sDumpty horseshad anda allgreat thefall."</span><span king'sstyle="color: men#0000FF;">,</span>
<span style="color: #008000;">"All the king's horses and all the king's men"</span><span style="color: #0000FF;">,</span>
"Couldn't put Humpty together again."},
<span style="color: #008000;">"Couldn't put Humpty together again."</span><span style="color: #0000FF;">},</span>
mg = {"Old Mother Goose",
<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>
"When she wanted to wander,",
<span style="color: #008000;">"When she wanted to wander,"</span><span style="color: #0000FF;">,</span>
"Would ride through the air",
<span style="color: #008000;">"Would ride through the air"</span><span style="color: #0000FF;">,</span>
"On a very fine gander.",
<span style="color: #008000;">"On a very fine gander."</span><span style="color: #0000FF;">,</span>
"Jack's mother came in,",
<span style="color: #008000;">"Jack's mother came in,"</span><span style="color: #0000FF;">,</span>
"And caught the goose soon,",
<span style="color: #008000;">"And mountingcaught itsthe backgoose soon,"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"And mounting its back,"</span><span style="color: #0000FF;">,</span>
"Flew up to the moon."}
<span style="color: #008000;">"Flew up to the moon."</span><span style="color: #0000FF;">}</span>
 
sequence hThreads = {create_thread(r_printer,{"hd",hd}),
<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>
create_thread(r_printer,{"mg",mg})}
<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>
wait_thread(hThreads)</lang>
<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>
Line 1,310 ⟶ 1,380:
=={{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 1,322 ⟶ 1,392:
 
Use case task:
<langsyntaxhighlight PicoLisplang="picolisp">(de printLine (Str)
(cond
((gt0 *Ink) (prinl *ID ": " Str) (dec '*Ink))
Line 1,377 ⟶ 1,447:
 
# Prepare to terminate all processes upon exit
(push '*Bye '(tell 'bye))</langsyntaxhighlight>
Output:
<pre>1: Old Mother Goose
Line 1,393 ⟶ 1,463:
=={{header|Python}}==
{{works with|Python|3.7}}
<langsyntaxhighlight lang="python">"""An approximation of the rendezvous pattern found in Ada using asyncio."""
from __future__ import annotations
 
Line 1,470 ⟶ 1,540:
 
if __name__ == "__main__":
asyncio.run(main(), debug=True)</langsyntaxhighlight>
 
{{out}}
Line 1,487 ⟶ 1,557:
 
=={{header|Racket}}==
<syntaxhighlight lang="racket">
<lang Racket>
#lang racket
 
Line 1,549 ⟶ 1,619:
(for ([l humpty]) (send main l))
(for ([l goose]) (send main l))
</syntaxhighlight>
</lang>
 
Output:
<syntaxhighlight lang="racket">
<lang Racket>
main:Humpty Dumpty sat on a wall.
main:Humpty Dumpty had a great fall.
Line 1,564 ⟶ 1,634:
reserve:And caught the goose soon,
uncaught exception: 'out-of-ink
</syntaxhighlight>
</lang>
 
=={{header|Raku}}==
Line 1,572 ⟶ 1,642:
 
{{works with|Rakudo|2016.08}}
<syntaxhighlight lang="raku" perl6line>class X::OutOfInk is Exception {
method message() { "Printer out of ink" }
}
Line 1,623 ⟶ 1,693:
And mounting its back,
Flew up to the moon.
END</langsyntaxhighlight>
 
{{out}}
Line 1,645 ⟶ 1,715:
<br>
{{works with|Tcl|8.6}}
<langsyntaxhighlight lang="tcl">package require Tcl 8.6
package require Thread
 
Line 1,801 ⟶ 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.
<langsyntaxhighlight lang="zkl">class OutOfInk(Exception.IOError){
const TEXT="Out of ink";
text=TEXT; // rename IOError to OutOfInk for this first/mother class
Line 1,839 ⟶ 1,999:
}
}
}</langsyntaxhighlight>
<langsyntaxhighlight 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
Line 1,859 ⟶ 2,019:
"And mounting its back,", "Flew up to the moon."
)
}</langsyntaxhighlight>
<langsyntaxhighlight 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</langsyntaxhighlight>
{{out}}
<pre>
9,476

edits