Sophie

Sophie

distrib > Mageia > 6 > x86_64 > media > core-updates > by-pkgid > eb1ec8bb814fe4d749258d46ae8ea988 > files > 65

erlang-doc-18.3.2-9.1.mga6.noarch.rpm

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html xmlns:fn="http://www.w3.org/2005/02/xpath-functions">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" href="../otp_doc.css" type="text/css">
<title>Erlang -- Robustness</title>
</head>
<body bgcolor="white" text="#000000" link="#0000ff" vlink="#ff00ff" alink="#ff0000"><div id="container">
<script id="js" type="text/javascript" language="JavaScript" src="../js/flipmenu/flipmenu.js"></script><script id="js2" type="text/javascript" src="../js/erlresolvelinks.js"></script><script language="JavaScript" type="text/javascript">
            <!--
              function getWinHeight() {
                var myHeight = 0;
                if( typeof( window.innerHeight ) == 'number' ) {
                  //Non-IE
                  myHeight = window.innerHeight;
                } else if( document.documentElement && ( document.documentElement.clientWidth ||
                                                         document.documentElement.clientHeight ) ) {
                  //IE 6+ in 'standards compliant mode'
                  myHeight = document.documentElement.clientHeight;
                } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
                  //IE 4 compatible
                  myHeight = document.body.clientHeight;
                }
                return myHeight;
              }

              function setscrollpos() {
                var objf=document.getElementById('loadscrollpos');
                 document.getElementById("leftnav").scrollTop = objf.offsetTop - getWinHeight()/2;
              }

              function addEvent(obj, evType, fn){
                if (obj.addEventListener){
                obj.addEventListener(evType, fn, true);
                return true;
              } else if (obj.attachEvent){
                var r = obj.attachEvent("on"+evType, fn);
                return r;
              } else {
                return false;
              }
             }

             addEvent(window, 'load', setscrollpos);

             //--></script><div id="leftnav"><div class="innertube">
<img alt="Erlang logo" src="../erlang-logo.png"><br><small><a href="users_guide.html">User's Guide</a><br><a href="../pdf/otp-system-documentation-7.3.1.pdf">PDF</a><br><a href="../index.html">Top</a></small><p><strong>Getting Started with Erlang</strong><br><strong>User's Guide</strong><br><small>Version 7.3.1</small></p>
<br><a href="javascript:openAllFlips()">Expand All</a><br><a href="javascript:closeAllFlips()">Contract All</a><p><small><strong>Chapters</strong></small></p>
<ul class="flipMenu" imagepath="../js/flipmenu">
<li id="no" title="Introduction" expanded="false">Introduction<ul>
<li><a href="intro.html">
              Top of chapter
            </a></li>
<li title="Prerequisites"><a href="intro.html#idp281230204">Prerequisites</a></li>
<li title="Omitted Topics"><a href="intro.html#idp281296740">Omitted Topics</a></li>
</ul>
</li>
<li id="no" title="Sequential Programming" expanded="false">Sequential Programming<ul>
<li><a href="seq_prog.html">
              Top of chapter
            </a></li>
<li title="The Erlang Shell"><a href="seq_prog.html#idp280867116">The Erlang Shell</a></li>
<li title="Modules and Functions"><a href="seq_prog.html#idp281301196">Modules and Functions</a></li>
<li title="Atoms"><a href="seq_prog.html#idp280980780">Atoms</a></li>
<li title="Tuples"><a href="seq_prog.html#idp281373380">Tuples</a></li>
<li title="Lists"><a href="seq_prog.html#idp280988244">Lists</a></li>
<li title="Maps"><a href="seq_prog.html#idp281292780">Maps</a></li>
<li title="Standard Modules and Manual Pages"><a href="seq_prog.html#idp281256484">Standard Modules and Manual Pages</a></li>
<li title="Writing Output to a Terminal"><a href="seq_prog.html#idp281225364">Writing Output to a Terminal</a></li>
<li title="A Larger Example"><a href="seq_prog.html#idp280901828">A Larger Example</a></li>
<li title="Matching, Guards, and Scope of Variables"><a href="seq_prog.html#idp280991068">Matching, Guards, and Scope of Variables</a></li>
<li title="More About Lists"><a href="seq_prog.html#idp281562988">More About Lists</a></li>
<li title="If and Case"><a href="seq_prog.html#idp281581828">If and Case</a></li>
<li title="Built-In Functions (BIFs)"><a href="seq_prog.html#idp281597868">Built-In Functions (BIFs)</a></li>
<li title="Higher-Order Functions (Funs)"><a href="seq_prog.html#idp281610788">Higher-Order Functions (Funs)</a></li>
</ul>
</li>
<li id="no" title="Concurrent Programming" expanded="false">Concurrent Programming<ul>
<li><a href="conc_prog.html">
              Top of chapter
            </a></li>
