A few months back there were some examples of preforking servers implemented in various languages showing how posix API's could be used in those languages. Some examples were:
I'm learning the Pure programming language and took a stab at doing a Pure implementation. Pure comes with a good FFI for calling C functions and the needed posix calls are provided in the 'posix' library that comes with it.
It doesn't have any socket routines however but there is a library by Mike Maul called pure-lang-extras that provides this. With pure-lang-extras installed the preforking echo server looks like this:
using ffi, system;
using posix, posix::socket;
using namespace posix, posix::socket;
let sockfd = socket AF_INET SOCK_STREAM 0;
let yes = {1};
setsockopt sockfd SOL_SOCKET SO_REUSEADDR (int_pointer yes) (sizeof sint_t) 0;
let ai = make_sockaddr AF_INET "*" 5000;
bind_socket sockfd ai;
listen sockfd 10;
fork_child f = if pid == 0 then f pid $$ exit 0 else pid when pid = fork end;
child pid = fprintf file "Child %d echo> " getpid $$
fflush file $$
fprintf file "%s\n" (fgets file) $$
fclose file $$
child pid
when
client = accept_socket sockfd ai;
file = fdopen client "r+";
end;
let children = [fork_child child|x=0..2];
do (printf "Forked: %d\n") children;
do (\n->(waitpid n NULL 0)) children;
This can be run (assuming it's in a file called 'test.pure') with:
$ pure -x test.pure
Forked: 10433
Forked: 10434
Forked: 10435
It will fork three worker processes all waiting on the same socket provided by the main process. The main process waits until all three worker processes complete or are killed. You can test it out by using telnet to port 5000:
$ telnet localhost 5000
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Child 10433 echo> test test test
test test test
Connection closed by foreign host.
If you do this a few times you'll see it cycle through the worker processes.
The example code mostly does posix calls and thin wrapper's around C functions so it doesn't show off much of the interesting parts of Pure. Pure has automatic currying (or partial evaluation) of functions by missing arguments from the function. For example, this code:
do (printf "Forked: %d\n") children;
printf
is like the C printf
function. It takes a format string followed by a tuple containing arguments for each format directive in that string. In the example above it has one of these, '%d'. The
code (printf "Forked: %d\n")
is missing the last argument expected to printf
. Thanks to partial evaluation this evaluates to a function that takes one argument. do
calls this function for each element in the children
list, passing the list element to the function resulting in the printf
call. It's equivalent to this more verbose code:
do (\n -> printf "Forked: %d\n" n) children;
Another Pure feature used is the ability to get raw pointers to matrices. The code {1}
is a one row matrix containing one element, the interger '1'. A later line gets a raw pointer to this, converting it to a array of integers if it isn't already in that format: int_pointer yes
.
To fork the child processes I use a list comprehension:
let children = [fork_child child|x=0..2];
0..2
is a short hand syntax to create a list of numbers from 0 to 2. The [...|....]
is a list comprehension. The left hand side is called for each element of the right hand side and the result accumulated in a list. For example, at the Pure REPL:
> [x*2|x=0..5];
[0,2,4,6,8,10]
For more on Pure there is quite a bit of documentation: