Erlang
for Concurrent Programming
The code here implements a simple client-server protocol. In a call, the client process sends a request message
to the server process and blocks waiting for a response
message. Here, the get_next/1 call request message is a
two-element tuple: the client’s own pid followed by the
atom get_next. The client sends its own pid to let the
cesses interact only by sending messages
to each other. The self/0 primitive
returns the pid of the caller. A pid is used
to address messages to a process. Here
the pid is also the data abstraction—a
sequence is just the pid of a server process that understands our sequence-spe-cific messaging protocol.
The new process starts executing the
function specified in spawn/1 and will
terminate when that function returns.
Long-lived processes therefore avoid
premature returns, often by executing
a loop function. Tail-call optimization
ensures that the stack does not grow in
functions such as sequence_loop/1. The
state of the sequence process is carried in
the argument to this eternal call.
Messages are sent with the syntax
pid message. A message can be any
Erlang value, and it is sent atomically
and immutably. The message is placed in
the receiving process’s mailbox, and the
sender continues to execute—it does not
wait for the receiving process to retrieve
the message.
A process uses the receive expression
to extract messages from its mailbox.
It specifies a set of patterns and associated handler code and scans the mailbox looking for the first message that
matches any of the patterns, blocking
if no such message is found. This is the
only blocking primitive in Erlang. Like
the patterns in function clauses, the patterns in receive options match structures
and bind new variables. If a pattern uses
a variable that has already been bound
to a value, then matching the pattern
requires a match with that value, as in
the value for Sequence in the receive
expression in get_next/1.
server.erl
-module(server).
-export([start/1, loop/2, call/2, cast/2]).
2
Client-server messaging framework.
%
The callback module implements the following callbacks:
% init() -> InitialState
handle_call(Params, State) -> {Reply, NewState}
% handle_cast(Params, State) -> NewState
Return the pid of a new server with the given callback module.
start(Module) ->
spawn(fun() -> loop(Module, Module:init()) end).
loop(Module, State) ->
receive
{call, {Client, Id}, Params} ->
{Reply, NewState} = Module:handle_call(Params, State),
Client {Id, Reply},
loop(Module, NewState);
{cast, Params} ->
NewState = Module:handle_cast(Params, State),
loop(Module, NewState)
end.
Client-side function to call the server and return its reply.
call(Server, Params) ->
Id = make_ref(),
Server {call, {self(), Id}, Params},
receive
{Id, Reply} -> Reply
end.
Like call, but no reply is returned.
cast(Server, Params) ->