I've been working on a concurrency library for Factor, based on Erlang and Termite
style concurrency. So far I've got light-weight processes implemented
using Factor threads (which are in turn implemented using
continuations). Each thread has a channel for sending and receiving
messages.
I've been able to create over 100,000 processes, each sending messages
around quite efficiently. Each process takes about 900 bytes of memory.
Once I've tidied up the code a bit and decided on decent names for
things I'll put the code in the 'contrib' directory of Factor CVS.
Most of the examples from the Termite papers work fine, including
sending continuations to processes to 'update' the process with new
code and linking processes so errors are propogated.
Everything works inside a single Factor instance at the moment but the
next step I will be working on is getting things working in a
distributed manner so processes can send messages to processes running
in other Factor images.
Using the current 'in-flux' syntax, here's an example of a simple rpc server:
: rpc-server ( -- server ) #! Process RPC requests where the message data #! is a list. The first item of the list is the function #! to execute. The remainder of the list are the arguments #! to that function. [ [ [ message? [ [ car add = ] [ cdr 0 [ + ] reduce ] !result ] ] [ message? [ [ car product = ] [ cdr 1 [ * ] reduce ] !result ] ] ] recv ] spawn-server ;
The '!result' word takes a message and two quotations from the stack.
It checks what the message is for (add or product) and then runs the
second quotation on the message. The result of that is sent back to the
calling process. Calling this server is as simple as:
[ add 1 2 3 ] rpc-server !? => 6 [ product 2 4 ] rpc-server !? => 8
'!' is send, '?' receive and '!?' sends and waits for the result. As
per the termite paper. I'll probably need to change these in the final
result for Factor though as '!' is usually the comment character and
'?' is used for other purposes too.
I'm leaning towards using Factor tuples as messages and just using the
predicate dispatch mechanism inside the server message loop. The
current 'recv' word sort of does this already and it seems to make
sense to use what is already built into Factor instead. Doing pattern
matching and deconstructing lists doesn't seem to make a lot of sense
when you don't have variables to assign them to.
On top of this framework I've added 'futures', which work similar to Alice and Io futures. This looks like:
[ 42 fib ] future ...some other stuff... ?future .
'future' runs a quotation inside a process and returns a 'future' on
the stack. '?future' will return the value of that quotation if it has
completed running, or it will block the current process until it is
complete and then return the value.
Lazy evaluation works as well but I think it is less useful in Factor:
[ 42 fib ] lazy ...some stuff... ?lazy .
This spawns a process and returns a lazy future. When that lazy future
is evaluated using '?lazy' then it will run it and return the result.
But I don't see any advantage over just using a normal quotation for
this purpose at the moment.
I hope to have some code released in the next week or so. And I'll post some benchmarking and examples here when done.
1:59:38 PM
|
|