<li title="Processes"><a href="conc_prog.html#idp281631836">Processes</a></li>
<li title="Message Passing"><a href="conc_prog.html#idp281641636">Message Passing</a></li>
<li title="Registered Process Names"><a href="conc_prog.html#idp281664988">Registered Process Names</a></li>
<li title="Distributed Programming"><a href="conc_prog.html#idp281671924">Distributed Programming</a></li>
<li title="A Larger Example"><a href="conc_prog.html#idp281692036">A Larger Example</a></li>
</ul>
</li>
<li id="loadscrollpos" title="Robustness" expanded="true">Robustness<ul>
<li><a href="robustness.html">
              Top of chapter
            </a></li>
<li title="Time-outs"><a href="robustness.html#idp281742052">Time-outs</a></li>
<li title="Error Handling"><a href="robustness.html#idp281750788">Error Handling</a></li>
<li title="The Larger Example with Robustness Added"><a href="robustness.html#idp281765524">The Larger Example with Robustness Added</a></li>
</ul>
</li>
<li id="no" title="Records and Macros" expanded="false">Records and Macros<ul>
<li><a href="record_macros.html">
              Top of chapter
            </a></li>
<li title="The Larger Example Divided into Several Files"><a href="record_macros.html#idp281781356">The Larger Example Divided into Several Files</a></li>
<li title="Header Files"><a href="record_macros.html#idp281794484">Header Files</a></li>
<li title="Records"><a href="record_macros.html#idp281797356">Records</a></li>
<li title="Macros"><a href="record_macros.html#idp281803804">Macros</a></li>
</ul>
</li>
</ul>
</div></div>
<div id="content">
<div class="innertube">
<h1>4 Robustness</h1>
  
  <p>Several things are wrong with the messenger example in
    <span class="bold_code"><a href="conc_prog.html#ex">A Larger Example</a></span>.
    For example, if a node where a user is logged
    on goes down without doing a logoff, the user remains in
    the server's <span class="code">User_List</span>, but the client disappears. This
    makes it impossible for the user to log on again as the server
    thinks the user already is logged on.</p>
  <p>Or what happens if the server goes down in the middle of sending a
    message, leaving the sending client hanging forever in
    the <span class="code">await_result</span> function?</p>

  <h3><a name="idp281742052">4.1 
        Time-outs</a></h3>
    
    <p>Before improving the messenger program, let us look at some
      general principles, using the ping pong program as an example.
      Recall that when "ping" finishes, it tells "pong" that it has
      done so by sending the atom <span class="code">finished</span> as a message to "pong"
      so that "pong" can also finish. Another way to let "pong"
      finish is to make "pong" exit if it does not receive a message
      from ping within a certain time. This can be done by adding a
      <strong>time-out</strong> to <span class="code">pong</span> as shown in the following example:</p>
    <div class="example"><pre>
-module(tut19).

-export([start_ping/1, start_pong/0,  ping/2, pong/0]).

ping(0, Pong_Node) -&gt;
    io:format("ping finished~n", []);

ping(N, Pong_Node) -&gt;
    {pong, Pong_Node} ! {ping, self()},
    receive
        pong -&gt;
            io:format("Ping received pong~n", [])
    end,
    ping(N - 1, Pong_Node).

