Erlang (programming language)/Tutorials/Processes: Difference between revisions

From Citizendium
Jump to navigation Jump to search
imported>Chris Day
No edit summary
imported>Tom Morris
 
(7 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 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.
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 1: The order of the final output depends on process scheduling.


Note 2: time flows downward(in each vertical line, see note 1).
Note 2: Time flows downward(in each vertical line for each process, see note 1).






This is a Process Message Diagram for the execution of: chain_hello:start(1).  
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 -----------> listen(1)
      The diagram has English mixed with code.
     |                  |
Local notation: Command line output is in quotes. Messages are in curly braces.
     |                spawns --------------------> listen(0)
 
     |                  |                            |
+——————————+                 
     |                sends ----> speak ----------> prints --> "hello world 0"
  | start(1) |
     |                  |                            |
+——————————+
  sends --> speak --> prints --> "hello world 1"    |
     ¦              +———————————+
                        |                            |
  spawns ———————> | listen(1) |
                        ok                          ok
     ¦              +———————————+
    ¦                    ¦                      +———————————+
     ¦                  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


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}]