202 lines
8.2 KiB
HTML
202 lines
8.2 KiB
HTML
|
<html>
|
||
|
<head>
|
||
|
<title>Factor Concurrency Library</title>
|
||
|
<link rel="stylesheet" type="text/css" href="style.css">
|
||
|
</head>
|
||
|
<body>
|
||
|
<h1>Factor Concurrency Library</h1>
|
||
|
<p class="note">The concurrency library here is based upon the style
|
||
|
of concurrency used in systems like Erlang and Termite. It is
|
||
|
currently at a very early stage and only supports concurrent
|
||
|
processes within a single Factor image. The interface is very likely to
|
||
|
change so it is quite experimental at this stage. The ability to
|
||
|
have distributed processes is planned.</p>
|
||
|
<h1>Overview</h1>
|
||
|
<p>A concurrency oriented program is one in which multiple processes
|
||
|
run simultaneously in a single Factor image. The processes can
|
||
|
communicate with each other by asynchronous message sends. Although
|
||
|
processes can share data via Factor's mutable data structures it is
|
||
|
not recommended as the use of shared state concurrency is often a
|
||
|
cause of problems.</p>
|
||
|
<h1>Processes</h1>
|
||
|
<p>A process is basically a thread with a message queue. Other
|
||
|
processes can place items on this queue by sending the process a
|
||
|
message. A process can check its queue for messages, blocking if none
|
||
|
are pending, and process them as they are queued.</p>
|
||
|
<p>Factor processes are very lightweight. Each process can take as
|
||
|
little as 900 bytes of memory. This library has been tested running
|
||
|
hundreds of thousands of simple processes.</p>
|
||
|
<p>The messages that are sent from process to process are any Factor
|
||
|
value. Factor tuples are ideal for this sort of thing as you can send
|
||
|
a tuple to a process and the predicate dispatch mechanism can be used
|
||
|
to perform actions depending on what the type of the tuple is.</p>
|
||
|
<p>Processes are usually created using the spawn' word:</p>
|
||
|
<pre class="code">
|
||
|
IN: concurrency
|
||
|
spawn ( quot -- process )
|
||
|
</pre>
|
||
|
<p>This word takes a quotation on the stack and starts a process that
|
||
|
will execute that quotation asynchronously. When the quotation
|
||
|
completes the process will die. 'spawn' leaves on the stack the
|
||
|
process object that was started. This object can be used to send
|
||
|
messages to the process using the 'send' word:</p>
|
||
|
<pre class="code">
|
||
|
IN: concurrency
|
||
|
send ( message process -- )
|
||
|
</pre>
|
||
|
<p>'send' will return immediately after placing the message in the
|
||
|
target processes message queue. A process can get an message from its
|
||
|
queue using the 'receive' word:</p>
|
||
|
<pre class="code">
|
||
|
IN: concurrency
|
||
|
: receive ( -- message )
|
||
|
</pre>
|
||
|
<p>This will get the most recent message
|
||
|
and leave it on the stack. If there are no messages in the queue the
|
||
|
process will 'block' until a message is available. When a process is
|
||
|
blocked it takes no CPU time at all.</p>
|
||
|
<pre class="code">
|
||
|
[ receive print ] spawn
|
||
|
"Hello Process!" swap send
|
||
|
</pre>
|
||
|
<p>This example spawns a process that first blocks, waiting to receive
|
||
|
a message. When a message is received, the 'receive' call returns
|
||
|
leaving it on the stack. It then prints the message and exits. 'spawn'
|
||
|
left the process on the stack its available to send the 'Hello
|
||
|
Process!' message to it. Immediately after the 'send' you should see
|
||
|
'Hello Process!' printer on the console.</p>
|
||
|
<h2>Self</h2>
|
||
|
<p>A process can get access to its own process object using the 'self'
|
||
|
variable so it can pass
|
||
|
it to other processes. This allows the other processes to send
|
||
|
messages back. A simple example of using this gets the current
|
||
|
processes 'self' and spawns a process which sends a message to it. We
|
||
|
then receive the message from the original process</p>
|
||
|
<pre class="code">
|
||
|
self get
|
||
|
.s
|
||
|
=> << process ... >>
|
||
|
[ "Hello!" swap send ] cons spawn drop
|
||
|
receive .
|
||
|
=> "Hello"
|
||
|
</pre>
|
||
|
<h1>Servers</h1>
|
||
|
<p>A common idiom is to create 'server' processes that act on messages
|
||
|
that are sent to it. These follow a basic pattern of blocking until a
|
||
|
message is received, processing that message then looping back to
|
||
|
blocking for a message.</p>
|
||
|
<p>The 'spawn-server' word does exactly that:</p>
|
||
|
<pre class="code">
|
||
|
IN: concurrency
|
||
|
spawn-server ( quot -- process )
|
||
|
</pre>
|
||
|
<p>A process is spawned in the same manner as 'spawn', but instead of
|
||
|
the process existing then the quotation completes, the quotation is
|
||
|
re-called. A process spawned using this method can break out of the
|
||
|
infinite loop and exit the process using the 'exit-server' call:</p>
|
||
|
<pre class="code">
|
||
|
IN: concurrency
|
||
|
: exit-server ( -- )
|
||
|
</pre>
|
||
|
<p>The following example shows a very simple server that expects a
|
||
|
cons cell as its message. The 'car' of the list should be the senders
|
||
|
process object. If the 'cdr' is 'ping' then the server sends 'pong'
|
||
|
back to the caller. If the 'cdr' is anything else then the server
|
||
|
exits:</p>
|
||
|
<pre class="code">
|
||
|
: pong-server1 ( -- process)
|
||
|
[
|
||
|
receive uncons "ping" = [
|
||
|
"pong" swap send
|
||
|
] [
|
||
|
"Pong server shutdown commenced" swap send
|
||
|
exit-server
|
||
|
] ifte
|
||
|
] spawn-server ;
|
||
|
|
||
|
pong-server1
|
||
|
self get "ping" cons over send receive .
|
||
|
=> "pong"
|
||
|
self get "ping" cons over send receive .
|
||
|
=> "pong"
|
||
|
self get "shutdown" cons over send receive .
|
||
|
=> "Pong server shutdown commenced"
|
||
|
Exiting process: G:12361
|
||
|
</pre>
|
||
|
<p>The idiom of sending the callers process object along with the
|
||
|
message is so common that some standard routines are built into the
|
||
|
concurrency library to handle this. A tuple called 'message' is used
|
||
|
as the standard message sent to processes that wish to acknowledge
|
||
|
receipt of the messaeg with a reply back to the caller:</p>
|
||
|
<pre class="code">
|
||
|
IN: concurrency
|
||
|
TUPLE: message data from tag ;
|
||
|
</pre>
|
||
|
<p>The 'data' contains the actual message data to be sent to the
|
||
|
server. 'from' is the process object of the caller. 'tag' is an
|
||
|
automatically generated unique value that the receving server will
|
||
|
send back along with the reply so the caller can match it up with the
|
||
|
original request. A 'send-reply' word is available that has the
|
||
|
following signature:</p>
|
||
|
<pre class="code">
|
||
|
IN: concurrency
|
||
|
send-reply ( message pred quot -- )
|
||
|
</pre>
|
||
|
<p>The 'message' is a message tuple. 'pred' is a quotation with the
|
||
|
signature ( data -- boolean ). It will be called with the message-data
|
||
|
portion of the message. If it returns false, all three arguments are
|
||
|
popped off the stack and nothing is done.</p>
|
||
|
<p>If the predicate returns true, then the quotation is called with
|
||
|
the message data on the stack again. This quotation has the signature
|
||
|
( data -- result ). The result of the quotation will be sent back to
|
||
|
the callinging process in a message tuple, with the same tag as the
|
||
|
original message and the message data will be the result.</p>
|
||
|
<p>To make it easier to send a message tuple without having to
|
||
|
generate a tag, get the 'self' process, etc, the 'send-message' word
|
||
|
is available:</p>
|
||
|
<pre class="code">
|
||
|
IN: concurrency
|
||
|
send-message ( data process -- reply )
|
||
|
</pre>
|
||
|
<p>Given the message data it will construct a message tuple with a
|
||
|
randomly generated tag and send it to the given process. It will then
|
||
|
wait for a reply containing that specific tag and take the message
|
||
|
data from it, leaving it on the stack.</p>
|
||
|
<p>Using these words our pong server example becomes:</p>
|
||
|
<pre class="code">
|
||
|
: pong-server2 ( -- process)
|
||
|
[
|
||
|
receive
|
||
|
dup [ "ping" = ] [ drop "pong" ] send-reply
|
||
|
dup [ "shutdown" = ] [ drop "Pong server shutdown commenced" ] send-reply
|
||
|
message-data "shutdown" = [ exit-server ] when
|
||
|
] spawn-server ;
|
||
|
|
||
|
pong-server2
|
||
|
"ping" over send-message .
|
||
|
=> "pong"
|
||
|
"ping" over send-message .
|
||
|
=> "pong"
|
||
|
"shutdown" over send-message .
|
||
|
=> "Pong server shutdown commenced"
|
||
|
Exiting process: G:12364
|
||
|
</pre>
|
||
|
<p class="note">'send-reply' is not really a good name, and it isn't
|
||
|
that useful an interface. This is currently being worked on.</p>
|
||
|
<h2>Linked processes</h2>
|
||
|
<p>Write about how processes can be linked using 'spawn-link'. An
|
||
|
error thrown in the quotation will cause the process to die, and the
|
||
|
process that called 'spawn-link' to receive the exception when it next
|
||
|
attempts to receive from its message queue.</p>
|
||
|
<h2>Futures</h2>
|
||
|
<p>Write about futures. Calling 'future' spawns a process to run a
|
||
|
quotation that returns a result. Using '?future' with that process on
|
||
|
the stack will block the calling process until the result is
|
||
|
returned.</p>
|
||
|
|
||
|
<p class="footer">
|
||
|
News and updates to this software can be obtained from the authors
|
||
|
weblog: <a href="http://radio.weblogs.com/0102385">Chris Double</a>.</p>
|
||
|
<p id="copyright">Copyright (c) 2004, Chris Double. All Rights Reserved.</p>
|
||
|
</body> </html>
|