pong() -&gt;
    receive
        {ping, Ping_PID} -&gt;
            io:format("Pong received ping~n", []),
            Ping_PID ! pong,
            pong()
    after 5000 -&gt;
            io:format("Pong timed out~n", [])
    end.

start_pong() -&gt;
    register(pong, spawn(tut19, pong, [])).

start_ping(Pong_Node) -&gt;
    spawn(tut19, ping, [3, Pong_Node]).</pre></div>
    <p>After this is compiled and the file <span class="code">tut19.beam</span>
      is copied to the necessary directories, the following is seen
     on (pong@kosken): </p>
    <div class="example"><pre>
(pong@kosken)1&gt; <span class="bold_code">tut19:start_pong().</span>
true
Pong received ping
Pong received ping
Pong received ping
Pong timed out</pre></div>
    <p>And the following is seen on (ping@gollum):</p>
    <div class="example"><pre>
(ping@gollum)1&gt; <span class="bold_code">tut19:start_ping(pong@kosken).</span>
&lt;0.36.0&gt;
Ping received pong
Ping received pong
Ping received pong
ping finished   </pre></div>
    <p>The time-out is set in:</p>
    <div class="example"><pre>
pong() -&gt;
    receive
        {ping, Ping_PID} -&gt;
            io:format("Pong received ping~n", []),
            Ping_PID ! pong,
            pong()
    after 5000 -&gt;
            io:format("Pong timed out~n", [])
    end.</pre></div>
    <p>The time-out (<span class="code">after 5000</span>) is started when
      <span class="code">receive</span> is entered.
      The time-out is canceled if <span class="code">{ping,Ping_PID}</span>
      is received. If <span class="code">{ping,Ping_PID}</span> is not received,
      the actions following the time-out are done after 5000
      milliseconds. <span class="code">after</span> must be last in the <span class="code">receive</span>,
      that is, preceded by all other message reception specifications in
      the <span class="code">receive</span>. It is also possible to call a function that
      returned an integer for the time-out:</p>
    <div class="example"><pre>
after pong_timeout() -&gt;</pre></div>
    <p>In general, there are better ways than using time-outs to
      supervise parts of a distributed Erlang system. Time-outs are
      usually appropriate to supervise external events, for example, if
      you have expected a message from some external system within a
      specified time. For example, a time-out can be used to log a user
      out of the messenger system if they have not accessed it for,
      say, ten minutes.</p>
  

  <h3><a name="idp281750788">4.2 
        Error Handling</a></h3>
    
    <p>Before going into details of the supervision and error handling
      in an Erlang system, let us see how Erlang processes terminate,
      or in Erlang terminology, <strong>exit</strong>.</p>
    <p>A process which executes <span class="code">exit(normal)</span> or simply runs out
      of things to do has a <strong>normal</strong> exit.</p>
    <p>A process which encounters a runtime error (for example, divide by zero,
      bad match, trying to call a function that does not exist and so on)
      exits with an error, that is, has an <strong>abnormal</strong> exit. A
      process which executes
      <span class="bold_code"><a href="javascript:erlhref('../../','erts','erlang.html#exit-1');">exit(Reason)</a></span>
      where <span class="code">Reason</span> is any Erlang term except the atom
      <span class="code">normal</span>, also has an abnormal exit.</p>
    <p>An Erlang process can set up links to other Erlang processes. If
      a process calls
      <span class="bold_code"><a href="javascript:erlhref('../../','erts','erlang.html#link-1');">link(Other_Pid)</a></span>
      it sets up a bidirectional link between itself and the process
      called <span class="code">Other_Pid</span>. When a process terminates, it sends
      something called a <strong>signal</strong> to all the processes it has
      links to.</p>
    <p>The signal carries information about the pid it was sent from and
      the exit reason.</p>
    <p>The default behaviour of a process that receives a normal exit
      is to ignore the signal.</p>
    <p>The default behaviour in the two other cases (that is, abnormal exit)
      above is to:</p>
    <ul>
      <li>Bypass all messages to the receiving process.</li>
      <li>Kill the receiving process.</li>
      <li>Propagate the same error signal to the links of the
      killed process.</li>
    </ul>
    <p>In this way you can connect all processes in a
      transaction together using links. If one of the processes
      exits abnormally, all the processes in the transaction are
      killed. As it is often wanted to create a process and link to it at
      the same time, there is a special BIF,
      <span class="bold_code"><a href="javascript:erlhref('../../','erts','erlang.html#spawn_link-1');">spawn_link</a></span>
      that does the same as <span class="code">spawn</span>, but also creates a link to
      the spawned process.</p>
    <p>Now an example of the ping pong example using links to terminate
      "pong":</p>
    <div class="example"><pre>
