diff options
Diffstat (limited to 'system/doc/design_principles/des_princ.xml')
-rw-r--r-- | system/doc/design_principles/des_princ.xml | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/system/doc/design_principles/des_princ.xml b/system/doc/design_principles/des_princ.xml new file mode 100644 index 0000000000..977eda49b5 --- /dev/null +++ b/system/doc/design_principles/des_princ.xml @@ -0,0 +1,285 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>1997</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Overview</title> + <prepared></prepared> + <docno></docno> + <date></date> + <rev></rev> + <file>des_princ.xml</file> + </header> + <p>The <em>OTP Design Principles</em> is a set of principles for how + to structure Erlang code in terms of processes, modules and + directories.</p> + + <section> + <title>Supervision Trees</title> + <p>A basic concept in Erlang/OTP is the <em>supervision tree</em>. + This is a process structuring model based on the idea of + <em>workers</em> and <em>supervisors</em>.</p> + <list type="bulleted"> + <item>Workers are processes which perform computations, that is, + they do the actual work.</item> + <item>Supervisors are processes which monitor the behaviour of + workers. A supervisor can restart a worker if something goes + wrong.</item> + <item>The supervision tree is a hierarchical arrangement of + code into supervisors and workers, making it possible to + design and program fault-tolerant software.</item> + </list> + <marker id="sup6"></marker> + <image file="../design_principles/sup6.gif"> + <icaption>Supervision Tree</icaption> + </image> + <p>In the figure above, square boxes represents supervisors and + circles represent workers.</p> + </section> + + <section> + <title>Behaviours</title> + <p>In a supervision tree, many of the processes have similar + structures, they follow similar patterns. For example, + the supervisors are very similar in structure. The only difference + between them is which child processes they supervise. Also, many + of the workers are servers in a server-client relation, finite + state machines, or event handlers such as error loggers.</p> + <p><em>Behaviours</em> are formalizations of these common patterns. + The idea is to divide the code for a process in a generic part + (a behaviour module) and a specific part (a <em>callback module</em>).</p> + <p>The behaviour module is part of Erlang/OTP. To implement a + process such as a supervisor, the user only has to implement + the callback module which should export a pre-defined set of + functions, the <em>callback functions</em>.</p> + <p>An example to illustrate how code can be divided into a generic + and a specific part: Consider the following code (written in + plain Erlang) for a simple server, which keeps track of a number + of "channels". Other processes can allocate and free the channels + by calling the functions <c>alloc/0</c> and <c>free/1</c>, + respectively.</p> + <marker id="ch1"></marker> + <code type="none"> +-module(ch1). +-export([start/0]). +-export([alloc/0, free/1]). +-export([init/0]). + +start() -> + spawn(ch1, init, []). + +alloc() -> + ch1 ! {self(), alloc}, + receive + {ch1, Res} -> + Res + end. + +free(Ch) -> + ch1 ! {free, Ch}, + ok. + +init() -> + register(ch1, self()), + Chs = channels(), + loop(Chs). + +loop(Chs) -> + receive + {From, alloc} -> + {Ch, Chs2} = alloc(Chs), + From ! {ch1, Ch}, + loop(Chs2); + {free, Ch} -> + Chs2 = free(Ch, Chs), + loop(Chs2) + end.</code> + <p>The code for the server can be rewritten into a generic part + <c>server.erl</c>:</p> + <code type="none"> +-module(server). +-export([start/1]). +-export([call/2, cast/2]). +-export([init/1]). + +start(Mod) -> + spawn(server, init, [Mod]). + +call(Name, Req) -> + Name ! {call, self(), Req}, + receive + {Name, Res} -> + Res + end. + +cast(Name, Req) -> + Name ! {cast, Req}, + ok. + +init(Mod) -> + register(Mod, self()), + State = Mod:init(), + loop(Mod, State). + +loop(Mod, State) -> + receive + {call, From, Req} -> + {Res, State2} = Mod:handle_call(Req, State), + From ! {Mod, Res}, + loop(Mod, State2); + {cast, Req} -> + State2 = Mod:handle_cast(Req, State), + loop(Mod, State2) + end.</code> + <p>and a callback module <c>ch2.erl</c>:</p> + <code type="none"> +-module(ch2). +-export([start/0]). +-export([alloc/0, free/1]). +-export([init/0, handle_call/2, handle_cast/2]). + +start() -> + server:start(ch2). + +alloc() -> + server:call(ch2, alloc). + +free(Ch) -> + server:cast(ch2, {free, Ch}). + +init() -> + channels(). + +handle_call(alloc, Chs) -> + alloc(Chs). % => {Ch,Chs2} + +handle_cast({free, Ch}, Chs) -> + free(Ch, Chs). % => Chs2</code> + <p>Note the following:</p> + <list type="bulleted"> + <item>The code in <c>server</c> can be re-used to build many + different servers.</item> + <item>The name of the server, in this example the atom + <c>ch2</c>, is hidden from the users of the client functions. + This means the name can be changed without affecting them.</item> + <item>The protcol (messages sent to and received from the server) + is hidden as well. This is good programming practice and allows + us to change the protocol without making changes to code using + the interface functions.</item> + <item>We can extend the functionality of <c>server</c>, without + having to change <c>ch2</c> or any other callback module.</item> + </list> + <p>(In <c>ch1.erl</c> and <c>ch2.erl</c> above, the implementation + of <c>channels/0</c>, <c>alloc/1</c> and <c>free/2</c> has been + intentionally left out, as it is not relevant to the example. + For completeness, one way to write these functions are given + below. Note that this is an example only, a realistic + implementation must be able to handle situations like running out + of channels to allocate etc.)</p> + <code type="none"> +channels() -> + {_Allocated = [], _Free = lists:seq(1,100)}. + +alloc({Allocated, [H|T] = _Free}) -> + {H, {[H|Allocated], T}}. + +free(Ch, {Alloc, Free} = Channels) -> + case lists:member(Ch, Alloc) of + true -> + {lists:delete(Ch, Alloc), [Ch|Free]}; + false -> + Channels + end. </code> + <p>Code written without making use of behaviours may be more + efficient, but the increased efficiency will be at the expense of + generality. The ability to manage all applications in the system + in a consistent manner is very important.</p> + <p>Using behaviours also makes it easier to read and understand + code written by other programmers. Ad hoc programming structures, + while possibly more efficient, are always more difficult to + understand.</p> + <p>The module <c>server</c> corresponds, greatly simplified, + to the Erlang/OTP behaviour <c>gen_server</c>.</p> + <p>The standard Erlang/OTP behaviours are:</p> + <taglist> + <tag><seealso marker="gen_server_concepts">gen_server</seealso></tag> + <item>For implementing the server of a client-server relation.</item> + <tag><seealso marker="fsm">gen_fsm</seealso></tag> + <item>For implementing finite state machines.</item> + <tag><seealso marker="events">gen_event</seealso></tag> + <item>For implementing event handling functionality.</item> + <tag><seealso marker="sup_princ">supervisor</seealso></tag> + <item>For implementing a supervisor in a supervision tree.</item> + </taglist> + <p>The compiler understands the module attribute + <c>-behaviour(Behaviour)</c> and issues warnings about + missing callback functions. Example:</p> + <code type="none"> +-module(chs3). +-behaviour(gen_server). +... + +3> c(chs3). +./chs3.erl:10: Warning: undefined call-back function handle_call/3 +{ok,chs3}</code> + </section> + + <section> + <title>Applications</title> + <p>Erlang/OTP comes with a number of components, each implementing + some specific functionality. Components are with Erlang/OTP + terminology called <em>applications</em>. Examples of Erlang/OTP + applications are Mnesia, which has everything needed for + programming database services, and Debugger which is used to + debug Erlang programs. The minimal system based on Erlang/OTP + consists of the applications Kernel and STDLIB.</p> + <p>The application concept applies both to program structure + (processes) and directory structure (modules).</p> + <p>The simplest kind of application does not have any processes, + but consists of a collection of functional modules. Such an + application is called a <em>library application</em>. An example + of a library application is STDLIB.</p> + <p>An application with processes is easiest implemented as a + supervision tree using the standard behaviours.</p> + <p>How to program applications is described in + <seealso marker="applications">Applications</seealso>.</p> + </section> + + <section> + <title>Releases</title> + <p>A <em>release</em> is a complete system made out from a subset of + the Erlang/OTP applications and a set of user-specific + applications.</p> + <p>How to program releases is described in + <seealso marker="release_structure">Releases</seealso>.</p> + <p>How to install a release in a target environment is described + in the chapter about Target Systems in System Principles.</p> + </section> + + <section> + <title>Release Handling</title> + <p><em>Release handling</em> is upgrading and downgrading between + different versions of a release, in a (possibly) running system. + How to do this is described in + <seealso marker="release_handling">Release Handling</seealso>.</p> + </section> +</chapter> + |