Erlang (programming language)/Tutorials/Processes: Difference between revisions
imported>Eric Evers |
imported>Tom Morris m (Erlang programming language/Tutorials/Processes moved to Erlang (programming language)/Tutorials/Processes) |
||
(6 intermediate revisions by one other user not shown) | |||
Line 3: | Line 3: | ||
==Erlang Processes and Messages== | ==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 | 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: | Note 1: The order of the final output depends on process scheduling. | ||
Note 2: | Note 2: Time flows downward(in each vertical line for each process, see note 1). | ||
This is a | This is a minimal [[Unified_Modeling_Language|UML]] sequence diagram showing the processes and messages for the execution of: | ||
chain_hello:start(1). | |||
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. | |||
spawns | 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 | Program listing for: chain_hello.erl | ||
Line 47: | Line 56: | ||
end. | end. | ||
% ---- sample output ---- % | % ---- 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). | % 14> chain_hello:start(4). | ||
Line 56: | Line 72: | ||
% okhello world!1 | % okhello world!1 | ||
% hello world!0 | % 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}] |
Latest revision as of 06:07, 8 August 2009
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}]