-module(tut20).

-export([start/1,  ping/2, pong/0]).

ping(N, Pong_Pid) -&gt;
    link(Pong_Pid),
    ping1(N, Pong_Pid).

ping1(0, _) -&gt;
    exit(ping);

ping1(N, Pong_Pid) -&gt;
    Pong_Pid ! {ping, self()},
    receive
        pong -&gt;
            io:format("Ping received pong~n", [])
    end,
    ping1(N - 1, Pong_Pid).

pong() -&gt;
    receive
        {ping, Ping_PID} -&gt;
            io:format("Pong received ping~n", []),
            Ping_PID ! pong,
            pong()
    end.

start(Ping_Node) -&gt;
    PongPID = spawn(tut20, pong, []),
    spawn(Ping_Node, tut20, ping, [3, PongPID]).</pre></div>
    <div class="example"><pre>
(s1@bill)3&gt; <span class="bold_code">tut20:start(s2@kosken).</span>
Pong received ping
&lt;3820.41.0&gt;
Ping received pong
Pong received ping
Ping received pong
Pong received ping
Ping received pong</pre></div>
    <p>This is a slight modification of the ping pong program where both
      processes are spawned from the same <span class="code">start/1</span> function,
      and the "ping" process can be spawned on a separate node. Notice
      the use of the <span class="code">link</span> BIF. "Ping" calls
      <span class="code">exit(ping)</span> when it finishes and this causes an exit
      signal to be sent to "pong", which also terminates.</p>
    <p>It is possible to modify the default behaviour of a process so
      that it does not get killed when it receives abnormal exit
      signals. Instead, all signals are turned into normal messages on
      the format <span class="code">{'EXIT',FromPID,Reason}</span> and added to the end of
      the receiving process' message queue. This behaviour is set by:</p>
    <div class="example"><pre>
process_flag(trap_exit, true)</pre></div>
    <p>There are several other process flags, see
      <span class="bold_code"><a href="javascript:erlhref('../../','erts','erlang.html#process_flag-2');">erlang(3)</a></span>.
      Changing the default behaviour of a process in this way is
      usually not done in standard user programs, but is left to
      the supervisory programs in OTP.
      However, the ping pong program is modified to illustrate exit
      trapping.</p>
    <div class="example"><pre>
-module(tut21).

-export([start/1,  ping/2, pong/0]).

ping(N, Pong_Pid) -&gt;
    link(Pong_Pid), 
    ping1(N, Pong_Pid).

ping1(0, _) -&gt;
    exit(ping);

ping1(N, Pong_Pid) -&gt;
    Pong_Pid ! {ping, self()},
    receive
        pong -&gt;
            io:format("Ping received pong~n", [])
    end,
    ping1(N - 1, Pong_Pid).

pong() -&gt;
    process_flag(trap_exit, true), 
    pong1().

pong1() -&gt;
    receive
        {ping, Ping_PID} -&gt;
            io:format("Pong received ping~n", []),
            Ping_PID ! pong,
            pong1();
        {'EXIT', From, Reason} -&gt;
            io:format("pong exiting, got ~p~n", [{'EXIT', From, Reason}])
    end.

