factor/contrib/concurrency/concurrency.html

202 lines
8.2 KiB
HTML
Raw Normal View History

2005-08-04 18:46:50 -04:00
<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>