Erlang (programming language)/Tutorials/Processes
| The metadata subpage is missing. You can start it via filling in this form or by following the instructions that come up after clicking on the [show] link to the right. | |||
|---|---|---|---|
|
Erlang Processes and Messages
Processes are easy to create and control in erlang. The program chain_hello.erl builds a chain of processes as long as you like. Each process creates one process then sends a message to it. The program creates a chain of N processes which each print out hello world! N. Processes send messages and receive messages from one another. Messages are read with pattern matching. The messages are matched in a fifo(first in, first out) way.
Note 1: The order of the final output depends on process scheduling.
Note 2: Time flows downward(in each vertical line for each process, see note 1).
This is a minimal UML sequence diagram showing the processes and messages for the execution of:
chain_hello:start(1).
UML sequence notation: Processes start in boxes. Dotted lines are life lines. Processes time lines end in X's.
Note: Some of the details of have been left out for tutorial purposes.
The diagram has English mixed with code.
Local notation: Command line output is in quotes. Messages are in curly braces.
+——————————+
| start(1) |
+——————————+
¦ +———————————+
spawns ———————> | listen(1) |
¦ +———————————+
¦ ¦ +———————————+
¦ spawns ————————————————> | listen(0) |
¦ ¦ +———————————+
¦ ¦ ¦
sends —> {speak} —> prints —> "hello world 1" ¦
¦ ¦ ¦
¦ sends ——> {speak} ———————> prints ———> "hello world 0"
¦ ¦ ¦
X X X
Program listing for: chain_hello.erl
-module(chain_hello).
-compile(export_all).
%
start(N)-> % startup
Pid1 = spawn(chain_hello, listen, [N]),
Pid1 ! speak,
io:format("done \n").
%
listen(0)-> % base case
receive
speak ->
io:format("hello world!~w\n", [0])
end;
listen(N)-> % recursive case
Pid2= spawn(chain_hello, listen, [N-1]),
Pid2! speak,
receive
speak->
io:format("hello world!~w\n", [N])
end.
% ---- sample output for chain_hello:start(1) --- % % % 14> chain_hello:start(1). % done % okhello world!1 % hello world!0 % % ---- sample output for chain_hello:start(4) --- % % % 14> chain_hello:start(4). % done % hello world!4 % hello world!3 % hello world!2 % okhello world!1 % hello world!0
Intermediate message passing
Now, suppose we have some commands in the form of a message. We wish to send this command message to a large set of processes. We might like answers from those processes that are able to respond. If some crash because of the message then they should be restarted.
% ===========================================================================
-module(freeze_clone). -compile(export_all).
% The purpose of freeze_clone is to non-destructively test a process with a
% message. Each process is a state machine.
% If the process receives any unknown message then it should self destruct,
% (Because we are following the rule: to fail early and often).
% Each msg_testable process requires the following message handlers:
% {freeze, clone, get_value, set_value, exit}.
%
% Steps to test a message on a process:
% 0) setup: spawn a process to listen to messages
% 1) freeze process in question so it is safe to clone
% 2) we clone the process
% 3) send the clone a test message
% 4) unfreeze the clone
% 5) see if clone lived after the message,
% if the clone is happy/alive then the message should be safe
% 6) kill clone if clone is alive
% 7) send message to original or
% pick another safer message if msg failed on clone
% 8) unfreeze original
% 9) request resulting value from original
%
% test(Pid, msg) loopy()
% | |
% | |
% o - freeze ---> o
% | |
% o - clone ----> o == spawn ==> loopy() clone
% | | |
% o - msg -------------------------------------> o
% | | |
% o - get_value -------------------------------> o
% | | |
% o <--- value --------------------------------- o
% | | |
% o ------------------ [if clone is alive] halt --> x
% | |
% o - unfreeze --> o
% | |
% \|/
% return msg is
% safe or unsafe:
% {true or false}
% --------------------------------------------------------------------
% Sample output:
% [{msg, keep_on_trucking, is_safe, false},
% {msg, bump, is_safe, true}]
% --------------------------------------------------------------------
start() ->
Pid = spawn(freeze_clone, loopy, [{1, false}] ), % process loopy
Msg_1 = keep_on_trucking, % message to test on process loopy
Safe_1 = test(Pid, Msg_1),
% ------------------------
Msg_2 = bump,
Safe_2 = test(Pid, Msg_2),
io:format("\n"),
[
{msg, Msg_1, is_safe, Safe_1},
{msg, Msg_2, is_safe, Safe_2}
].
test(Pid, Msg) ->
Pid ! freeze,
ClonePid = rpc(Pid, clone),
ClonePid ! unfreeze,
Before = rpc(ClonePid, get_value),
% send test msg to clone
ClonePid ! Msg,
Result = rpc(ClonePid, get_value),
if
Result == no_one_can_answer -> Safe = false;
true -> Safe = true
end,
if
Safe == true ->
ClonePid ! exit,
Pid ! unfreeze,
Pid ! Msg,
After = rpc(Pid, get_value);
true ->
Pid ! unfreeze,
After = no_value
end,
Results = {'message_tried:', Msg,
'message_transition_is_safe:', Safe,
'state_machine_name:', loopy,
'value_before:', Before,
'value_after:', After},
io:format("\n ~w \n",[Results]),
Safe.
loopy(State) ->
{Value, Freeze} = State,
if
Freeze == false ->
receive
{From, get_value} ->
From ! Value;
{set_value, NValue} ->
loopy({NValue, Freeze});
bump ->
loopy({Value+1, Freeze});
freeze ->
loopy({Value, true});
exit ->
exit(normal);
{Error, Error_Msg} ->
io:format("error: ~w \n",[{Error, Error_Msg}]);
_Any ->
exit(normal)
end;
Freeze == true ->
receive
unfreeze ->
loopy({Value,false});
{From, clone} ->
Pid2 = spawn( freeze_clone,
loopy,
[{Value, Freeze}]),
From ! Pid2
end;
true -> ok
end,
loopy(State).
rpc(To, Msg) ->
To ! {self(), Msg},
receive
Answer -> Answer
after 1000 ->
Answer = no_one_can_answer
end,
Answer.
% ===============================================================
%6> c(freeze_clone).
%{ok,freeze_clone}
%7> freeze_clone:start().
%{'message_tried:',keep_on_trucking,'message_transition_is_safe:',
false,'state_machine_name:',loopy,'value_before:',1,'value_after:',no_value}
%{'message_tried:',bump,'message_transition_is_safe:',
true,'state_machine_name:',loopy,'value_before:',1,'value_after:',2}
%[{msg,keep_on_trucking,is_safe,false},
%{msg,bump,is_safe,true}]