start(Ping_Node) -&gt;
    PongPID = spawn(tut21, pong, []),
    spawn(Ping_Node, tut21, ping, [3, PongPID]).</pre></div>
    <div class="example"><pre>
(s1@bill)1&gt; <span class="bold_code">tut21:start(s2@gollum).</span>
&lt;3820.39.0&gt;
Pong received ping
Ping received pong
Pong received ping
Ping received pong
Pong received ping
Ping received pong
pong exiting, got {'EXIT',&lt;3820.39.0&gt;,ping}</pre></div>
  

  <h3><a name="idp281765524">4.3 
        The Larger Example with Robustness Added</a></h3>
    
    <p>Let us return to the messenger program and add changes to
      make it more robust:</p>
    <div class="example"><pre>
%%% Message passing utility.  
%%% User interface:
%%% login(Name)
%%%     One user at a time can log in from each Erlang node in the
%%%     system messenger: and choose a suitable Name. If the Name
%%%     is already logged in at another node or if someone else is
%%%     already logged in at the same node, login will be rejected
%%%     with a suitable error message.
%%% logoff()
%%%     Logs off anybody at that node
%%% message(ToName, Message)
%%%     sends Message to ToName. Error messages if the user of this 
%%%     function is not logged on or if ToName is not logged on at
%%%     any node.
%%%
%%% One node in the network of Erlang nodes runs a server which maintains
%%% data about the logged on users. The server is registered as "messenger"
%%% Each node where there is a user logged on runs a client process registered
%%% as "mess_client" 
%%%
%%% Protocol between the client processes and the server
%%% ----------------------------------------------------
%%% 
%%% To server: {ClientPid, logon, UserName}
%%% Reply {messenger, stop, user_exists_at_other_node} stops the client
%%% Reply {messenger, logged_on} logon was successful
%%%
%%% When the client terminates for some reason
%%% To server: {'EXIT', ClientPid, Reason}
%%%
%%% To server: {ClientPid, message_to, ToName, Message} send a message
%%% Reply: {messenger, stop, you_are_not_logged_on} stops the client
%%% Reply: {messenger, receiver_not_found} no user with this name logged on
%%% Reply: {messenger, sent} Message has been sent (but no guarantee)
%%%
%%% To client: {message_from, Name, Message},
%%%
%%% Protocol between the "commands" and the client
%%% ---------------------------------------------- 
%%%
%%% Started: messenger:client(Server_Node, Name)
%%% To client: logoff
%%% To client: {message_to, ToName, Message}
%%%
%%% Configuration: change the server_node() function to return the
%%% name of the node where the messenger server runs

-module(messenger).
-export([start_server/0, server/0, 
         logon/1, logoff/0, message/2, client/2]).

%%% Change the function below to return the name of the node where the
%%% messenger server runs
server_node() -&gt;
    messenger@super.

%%% This is the server process for the "messenger"
%%% the user list has the format [{ClientPid1, Name1},{ClientPid22, Name2},...]
server() -&gt;
    process_flag(trap_exit, true),
    server([]).

server(User_List) -&gt;
    receive
        {From, logon, Name} -&gt;
            New_User_List = server_logon(From, Name, User_List),
            server(New_User_List);
        {'EXIT', From, _} -&gt;
            New_User_List = server_logoff(From, User_List),
            server(New_User_List);
        {From, message_to, To, Message} -&gt;
            server_transfer(From, To, Message, User_List),
            io:format("list is now: ~p~n", [User_List]),
            server(User_List)
    end.

%%% Start the server
start_server() -&gt;
    register(messenger, spawn(messenger, server, [])).

%%% Server adds a new user to the user list
server_logon(From, Name, User_List) -&gt;
    %% check if logged on anywhere else
    case lists:keymember(Name, 2, User_List) of
        true -&gt;
            From ! {messenger, stop, user_exists_at_other_node},  %reject logon
            User_List;
        false -&gt;
            From ! {messenger, logged_on},
            link(From),
            [{From, Name} | User_List]        %add user to the list
    end.

%%% Server deletes a user from the user list
server_logoff(From, User_List) -&gt;
    lists:keydelete(From, 1, User_List).


%%% Server transfers a message between user
server_transfer(From, To, Message, User_List) -&gt;
    %% check that the user is logged on and who he is
    case lists:keysearch(From, 1, User_List) of
        false -&gt;
            From ! {messenger, stop, you_are_not_logged_on};
        {value, {_, Name}} -&gt;
            server_transfer(From, Name, To, Message, User_List)
    end.

%%% If the user exists, send the message
server_transfer(From, Name, To, Message, User_List) -&gt;
    %% Find the receiver and send the message
    case lists:keysearch(To, 2, User_List) of
        false -&gt;
            From ! {messenger, receiver_not_found};
        {value, {ToPid, To}} -&gt;
            ToPid ! {message_from, Name, Message}, 
            From ! {messenger, sent} 
    end.

%%% User Commands
logon(Name) -&gt;
    case whereis(mess_client) of 
        undefined -&gt;
            register(mess_client, 
                     spawn(messenger, client, [server_node(), Name]));
        _ -&gt; already_logged_on
    end.

logoff() -&gt;
    mess_client ! logoff.

message(ToName, Message) -&gt;
    case whereis(mess_client) of % Test if the client is running
        undefined -&gt;
            not_logged_on;
        _ -&gt; mess_client ! {message_to, ToName, Message},
             ok
end.

%%% The client process which runs on each user node
client(Server_Node, Name) -&gt;
    {messenger, Server_Node} ! {self(), logon, Name},
    await_result(),
    client(Server_Node).

client(Server_Node) -&gt;
    receive
        logoff -&gt;
            exit(normal);
        {message_to, ToName, Message} -&gt;
            {messenger, Server_Node} ! {self(), message_to, ToName, Message},
            await_result();
        {message_from, FromName, Message} -&gt;
            io:format("Message from ~p: ~p~n", [FromName, Message])
    end,
    client(Server_Node).

%%% wait for a response from the server
await_result() -&gt;
    receive
        {messenger, stop, Why} -&gt; % Stop the client 
            io:format("~p~n", [Why]),
            exit(normal);
        {messenger, What} -&gt;  % Normal response
            io:format("~p~n", [What])
    after 5000 -&gt;
            io:format("No response from server~n", []),
            exit(timeout)
    end.</pre></div>
    <p>The following changes are added:</p>
    <p>The messenger server traps exits. If it receives an exit signal,
      <span class="code">{'EXIT',From,Reason}</span>, this means that a client process has
      terminated or is unreachable for one of the following reasons:</p>
    <ul>
      <li>The user has logged off (the "logoff"
       message is removed).</li>
      <li>The network connection to the client is broken.</li>
      <li>The node on which the client process resides has gone down.</li>
      <li>The client processes has done some illegal operation.</li>
    </ul>
    <p>If an exit signal is received as above, the tuple
      <span class="code">{From,Name}</span> is deleted from the servers <span class="code">User_List</span> using
      the <span class="code">server_logoff</span> function. If the node on which the server
      runs goes down, an exit signal (automatically generated by
      the system) is sent to all of the client processes:
      <span class="code">{'EXIT',MessengerPID,noconnection}</span> causing all the client
      processes to terminate.</p>
    <p>Also, a time-out of five seconds has been introduced in
      the <span class="code">await_result</span> function. That is, if the server does not
      reply within five seconds (5000 ms), the client terminates. This
      is only needed in the logon sequence before the client and the
      server are linked.</p>
    <p>An interesting case is if the client terminates before
      the server links to it. This is taken care of because linking to a
      non-existent process causes an exit signal,
      <span class="code">{'EXIT',From,noproc}</span>, to be automatically generated. This is
      as if the process terminated immediately after the link operation.</p>
  
</div>
<div class="footer">
<hr>
<p>Copyright © 1996-2018 Ericsson AB. All Rights Reserved.</p>
</div>
</div>
</div></body>
</html>