From 42a0387e886ddbf60b0e2cb977758e2ca74954ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 12 Mar 2015 15:35:13 +0100 Subject: Update Design Principles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Language cleaned up by the technical writers xsipewe and tmanevik from Combitech. Proofreading and corrections by Björn Gustavsson. --- system/doc/design_principles/applications.xml | 270 ++++----- system/doc/design_principles/appup_cookbook.xml | 368 ++++++------ system/doc/design_principles/des_princ.xml | 113 ++-- .../design_principles/distributed_applications.xml | 182 +++--- system/doc/design_principles/events.xml | 95 ++-- system/doc/design_principles/fsm.xml | 168 +++--- .../doc/design_principles/gen_server_concepts.xml | 160 +++--- .../design_principles/included_applications.xml | 67 +-- system/doc/design_principles/release_handling.xml | 628 +++++++++++---------- system/doc/design_principles/release_structure.xml | 190 ++++--- system/doc/design_principles/spec_proc.xml | 214 ++++--- system/doc/design_principles/sup_princ.xml | 141 ++--- 12 files changed, 1337 insertions(+), 1259 deletions(-) diff --git a/system/doc/design_principles/applications.xml b/system/doc/design_principles/applications.xml index 7b030115df..9d3a204999 100644 --- a/system/doc/design_principles/applications.xml +++ b/system/doc/design_principles/applications.xml @@ -29,55 +29,63 @@ applications.xml -

This chapter should be read in conjunction with app(4) and - application(3).

+

This section is to be read with the app(4) and + application(3) manual pages in Kernel.

Application Concept -

When we have written code implementing some specific - functionality, we might want to make the code into an - application, that is a component that can be started and - stopped as a unit, and which can be re-used in other systems as - well.

-

To do this, we create an - application callback module, where we describe how the application should - be started and stopped.

+

When you have written code implementing some specific functionality + you might want to make the code into an application, + that is, a component that can be started and stopped as a unit, + and which can also be reused in other systems.

+

To do this, create an + application callback module, + and describe how the application is to be started and stopped.

Then, an application specification is needed, which is - put in an application resource file. Among other things, we specify which - modules the application consists of and the name of the callback - module.

-

If we use systools, the Erlang/OTP tools for packaging code + put in an + application resource file. + Among other things, this file specifies which modules the application + consists of and the name of the callback module.

+

If you use systools, the Erlang/OTP tools for packaging code (see Releases), - the code for each application is placed in a separate directory - following a pre-defined directory structure.

+ the code for each application is placed in a + separate directory following a pre-defined + directory structure.

Application Callback Module -

How to start and stop the code for the application, i.e. +

How to start and stop the code for the application, that is, the supervision tree, is described by two callback functions:

start(StartType, StartArgs) -> {ok, Pid} | {ok, Pid, State} -stop(State) -

start is called when starting the application and should - create the supervision tree by starting the top supervisor. - It is expected to return the pid of the top supervisor and an - optional term State, which defaults to []. This term is - passed as-is to stop.

-

StartType is usually the atom normal. It has other +stop(State) + + + start is called when starting the application and is to + create the supervision tree by starting the top supervisor. It is + expected to return the pid of the top supervisor and an optional + term, State, which defaults to []. This term is passed + as is to stop. + StartType is usually the atom normal. It has other values only in the case of a takeover or failover, see - Distributed Applications. StartArgs is defined by the key - mod in the application resource file file.

-

stop/1 is called after the application has been - stopped and should do any necessary cleaning up. Note that - the actual stopping of the application, that is the shutdown of - the supervision tree, is handled automatically as described in - Starting and Stopping Applications.

+ Distributed Applications. + + StartArgs is defined by the key mod in the + application + resource file. + stop/1 is called after the application has been + stopped and is to do any necessary cleaning up. The actual stopping of + the application, that is, the shutdown of the supervision tree, is + handled automatically as described in + Starting and Stopping Applications. + +

Example of an application callback module for packaging the supervision tree from - the Supervisor chapter:

+ Supervisor Behaviour:

-module(ch_app). -behaviour(application). @@ -89,44 +97,48 @@ start(_Type, _Args) -> stop(_State) -> ok. -

A library application, which can not be started or stopped, - does not need any application callback module.

+

A library application that cannot be started or stopped, does not + need any application callback module.

Application Resource File -

To define an application, we create an application specification which is put in an application resource file, or in short .app file:

+

To define an application, an application specification is + created, which is put in an application resource file, or in + short an .app file:

{application, Application, [Opt1,...,OptN]}. -

Application, an atom, is the name of the application. - The file must be named Application.app.

-

Each Opt is a tuple {Key, Value} which define a + + Application, an atom, is the name of the application. + The file must be named Application.app. + Each Opt is a tuple {Key,Value}, which define a certain property of the application. All keys are optional. - Default values are used for any omitted keys.

+ Default values are used for any omitted keys. +

The contents of a minimal .app file for a library - application libapp looks like this:

+ application libapp looks as follows:

{application, libapp, []}.

The contents of a minimal .app file ch_app.app for - a supervision tree application like ch_app looks like this:

+ a supervision tree application like ch_app looks as follows:

{application, ch_app, [{mod, {ch_app,[]}}]}. -

The key mod defines the callback module and start - argument of the application, in this case ch_app and - [], respectively. This means that

+

The key mod defines the callback module and start argument of + the application, in this case ch_app and [], respectively. + This means that the following is called when the application is to be + started:

ch_app:start(normal, []) -

will be called when the application should be started and

+

The following is called when the application is stopped.

ch_app:stop([]) -

will be called when the application has been stopped.

When using systools, the Erlang/OTP tools for packaging - code (see Releases), - the keys description, vsn, modules, - registered and applications should also be - specified:

+ code (see Section + Releases), the keys + description, vsn, modules, registered, + and applications are also to be specified:

{application, ch_app, [{description, "Channel allocator"}, @@ -136,67 +148,54 @@ ch_app:stop([]) {applications, [kernel, stdlib, sasl]}, {mod, {ch_app,[]}} ]}. - - description - A short description, a string. Defaults to "". - vsn - Version number, a string. Defaults to "". - modules - All modules introduced by this application. - systools uses this list when generating boot scripts and - tar files. A module must be defined in one and only one - application. Defaults to []. - registered - All names of registered processes in the application. - systools uses this list to detect name clashes - between applications. Defaults to []. - applications - All applications which must be started before this - application is started. systools uses this list to - generate correct boot scripts. Defaults to [], but note that - all applications have dependencies to at least kernel - and stdlib. - -

The syntax and contents of of the application resource file - are described in detail in the - Application resource file reference.

+ + description - A short description, a string. Defaults to + "". + vsn - Version number, a string. Defaults to "". + modules - All modules introduced by this + application. systools uses this list when generating boot scripts + and tar files. A module must be defined in only one application. + Defaults to []. + registered - All names of registered processes in the + application. systools uses this list to detect name clashes + between applications. Defaults to []. + applications - All applications that must be + started before this application is started. systools uses this + list to generate correct boot scripts. Defaults to []. Notice + that all applications have dependencies to at least Kernel + and STDLIB. + +

For details about the syntax and contents of the application + resource file, see the app + manual page in Kernel.

Directory Structure

When packaging code using systools, the code for each - application is placed in a separate directory + application is placed in a separate directory, lib/Application-Vsn, where Vsn is the version number.

-

This may be useful to know, even if systools is not used, - since Erlang/OTP itself is packaged according to the OTP principles +

This can be useful to know, even if systools is not used, + since Erlang/OTP is packaged according to the OTP principles and thus comes with this directory structure. The code server - (see code(3)) will automatically use code from - the directory with the highest version number, if there are - more than one version of an application present.

-

The application directory structure can of course be used in - the development environment as well. The version number may then + (see the code(3) manual page in Kernel) automatically + uses code from + the directory with the highest version number, if more than one + version of an application is present.

+

The application directory structure can also be used in the + development environment. The version number can then be omitted from the name.

-

The application directory have the following sub-directories:

+

The application directory has the following sub-directories:

- src - ebin - priv - include + src - Contains the Erlang source code. + ebin - Contains the Erlang object code, the + beam files. The .app file is also placed here. + priv - Used for application specific files. For + example, C executables are placed here. The function + code:priv_dir/1 is to be used to access this directory. + include - Used for include files. - - src - Contains the Erlang source code. - ebin - Contains the Erlang object code, the beam files. - The .app file is also placed here. - priv - Used for application specific files. For example, C - executables are placed here. The function code:priv_dir/1 - should be used to access this directory. - include - Used for include files. -
@@ -207,17 +206,17 @@ ch_app:stop([]) processes is the application controller process, registered as application_controller.

All operations on applications are coordinated by the application - controller. It is interfaced through the functions in - the module application, see application(3). - In particular, applications can be loaded, unloaded, started and - stopped.

+ controller. It is interacted through the functions in + the module application, see the application(3) + manual page in Kernel. In particular, applications can be + loaded, unloaded, started, and stopped.

Loading and Unloading Applications

Before an application can be started, it must be loaded. The application controller reads and stores the information from - the .app file.

+ the .app file:

 1> application:load(ch_app).
 ok
@@ -236,7 +235,7 @@ ok
  {stdlib,"ERTS  CXC 138 10","1.11.4.3"}]

Loading/unloading an application does not load/unload the code - used by the application. Code loading is done the usual way.

+ used by the application. Code loading is done the usual way.

@@ -252,13 +251,14 @@ ok {stdlib,"ERTS CXC 138 10","1.11.4.3"}, {ch_app,"Channel allocator","1"}]

If the application is not already loaded, the application - controller will first load it using application:load/1. It - will check the value of the applications key, to ensure - that all applications that should be started before this + controller first loads it using application:load/1. It + checks the value of the applications key, to ensure + that all applications that are to be started before this application are running.

-

The application controller then creates an application master for the application. The application master is - the group leader of all the processes in the application. +

The application controller then creates an + application master for the application. The application master + is the group leader of all the processes in the application. The application master starts the application by calling the application callback function start/2 in the module, and with the start argument, defined by the mod key in @@ -268,8 +268,8 @@ ok 7> application:stop(ch_app). ok

The application master stops the application by telling the top - supervisor to shutdown. The top supervisor tells all its child - processes to shutdown etc. and the entire tree is terminated in + supervisor to shut down. The top supervisor tells all its child + processes to shut down, and so on; the entire tree is terminated in reversed start order. The application master then calls the application callback function stop/1 in the module defined by the mod key.

@@ -277,8 +277,10 @@ ok
Configuring an Application -

An application can be configured using configuration parameters. These are a list of {Par, Val} tuples - specified by a key env in the .app file.

+

An application can be configured using + configuration parameters. These are a list of + {Par,Val} tuples + specified by a key env in the .app file:

{application, ch_app, [{description, "Channel allocator"}, @@ -289,11 +291,12 @@ ok {mod, {ch_app,[]}}, {env, [{file, "/usr/local/log"}]} ]}. -

Par should be an atom, Val is any term. +

Par is to be an atom. Val is any term. The application can retrieve the value of a configuration parameter by calling application:get_env(App, Par) or a - number of similar functions, see application(3).

-

Example:

+ number of similar functions, see the application(3) + manual page in Kernel.

+

Example:

 % erl
 Erlang (BEAM) emulator version 5.2.3.6 [hipe] [threads:0]
@@ -304,20 +307,21 @@ ok
 2> application:get_env(ch_app, file).
 {ok,"/usr/local/log"}

The values in the .app file can be overridden by values - in a system configuration file. This is a file which + in a system configuration file. This is a file that contains configuration parameters for relevant applications:

[{Application1, [{Par11,Val11},...]}, ..., {ApplicationN, [{ParN1,ValN1},...]}]. -

The system configuration should be called Name.config and - Erlang should be started with the command line argument - -config Name. See config(4) for more information.

-

Example: A file test.config is created with the following - contents:

+

The system configuration is to be called Name.config and + Erlang is to be started with the command-line argument + -config Name. For details, see the config(4) + manual page in Kernel.

+

Example:

+

A file test.config is created with the following contents:

[{ch_app, [{file, "testlog"}]}]. -

The value of file will override the value of file +

The value of file overrides the value of file as defined in the .app file:

 % erl -config test
@@ -330,14 +334,14 @@ ok
 {ok,"testlog"}

If release handling - is used, exactly one system configuration file should be used and - that file should be called sys.config

-

The values in the .app file, as well as the values in a - system configuration file, can be overridden directly from + is used, exactly one system configuration file is to be used and + that file is to be called sys.config.

+

The values in the .app file and the values in a + system configuration file can be overridden directly from the command line:

 % erl -ApplName Par1 Val1 ... ParN ValN
-

Example:

+

Example:

 % erl -ch_app file '"testlog"'
 Erlang (BEAM) emulator version 5.2.3.6 [hipe] [threads:0]
@@ -368,10 +372,10 @@ application:start(Application, Type)
       If a temporary application terminates, this is reported but
        no other applications are terminated.
     
-    

It is always possible to stop an application explicitly by +

An application can always be stopped explicitly by calling application:stop/1. Regardless of the mode, no - other applications will be affected.

-

Note that transient mode is of little practical use, since when + other applications are affected.

+

The transient mode is of little practical use, since when a supervision tree terminates, the reason is set to shutdown, not normal.

diff --git a/system/doc/design_principles/appup_cookbook.xml b/system/doc/design_principles/appup_cookbook.xml index 22c48db855..63adea8a5c 100644 --- a/system/doc/design_principles/appup_cookbook.xml +++ b/system/doc/design_principles/appup_cookbook.xml @@ -28,15 +28,15 @@ appup_cookbook.xml -

This chapter contains examples of .appup files for - typical cases of upgrades/downgrades done in run-time.

+ +

This section includes examples of .appup files for + typical cases of upgrades/downgrades done in runtime.

Changing a Functional Module -

When a change has been made to a functional module, for example +

When a functional module has been changed, for example, if a new function has been added or a bug has been corrected, - simple code replacement is sufficient.

-

Example:

+ simple code replacement is sufficient, for example:

{"2", [{"1", [{load_module, m}]}], @@ -46,29 +46,31 @@
Changing a Residence Module -

In a system implemented according to the OTP Design Principles, +

In a system implemented according to the OTP design principles, all processes, except system processes and special processes, reside in one of the behaviours supervisor, - gen_server, gen_fsm or gen_event. These + gen_server, gen_fsm, or gen_event. These belong to the STDLIB application and upgrading/downgrading normally requires an emulator restart.

-

OTP thus provides no support for changing residence modules - except in the case of special processes.

+

OTP thus provides no support for changing residence modules except + in the case of special processes.

Changing a Callback Module -

A callback module is a functional module, and for code +

A callback module is a functional module, and for code extensions simple code replacement is sufficient.

-

Example: When adding a function to ch3 as described in - the example in Release Handling, ch_app.appup looks as follows:

+

Example: When adding a function to ch3, + as described in the example in + Release Handling, + ch_app.appup looks as follows:

{"2", [{"1", [{load_module, ch3}]}], [{"1", [{load_module, ch3}]}] }.

OTP also supports changing the internal state of behaviour - processes, see Changing Internal State below.

+ processes, see Changing Internal State.

@@ -77,26 +79,28 @@

In this case, simple code replacement is not sufficient. The process must explicitly transform its state using the callback function code_change before switching to the new version - of the callback module. Thus synchronized code replacement is + of the callback module. Thus, synchronized code replacement is used.

-

Example: Consider the gen_server ch3 from the chapter - about the gen_server behaviour. The internal state is a term Chs - representing the available channels. Assume we want add a counter - N which keeps track of the number of alloc requests - so far. This means we need to change the format to +

Example: Consider gen_server ch3 from + gen_server Behaviour. + The internal state is a term Chs + representing the available channels. Assume you want to add a counter + N, which keeps track of the number of alloc requests + so far. This means that the format must be changed to {Chs,N}.

-

The .appup file could look as follows:

+

The .appup file can look as follows:

{"2", [{"1", [{update, ch3, {advanced, []}}]}], [{"1", [{update, ch3, {advanced, []}}]}] }.

The third element of the update instruction is a tuple - {advanced,Extra} which says that the affected processes - should do a state transformation before loading the new version + {advanced,Extra}, which says that the affected processes + are to do a state transformation before loading the new version of the module. This is done by the processes calling the callback - function code_change (see gen_server(3)). The term - Extra, in this case [], is passed as-is to the function:

+ function code_change (see the gen_server(3) manual + page in STDLIB). The term Extra, in this case + [], is passed as is to the function:

-module(ch3). @@ -107,40 +111,41 @@ code_change({down, _Vsn}, {Chs, N}, _Extra) -> {ok, Chs}; code_change(_Vsn, Chs, _Extra) -> {ok, {Chs, 0}}. -

The first argument is {down,Vsn} in case of a downgrade, - or Vsn in case of an upgrade. The term Vsn is - fetched from the 'original' version of the module, i.e. - the version we are upgrading from, or downgrading to.

+

The first argument is {down,Vsn} if there is a downgrade, + or Vsn if there is a upgrade. The term Vsn is + fetched from the 'original' version of the module, that is, + the version you are upgrading from, or downgrading to.

The version is defined by the module attribute vsn, if any. There is no such attribute in ch3, so in this case - the version is the checksum (a huge integer) of the BEAM file, an - uninteresting value which is ignored.

-

(The other callback functions of ch3 need to be modified - as well and perhaps a new interface function added, this is not - shown here).

+ the version is the checksum (a huge integer) of the beam file, an + uninteresting value, which is ignored.

+

The other callback functions of ch3 must also be modified + and perhaps a new interface function must be added, but this is not + shown here.

Module Dependencies -

Assume we extend a module by adding a new interface function, as - in the example in Release Handling, where a function available/0 is - added to ch3.

-

If we also add a call to this function, say in the module - m1, a run-time error could occur during release upgrade if +

Assume that a module is extended by adding an interface function, + as in the example in + Release Handling, + where a function available/0 is added to ch3.

+

If a call is added to this function, say in module + m1, a runtime error could can occur during release upgrade if the new version of m1 is loaded first and calls ch3:available/0 before the new version of ch3 is loaded.

-

Thus, ch3 must be loaded before m1 is, in - the upgrade case, and vice versa in the downgrade case. We say - that m1 is dependent on ch3. In a release - handling instruction, this is expressed by the element - DepMods:

+

Thus, ch3 must be loaded before m1, in + the upgrade case, and conversely in the downgrade case. + m1 is said to be dependent on ch3. In a release + handling instruction, this is expressed by the + DepMods element:

{load_module, Module, DepMods} {update, Module, {advanced, Extra}, DepMods}

DepMods is a list of modules, on which Module is dependent.

-

Example: The module m1 in the application myapp is +

Example: The module m1 in application myapp is dependent on ch3 when upgrading from "1" to "2", or downgrading from "2" to "1":

@@ -157,8 +162,8 @@ ch_app.appup: [{"1", [{load_module, ch3}]}], [{"1", [{load_module, ch3}]}] }. -

If m1 and ch3 had belonged to the same application, - the .appup file could have looked like this:

+

If instead m1 and ch3 belong to the same application, + the .appup file can look as follows:

{"2", [{"1", @@ -168,48 +173,48 @@ ch_app.appup: [{load_module, ch3}, {load_module, m1, [ch3]}]}] }. -

Note that it is m1 that is dependent on ch3 also +

m1 is dependent on ch3 also when downgrading. systools knows the difference between - up- and downgrading and will generate a correct relup, - where ch3 is loaded before m1 when upgrading but + up- and downgrading and generates a correct relup, + where ch3 is loaded before m1 when upgrading, but m1 is loaded before ch3 when downgrading.

- Changing Code For a Special Process + Changing Code for a Special Process

In this case, simple code replacement is not sufficient. When a new version of a residence module for a special process is loaded, the process must make a fully qualified call to - its loop function to switch to the new code. Thus synchronized + its loop function to switch to the new code. Thus, synchronized code replacement must be used.

The name(s) of the user-defined residence module(s) must be listed in the Modules part of the child specification - for the special process, in order for the release handler to + for the special process. Otherwise the release handler cannot find the process.

-

Example. Consider the example ch4 from the chapter about +

Example: Consider the example ch4 in sys and proc_lib. - When started by a supervisor, the child specification could look - like this:

+ When started by a supervisor, the child specification can look + as follows:

{ch4, {ch4, start_link, []}, permanent, brutal_kill, worker, [ch4]}

If ch4 is part of the application sp_app and a new - version of the module should be loaded when upgrading from - version "1" to "2" of this application, sp_app.appup could - look like this:

+ version of the module is to be loaded when upgrading from + version "1" to "2" of this application, sp_app.appup can + look as follows:

{"2", [{"1", [{update, ch4, {advanced, []}}]}], [{"1", [{update, ch4, {advanced, []}}]}] }.

The update instruction must contain the tuple - {advanced,Extra}. The instruction will make the special + {advanced,Extra}. The instruction makes the special process call the callback function system_code_change/4, a function the user must implement. The term Extra, in this - case [], is passed as-is to system_code_change/4:

+ case [], is passed as is to system_code_change/4:

-module(ch4). ... @@ -218,39 +223,43 @@ ch_app.appup: system_code_change(Chs, _Module, _OldVsn, _Extra) -> {ok, Chs}. -

The first argument is the internal state State passed from - the function sys:handle_system_msg(Request, From, Parent, Module, Deb, State), called by the special process when - a system message is received. In ch4, the internal state is - the set of available channels Chs.

-

The second argument is the name of the module (ch4).

-

The third argument is Vsn or {down,Vsn} as - described for - gen_server:code_change/3.

+ + The first argument is the internal state State, + passed from function + sys:handle_system_msg(Request, From, Parent, Module, Deb, State), + and called by the special process when a system message is received. + In ch4, the internal state is the set of available channels + Chs. + The second argument is the name of the module + (ch4). + The third argument is Vsn or {down,Vsn}, as + described for gen_server:code_change/3 in + Changing Internal State. +

In this case, all arguments but the first are ignored and the function simply returns the internal state again. This is - enough if the code only has been extended. If we had wanted to - change the internal state (similar to the example in + enough if the code only has been extended. If instead the + internal state is changed (similar to the example in Changing Internal State), - it would have been done in this function and - {ok,Chs2} returned.

+ this is done in this function and {ok,Chs2} returned.

Changing a Supervisor

The supervisor behaviour supports changing the internal state, - i.e. changing restart strategy and maximum restart intensity - properties, as well as changing existing child specifications.

-

Adding and deleting child processes are also possible, but not + that is, changing the restart strategy and maximum restart frequency + properties, as well as changing the existing child specifications.

+

Child processes can be added or deleted, but this is not handled automatically. Instructions must be given by in the .appup file.

Changing Properties -

Since the supervisor should change its internal state, +

Since the supervisor is to change its internal state, synchronized code replacement is required. However, a special update instruction must be used.

-

The new version of the callback module must be loaded first +

First, the new version of the callback module must be loaded, both in the case of upgrade and downgrade. Then the new return value of init/1 can be checked and the internal state be changed accordingly.

@@ -258,10 +267,11 @@ system_code_change(Chs, _Module, _OldVsn, _Extra) -> supervisors:

{update, Module, supervisor} -

Example: Assume we want to change the restart strategy of - ch_sup from the Supervisor Behaviour chapter from one_for_one to one_for_all. - We change the callback function init/1 in - ch_sup.erl:

+

Example: To change the restart strategy of + ch_sup (from + Supervisor Behaviour) + from one_for_one to one_for_all, change the callback + function init/1 in ch_sup.erl:

-module(ch_sup). ... @@ -280,7 +290,7 @@ init(_Args) -> Changing Child Specifications

The instruction, and thus the .appup file, when changing an existing child specification, is the same as when - changing properties as described above:

+ changing properties as described earlier:

{"2", [{"1", [{update, ch_sup, supervisor}]}], @@ -288,25 +298,25 @@ init(_Args) -> }.

The changes do not affect existing child processes. For example, changing the start function only specifies how - the child process should be restarted, if needed later on.

-

Note that the id of the child specification cannot be changed.

-

Note also that changing the Modules field of the child - specification may affect the release handling process itself, + the child process is to be restarted, if needed later on.

+

The id of the child specification cannot be changed.

+

Changing the Modules field of the child + specification can affect the release handling process itself, as this field is used to identify which processes are affected when doing a synchronized code replacement.

- Adding And Deleting Child Processes -

As stated above, changing child specifications does not affect + Adding and Deleting Child Processes +

As stated earlier, changing child specifications does not affect existing child processes. New child specifications are - automatically added, but not deleted. Also, child processes are - not automatically started or terminated. Instead, this must be - done explicitly using apply instructions.

-

Example: Assume we want to add a new child process m1 to - ch_sup when upgrading ch_app from "1" to "2". - This means m1 should be deleted when downgrading from + automatically added, but not deleted. Child processes are + not automatically started or terminated, this must be + done using apply instructions.

+

Example: Assume a new child process m1 is to be + added to ch_sup when upgrading ch_app from "1" to "2". + This means m1 is to be deleted when downgrading from "2" to "1":

{"2", @@ -320,13 +330,13 @@ init(_Args) -> {update, ch_sup, supervisor} ]}] }. -

Note that the order of the instructions is important.

-

Note also that the supervisor must be registered as +

The order of the instructions is important.

+

The supervisor must be registered as ch_sup for the script to work. If the supervisor is not registered, it cannot be accessed directly from the script. Instead a help function that finds the pid of the supervisor - and calls supervisor:restart_child etc. must be written, - and it is this function that should be called from the script + and calls supervisor:restart_child, and so on, must be + written. This function is then to be called from the script using the apply instruction.

If the module m1 is introduced in version "2" of ch_app, it must also be loaded when upgrading and @@ -345,18 +355,18 @@ init(_Args) -> {delete_module, m1} ]}] }. -

Note again that the order of the instructions is important. - When upgrading, m1 must be loaded and the supervisor's +

As stated earlier, the order of the instructions is important. + When upgrading, m1 must be loaded, and the supervisor child specification changed, before the new child process can be started. When downgrading, the child process must be - terminated before child specification is changed and the module + terminated before the child specification is changed and the module is deleted.

Adding or Deleting a Module -

Example: A new functional module m is added to +

Example: A new functional module m is added to ch_app:

{"2", @@ -367,15 +377,16 @@ init(_Args) ->
Starting or Terminating a Process

In a system structured according to the OTP design principles, - any process would be a child process belonging to a supervisor, - see Adding and Deleting Child Processes above.

+ any process would be a child process belonging to a supervisor, see + Adding and Deleting Child Processes + in Changing a Supervisor.

Adding or Removing an Application

When adding or removing an application, no .appup file is needed. When generating relup, the .rel files - are compared and add_application and + are compared and the add_application and remove_application instructions are added automatically.

@@ -383,11 +394,11 @@ init(_Args) -> Restarting an Application

Restarting an application is useful when a change is too complicated to be made without restarting the processes, for - example if the supervisor hierarchy has been restructured.

-

Example: When adding a new child m1 to ch_sup, as - in the example above, an - alternative to updating the supervisor is to restart the entire - application:

+ example, if the supervisor hierarchy has been restructured.

+

Example: When adding a child m1 to ch_sup, as in + Adding and Deleting Child Processes + in Changing a Supervisor, an alternative to updating + the supervisor is to restart the entire application:

{"2", [{"1", [{restart_application, ch_app}]}], @@ -400,7 +411,7 @@ init(_Args) -> Changing an Application Specification

When installing a release, the application specifications are automatically updated before evaluating the relup script. - Hence, no instructions are needed in the .appup file:

+ Thus, no instructions are needed in the .appup file:

 {"2",
  [{"1", []}],
@@ -412,28 +423,29 @@ init(_Args) ->
     Changing Application Configuration
     

Changing an application configuration by updating the env key in the .app file is an instance of changing an - application specification, see above.

+ application specification, see the previous section.

Alternatively, application configuration parameters can be added or updated in sys.config.

Changing Included Applications -

The release handling instructions for adding, removing and +

The release handling instructions for adding, removing, and restarting applications apply to primary applications only. There are no corresponding instructions for included applications. However, since an included application is really a supervision tree with a topmost supervisor, started as a child process to a supervisor in the including application, a relup file can be manually created.

-

Example: Assume we have a release containing an application - prim_app which have a supervisor prim_sup in its +

Example: Assume there is a release containing an application + prim_app, which have a supervisor prim_sup in its supervision tree.

-

In a new version of the release, our example application - ch_app should be included in prim_app. That is, - its topmost supervisor ch_sup should be started as a child +

In a new version of the release, the application ch_app + is to be included in prim_app. That is, + its topmost supervisor ch_sup is to be started as a child process to prim_sup.

-

1) Edit the code for prim_sup:

+

The workflow is as follows:

+

Step 1) Edit the code for prim_sup:

init(...) -> {ok, {...supervisor flags..., @@ -441,7 +453,7 @@ init(...) -> {ch_sup, {ch_sup,start_link,[]}, permanent,infinity,supervisor,[ch_sup]}, ...]}}. -

2) Edit the .app file for prim_app:

+

Step 2) Edit the .app file for prim_app:

{application, prim_app, [..., @@ -450,27 +462,29 @@ init(...) -> {included_applications, [ch_app]}, ... ]}. -

3) Create a new .rel file, including ch_app:

+

Step 3) Create a new .rel file, including + ch_app:

{release, ..., [..., {prim_app, "2"}, {ch_app, "1"}]}. +

The included application can be started in two ways. + This is described in the next two sections.

Application Restart -

4a) One way to start the included application is to restart - the entire prim_app application. Normally, we would then - use the restart_application instruction in - the .appup file for prim_app.

-

However, if we did this and then generated a relup file, - not only would it contain instructions for restarting (i.e. +

Step 4a) One way to start the included application is to + restart the entire prim_app application. Normally, the + restart_application instruction in the .appup file + for prim_app would be used.

+

However, if this is done and a relup file is generated, + not only would it contain instructions for restarting (that is, removing and adding) prim_app, it would also contain instructions for starting ch_app (and stopping it, in - the case of downgrade). This is due to the fact that - ch_app is included in the new .rel file, but not - in the old one.

+ the case of downgrade). This is because ch_app is included + in the new .rel file, but not in the old one.

Instead, a correct relup file can be created manually, either from scratch or by editing the generated version. The instructions for starting/stopping ch_app are @@ -512,7 +526,8 @@ init(...) ->

Supervisor Change -

4b) Another way to start the included application (or stop it +

Step 4b) Another way to start the included + application (or stop it in the case of downgrade) is by combining instructions for adding and removing child processes to/from prim_sup with instructions for loading/unloading all ch_app code and @@ -521,7 +536,7 @@ init(...) -> scratch or by editing a generated version. Load all code for ch_app first, and also load the application specification, before prim_sup is updated. When - downgrading, prim_sup should be updated first, before + downgrading, prim_sup is to updated first, before the code for ch_app and its application specification are unloaded.

@@ -560,10 +575,10 @@ init(...) ->
Changing Non-Erlang Code

Changing code for a program written in another programming - language than Erlang, for example a port program, is very - application dependent and OTP provides no special support for it.

-

Example, changing code for a port program: Assume that - the Erlang process controlling the port is a gen_server + language than Erlang, for example, a port program, is + application-dependent and OTP provides no special support for it.

+

Example: When changing code for a port program, assume that + the Erlang process controlling the port is a gen_server portc and that the port is opened in the callback function init/1:

@@ -573,10 +588,11 @@ init(...) -> Port = open_port({spawn,PortPrg}, [...]), ..., {ok, #state{port=Port, ...}}. -

If the port program should be updated, we can extend the code for - the gen_server with a code_change function which closes - the old port and opens a new port. (If necessary, the gen_server - may first request data that needs to be saved from the port +

If the port program is to be updated, the code for the + gen_server can be extended with a code_change function, + which closes the old port and opens a new port. + (If necessary, the gen_server can + first request data that must be saved from the port program and pass this data to the new port):

code_change(_OldVsn, State, port) -> @@ -595,8 +611,8 @@ code_change(_OldVsn, State, port) -> [{"1", [{update, portc, {advanced,port}}]}], [{"1", [{update, portc, {advanced,port}}]}] ]. -

Make sure the priv directory where the C program is - located is included in the new release package:

+

Ensure that the priv directory, where the C program is + located, is included in the new release package:

 1> systools:make_tar("my_release", [{dirs,[priv]}]).
 ...
@@ -604,28 +620,29 @@ code_change(_OldVsn, State, port) ->
Emulator Restart and Upgrade -

There are two upgrade instructions that will restart the emulator:

- - restart_new_emulator - Intended for when erts, kernel, stdlib or sasl is - upgraded. It is automatically added when the relup file is - generated by systools:make_relup/3,4. It is executed - before all other upgrade instructions. See - Release - Handling for more information about this - instruction. - restart_emulator - Used when a restart of the emulator is required after all - other upgrade instructions are executed. See - Release - Handling for more information about this - instruction. - - +

Two upgrade instructions restart the emulator:

+ +

restart_new_emulator

+

Intended when ERTS, Kernel, STDLIB, or + SASL is upgraded. It is automatically added when the + relup file is generated by systools:make_relup/3,4. + It is executed before all other upgrade instructions. + For more information about this instruction, see + restart_new_emulator (Low-Level) in + Release Handling Instructions. +

+

restart_emulator

+

Used when a restart of the emulator is required after all + other upgrade instructions are executed. + For more information about this instruction, see + restart_emulator (Low-Level) in + Release Handling Instructions. +

+

If an emulator restart is necessary and no upgrade instructions - are needed, i.e. if the restart itself is enough for the - upgraded applications to start running the new versions, a very - simple .relup file can be created manually:

+ are needed, that is, if the restart itself is enough for the + upgraded applications to start running the new versions, a + simple relup file can be created manually:

{"B", [{"A", @@ -637,26 +654,27 @@ code_change(_OldVsn, State, port) -> }.

In this case, the release handler framework with automatic packing and unpacking of release packages, automatic path - updates etc. can be used without having to specify .appup - files.

+ updates, and so on, can be used without having to specify + .appup files.

- Emulator Upgrade from pre OTP R15 + Emulator Upgrade From Pre OTP R15

From OTP R15, an emulator upgrade is performed by restarting the emulator with new versions of the core applications - (kernel, stdlib and sasl) before loading code + (Kernel, STDLIB, and SASL) before loading code and running upgrade instruction for other applications. For this - to work, the release to upgrade from must includes OTP R15 or - later. For the case where the release to upgrade from includes an - earlier emulator version, systools:make_relup will create a + to work, the release to upgrade from must include OTP R15 or + later.

+

For the case where the release to upgrade from includes an + earlier emulator version, systools:make_relup creates a backwards compatible relup file. This means that all upgrade - instructions will be executed before the emulator is - restarted. The new application code will therefore be loaded into + instructions are executed before the emulator is + restarted. The new application code is therefore loaded into the old emulator. If the new code is compiled with the new - emulator, there might be cases where the beam format has changed - and beam files can not be loaded. To overcome this problem, the - new code should be compiled with the old emulator.

+ emulator, there can be cases where the beam format has changed + and beam files cannot be loaded. To overcome this problem, compile + the new code with the old emulator.

diff --git a/system/doc/design_principles/des_princ.xml b/system/doc/design_principles/des_princ.xml index e8f289b905..77c61eafb0 100644 --- a/system/doc/design_principles/des_princ.xml +++ b/system/doc/design_principles/des_princ.xml @@ -28,50 +28,52 @@ des_princ.xml -

The OTP Design Principles is a set of principles for how - to structure Erlang code in terms of processes, modules and - directories.

+ +

The OTP design principles define how to + structure Erlang code in terms of processes, modules, + and directories.

Supervision Trees

A basic concept in Erlang/OTP is the supervision tree. This is a process structuring model based on the idea of - workers and supervisors.

+ workers and supervisors:

- Workers are processes which perform computations, that is, + Workers are processes that perform computations, that is, they do the actual work. - Supervisors are processes which monitor the behaviour of + Supervisors are processes that monitor the behaviour of workers. A supervisor can restart a worker if something goes wrong. The supervision tree is a hierarchical arrangement of - code into supervisors and workers, making it possible to + code into supervisors and workers, which makes it possible to design and program fault-tolerant software. +

In the following figure, square boxes represents supervisors and + circles represent workers:

Supervision Tree -

In the figure above, square boxes represents supervisors and - circles represent workers.

Behaviours

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.

+ the supervisors are similar in structure. The only difference + between them is which child processes they supervise. Many + of the workers are servers in a server-client relation, + finite-state machines, or event handlers such as error loggers.

Behaviours 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 callback module).

+ (a behaviour module) and a specific part (a + callback module).

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 + the callback module which is to export a pre-defined set of functions, the callback functions.

-

An example to illustrate how code can be divided into a generic - and a specific part: Consider the following code (written in +

The following example 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 alloc/0 and free/1, @@ -149,7 +151,7 @@ loop(Mod, State) -> State2 = Mod:handle_cast(Req, State), loop(Mod, State2) end. -

and a callback module ch2.erl:

+

And a callback module ch2.erl:

-module(ch2). -export([start/0]). @@ -173,27 +175,27 @@ handle_call(alloc, Chs) -> handle_cast({free, Ch}, Chs) -> free(Ch, Chs). % => Chs2 -

Note the following:

+

Notice the following:

- The code in server can be re-used to build many + The code in server can be reused to build many different servers. - The name of the server, in this example the atom - ch2, is hidden from the users of the client functions. - This means the name can be changed without affecting them. + The server name, in this example the atom + ch2, is hidden from the users of the client functions. This + means that the name can be changed without affecting them. 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 + is also hidden. This is good programming practice and allows + one to change the protocol without changing the code using the interface functions. - We can extend the functionality of server, without + The functionality of server can be extended without having to change ch2 or any other callback module. -

(In ch1.erl and ch2.erl above, the implementation - of channels/0, alloc/1 and free/2 has been +

In ch1.erl and ch2.erl above, the implementation + of channels/0, alloc/1, and free/2 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 + below. This is an example only, a realistic implementation must be able to handle situations like running out - of channels to allocate etc.)

+ of channels to allocate, and so on.

channels() -> {_Allocated = [], _Free = lists:seq(1,100)}. @@ -208,30 +210,30 @@ free(Ch, {Alloc, Free} = Channels) -> false -> Channels end. -

Code written without making use of behaviours may be more - efficient, but the increased efficiency will be at the expense of +

Code written without using behaviours can be more + efficient, but the increased efficiency is at the expense of generality. The ability to manage all applications in the system - in a consistent manner is very important.

+ in a consistent manner is important.

Using behaviours also makes it easier to read and understand - code written by other programmers. Ad hoc programming structures, + code written by other programmers. Improvised programming structures, while possibly more efficient, are always more difficult to understand.

-

The module server corresponds, greatly simplified, +

The server module corresponds, greatly simplified, to the Erlang/OTP behaviour gen_server.

The standard Erlang/OTP behaviours are:

- - gen_server - For implementing the server of a client-server relation. - gen_fsm - For implementing finite state machines. - gen_event - For implementing event handling functionality. - supervisor - For implementing a supervisor in a supervision tree. - + +

gen_server

+

For implementing the server of a client-server relation

+

gen_fsm

+

For implementing finite-state machines

+

gen_event

+

For implementing event handling functionality

+

supervisor

+

For implementing a supervisor in a supervision tree

+

The compiler understands the module attribute -behaviour(Behaviour) and issues warnings about - missing callback functions. Example:

+ missing callback functions, for example:

-module(chs3). -behaviour(gen_server). @@ -248,13 +250,17 @@ free(Ch, {Alloc, Free} = Channels) -> some specific functionality. Components are with Erlang/OTP terminology called applications. 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.

+ programming database services, and Debugger, which is used + to debug Erlang programs. The minimal system based on Erlang/OTP + consists of the following two applications:

+ + Kernel - Functionality necessary to run Erlang + STDLIB - Erlang standard libraries +

The application concept applies both to program structure (processes) and directory structure (modules).

-

The simplest kind of application does not have any processes, - but consists of a collection of functional modules. Such an +

The simplest applications do not have any processes, + but consist of a collection of functional modules. Such an application is called a library application. An example of a library application is STDLIB.

An application with processes is easiest implemented as a @@ -266,12 +272,11 @@ free(Ch, {Alloc, Free} = Channels) ->

Releases

A release is a complete system made out from a subset of - the Erlang/OTP applications and a set of user-specific - applications.

+ Erlang/OTP applications and a set of user-specific applications.

How to program releases is described in Releases.

How to install a release in a target environment is described - in the chapter about Target Systems in System Principles.

+ in the section about target systems in Section 2 System Principles.

diff --git a/system/doc/design_principles/distributed_applications.xml b/system/doc/design_principles/distributed_applications.xml index 4d4ba3136e..f40a24fdf5 100644 --- a/system/doc/design_principles/distributed_applications.xml +++ b/system/doc/design_principles/distributed_applications.xml @@ -28,71 +28,73 @@ distributed_applications.xml +
- Definition -

In a distributed system with several Erlang nodes, there may be - a need to control applications in a distributed manner. If + Introduction +

In a distributed system with several Erlang nodes, it can be + necessary to control applications in a distributed manner. If the node, where a certain application is running, goes down, - the application should be restarted at another node.

+ the application is to be restarted at another node.

Such an application is called a distributed application. - Note that it is the control of the application which is - distributed, all applications can of course be distributed in - the sense that they, for example, use services on other nodes.

-

Because a distributed application may move between nodes, some + Notice that it is the control of the application that is distributed. + All applications can be distributed in the sense that they, + for example, use services on other nodes.

+

Since a distributed application can move between nodes, some addressing mechanism is required to ensure that it can be addressed by other applications, regardless on which node it currently executes. This issue is not addressed here, but the - Kernel modules global or pg2 can be - used for this purpose.

+ global or pg2 modules in Kernel + can be used for this purpose.

Specifying Distributed Applications

Distributed applications are controlled by both the application - controller and a distributed application controller process, - dist_ac. Both these processes are part of the kernel - application. Therefore, distributed applications are specified by - configuring the kernel application, using the following - configuration parameter (see also kernel(6)):

- - distributed = [{Application, [Timeout,] NodeDesc}] - -

Specifies where the application Application = atom() - may execute. NodeDesc = [Node | {Node,...,Node}] is - a list of node names in priority order. The order between - nodes in a tuple is undefined.

-

Timeout = integer() specifies how many milliseconds to - wait before restarting the application at another node. - Defaults to 0.

-
-
+ controller and a distributed application controller process, + dist_ac. Both these processes are part of the Kernel + application. Distributed applications are thus specified by + configuring the Kernel application, using the following + configuration parameter (see also kernel(6)):

+

distributed = [{Application, [Timeout,] NodeDesc}]

+ + Specifies where the application Application = atom() + can execute. + >NodeDesc = [Node | {Node,...,Node}] is a list of + node names in priority order. The order between nodes in a tuple + is undefined. + Timeout = integer() specifies how many milliseconds + to wait before restarting the application at another node. It + defaults to 0. +

For distribution of application control to work properly, - the nodes where a distributed application may run must contact + the nodes where a distributed application can run must contact each other and negotiate where to start the application. This is - done using the following kernel configuration parameters:

- - sync_nodes_mandatory = [Node] - Specifies which other nodes must be started (within - the timeout specified by sync_nodes_timeout. - sync_nodes_optional = [Node] - Specifies which other nodes can be started (within - the timeout specified by sync_nodes_timeout. - sync_nodes_timeout = integer() | infinity - Specifies how many milliseconds to wait for the other nodes - to start. - -

When started, the node will wait for all nodes specified by + done using the following configuration parameters in + Kernel:

+ + sync_nodes_mandatory = [Node] - Specifies which + other nodes must be started (within the time-out specified by + sync_nodes_timeout). + sync_nodes_optional = [Node] - Specifies which + other nodes can be started (within the time-out specified by + sync_nodes_timeout). + sync_nodes_timeout = integer() | infinity - + Specifies how many milliseconds to wait for the other nodes to + start. + +

When started, the node waits for all nodes specified by sync_nodes_mandatory and sync_nodes_optional to - come up. When all nodes have come up, or when all mandatory nodes - have come up and the time specified by sync_nodes_timeout - has elapsed, all applications will be started. If not all - mandatory nodes have come up, the node will terminate.

-

Example: An application myapp should run at the node - cp1@cave. If this node goes down, myapp should + come up. When all nodes are up, or when all mandatory nodes + are up and the time specified by sync_nodes_timeout + has elapsed, all applications start. If not all + mandatory nodes are up, the node terminates.

+

Example:

+

An application myapp is to run at the node + cp1@cave. If this node goes down, myapp is to be restarted at cp2@cave or cp3@cave. A system - configuration file cp1.config for cp1@cave could - look like:

+ configuration file cp1.config for cp1@cave can + look as follows:

[{kernel, [{distributed, [{myapp, 5000, [cp1@cave, {cp2@cave, cp3@cave}]}]}, @@ -103,13 +105,13 @@ ].

The system configuration files for cp2@cave and cp3@cave are identical, except for the list of mandatory - nodes which should be [cp1@cave, cp3@cave] for + nodes, which is to be [cp1@cave, cp3@cave] for cp2@cave and [cp1@cave, cp2@cave] for cp3@cave.

All involved nodes must have the same value for - distributed and sync_nodes_timeout, or - the behaviour of the system is undefined.

+ distributed and sync_nodes_timeout. + Otherwise the system behaviour is undefined.

@@ -117,28 +119,29 @@ Starting and Stopping Distributed Applications

When all involved (mandatory) nodes have been started, the distributed application can be started by calling - application:start(Application) at all of these nodes.

-

It is of course also possible to use a boot script (see - Releases) which - automatically starts the application.

-

The application will be started at the first node, specified - by the distributed configuration parameter, which is up - and running. The application is started as usual. That is, an - application master is created and calls the application callback - function:

+ application:start(Application) at all of these + nodes.

+

A boot script (see + Releases) + can be used that automatically starts the application.

+

The application is started at the first operational node that + is listed in the list of nodes in the distributed + configuration parameter. The application is started as usual. + That is, an application master is created and calls the + application callback function:

Module:start(normal, StartArgs) -

Example: Continuing the example from the previous section, - the three nodes are started, specifying the system configuration - file:

+

Example:

+

Continuing the example from the previous section, the three nodes + are started, specifying the system configuration file:

 > erl -sname cp1 -config cp1
 > erl -sname cp2 -config cp2
 > erl -sname cp3 -config cp3
-

When all nodes are up and running, myapp can be started. +

When all nodes are operational, myapp can be started. This is achieved by calling application:start(myapp) at all three nodes. It is then started at cp1, as shown in - the figure below.

+ the following figure:

Application myapp - Situation 1 @@ -150,31 +153,33 @@ Module:start(normal, StartArgs)
Failover

If the node where the application is running goes down, - the application is restarted (after the specified timeout) at - the first node, specified by the distributed configuration - parameter, which is up and running. This is called a + the application is restarted (after the specified time-out) at + the first operational node that is listed in the list of nodes + in the distributed configuration parameter. This is called a failover.

The application is started the normal way at the new node, that is, by the application master calling:

Module:start(normal, StartArgs) -

Exception: If the application has the start_phases key - defined (see Included Applications), then the application is instead started - by calling:

+

An exception is if the application has the start_phases + key defined + (see Included Applications). + The application is then instead started by calling:

Module:start({failover, Node}, StartArgs) -

where Node is the terminated node.

-

Example: If cp1 goes down, the system checks which one of +

Here Node is the terminated node.

+

Example:

+

If cp1 goes down, the system checks which one of the other nodes, cp2 or cp3, has the least number of running applications, but waits for 5 seconds for cp1 to restart. If cp1 does not restart and cp2 runs fewer - applications than cp3, then myapp is restarted on + applications than cp3, myapp is restarted on cp2.

Application myapp - Situation 2 -

Suppose now that cp2 goes down as well and does not +

Suppose now that cp2 goes also down and does not restart within 5 seconds. myapp is now restarted on cp3.

@@ -186,28 +191,29 @@ Module:start({failover, Node}, StartArgs)
Takeover

If a node is started, which has higher priority according - to distributed, than the node where a distributed - application is currently running, the application will be - restarted at the new node and stopped at the old node. This is + to distributed than the node where a distributed + application is running, the application is restarted at the + new node and stopped at the old node. This is called a takeover.

The application is started by the application master calling:

Module:start({takeover, Node}, StartArgs) -

where Node is the old node.

-

Example: If myapp is running at cp3, and if - cp2 now restarts, it will not restart myapp, - because the order between nodes cp2 and cp3 is +

Here Node is the old node.

+

Example:

+

If myapp is running at cp3, and if + cp2 now restarts, it does not restart myapp, + as the order between the cp2 and cp3 nodes is undefined.

Application myapp - Situation 4 -

However, if cp1 restarts as well, the function +

However, if cp1 also restarts, the function application:takeover/2 moves myapp to cp1, - because cp1 has a higher priority than cp3 for this - application. In this case, - Module:start({takeover, cp3@cave}, StartArgs) is executed - at cp1 to start the application.

+ as cp1 has a higher priority than cp3 for this + application. In this case, + Module:start({takeover, cp3@cave}, StartArgs) is + executed at cp1 to start the application.

Application myapp - Situation 5 diff --git a/system/doc/design_principles/events.xml b/system/doc/design_principles/events.xml index 529e12c216..6e5afb939e 100644 --- a/system/doc/design_principles/events.xml +++ b/system/doc/design_principles/events.xml @@ -21,7 +21,7 @@ - Gen_Event Behaviour + gen_event Behaviour @@ -29,35 +29,36 @@ events.xml -

This chapter should be read in conjunction with - gen_event(3), where all interface functions and callback - functions are described in detail.

+

This section is to be read with the gen_event(3) manual + page in STDLIB, where all interface functions and callback + functions are described in detail.

Event Handling Principles

In OTP, an event manager is a named object to which - events can be sent. An event could be, for example, - an error, an alarm or some information that should be logged.

-

In the event manager, zero, one or several event handlers are installed. When the event manager is notified - about an event, the event will be processed by all the installed + events can be sent. An event can be, for example, + an error, an alarm, or some information that is to be logged.

+

In the event manager, zero, one, or many event handlers + are installed. When the event manager is notified + about an event, the event is processed by all the installed event handlers. For example, an event manager for handling errors - can by default have a handler installed which writes error + can by default have a handler installed, which writes error messages to the terminal. If the error messages during a certain - period should be saved to a file as well, the user adds another - event handler which does this. When logging to file is no longer - necessary, this event handler is deleted.

+ period is to be saved to a file as well, the user adds another + event handler that does this. When logging to the file is no + longer necessary, this event handler is deleted.

An event manager is implemented as a process and each event handler is implemented as a callback module.

The event manager essentially maintains a list of {Module, State} pairs, where each Module is an - event handler, and State the internal state of that event - handler.

+ event handler, and State is the internal state of that + event handler.

Example

The callback module for the event handler writing error messages - to the terminal could look like:

+ to the terminal can look as follows:

-module(terminal_logger). -behaviour(gen_event). @@ -74,7 +75,7 @@ handle_event(ErrorMsg, State) -> terminate(_Args, _State) -> ok.

The callback module for the event handler writing error messages - to a file could look like:

+ to a file can look as follows:

-module(file_logger). -behaviour(gen_event). @@ -98,29 +99,28 @@ terminate(_Args, Fd) -> Starting an Event Manager

To start an event manager for handling errors, as described in - the example above, call the following function:

+ the previous example, call the following function:

gen_event:start_link({local, error_man})

This function spawns and links to a new process, an event manager.

-

The argument, {local, error_man} specifies the name. In - this case, the event manager will be locally registered as - error_man.

+

The argument, {local, error_man} specifies the name. The + event manager is then locally registered as error_man.

If the name is omitted, the event manager is not registered. - Instead its pid must be used. The name could also be given + Instead its pid must be used. The name can also be given as {global, Name}, in which case the event manager is registered using global:register_name/2.

gen_event:start_link must be used if the event manager is - part of a supervision tree, i.e. is started by a supervisor. - There is another function gen_event:start to start a - stand-alone event manager, i.e. an event manager which is not + part of a supervision tree, that is, started by a supervisor. + There is another function, gen_event:start, to start a + standalone event manager, that is, an event manager that is not part of a supervision tree.

Adding an Event Handler -

Here is an example using the shell on how to start an event - manager and add an event handler to it:

+

The following example shows how to start an event manager and + add an event handler to it by using the shell:

 1> gen_event:start({local, error_man}).
 {ok,<0.31.0>}
@@ -128,16 +128,16 @@ gen_event:start_link({local, error_man})
 ok

This function sends a message to the event manager registered as error_man, telling it to add the event handler - terminal_logger. The event manager will call the callback - function terminal_logger:init([]), where the argument [] - is the third argument to add_handler. init is - expected to return {ok, State}, where State is + terminal_logger. The event manager calls the callback + function terminal_logger:init([]), where the argument + [] is the third argument to add_handler. init + is expected to return {ok, State}, where State is the internal state of the event handler.

init(_Args) -> {ok, []}.

Here, init does not need any input data and ignores its - argument. Also, for terminal_logger the internal state is + argument. For terminal_logger, the internal state is not used. For file_logger, the internal state is used to save the open file descriptor.

@@ -147,7 +147,7 @@ init(File) ->
- Notifying About Events + Notifying about Events
 3> gen_event:notify(error_man, no_reply).
 ***Error*** no_reply
@@ -158,7 +158,7 @@ ok
When the event is received, the event manager calls handle_event(Event, State) for each installed event handler, in the same order as they were added. The function is - expected to return a tuple {ok, State1}, where + expected to return a tuple {ok,State1}, where State1 is a new value for the state of the event handler.

In terminal_logger:

@@ -179,17 +179,17 @@ handle_event(ErrorMsg, Fd) -> ok

This function sends a message to the event manager registered as error_man, telling it to delete the event handler - terminal_logger. The event manager will call the callback + terminal_logger. The event manager calls the callback function terminal_logger:terminate([], State), where - the argument [] is the third argument to delete_handler. - terminate should be the opposite of init and do any + the argument [] is the third argument to delete_handler. + terminate is to be the opposite of init and do any necessary cleaning up. Its return value is ignored.

For terminal_logger, no cleaning up is necessary:

terminate(_Args, _State) -> ok.

For file_logger, the file descriptor opened in init - needs to be closed:

+ must be closed:

terminate(_Args, Fd) -> file:close(Fd). @@ -197,20 +197,22 @@ terminate(_Args, Fd) ->
Stopping -

When an event manager is stopped, it will give each of +

When an event manager is stopped, it gives each of the installed event handlers the chance to clean up by calling terminate/2, the same way as when deleting a handler.

In a Supervision Tree

If the event manager is part of a supervision tree, no stop - function is needed. The event manager will automatically be + function is needed. The event manager is automatically terminated by its supervisor. Exactly how this is done is - defined by a shutdown strategy set in the supervisor.

+ defined by a + shutdown strategy + set in the supervisor.

- Stand-Alone Event Managers + Standalone Event Managers

An event manager can also be stopped by calling:

 > gen_event:stop(error_man).
@@ -219,16 +221,17 @@ ok
Handling Other Messages -

If the gen_event should be able to receive other messages than - events, the callback function handle_info(Info, StateName, StateData) - must be implemented to handle them. Examples of - other messages are exit messages, if the gen_event is linked to +

If the gen_event is to be able to receive other messages + than events, the callback function + handle_info(Info, StateName, StateData) + must be implemented to handle them. Examples of other + messages are exit messages, if the gen_event is linked to other processes (than the supervisor) and trapping exit signals.

handle_info({'EXIT', Pid, Reason}, State) -> ..code to handle exits here.. {ok, NewState}. -

The code_change method also has to be implemented.

+

The code_change method must also be implemented.

code_change(OldVsn, State, Extra) -> ..code to convert state (and more) during code change diff --git a/system/doc/design_principles/fsm.xml b/system/doc/design_principles/fsm.xml index 9dce159dca..ef961f5fad 100644 --- a/system/doc/design_principles/fsm.xml +++ b/system/doc/design_principles/fsm.xml @@ -21,32 +21,33 @@ - Gen_Fsm Behaviour + gen_fsm Behaviour fsm.xml -

This chapter should be read in conjunction with gen_fsm(3), - where all interface functions and callback functions are described - in detail.

+ +

This section is to be read with the gen_fsm(3) manual page + in STDLIB, where all interface functions and callback + functions are described in detail.

- Finite State Machines -

A finite state machine, FSM, can be described as a set of + Finite-State Machines +

A Finite-State Machine (FSM) can be described as a set of relations of the form:

 State(S) x Event(E) -> Actions(A), State(S')

These relations are interpreted as meaning:

-

If we are in state S and the event E occurs, we - should perform the actions A and make a transition to - the state S'.

+

If we are in state S and event E occurs, we + are to perform actions A and make a transition to + state S'.

For an FSM implemented using the gen_fsm behaviour, the state transition rules are written as a number of Erlang - functions which conform to the following convention:

+ functions, which conform to the following convention:

 StateName(Event, StateData) ->
     .. code for actions here ...
@@ -55,16 +56,16 @@ StateName(Event, StateData) ->
 
   
Example -

A door with a code lock could be viewed as an FSM. Initially, +

A door with a code lock can be viewed as an FSM. Initially, the door is locked. Anytime someone presses a button, this generates an event. Depending on what buttons have been pressed - before, the sequence so far may be correct, incomplete or wrong.

-

If it is correct, the door is unlocked for 30 seconds (30000 ms). + before, the sequence so far can be correct, incomplete, or wrong.

+

If it is correct, the door is unlocked for 30 seconds (30,000 ms). If it is incomplete, we wait for another button to be pressed. If it is is wrong, we start all over, waiting for a new button sequence.

Implementing the code lock FSM using gen_fsm results in - this callback module:

+ the following callback module:

- Starting a Gen_Fsm -

In the example in the previous section, the gen_fsm is started by - calling code_lock:start_link(Code):

+ Starting gen_fsm +

In the example in the previous section, the gen_fsm is + started by calling code_lock:start_link(Code):

start_link(Code) -> gen_fsm:start_link({local, code_lock}, code_lock, lists:reverse(Code), []). -

start_link calls the function gen_fsm:start_link/4. - This function spawns and links to a new process, a gen_fsm.

+

start_link calls the function gen_fsm:start_link/4, + which spawns and links to a new process, a gen_fsm.

-

The first argument {local, code_lock} specifies - the name. In this case, the gen_fsm will be locally registered - as code_lock.

-

If the name is omitted, the gen_fsm is not registered. - Instead its pid must be used. The name could also be given as - {global, Name}, in which case the gen_fsm is registered - using global:register_name/2.

+

The first argument, {local, code_lock}, specifies + the name. In this case, the gen_fsm is locally + registered as code_lock.

+

If the name is omitted, the gen_fsm is not registered. + Instead its pid must be used. The name can also be given + as {global, Name}, in which case the gen_fsm is + registered using global:register_name/2.

The second argument, code_lock, is the name of - the callback module, that is the module where the callback + the callback module, that is, the module where the callback functions are located.

-

In this case, the interface functions (start_link and - button) are located in the same module as the callback - functions (init, locked and open). This +

The interface functions (start_link and button) + are then located in the same module as the callback + functions (init, locked, and open). This is normally good programming practice, to have the code corresponding to one process contained in one module.

-

The third argument, Code, is a list of digits which is passed - reversed to the callback function init. Here, init +

The third argument, Code, is a list of digits that + which is passed reversed to the callback function init. + Here, init gets the correct code for the lock as indata.

-

The fourth argument, [], is a list of options. See - gen_fsm(3) for available options.

+

The fourth argument, [], is a list of options. See + the gen_fsm(3) manual page for available options.

-

If name registration succeeds, the new gen_fsm process calls +

If name registration succeeds, the new gen_fsm process calls the callback function code_lock:init(Code). This function is expected to return {ok, StateName, StateData}, where - StateName is the name of the initial state of the gen_fsm. - In this case locked, assuming the door is locked to begin - with. StateData is the internal state of the gen_fsm. (For - gen_fsms, the internal state is often referred to 'state data' to + StateName is the name of the initial state of the + gen_fsm. In this case locked, assuming the door is + locked to begin with. StateData is the internal state of + the gen_fsm. (For gen_fsm, the internal state is + often referred to 'state data' to distinguish it from the state as in states of a state machine.) In this case, the state data is the button sequence so far (empty to begin with) and the correct code of the lock.

init(Code) -> {ok, locked, {[], Code}}. -

Note that gen_fsm:start_link is synchronous. It does not - return until the gen_fsm has been initialized and is ready to +

gen_fsm:start_link is synchronous. It does not return until + the gen_fsm has been initialized and is ready to receive notifications.

-

gen_fsm:start_link must be used if the gen_fsm is part of - a supervision tree, i.e. is started by a supervisor. There is - another function gen_fsm:start to start a stand-alone - gen_fsm, i.e. a gen_fsm which is not part of a supervision tree.

+

gen_fsm:start_link must be used if the gen_fsm is + part of a supervision tree, that is, started by a supervisor. There + is another function, gen_fsm:start, to start a standalone + gen_fsm, that is, a gen_fsm that is not part of a + supervision tree.

- Notifying About Events + Notifying about Events

The function notifying the code lock about a button event is implemented using gen_fsm:send_event/2:

button(Digit) -> gen_fsm:send_event(code_lock, {button, Digit}). -

code_lock is the name of the gen_fsm and must agree with - the name used to start it. {button, Digit} is the actual - event.

-

The event is made into a message and sent to the gen_fsm. When - the event is received, the gen_fsm calls - StateName(Event, StateData) which is expected to return a - tuple {next_state, StateName1, StateData1}. +

code_lock is the name of the gen_fsm and must + agree with the name used to start it. + {button, Digit} is the actual event.

+

The event is made into a message and sent to the gen_fsm. + When the event is received, the gen_fsm calls + StateName(Event, StateData), which is expected to return a + tuple {next_state,StateName1,StateData1}. StateName is the name of the current state and StateName1 is the name of the next state to go to. StateData1 is a new value for the state data of - the gen_fsm.

+ the gen_fsm.

case [Digit|SoFar] of @@ -198,20 +202,21 @@ open(timeout, State) ->

If the door is locked and a button is pressed, the complete button sequence so far is compared with the correct code for the lock and, depending on the result, the door is either unlocked - and the gen_fsm goes to state open, or the door remains in - state locked.

+ and the gen_fsm goes to state open, or the door + remains in state locked.

- Timeouts + Time-Outs

When a correct code has been given, the door is unlocked and the following tuple is returned from locked/2:

{next_state, open, {[], Code}, 30000}; -

30000 is a timeout value in milliseconds. After 30000 ms, i.e. - 30 seconds, a timeout occurs. Then StateName(timeout, StateData) is called. In this case, the timeout occurs when - the door has been in state open for 30 seconds. After that - the door is locked again:

+

30,000 is a time-out value in milliseconds. After this time, + that is, 30 seconds, a time-out occurs. Then, + StateName(timeout, StateData) is called. The time-out + then occurs when the door has been in state open for 30 + seconds. After that the door is locked again:

open(timeout, State) -> do_lock(), @@ -220,7 +225,7 @@ open(timeout, State) ->
All State Events -

Sometimes an event can arrive at any state of the gen_fsm. +

Sometimes an event can arrive at any state of the gen_fsm. Instead of sending the message with gen_fsm:send_event/2 and writing one clause handling the event for each state function, the message can be sent with gen_fsm:send_all_state_event/2 @@ -245,15 +250,16 @@ handle_event(stop, _StateName, StateData) ->

In a Supervision Tree -

If the gen_fsm is part of a supervision tree, no stop function - is needed. The gen_fsm will automatically be terminated by its - supervisor. Exactly how this is done is defined by a - shutdown strategy +

If the gen_fsm is part of a supervision tree, no stop + function is needed. The gen_fsm is automatically + terminated by its supervisor. Exactly how this is done is + defined by a + shutdown strategy set in the supervisor.

If it is necessary to clean up before termination, the shutdown - strategy must be a timeout value and the gen_fsm must be set to - trap exit signals in the init function. When ordered - to shutdown, the gen_fsm will then call the callback function + strategy must be a time-out value and the gen_fsm must be + set to trap exit signals in the init function. When ordered + to shutdown, the gen_fsm then calls the callback function terminate(shutdown, StateName, StateData):

init(Args) -> @@ -270,9 +276,9 @@ terminate(shutdown, StateName, StateData) ->
- Stand-Alone Gen_Fsms -

If the gen_fsm is not part of a supervision tree, a stop - function may be useful, for example:

+ Standalone gen_fsm +

If the gen_fsm is not part of a supervision tree, a stop + function can be useful, for example:

... -export([stop/0]). @@ -290,26 +296,28 @@ handle_event(stop, _StateName, StateData) -> terminate(normal, _StateName, _StateData) -> ok.

The callback function handling the stop event returns a - tuple {stop,normal,StateData1}, where normal + tuple, {stop,normal,StateData1}, where normal specifies that it is a normal termination and StateData1 - is a new value for the state data of the gen_fsm. This will - cause the gen_fsm to call + is a new value for the state data of the gen_fsm. This + causes the gen_fsm to call terminate(normal,StateName,StateData1) and then - terminate gracefully:

+ it terminates gracefully:

Handling Other Messages -

If the gen_fsm should be able to receive other messages than - events, the callback function handle_info(Info, StateName, StateData) must be implemented to handle them. Examples of - other messages are exit messages, if the gen_fsm is linked to +

If the gen_fsm is to be able to receive other messages + than events, the callback function + handle_info(Info, StateName, StateData) must be implemented + to handle them. Examples of + other messages are exit messages, if the gen_fsm is linked to other processes (than the supervisor) and trapping exit signals.

handle_info({'EXIT', Pid, Reason}, StateName, StateData) -> ..code to handle exits here.. {next_state, StateName1, StateData1}. -

The code_change method also has to be implemented.

+

The code_change method must also be implemented.

code_change(OldVsn, StateName, StateData, Extra) -> ..code to convert state (and more) during code change diff --git a/system/doc/design_principles/gen_server_concepts.xml b/system/doc/design_principles/gen_server_concepts.xml index d24d87aa03..d721845c6d 100644 --- a/system/doc/design_principles/gen_server_concepts.xml +++ b/system/doc/design_principles/gen_server_concepts.xml @@ -21,7 +21,7 @@ - Gen_Server Behaviour + gen_server Behaviour @@ -29,16 +29,16 @@ gen_server_concepts.xml -

This chapter should be read in conjunction with - gen_server(3), - where all interface functions and callback - functions are described in detail.

+

This section is to be read with the + gen_server(3) + manual page in stdblib, where all interface functions and + callback functions are described in detail.

Client-Server Principles

The client-server model is characterized by a central server and an arbitrary number of clients. The client-server model is - generally used for resource management operations, where several + used for resource management operations, where several different clients want to share a common resource. The server is responsible for managing this resource.

@@ -49,9 +49,10 @@
Example -

An example of a simple server written in plain Erlang was - given in Overview. - The server can be re-implemented using gen_server, +

An example of a simple server written in plain Erlang is + provided in + Overview. + The server can be reimplemented using gen_server, resulting in this callback module:

@@ -86,61 +87,60 @@ handle_cast({free, Ch}, Chs) ->
Starting a Gen_Server -

In the example in the previous section, the gen_server is started - by calling ch3:start_link():

+

In the example in the previous section, gen_server is + started by calling ch3:start_link():

start_link() -> gen_server:start_link({local, ch3}, ch3, [], []) => {ok, Pid} -

start_link calls the function - gen_server:start_link/4. This function spawns and links to - a new process, a gen_server.

+

start_link calls function gen_server:start_link/4. + This function spawns and links to a new process, a + gen_server.

-

The first argument {local, ch3} specifies the name. In - this case, the gen_server will be locally registered as - ch3.

-

If the name is omitted, the gen_server is not registered. - Instead its pid must be used. The name could also be given - as {global, Name}, in which case the gen_server is +

The first argument, {local, ch3}, specifies the name. + The gen_server is then locally registered as ch3.

+

If the name is omitted, the gen_server is not registered. + Instead its pid must be used. The name can also be given + as {global, Name}, in which case the gen_server is registered using global:register_name/2.

The second argument, ch3, is the name of the callback - module, that is the module where the callback functions are + module, that is, the module where the callback functions are located.

-

In this case, the interface functions (start_link, - alloc and free) are located in the same module - as the callback functions (init, handle_call and +

The interface functions (start_link, alloc, + and free) are then located in the same module + as the callback functions (init, handle_call, and handle_cast). This is normally good programming practice, to have the code corresponding to one process contained in one module.

-

The third argument, [], is a term which is passed as-is to - the callback function init. Here, init does not +

The third argument, [], is a term that is passed as is + to the callback function init. Here, init does not need any indata and ignores the argument.

-

The fourth argument, [], is a list of options. See - gen_server(3) for available options.

+

The fourth argument, [], is a list of options. See the + gen_server(3) manual page for available options.

-

If name registration succeeds, the new gen_server process calls - the callback function ch3:init([]). init is expected - to return {ok, State}, where State is the internal - state of the gen_server. In this case, the state is the available - channels.

+

If name registration succeeds, the new gen_server process + calls the callback function ch3:init([]). init is + expected to return {ok, State}, where State is the + internal state of the gen_server. In this case, the state + is the available channels.

init(_Args) -> {ok, channels()}. -

Note that gen_server:start_link is synchronous. It does - not return until the gen_server has been initialized and is ready +

gen_server:start_link is synchronous. It does not return + until the gen_server has been initialized and is ready to receive requests.

-

gen_server:start_link must be used if the gen_server is - part of a supervision tree, i.e. is started by a supervisor. - There is another function gen_server:start to start a - stand-alone gen_server, i.e. a gen_server which is not part of a - supervision tree.

+

gen_server:start_link must be used if the gen_server + is part of a supervision tree, that is, started by a supervisor. + There is another function, gen_server:start, to start a + standalone gen_server, that is, a gen_server that + is not part of a supervision tree.

@@ -150,14 +150,17 @@ init(_Args) -> alloc() -> gen_server:call(ch3, alloc). -

ch3 is the name of the gen_server and must agree with - the name used to start it. alloc is the actual request.

-

The request is made into a message and sent to the gen_server. - When the request is received, the gen_server calls - handle_call(Request, From, State) which is expected to - return a tuple {reply, Reply, State1}. Reply is - the reply which should be sent back to the client, and - State1 is a new value for the state of the gen_server.

+

ch3 is the name of the gen_server and must agree + with the name used to start it. alloc is the actual + request.

+

The request is made into a message and sent to the + gen_server. When the request is received, the + gen_server calls + handle_call(Request, From, State), which is expected to + return a tuple {reply,Reply,State1}. Reply is + the reply that is to be sent back to the client, and + State1 is a new value for the state of the + gen_server.

handle_call(alloc, _From, Chs) -> {Ch, Chs2} = alloc(Chs), @@ -166,8 +169,8 @@ handle_call(alloc, _From, Chs) -> the new state is the set of remaining available channels Chs2.

Thus, the call ch3:alloc() returns the allocated channel - Ch and the gen_server then waits for new requests, now - with an updated list of available channels.

+ Ch and the gen_server then waits for new requests, + now with an updated list of available channels.

@@ -177,20 +180,21 @@ handle_call(alloc, _From, Chs) -> free(Ch) -> gen_server:cast(ch3, {free, Ch}). -

ch3 is the name of the gen_server. {free, Ch} is - the actual request.

-

The request is made into a message and sent to the gen_server. +

ch3 is the name of the gen_server. + {free, Ch} is the actual request.

+

The request is made into a message and sent to the + gen_server. cast, and thus free, then returns ok.

-

When the request is received, the gen_server calls - handle_cast(Request, State) which is expected to - return a tuple {noreply, State1}. State1 is a new - value for the state of the gen_server.

+

When the request is received, the gen_server calls + handle_cast(Request, State), which is expected to + return a tuple {noreply,State1}. State1 is a new + value for the state of the gen_server.

handle_cast({free, Ch}, Chs) -> Chs2 = free(Ch, Chs), {noreply, Chs2}.

In this case, the new state is the updated list of available - channels Chs2. The gen_server is now ready for new + channels Chs2. The gen_server is now ready for new requests.

@@ -199,15 +203,17 @@ handle_cast({free, Ch}, Chs) ->
In a Supervision Tree -

If the gen_server is part of a supervision tree, no stop - function is needed. The gen_server will automatically be +

If the gen_server is part of a supervision tree, no stop + function is needed. The gen_server is automatically terminated by its supervisor. Exactly how this is done is - defined by a shutdown strategy set in the supervisor.

+ defined by a + shutdown strategy + set in the supervisor.

If it is necessary to clean up before termination, the shutdown - strategy must be a timeout value and the gen_server must be set - to trap exit signals in the init function. When ordered - to shutdown, the gen_server will then call the callback function - terminate(shutdown, State):

+ strategy must be a time-out value and the gen_server must + be set to trap exit signals in function init. When ordered + to shutdown, the gen_server then calls the callback + function terminate(shutdown, State):

init(Args) -> ..., @@ -223,9 +229,9 @@ terminate(shutdown, State) ->
- Stand-Alone Gen_Servers -

If the gen_server is not part of a supervision tree, a stop - function may be useful, for example:

+ Standalone Gen_Servers +

If the gen_server is not part of a supervision tree, a + stop function can be useful, for example:

... export([stop/0]). @@ -245,26 +251,26 @@ handle_cast({free, Ch}, State) -> terminate(normal, State) -> ok.

The callback function handling the stop request returns - a tuple {stop, normal, State1}, where normal + a tuple {stop,normal,State1}, where normal specifies that it is a normal termination and State1 is - a new value for the state of the gen_server. This will cause - the gen_server to call terminate(normal,State1) and then - terminate gracefully.

+ a new value for the state of the gen_server. This causes + the gen_server to call terminate(normal, State1) + and then it terminates gracefully.

Handling Other Messages -

If the gen_server should be able to receive other messages than - requests, the callback function handle_info(Info, State) +

If the gen_server is to be able to receive other messages + than requests, the callback function handle_info(Info, State) must be implemented to handle them. Examples of other messages - are exit messages, if the gen_server is linked to other processes - (than the supervisor) and trapping exit signals.

+ are exit messages, if the gen_server is linked to other + processes (than the supervisor) and trapping exit signals.

handle_info({'EXIT', Pid, Reason}, State) -> ..code to handle exits here.. {noreply, State1}. -

The code_change method also has to be implemented.

+

The code_change method must also be implemented.

code_change(OldVsn, State, Extra) -> ..code to convert state (and more) during code change diff --git a/system/doc/design_principles/included_applications.xml b/system/doc/design_principles/included_applications.xml index 3aa43fd595..7f139edf76 100644 --- a/system/doc/design_principles/included_applications.xml +++ b/system/doc/design_principles/included_applications.xml @@ -28,35 +28,36 @@ included_applications.xml +
- Definition + Introduction

An application can include other applications. An included application has its own application directory and .app file, but it is started as part of the supervisor tree of another application.

An application can only be included by one other application.

An included application can include other applications.

-

An application which is not included by any other application is +

An application that is not included by any other application is called a primary application.

- Primary Application and Included Applications. + Primary Application and Included Applications -

The application controller will automatically load any included - applications when loading a primary application, but not start +

The application controller automatically loads any included + applications when loading a primary application, but does not start them. Instead, the top supervisor of the included application must be started by a supervisor in the including application.

This means that when running, an included application is in fact - part of the primary application and a process in an included - application will consider itself belonging to the primary + part of the primary application, and a process in an included + application considers itself belonging to the primary application.

Specifying Included Applications

Which applications to include is defined by - the included_applications key in the .app file.

+ the included_applications key in the .app file:

 {application, prim_app,
  [{description, "Tree application"},
@@ -71,7 +72,7 @@
   
- Synchronizing Processes During Startup + Synchronizing Processes during Startup

The supervisor tree of an included application is started as part of the supervisor tree of the including application. If there is a need for synchronization between processes in @@ -79,12 +80,12 @@ by using start phases.

Start phases are defined by the start_phases key in the .app file as a list of tuples {Phase,PhaseArgs}, - where Phase is an atom and PhaseArgs is a term. - Also, the value of the mod key of the including application + where Phase is an atom and PhaseArgs is a term.

+

The value of the mod key of the including application must be set to {application_starter,[Module,StartArgs]}, - where Module as usual is the application callback module - and StartArgs a term provided as argument to the callback - function Module:start/2.

+ where Module as usual is the application callback module. + StartArgs is a term provided as argument to the callback + function Module:start/2:

{application, prim_app, [{description, "Tree application"}, @@ -108,36 +109,38 @@ {mod, {incl_app_cb,[]}} ]}.

When starting a primary application with included applications, - the primary application is started the normal way: - The application controller creates an application master for - the application, and the application master calls + the primary application is started the normal way, that is:

+ + The application controller creates an application master for + the application + The application master calls Module:start(normal, StartArgs) to start the top - supervisor.

+ supervisor.
+

Then, for the primary application and each included application in top-down, left-to-right order, the application master calls Module:start_phase(Phase, Type, PhaseArgs) for each phase - defined for for the primary application, in that order. - Note that if a phase is not defined for an included application, + defined for the primary application, in that order. If a phase + is not defined for an included application, the function is not called for this phase and application.

The following requirements apply to the .app file for an included application:

- The {mod, {Module,StartArgs}} option must be - included. This option is used to find the callback module - Module of the application. StartArgs is ignored, - as Module:start/2 is called only for the primary - application. + The {mod, {Module,StartArgs}} option must be included. + This option is used to find the callback module Module of the + application. StartArgs is ignored, as Module:start/2 + is called only for the primary application. If the included application itself contains included - applications, instead the option - {mod, {application_starter, [Module,StartArgs]}} must be - included. + applications, instead the + {mod, {application_starter, [Module,StartArgs]}} option + must be included. The {start_phases, [{Phase,PhaseArgs}]} option must - be included, and the set of specified phases must be a subset - of the set of phases specified for the primary application. + be included, and the set of specified phases must be a subset + of the set of phases specified for the primary application.

When starting prim_app as defined above, the application - controller will call the following callback functions, before - application:start(prim_app) returns a value:

+ controller calls the following callback functions before + application:start(prim_app) returns a value:

application:start(prim_app) => prim_app_cb:start(normal, []) diff --git a/system/doc/design_principles/release_handling.xml b/system/doc/design_principles/release_handling.xml index 9d1e2c8669..eeb71125b6 100644 --- a/system/doc/design_principles/release_handling.xml +++ b/system/doc/design_principles/release_handling.xml @@ -28,128 +28,126 @@ release_handling.xml +
Release Handling Principles

An important feature of the Erlang programming language is - the ability to change module code in run-time, code replacement, as described in Erlang Reference Manual.

+ the ability to change module code in runtime, + code replacement, as described in the Erlang + Reference Manual.

Based on this feature, the OTP application SASL provides a framework for upgrading and downgrading between different - versions of an entire release in run-time. This is what we call + versions of an entire release in runtime. This is called release handling.

-

The framework consists of off-line support (systools) for - generating scripts and building release packages, and on-line - support (release_handler) for unpacking and installing - release packages.

-

Note that the minimal system based on Erlang/OTP, enabling - release handling, thus consists of Kernel, STDLIB and SASL.

- - -

A release is created as described in the previous chapter - Releases. - The release is transferred to and installed at target - environment. Refer to System Principles for - information of how to install the first target system.

-
- -

Modifications, for example error corrections, are made to - the code in the development environment.

-
- -

At some point it is time to make a new version of release. - The relevant .app files are updated and a new - .rel file is written.

-
- -

For each modified application, an - application upgrade file, - .appup, is created. In this file, it is described how - to upgrade and/or downgrade between the old and new version of - the application.

-
- -

Based on the .appup files, a - release upgrade file called - relup, is created. This file describes how to upgrade - and/or downgrade between the old and new version of - the entire release.

-
- -

A new release package is made and transferred to - the target system.

-
- -

The new release package is unpacked using the release - handler.

-
- -

The new version of the release is installed, also using - the release handler. This is done by evaluating - the instructions in relup. Modules may be added, - deleted or re-loaded, applications may be started, stopped or - re-started etc. In some cases, it is even necessary to restart - the entire emulator.

-

If the installation fails, the system may be rebooted. - The old release version is then automatically used.

-
- -

If the installation succeeds, the new version is made - the default version, which should now be used in case of a - system reboot.

-
-
-

The next chapter, Appup Cookbook, contains examples of .appup files - for typical cases of upgrades/downgrades that are normally easy - to handle in run-time. However, there are a many aspects that can - make release handling complicated. To name a few examples:

+

The framework consists of:

- -

Complicated or circular dependencies can make it difficult - or even impossible to decide in which order things must be - done without risking run-time errors during an upgrade or - downgrade. Dependencies may be:

- - between nodes, - between processes, and - between modules. - -
- -

During release handling, non-affected processes continue - normal execution. This may lead to timeouts or other problems. - For example, new processes created in the time window between - suspending processes using a certain module and loading a new - version of this module, may execute old code.

-
+ Offline support - systools for generating scripts + and building release packages + Online support - release_handler for unpacking and + installing release packages
-

It is therefore recommended that code is changed in as small +

The minimal system based on Erlang/OTP, enabling release handling, + thus consists of the Kernel, STDLIB, and SASL + applications.

+ +
+ Release Handling Workflow +

Step 1) A release is created as described in + Releases.

+

Step 2) The release is transferred to and installed at + target environment. For information of how to install the first + target system, see System Principles.

+

Step 3) Modifications, for example, error corrections, + are made to the code in the development environment.

+

Step 4) At some point, it is time to make a new version + of release. The relevant .app files are updated and a new + .rel file is written.

+

Step 5) For each modified application, an + application upgrade file, + .appup, is created. In this file, it is described how to + upgrade and/or downgrade between the old and new version of the + application.

+

Step 6) Based on the .appup files, a + release upgrade file called + relup, is created. This file describes how to upgrade and/or + downgrade between the old and new version of the entire release.

+

Step 7) A new release package is made and transferred to + the target system.

+

Step 8) The new release package is unpacked using the + release handler.

+

Step 9) The new version of the release is installed, + also using the release handler. This is done by evaluating the + instructions in relup. Modules can be added, deleted, or + reloaded, applications can be started, stopped, or restarted, and so + on. In some cases, it is even necessary to restart the entire + emulator.

+ + If the installation fails, the system can be rebooted. + The old release version is then automatically used. + If the installation succeeds, the new version is made + the default version, which is to now be used if there is a + system reboot. + +
+ +
+ Release Handling Aspects +

Appup Cookbook, + contains examples of .appup files + for typical cases of upgrades/downgrades that are normally easy to + handle in runtime. However, many aspects can make release handling + complicated, for example:

+ + +

Complicated or circular dependencies can make it difficult + or even impossible to decide in which order things must be + done without risking runtime errors during an upgrade or + downgrade. Dependencies can be:

+ + Between nodes + Between processes + Between modules + +
+ +

During release handling, non-affected processes continue + normal execution. This can lead to time-outs or other problems. + For example, new processes created in the time window between + suspending processes using a certain module, and loading a new + version of this module, can execute old code.

+
+
+

It is thus recommended that code is changed in as small steps as possible, and always kept backwards compatible.

+
Requirements -

For release handling to work properly, the runtime system needs - to have knowledge about which release it is currently running. It - must also be able to change (in run-time) which boot script and - system configuration file should be used if the system is - rebooted, for example by heart after a failure. - Therefore, Erlang must be started as an embedded system, see - Embedded System for information on how to do this.

+

For release handling to work properly, the runtime system must + have knowledge about which release it is running. It + must also be able to change (in runtime) which boot script and + system configuration file to use if the system is + rebooted, for example, by heart after a failure. + Thus, Erlang must be started as an embedded system; for + information on how to do this, see Embedded System.

For system reboots to work properly, it is also required that - the system is started with heart beat monitoring, see - erl(1) and heart(3).

+ the system is started with heartbeat monitoring, see the + erl(1) manual page in ERTS and the heart(3) + manual page in Kernel

Other requirements:

The boot script included in a release package must be generated from the same .rel file as the release package itself.

-

Information about applications are fetched from the script +

Information about applications is fetched from the script when an upgrade or downgrade is performed.

-

The system must be configured using one and only one system +

The system must be configured using only one system configuration file, called sys.config.

If found, this file is automatically included when a release package is created.

@@ -165,13 +163,13 @@
Distributed Systems -

If the system consists of several Erlang nodes, each node may use +

If the system consists of several Erlang nodes, each node can use its own version of the release. The release handler is a locally registered process and must be called at each node where an - upgrade or downgrade is required. There is a release handling - instruction that can be used to synchronize the release handler - processes at a number of nodes: sync_nodes. See - appup(4).

+ upgrade or downgrade is required. A release handling + instruction, sync_nodes, can be used to synchronize the + release handler processes at a number of nodes, see the + appup(4) manual page in SASL.

@@ -183,31 +181,26 @@ instructions. To make it easier for the user, there are also a number of high-level instructions, which are translated to low-level instructions by systools:make_relup.

-

Here, some of the most frequently used instructions are - described. The complete list of instructions can be found in - appup(4).

+

Some of the most frequently used instructions are described in + this section. The complete list of instructions is included in the + appup(4) manual page in SASL.

First, some definitions:

- - Residence module - -

The module where a process has its tail-recursive loop - function(s). If the tail-recursive loop functions are - implemented in several modules, all those modules are residence - modules for the process.

-
- Functional module - -

A module which is not a residence module for any process.

-
-
-

Note that for a process implemented using an OTP behaviour, - the behaviour module is the residence module for that process. - The callback module is a functional module.

+ + Residence module - The module where a process has + its tail-recursive loop function(s). If these functions are + implemented in several modules, all those modules are residence + modules for the process. + Functional module - A module that is not a + residence module for any process. + +

For a process implemented using an OTP behaviour, the behaviour + module is the residence module for that process. + The callback module is a functional module.

load_module

If a simple extension has been made to a functional module, it - is sufficient to simply load the new version of the module into + is sufficient to load the new version of the module into the system, and remove the old version. This is called simple code replacement and for this the following instruction is used:

@@ -217,44 +210,50 @@
update -

If a more complex change has been made, for example a change - to the format of the internal state of a gen_server, simple code - replacement is not sufficient. Instead it is necessary to - suspend the processes using the module (to avoid that they try - to handle any requests before the code replacement is - completed), ask them to transform the internal state format and - switch to the new version of the module, remove the old version - and last, resume the processes. This is called synchronized code replacement and for this the following instructions - are used:

+

If a more complex change has been made, for example, a change + to the format of the internal state of a gen_server, simple + code replacement is not sufficient. Instead, it is necessary to:

+ + Suspend the processes using the module (to avoid that + they try to handle any requests before the code replacement is + completed). + Ask them to transform the internal state format and + switch to the new version of the module. + Remove the old version. + Resume the processes. + +

This is called synchronized code replacement and for + this the following instructions are used:

{update, Module, {advanced, Extra}} {update, Module, supervisor}

update with argument {advanced,Extra} is used when changing the internal state of a behaviour as described - above. It will cause behaviour processes to call the callback + above. It causes behaviour processes to call the callback function code_change, passing the term Extra and - some other information as arguments. See the man pages for + some other information as arguments. See the manual pages for the respective behaviours and - Appup Cookbook.

+ Appup Cookbook.

update with argument supervisor is used when changing the start specification of a supervisor. See - Appup Cookbook.

+ Appup Cookbook.

When a module is to be updated, the release handler finds which processes that are using the module by traversing the supervision tree of each running application and checking all the child specifications:

{Id, StartFunc, Restart, Shutdown, Type, Modules} -

A process is using a module if the name is listed in +

A process uses a module if the name is listed in Modules in the child specification for the process.

If Modules=dynamic, which is the case for event managers, the event manager process informs the release handler - about the list of currently installed event handlers (gen_fsm) - and it is checked if the module name is in this list instead.

+ about the list of currently installed event handlers + (gen_fsm), and it is checked if the module name is in + this list instead.

The release handler suspends, asks for code change, and resumes processes by calling the functions - sys:suspend/1,2, sys:change_code/4,5 and - sys:resume/1,2 respectively.

+ sys:suspend/1,2, sys:change_code/4,5, and + sys:resume/1,2, respectively.

@@ -263,39 +262,39 @@ used:

{add_module, Module} -

The instruction loads the module and is absolutely necessary +

The instruction loads the module and is necessary when running Erlang in embedded mode. It is not strictly required when running Erlang in interactive (default) mode, since the code server then automatically searches for and loads unloaded modules.

-

The opposite of add_module is delete_module which +

The opposite of add_module is delete_module, which unloads a module:

{delete_module, Module} -

Note that any process, in any application, with Module +

Any process, in any application, with Module as residence module, is killed when the instruction is - evaluated. The user should therefore ensure that all such + evaluated. The user must therefore ensure that all such processes are terminated before deleting the module, to avoid - a possible situation with failing supervisor restarts.

+ a situation with failing supervisor restarts.

Application Instructions -

Instruction for adding an application:

+

The following is the instruction for adding an application:

{add_application, Application}

Adding an application means that the modules defined by the modules key in the .app file are loaded using - a number of add_module instructions, then the application + a number of add_module instructions, and then the application is started.

-

Instruction for removing an application:

+

The following is the instruction for removing an application:

{remove_application, Application}

Removing an application means that the application is stopped, the modules are unloaded using a number of delete_module - instructions and then the application specification is unloaded + instructions, and then the application specification is unloaded from the application controller.

-

Instruction for restarting an application:

+

The following is the instruction for restarting an application:

{restart_application, Application}

Restarting an application means that the application is stopped @@ -305,46 +304,48 @@

- apply (low-level) + apply (Low-Level)

To call an arbitrary function from the release handler, the following instruction is used:

{apply, {M, F, A}} -

The release handler will evaluate apply(M, F, A).

+

The release handler evalutes apply(M, F, A).

- restart_new_emulator (low-level) + restart_new_emulator (Low-Level)

This instruction is used when changing to a new emulator - version, or when any of the core applications kernel, stdlib - or sasl is upgraded. If a system reboot is needed for some - other reason, the restart_emulator instruction should - be used instead.

-

Requires that the system is started with heart beat - monitoring, see erl(1) and heart(3).

-

The restart_new_emulator instruction shall always be - the very first instruction in a relup. If the relup is - generated by systools:make_relup/3,4 this is + version, or when any of the core applications Kernel, + STDLIB, or SASL is upgraded. If a system reboot + is needed for another reason, the restart_emulator + instruction is to be used instead.

+

This instruction requires that the system is started with + heartbeat monitoring, see the erl(1) manual page in + ERTS and the heart(3) manual page in Kernel.

+

The restart_new_emulator instruction must always be + the first instruction in a relup. If the relup is + generated by systools:make_relup/3,4, this is automatically ensured.

When the release handler encounters the instruction, it first generates a temporary boot file, which starts the new versions of the emulator and the core applications, and the old version of all other applications. Then it shuts down - the current emulator by calling init:reboot(), see - init(3). All processes are terminated gracefully and - the system is rebooted by the heart program, using the + the current emulator by calling init:reboot(), see the + init(3) manual page in Kernel. + All processes are terminated gracefully and + the system is rebooted by the heart program, using the temporary boot file. After the reboot, the rest of the relup instructions are executed. This is done as a part of the temporary boot script.

-

Since this mechanism causes the new versions of the - emulator and core applications to run with the old version of - other applications during startup, extra care must be taken to +

This mechanism causes the new versions of the emulator and + core applications to run with the old version of other + applications during startup. Thus, take extra care to avoid incompatibility. Incompatible changes in the core - applications may in some situations be necessary. If possible, + applications can in some situations be necessary. If possible, such changes are preceded by deprecation over two major - releases before the actual change. To make sure your + releases before the actual change. To ensure the application is not crashed by an incompatible change, always remove any call to deprecated functions as soon as possible.

@@ -352,35 +353,36 @@

An info report is written when the upgrade is completed. To programmatically find out if the upgrade is complete, call release_handler:which_releases(current) and check - if it returns the expected (i.e. the new) release.

+ if it returns the expected (that is, the new) release.

The new release version must be made permanent when the new - emulator is up and running. Otherwise, the old version will be - used in case of a new system reboot.

-

On UNIX, the release handler tells the heart program which - command to use to reboot the system. Note that the environment - variable HEART_COMMAND, normally used by the heart - program, in this case is ignored. The command instead defaults - to $ROOT/bin/start. Another command can be set - by using the SASL configuration parameter start_prg, see - sasl(6).

+ emulator is operational. Otherwise, the old version will be + used if there is a new system reboot.

+

On UNIX, the release handler tells the heart program + which command to use to reboot the system. The environment + variable HEART_COMMAND, normally used by the heart + program, is ignored in this case. The command instead defaults + to $ROOT/bin/start. Another command can be set by using + the SASL configuration parameter start_prg, see + the sasl(6) manual page.

- restart_emulator (low-level) -

This instruction is not related to upgrades of erts or any of - the core applications. It can be used by any application to + restart_emulator (Low-Level) +

This instruction is not related to upgrades of ERTS or any + of the core applications. It can be used by any application to force a restart of the emulator after all upgrade instructions are executed.

-

There can only be one restart_emulator instruction in - a relup script, and it shall always be placed at the end. If - the relup is generated by systools:make_relup/3,4 this +

A relup script can only have one restart_emulator + instruction and it must always be placed at the end. If + the relup is generated by systools:make_relup/3,4, this is automatically ensured.

When the release handler encounters the instruction, it shuts - down the emulator by calling init:reboot(), see - init(3). All processes are terminated gracefully and - the system can then be rebooted by the heart program using the - new release version. No more upgrade instruction will be + down the emulator by calling init:reboot(), see the + init(3) manual page in Kernel. + All processes are terminated gracefully and the system + can then be rebooted by the heart program using the + new release version. No more upgrade instruction is executed after the restart.

@@ -389,10 +391,11 @@ Application Upgrade File

To define how to upgrade/downgrade between the current version - and previous versions of an application, we create an - application upgrade file, or in short .appup file. - The file should be called Application.appup, where - Application is the name of the application:

+ and previous versions of an application, an + application upgrade file, or in short an .appup + file is created. + The file is to be called Application.appup, where + Application is the application name:

{Vsn, [{UpFromVsn1, InstructionsU1}, @@ -401,24 +404,27 @@ [{DownToVsn1, InstructionsD1}, ..., {DownToVsnK, InstructionsDK}]}. -

Vsn, a string, is the current version of the application, - as defined in the .app file. Each UpFromVsn - is a previous version of the application to upgrade from, and each - DownToVsn is a previous version of the application to - downgrade to. Each Instructions is a list of release - handling instructions.

-

The syntax and contents of the appup file are described - in detail in appup(4).

-

In the chapter Appup Cookbook, examples of .appup files for typical - upgrade/downgrade cases are given.

-

Example: Consider the release ch_rel-1 from - the Releases - chapter. Assume we want to add a function available/0 to - the server ch3 which returns the number of available - channels:

-

(Hint: When trying out the example, make the changes in a copy of - the original directory, so that the first versions are still - available.)

+ + Vsn, a string, is the current version of the application, + as defined in the .app file. + Each UpFromVsn is a previous version of the application + to upgrade from. + Each DownToVsn is a previous version of the application + to downgrade to. + Each Instructions is a list of release handling + instructions. + +

For information about the syntax and contents of the .appup + file, see the appup(4) manual page in SASL.

+

Appup Cookbook + includes examples of .appup files for typical upgrade/downgrade + cases.

+

Example: Consider the release ch_rel-1 from + Releases. + Assume you want to add a function available/0 to server + ch3, which returns the number of available channels (when + trying out the example, change in a copy of the original + directory, so that the first versions are still available):

-module(ch3). -behaviour(gen_server). @@ -465,9 +471,9 @@ handle_cast({free, Ch}, Chs) -> {mod, {ch_app,[]}} ]}.

To upgrade ch_app from "1" to "2" (and - to downgrade from "2" to "1"), we simply need to + to downgrade from "2" to "1"), you only need to load the new (old) version of the ch3 callback module. - We create the application upgrade file ch_app.appup in + Create the application upgrade file ch_app.appup in the ebin directory:

{"2", @@ -480,25 +486,25 @@ handle_cast({free, Ch}, Chs) -> Release Upgrade File

To define how to upgrade/downgrade between the new version and - previous versions of a release, we create a release upgrade file, or in short relup file.

+ previous versions of a release, a release upgrade file, + or in short relup file, is to be created.

This file does not need to be created manually, it can be generated by systools:make_relup/3,4. The relevant versions - of the .rel file, .app files and .appup files - are used as input. It is deducted which applications should be - added and deleted, and which applications that need to be upgraded - and/or downgraded. The instructions for this is fetched from + of the .rel file, .app files, and .appup files + are used as input. It is deducted which applications are to be + added and deleted, and which applications that must be upgraded + and/or downgraded. The instructions for this are fetched from the .appup files and transformed into a single list of low-level instructions in the right order.

If the relup file is relatively simple, it can be created - manually. Remember that it should only contain low-level - instructions.

-

The syntax and contents of the release upgrade file are - described in detail in relup(4).

-

Example, continued from the previous section. We have a new - version "2" of ch_app and an .appup file. We also - need a new version of the .rel file. This time the file is - called ch_rel-2.rel and the release version string is - changed changed from "A" to "B":

+ manually. It it only to contain low-level instructions.

+

For details about the syntax and contents of the release upgrade + file, see the relup(4) manual page in SASL.

+

Example, continued from the previous section: You have a + new version "2" of ch_app and an .appup file. A new + version of the .rel file is also needed. This time the file + is called ch_rel-2.rel and the release version string is + changed from "A" to "B":

{release, {"ch_rel", "B"}, @@ -512,13 +518,13 @@ handle_cast({free, Ch}, Chs) ->
 1> systools:make_relup("ch_rel-2", ["ch_rel-1"], ["ch_rel-1"]).
 ok
-

This will generate a relup file with instructions for +

This generates a relup file with instructions for how to upgrade from version "A" ("ch_rel-1") to version "B" ("ch_rel-2") and how to downgrade from version "B" to version "A".

-

Note that both the old and new versions of the .app and - .rel files must be in the code path, as well as - the .appup and (new) .beam files. It is possible - to extend the code path by using the option path:

+

Both the old and new versions of the .app and + .rel files must be in the code path, as well as the + .appup and (new) .beam files. The code path can be + extended by using the option path:

 1> systools:make_relup("ch_rel-2", ["ch_rel-1"], ["ch_rel-1"],
 [{path,["../ch_rel-1",
@@ -529,31 +535,34 @@ ok
Installing a Release -

When we have made a new version of a release, a release package +

When you have made a new version of a release, a release package can be created with this new version and transferred to the target environment.

-

To install the new version of the release in run-time, +

To install the new version of the release in runtime, the release handler is used. This is a process belonging - to the SASL application, that handles unpacking, installation, - and removal of release packages. It is interfaced through - the module release_handler, which is described in detail in - release_handler(3).

-

Assuming there is a target system up and running with + to the SASL application, which handles unpacking, installation, + and removal of release packages. It is communicated through + the release_handler module. For details, see the + release_handler(3) manual page in SASL.

+

Assuming there is an operational target system with installation root directory $ROOT, the release package with - the new version of the release should be copied to + the new version of the release is to be copied to $ROOT/releases.

-

The first action is to unpack the release package, - the files are then extracted from the package:

+

First, unpack the release package. + The files are then extracted from the package:

release_handler:unpack_release(ReleaseName) => {ok, Vsn} -

ReleaseName is the name of the release package except - the .tar.gz extension. Vsn is the version of - the unpacked release, as defined in its .rel file.

-

A directory $ROOT/lib/releases/Vsn will be created, where + + ReleaseName is the name of the release package except + the .tar.gz extension. + Vsn is the version of the unpacked release, as + defined in its .rel file. + +

A directory $ROOT/lib/releases/Vsn is created, where the .rel file, the boot script start.boot, - the system configuration file sys.config and relup + the system configuration file sys.config, and relup are placed. For applications with new version numbers, - the application directories will be placed under $ROOT/lib. + the application directories are placed under $ROOT/lib. Unchanged applications are not affected.

An unpacked release can be installed. The release handler then evaluates the instructions in relup, step by @@ -563,11 +572,11 @@ release_handler:install_release(Vsn) => {ok, FromVsn, []}

If an error occurs during the installation, the system is rebooted using the old version of the release. If installation succeeds, the system is afterwards using the new version of - the release, but should anything happen and the system is - rebooted, it would start using the previous version again. To be - made the default version, the newly installed release must be made - permanent, which means the previous version becomes - old:

+ the release, but if anything happens and the system is + rebooted, it starts using the previous version again.

+

To be made the default version, the newly installed release + must be made permanent, which means the previous + version becomes old:

release_handler:make_permanent(Vsn) => ok

The system keeps information about which versions are old and @@ -579,41 +588,44 @@ release_handler:make_permanent(Vsn) => ok release_handler:install_release(FromVsn) => {ok, Vsn, []}

An installed, but not permanent, release can be removed. Information about the release is then deleted from - $ROOT/releases/RELEASES and the release specific code, - that is the new application directories and + $ROOT/releases/RELEASES and the release-specific code, + that is, the new application directories and the $ROOT/releases/Vsn directory, are removed.

release_handler:remove_release(Vsn) => ok -

Example, continued from the previous sections:

-

1) Create a target system as described in System Principles of the first version "A" of ch_rel - from - the Releases - chapter. This time sys.config must be included in - the release package. If no configuration is needed, the file - should contain the empty list:

- + +
+ Example (continued from the previous sections) +

Step 1) Create a target system as described in + System Principles of the first version "A" + of ch_rel from + Releases. + This time sys.config must be included in the release package. + If no configuration is needed, the file is to contain the empty + list:

+ []. -

2) Start the system as a simple target system. Note that in - reality, it should be started as an embedded system. However, - using erl with the correct boot script and config file is - enough for illustration purposes:

-
+    

Step 2) Start the system as a simple target system. In + reality, it is to be started as an embedded system. However, using + erl with the correct boot script and config file is enough for + illustration purposes:

+
 % cd $ROOT
 % bin/erl -boot $ROOT/releases/A/start -config $ROOT/releases/A/sys
 ...

$ROOT is the installation directory of the target system.

-

3) In another Erlang shell, generate start scripts and create a - release package for the new version "B". Remember to - include (a possible updated) sys.config and - the relup file, see Release Upgrade File above.

-
+    

Step 3) In another Erlang shell, generate start scripts and + create a release package for the new version "B". Remember to + include (a possible updated) sys.config and the relup file, + see Release Upgrade File.

+
 1> systools:make_script("ch_rel-2").
 ok
 2> systools:make_tar("ch_rel-2").
 ok
-

The new release package now contains version "2" of ch_app - and the relup file as well:

- +

The new release package now also contains version "2" of ch_app + and the relup file:

+ % tar tf ch_rel-2.tar lib/kernel-2.9/ebin/kernel.app lib/kernel-2.9/ebin/application.beam @@ -633,28 +645,30 @@ releases/B/relup releases/B/sys.config releases/B/ch_rel-2.rel releases/ch_rel-2.rel -

4) Copy the release package ch_rel-2.tar.gz to - the $ROOT/releases directory.

-

5) In the running target system, unpack the release package:

-
+    

Step 4) Copy the release package ch_rel-2.tar.gz + to the $ROOT/releases directory.

+

Step 5) In the running target system, unpack the release + package:

+
 1> release_handler:unpack_release("ch_rel-2").
 {ok,"B"}

The new application version ch_app-2 is installed under $ROOT/lib next to ch_app-1. The kernel, - stdlib and sasl directories are not affected, as + stdlib, and sasl directories are not affected, as they have not changed.

Under $ROOT/releases, a new directory B is created, containing ch_rel-2.rel, start.boot, - sys.config and relup.

-

6) Check if the function ch3:available/0 is available:

-
+      sys.config, and relup.

+

Step 6) Check if the function ch3:available/0 is + available:

+
 2> ch3:available().
 ** exception error: undefined function ch3:available/0
-

7) Install the new release. The instructions in +

Step 7) Install the new release. The instructions in $ROOT/releases/B/relup are executed one by one, resulting in the new version of ch3 being loaded. The function ch3:available/0 is now available:

-
+      
 3> release_handler:install_release("B").
 {ok,"A",[]}
 4> ch3:available().
@@ -663,15 +677,16 @@ releases/ch_rel-2.rel
 ".../lib/ch_app-2/ebin/ch3.beam"
 6> code:which(ch_sup).
 ".../lib/ch_app-1/ebin/ch_sup.beam"
-

Note that processes in ch_app for which code have not - been updated, for example the supervisor, are still evaluating +

Processes in ch_app for which code have not + been updated, for example, the supervisor, are still evaluating code from ch_app-1.

-

8) If the target system is now rebooted, it will use version "A" - again. The "B" version must be made permanent, in order to be +

Step 8) If the target system is now rebooted, it uses + version "A" again. The "B" version must be made permanent, to be used when the system is rebooted.

-
+      
 7> release_handler:make_permanent("B").
 ok
+
@@ -681,37 +696,40 @@ ok
specifications are automatically updated for all loaded applications.

-

The information about the new application specifications are +

The information about the new application specifications is fetched from the boot script included in the release package. - It is therefore important that the boot script is generated from + Thus, it is important that the boot script is generated from the same .rel file as is used to build the release package itself.

Specifically, the application configuration parameters are automatically updated according to (in increasing priority order):

- + The data in the boot script, fetched from the new application resource file App.app The new sys.config - Command line arguments -App Par Val + Command-line arguments -App Par Val

This means that parameter values set in the other system - configuration files, as well as values set using - application:set_env/3, are disregarded.

+ configuration files and values set using + application:set_env/3 are disregarded.

When an installed release is made permanent, the system process init is set to point out the new sys.config.

-

After the installation, the application controller will compare +

After the installation, the application controller compares the old and new configuration parameters for all running applications and call the callback function:

Module:config_change(Changed, New, Removed) -

Module is the application callback module as defined by - the mod key in the .app file. Changed and - New are lists of {Par,Val} for all changed and - added configuration parameters, respectively. Removed is - a list of all parameters Par that have been removed.

-

The function is optional and may be omitted when implementing an + + Module is the application callback module as defined + by the mod key in the .app file. + Changed and New are lists of {Par,Val} for + all changed and added configuration parameters, respectively. + Removed is a list of all parameters Par that have + been removed. + +

The function is optional and can be omitted when implementing an application callback module.

diff --git a/system/doc/design_principles/release_structure.xml b/system/doc/design_principles/release_structure.xml index cec33f42e3..aa04f5e6a3 100644 --- a/system/doc/design_principles/release_structure.xml +++ b/system/doc/design_principles/release_structure.xml @@ -28,21 +28,23 @@ release_structure.xml -

This chapter should be read in conjuction with rel(4), - systools(3) and script(4).

+ +

This section is to be read with the rel(4), systools(3), + and script(4) manual pages in SASL.

Release Concept -

When we have written one or more applications, we might want to - create a complete system consisting of these applications and a +

When you have written one or more applications, you might want + to create a complete system with these applications and a subset of the Erlang/OTP applications. This is called a release.

-

To do this, we create a release resource file which defines which applications - are included in the release.

+

To do this, create a + release resource file that + defines which applications are included in the release.

The release resource file is used to generate boot scripts and release packages. A system - which is transfered to and installed at another site is called a + that is transferred to and installed at another site is called a target system. How to use a release package to create a target system is described in System Principles.

@@ -50,29 +52,30 @@
Release Resource File -

To define a release, we create a release resource file, - or in short .rel file, where we specify the name and - version of the release, which ERTS version it is based on, and - which applications it consists of:

+

To define a release, create a release resource file, + or in short a .rel file. In the file, specify the name and + version of the release, which ERTS version it is based on, + and which applications it consists of:

{release, {Name,Vsn}, {erts, EVsn}, [{Application1, AppVsn1}, ... {ApplicationN, AppVsnN}]}. +

Name, Vsn, EVsn, and AppVsn are + strings.

The file must be named Rel.rel, where Rel is a unique name.

-

Name, Vsn and EVsn are strings.

-

Each Application (atom) and AppVsn (string) is +

Each Application (atom) and AppVsn is the name and version of an application included in the release. - Note that the minimal release based on Erlang/OTP consists of - the kernel and stdlib applications, so these + The minimal release based on Erlang/OTP consists of + the Kernel and STDLIB applications, so these applications must be included in the list.

If the release is to be upgraded, it must also include - the sasl application.

+ the SASL application.

-

Example: We want to make a release of ch_app from - the Applications - chapter. It has the following .app file:

+

Example: A release of ch_app from + Applications + has the following .app file:

{application, ch_app, [{description, "Channel allocator"}, @@ -83,8 +86,8 @@ {mod, {ch_app,[]}} ]}.

The .rel file must also contain kernel, - stdlib and sasl, since these applications are - required by ch_app. We call the file ch_rel-1.rel:

+ stdlib, and sasl, as these applications are required + by ch_app. The file is called ch_rel-1.rel:

{release, {"ch_rel", "A"}, @@ -99,24 +102,28 @@
Generating Boot Scripts -

There are tools in the SASL module systools available to - build and check releases. The functions read the .rel and +

systools in the SASL application includes tools to + build and check releases. The functions read the rel and .app files and performs syntax and dependency checks. - The function systools:make_script/1,2 is used to generate - a boot script (see System Principles).

+ The systools:make_script/1,2 function is used to generate + a boot script (see System Principles):

 1> systools:make_script("ch_rel-1", [local]).
 ok
-

This creates a boot script, both the readable version - ch_rel-1.script and the binary version used by the runtime - system, ch_rel-1.boot. "ch_rel-1" is the name of - the .rel file, minus the extension. local is an - option that means that the directories where the applications are - found are used in the boot script, instead of $ROOT/lib. - ($ROOT is the root directory of the installed release.) - This is a useful way to test a generated boot script locally.

+

This creates a boot script, both the readable version, + ch_rel-1.script, and the binary version, ch_rel-1.boot, + used by the runtime system.

+ + "ch_rel-1" is the name of the .rel file, + minus the extension. + local is an option that means that the directories + where the applications are found are used in the boot script, + instead of $ROOT/lib ($ROOT is the root directory + of the installed release). + +

This is a useful way to test a generated boot script locally.

When starting Erlang/OTP using the boot script, all applications - from the .rel file are automatically loaded and started:

+ from the .rel file are automatically loaded and started:

 % erl -boot ch_rel-1
 Erlang (BEAM) emulator version 5.3
@@ -147,18 +154,24 @@ Eshell V5.3  (abort with ^G)
   
Creating a Release Package -

There is a function systools:make_tar/1,2 which takes - a .rel file as input and creates a zipped tar-file with - the code for the specified applications, a release package.

+

The systools:make_tar/1,2 function takes a .rel file + as input and creates a zipped tar file with the code for the specified + applications, a release package:

 1> systools:make_script("ch_rel-1").
 ok
 2> systools:make_tar("ch_rel-1").
 ok
-

The release package by default contains the .app files and - object code for all applications, structured according to - the application directory structure, the binary boot script renamed to - start.boot, and the .rel file.

+

The release package by default contains:

+ + The .app files + The .rel file + The object code for all applications, structured according + to the + application directory + structure + The binary boot script renamed to start.boot +
 % tar tf ch_rel-1.tar
 lib/kernel-2.9/ebin/kernel.app
@@ -177,40 +190,39 @@ lib/ch_app-1/ebin/ch3.beam
 releases/A/start.boot
 releases/A/ch_rel-1.rel
 releases/ch_rel-1.rel
-

Note that a new boot script was generated, without +

A new boot script was generated, without the local option set, before the release package was made. In the release package, all application directories are placed - under lib. Also, we do not know where the release package - will be installed, so we do not want any hardcoded absolute paths - in the boot script here.

+ under lib. You do not know where the release package + will be installed, so no hard-coded absolute paths are allowed.

The release resource file mysystem.rel is duplicated in the tar file. Originally, this file was only stored in - the releases directory in order to make it possible for + the releases directory to make it possible for the release_handler to extract this file separately. After unpacking the tar file, release_handler would automatically copy the file to releases/FIRST. However, sometimes the tar file is - unpacked without involving the release_handler (e.g. when - unpacking the first target system) and therefore the file is now - instead duplicated in the tar file so no manual copying is - necessary.

+ unpacked without involving the release_handler (for + example, when unpacking the first target system) and the file + is therefore now instead duplicated in the tar file so no manual + copying is necessary.

If a relup file and/or a system configuration file called - sys.config is found, these files are included in - the release package as well. See + sys.config is found, these files are also included in + the release package. See Release Handling.

Options can be set to make the release package include source code and the ERTS binary as well.

-

Refer to System Principles for how to install the first target - system, using a release package, and to - Release Handling for - how to install a new release package in an existing system.

+

For information on how to install the first target system, using + a release package, see System Principles. For information + on how to install a new release package in an existing system, see + Release Handling.

Directory Structure -

Directory structure for the code installed by the release handler - from a release package:

+

The directory structure for the code installed by the release handler + from a release package is as follows:

$ROOT/lib/App1-AVsn1/ebin /priv @@ -222,24 +234,18 @@ $ROOT/lib/App1-AVsn1/ebin /erts-EVsn/bin /releases/Vsn /bin - - lib - Application directories. - erts-EVsn/bin - Erlang runtime system executables. - releases/Vsn - .rel file and boot script start.boot.

- - If present in the release package,

-relup and/or sys.config.
- bin - Top level Erlang runtime system executables. -
-

Applications are not required to be located under the - $ROOT/lib directory. Accordingly, several installation - directories may exist which contain different parts of a - system. For example, the previous example could be extended as - follows:

+ + lib - Application directories + erts-EVsn/bin - Erlang runtime system executables + releases/Vsn - .rel file and boot script + start.boot; if present in the release package, relup + and/or sys.config + bin - Top-level Erlang runtime system executables + +

Applications are not required to be located under directory + $ROOT/lib. Several installation directories, which contain + different parts of a system, can thus exist. + For example, the previous example can be extended as follows:

 $SECOND_ROOT/.../SApp1-SAVsn1/ebin
                              /priv
@@ -256,24 +262,24 @@ $THIRD_ROOT/TApp1-TAVsn1/ebin
            ...
            /TAppN-TAVsnN/ebin
                         /priv
-

The $SECOND_ROOT and $THIRD_ROOT are introduced as +

$SECOND_ROOT and $THIRD_ROOT are introduced as variables in the call to the systools:make_script/2 function.

Disk-Less and/or Read-Only Clients -

If a complete system consists of some disk-less and/or - read-only client nodes, a clients directory should be - added to the $ROOT directory. By a read-only node we - mean a node with a read-only file system.

-

The clients directory should have one sub-directory +

If a complete system consists of disk-less and/or + read-only client nodes, a clients directory is to be + added to the $ROOT directory. A read-only node is + a node with a read-only file system.

+

The clients directory is to have one subdirectory per supported client node. The name of each client directory - should be the name of the corresponding client node. As a - minimum, each client directory should contain the bin and - releases sub-directories. These directories are used to + is to be the name of the corresponding client node. As a + minimum, each client directory is to contain the bin and + releases subdirectories. These directories are used to store information about installed releases and to appoint the - current release to the client. Accordingly, the $ROOT - directory contains the following:

+ current release to the client. The $ROOT + directory thus contains the following:

$ROOT/... /clients/ClientName1/bin @@ -283,14 +289,14 @@ $ROOT/... ... /ClientNameN/bin /releases/Vsn -

This structure should be used if all clients are running +

This structure is to be used if all clients are running the same type of Erlang machine. If there are clients running different types of Erlang machines, or on different operating - systems, the clients directory could be divided into one - sub-directory per type of Erlang machine. Alternatively, you - can set up one $ROOT per type of machine. For each + systems, the clients directory can be divided into one + subdirectory per type of Erlang machine. Alternatively, one + $ROOT can be set up per type of machine. For each type, some of the directories specified for the $ROOT - directory should be included:

+ directory are to be included:

$ROOT/... /clients/Type1/lib diff --git a/system/doc/design_principles/spec_proc.xml b/system/doc/design_principles/spec_proc.xml index e849388a38..aceb5ba99e 100644 --- a/system/doc/design_principles/spec_proc.xml +++ b/system/doc/design_principles/spec_proc.xml @@ -21,30 +21,31 @@ - Sys and Proc_Lib + sys and proc_lib spec_proc.xml -

The module sys contains functions for simple debugging of - processes implemented using behaviours.

-

There are also functions that, together with functions in - the module proc_lib, can be used to implement a - special process, a process which comply to the OTP design - principles without making use of a standard behaviour. They can - also be used to implement user defined (non-standard) behaviours.

-

Both sys and proc_lib belong to the STDLIB - application.

+ +

The sys module has functions for simple debugging of + processes implemented using behaviours. It also has functions that, + together with functions in the proc_lib module, can be used + to implement a special process that complies to the OTP + design principles without using a standard behaviour. These + functions can also be used to implement user-defined (non-standard) + behaviours.

+

Both sys and proc_lib belong to the STDLIB + application.

Simple Debugging -

The module sys contains some functions for simple debugging - of processes implemented using behaviours. We use the - code_lock example from - the gen_fsm chapter to - illustrate this:

+

The sys module has functions for simple debugging of + processes implemented using behaviours. The code_lock + example from + gen_fsm Behaviour + is used to illustrate this:

 % erl
 Erlang (BEAM) emulator version 5.2.3.6 [hipe] [threads:0]
@@ -102,16 +103,18 @@ ok
 
   
Special Processes -

This section describes how to write a process which comply to - the OTP design principles, without making use of a standard - behaviour. Such a process should:

+

This section describes how to write a process that complies to + the OTP design principles, without using a standard behaviour. + Such a process is to:

- be started in a way that makes the process fit into a - supervision tree, - support the sys debug facilities, and - take care of system messages. + Be started in a way that makes the process fit into a + supervision tree + Support the sys + debug facilities + Take care of + system messages. -

System messages are messages with special meaning, used in +

System messages are messages with a special meaning, used in the supervision tree. Typical system messages are requests for trace output, and requests to suspend or resume process execution (used during release handling). Processes implemented using @@ -120,9 +123,9 @@ ok

Example

The simple server from - the Overview chapter, - implemented using sys and proc_lib so it fits into - a supervision tree:

+ Overview, + implemented using sys and proc_lib so it fits into a + supervision tree:

 -module(ch4).
@@ -190,8 +193,8 @@ system_replace_state(StateFun, Chs) ->
 
 write_debug(Dev, Event, Name) ->
     io:format(Dev, "~p event = ~p~n", [Name, Event]).
-

Example on how the simple debugging functions in sys can - be used for ch4 as well:

+

Example on how the simple debugging functions in the sys + module can also be used for ch4:

 % erl
 Erlang (BEAM) emulator version 5.2.3.6 [hipe] [threads:0]
@@ -230,31 +233,32 @@ ok
 
     
Starting the Process -

A function in the proc_lib module should be used to - start the process. There are several possible functions, for - example spawn_link/3,4 for asynchronous start and +

A function in the proc_lib module is to be used to + start the process. Several functions are available, for + example, spawn_link/3,4 for asynchronous start and start_link/3,4,5 for synchronous start.

-

A process started using one of these functions will store - information that is needed for a process in a supervision tree, - for example about the ancestors and initial call.

-

Also, if the process terminates with another reason than - normal or shutdown, a crash report (see SASL - User's Guide) is generated.

-

In the example, synchronous start is used. The process is - started by calling ch4:start_link():

+

A process started using one of these functions stores + information (for example, about the ancestors and initial call) + that is needed for a process in a supervision tree.

+

If the process terminates with another reason than + normal or shutdown, a crash report is generated. + For more information about the crash report, see the SASL + User's Guide.

+

In the example, synchronous start is used. The process + starts by calling ch4:start_link():

start_link() -> proc_lib:start_link(ch4, init, [self()]).

ch4:start_link calls the function proc_lib:start_link. This function takes a module name, - a function name and an argument list as arguments and spawns + a function name, and an argument list as arguments, spawns, and links to a new process. The new process starts by executing - the given function, in this case ch4:init(Pid), where - Pid is the pid (self()) of the first process, that - is the parent process.

-

In init, all initialization including name registration - is done. The new process must also acknowledge that it has been - started to the parent:

+ the given function, here ch4:init(Pid), where + Pid is the pid (self()) of the first process, + which is the parent process.

+

All initialization, including name registration, is done in + init. The new process must also acknowledge that it has + been started to the parent:

init(Parent) -> ... @@ -267,8 +271,8 @@ init(Parent) ->
Debugging -

To support the debug facilites in sys, we need a - debug structure, a term Deb which is +

To support the debug facilites in sys, a + debug structure is needed. The Deb term is initialized using sys:debug_options/1:

init(Parent) -> @@ -278,50 +282,41 @@ init(Parent) -> loop(Chs, Parent, Deb).

sys:debug_options/1 takes a list of options as argument. Here the list is empty, which means no debugging is enabled - initially. See sys(3) for information about possible - options.

-

Then for each system event that we want to be logged - or traced, the following function should be called.

+ initially. For information about the possible options, see the + sys(3) manual page in STDLIB.

+

Then, for each system event to be logged + or traced, the following function is to be called.

sys:handle_debug(Deb, Func, Info, Event) => Deb1 +

Here:

- -

Deb is the debug structure.

-
- -

Func is a fun specifying - a (user defined) function used to format + Deb is the debug structure. + Func is a fun specifying + a (user-defined) function used to format trace output. For each system event, the format function is - called as Func(Dev, Event, Info), where:

+ called as Func(Dev, Event, Info), where: - -

Dev is the IO device to which the output should - be printed. See io(3).

-
- -

Event and Info are passed as-is from - handle_debug.

-
+ Dev is the I/O device to which the output is to + be printed. See the io(3) manual page in + STDLIB. + Event and Info are passed as is from + handle_debug.
- -

Info is used to pass additional information to - Func, it can be any term and is passed as-is.

-
- -

Event is the system event. It is up to the user to - define what a system event is and how it should be - represented, but typically at least incoming and outgoing + Info is used to pass more information to + Func. It can be any term and is passed as is. + Event is the system event. It is up to the user to + define what a system event is and how it is to be + represented. Typically at least incoming and outgoing messages are considered system events and represented by the tuples {in,Msg[,From]} and {out,Msg,To}, - respectively.

-
+ respectively.

handle_debug returns an updated debug structure Deb1.

In the example, handle_debug is called for each incoming and outgoing message. The format function Func is - the function ch4:write_debug/3 which prints the message + the function ch4:write_debug/3, which prints the message using io:format/3.

loop(Chs, Parent, Deb) -> @@ -354,22 +349,22 @@ write_debug(Dev, Event, Name) -> {system, From, Request}

The content and meaning of these messages do not need to be interpreted by the process. Instead the following function - should be called:

+ is to be called:

sys:handle_system_msg(Request, From, Parent, Module, Deb, State) -

This function does not return. It will handle the system - message and then call:

+

This function does not return. It handles the system + message and then either calls the following if process execution is + to continue:

Module:system_continue(Parent, Deb, State) -

if process execution should continue, or:

+

Or calls the following if the process is to terminate:

Module:system_terminate(Reason, Parent, Deb, State) -

if the process should terminate. Note that a process in a - supervision tree is expected to terminate with the same reason as - its parent.

+

A process in a supervision tree is expected to terminate with + the same reason as its parent.

- Request and From should be passed as-is from - the system message to the call to handle_system_msg. + Request and From are to be passed as is from + the system message to the call to handle_system_msg. Parent is the pid of the parent. Module is the name of the module. Deb is the debug structure. @@ -377,10 +372,12 @@ Module:system_terminate(Reason, Parent, Deb, State)
is passed to system_continue/system_terminate/ system_get_state/system_replace_state. -

If the process should return its state handle_system_msg will call:

+

If the process is to return its state, handle_system_msg + calls:

Module:system_get_state(State) -

or if the process should replace its state using the fun StateFun:

+

If the process is to replace its state using the fun StateFun, + handle_system_msg calls:

Module:system_replace_state(StateFun, State)

In the example:

@@ -407,9 +404,9 @@ system_replace_state(StateFun, Chs) -> NChs = StateFun(Chs), {ok, NChs, NChs}.
-

If the special process is set to trap exits, note that if - the parent process terminates, the expected behavior is to - terminate with the same reason:

+

If the special process is set to trap exits and if the parent + process terminates, the expected behavior is to terminate with + the same reason:

init(...) -> ..., @@ -431,28 +428,23 @@ loop(...) ->
User-Defined Behaviours -

To implement a user-defined behaviour, - write code similar to code for a special process but calling - functions in a callback module for handling specific tasks.

-

If it is desired that the compiler should warn for missing - callback functions, as it does for the OTP behaviours, add - -callback attributes in the behaviour module to describe - the expected callback functions:

- + write code similar to + code for a special process, but call functions in a callback + module for handling specific tasks.

+

If the compiler is to warn for missing callback functions, as it + does for the OTP behaviours, add -callback attributes in the + behaviour module to describe the expected callbacks:

-callback Name1(Arg1_1, Arg1_2, ..., Arg1_N1) -> Res1. -callback Name2(Arg2_1, Arg2_2, ..., Arg2_N2) -> Res2. ... -callback NameM(ArgM_1, ArgM_2, ..., ArgM_NM) -> ResM. - -

where each Name is the name of a callback function and - Arg and Res are types as described in - Specifications for functions in Types and Function - Specifications. The whole syntax of the - -spec attribute is supported by -callback - attribute.

+

NameX are the names of the expected callbacks. + ArgX_Y and ResX are types as they are described in + Types and + Function Specifications. The whole syntax of the -spec + attribute is supported by the -callback attribute.

Callback functions that are optional for the user of the behaviour to implement are specified by use of the -optional_callbacks attribute:

@@ -487,10 +479,10 @@ behaviour_info(callbacks) -> generated by the compiler using the -callback attributes.

When the compiler encounters the module attribute - -behaviour(Behaviour). in a module Mod, it will - call Behaviour:behaviour_info(callbacks) and compare the + -behaviour(Behaviour). in a module Mod, it + calls Behaviour:behaviour_info(callbacks) and compares the result with the set of functions actually exported from - Mod, and issue a warning if any callback function is + Mod, and issues a warning if any callback function is missing.

Example:

diff --git a/system/doc/design_principles/sup_princ.xml b/system/doc/design_principles/sup_princ.xml index 3d7b53e339..9583ca5c55 100644 --- a/system/doc/design_principles/sup_princ.xml +++ b/system/doc/design_principles/sup_princ.xml @@ -28,15 +28,16 @@ sup_princ.xml -

This section should be read in conjunction with - supervisor(3), where - all details about the supervisor behaviour are described.

+

This section should be read with the + supervisor(3) manual page + in STDLIB, where all details about the supervisor + behaviour is given.

Supervision Principles

A supervisor is responsible for starting, stopping, and monitoring its child processes. The basic idea of a supervisor is - that it shall keep its child processes alive by restarting them + that it is to keep its child processes alive by restarting them when necessary.

Which child processes to start and monitor is specified by a list of child specifications. @@ -47,8 +48,8 @@

Example

The callback module for a supervisor starting the server from - the gen_server chapter - could look like this:

+ gen_server Behaviour + can look as follows:

-module(ch_sup). @@ -79,6 +80,7 @@ init(_Args) ->
Supervisor Flags +

This is the type definition for the supervisor flags:

strategy(), % optional @@ -136,9 +138,9 @@ SupFlags = #{strategy => Strategy, ...}
rest_for_one -

If a child process terminates, the 'rest' of the child - processes -- i.e. the child processes after the terminated - process in start order -- are terminated. Then the terminated +

If a child process terminates, the rest of the child + processes (that is, the child processes after the terminated + process in start order) are terminated. Then the terminated child process and the rest of the child processes are restarted.

@@ -162,7 +164,7 @@ SupFlags = #{intensity => MaxR, period => MaxT, ...}

If more than MaxR number of restarts occur in the last MaxT seconds, the supervisor terminates all the child processes and then itself.

-

When the supervisor terminates, the next higher level +

When the supervisor terminates, then the next higher-level supervisor takes some action. It either restarts the terminated supervisor or terminates itself.

The intention of the restart mechanism is to prevent a situation @@ -176,14 +178,14 @@ SupFlags = #{intensity => MaxR, period => MaxT, ...}

Child Specification -

This is the type definition for a child specification:

+

The type definition for a child specification is as follows:

child_id(), % mandatory start => mfargs(), % mandatory restart => restart(), % optional shutdown => shutdown(), % optional type => worker(), % optional - modules => modules()} % optional
+ modules => modules()} % optional child_id() = term() mfargs() = {M :: module(), F :: atom(), A :: [term()]} modules() = [module()] | dynamic @@ -195,7 +197,7 @@ child_spec() = #{id => child_id(), % mandatory

id is used to identify the child specification internally by the supervisor.

The id key is mandatory.

-

Note that this identifier on occations has been called +

Note that this identifier occasionally has been called "name". As far as possible, the terms "identifier" or "id" are now used but in order to keep backwards compatibility, some occurences of "name" can still be found, for example @@ -205,24 +207,28 @@ child_spec() = #{id => child_id(), % mandatory

start defines the function call used to start the child process. It is a module-function-arguments tuple used as apply(M, F, A).

-

It should be (or result in) a call to - supervisor:start_link, gen_server:start_link, - gen_fsm:start_link, or gen_event:start_link. - (Or a function compliant with these functions, see - supervisor(3) for details.

+

It is to be (or result in) a call to any of the following:

+ + supervisor:start_link + gen_server:start_link + gen_fsm:start_link + gen_event:start_link + A function compliant with these functions. For details, + see the supervisor(3) manual page. +

The start key is mandatory.

-

restart defines when a terminated child process shall +

restart defines when a terminated child process is to be restarted.

A permanent child process is always restarted. A temporary child process is never restarted - (not even when the supervisor's restart strategy - is rest_for_one or one_for_all and a sibling's + (not even when the supervisor restart strategy + is rest_for_one or one_for_all and a sibling death causes the temporary process to be terminated). A transient child process is restarted only if it - terminates abnormally, i.e. with another exit reason than + terminates abnormally, that is, with another exit reason than normal, shutdown, or {shutdown,Term}.

The restart key is optional. If it is not given, the @@ -230,27 +236,27 @@ child_spec() = #{id => child_id(), % mandatory -

shutdown defines how a child process shall be +

shutdown defines how a child process is to be terminated.

- brutal_kill means the child process is + brutal_kill means that the child process is unconditionally terminated using exit(Child, kill). - An integer timeout value means that the supervisor tells + An integer time-out value means that the supervisor tells the child process to terminate by calling exit(Child, shutdown) and then waits for an exit signal back. If no exit signal is received within the specified time, the child process is unconditionally terminated using exit(Child, kill). - If the child process is another supervisor, it should be + If the child process is another supervisor, it is to be set to infinity to give the subtree enough time to shut down. It is also allowed to set it to infinity, - if the child process is a worker. + if the child process is a worker. See the warning below:

Be careful when setting the shutdown time to infinity when the child process is a worker. Because, in this situation, the termination of the supervision tree depends on the - child process, it must be implemented in a safe way and its cleanup + child process; it must be implemented in a safe way and its cleanup procedure must always return.

The shutdown key is optional. If it is not given, @@ -266,7 +272,7 @@ child_spec() = #{id => child_id(), % mandatory default value worker will be used.

-

modules should be a list with one element +

modules are to be a list with one element [Module], where Module is the name of the callback module, if the child process is a supervisor, gen_server or gen_fsm. If the child process is a gen_event, @@ -279,8 +285,8 @@ child_spec() = #{id => child_id(), % mandatory child's start {M,F,A}.

-

Example: The child specification to start the server ch3 - in the example above looks like:

+

Example: The child specification to start the server + ch3 in the previous example look as follows:

#{id => ch3, start => {ch3, start_link, []}, @@ -301,11 +307,11 @@ child_spec() = #{id => child_id(), % mandatory start => {gen_event, start_link, [{local, error_man}]}, modules => dynamic}

Both server and event manager are registered processes which - can be expected to be accessible at all times, thus they are + can be expected to be always accessible. Thus they are specified to be permanent.

ch3 does not need to do any cleaning up before - termination, thus no shutdown time is needed but - brutal_kill should be sufficient. error_man may + termination. Thus, no shutdown time is needed, but + brutal_kill is sufficient. error_man can need some time for the event handlers to clean up, thus the shutdown time is set to 5000 ms (which is the default value).

@@ -320,19 +326,20 @@ child_spec() = #{id => child_id(), % mandatory
Starting a Supervisor -

In the example above, the supervisor is started by calling +

In the previous example, the supervisor is started by calling ch_sup:start_link():

start_link() -> supervisor:start_link(ch_sup, []). -

ch_sup:start_link calls the function - supervisor:start_link/2. This function spawns and links to - a new process, a supervisor.

+

ch_sup:start_link calls function + supervisor:start_link/2, which spawns and links to a new + process, a supervisor.

The first argument, ch_sup, is the name of - the callback module, that is the module where the init + the callback module, that is, the module where the init callback function is located. - The second argument, [], is a term which is passed as-is to + The second argument, [], is a term that is passed + as is to the callback function init. Here, init does not need any indata and ignores the argument. @@ -351,26 +358,27 @@ init(_Args) -> shutdown => brutal_kill}], {ok, {SupFlags, ChildSpecs}}.

The supervisor then starts all its child processes according to - the given child specifications. In this case there, is one child - process, ch3.

-

Note that supervisor:start_link is synchronous. It does + the child specifications in the start specification. In this case + there is one child process, ch3.

+

supervisor:start_link is synchronous. It does not return until all child processes have been started.

Adding a Child Process -

In addition to the static supervision tree, we can also add - dynamic child processes to an existing supervisor with - the following call:

+

In addition to the static supervision tree, dynamic child + processes can be added to an existing supervisor with the following + call:

supervisor:start_child(Sup, ChildSpec)

Sup is the pid, or name, of the supervisor. - ChildSpec is a child specification.

+ ChildSpec is a + child specification.

Child processes added using start_child/2 behave in - the same manner as the other child processes, with the following - important exception: If a supervisor dies and is re-created, then - all child processes which were dynamically added to the supervisor - will be lost.

+ the same way as the other child processes, with the an important + exception: if a supervisor dies and is recreated, then + all child processes that were dynamically added to the supervisor + are lost.

@@ -393,11 +401,12 @@ supervisor:delete_child(Sup, Id)
- Simple-One-For-One Supervisors + Simplified one_for_one Supervisors

A supervisor with restart strategy simple_one_for_one is - a simplified one_for_one supervisor, where all child processes are - dynamically added instances of the same child specification.

-

Example of a callback module for a simple_one_for_one supervisor:

+ a simplified one_for_one supervisor, where all child + processes are dynamically added instances of the same process.

+

The following is an example of a callback module for a + simple_one_for_one supervisor:

-module(simple_sup). -behaviour(supervisor). @@ -416,12 +425,12 @@ init(_Args) -> start => {call, start_link, []}, shutdown => brutal_kill}], {ok, {SupFlags, ChildSpecs}}. -

When started, the supervisor will not start any child processes. +

When started, the supervisor does not start any child processes. Instead, all child processes are added dynamically by calling:

supervisor:start_child(Sup, List)

Sup is the pid, or name, of the supervisor. - List is an arbitrary list of terms which will be added to + List is an arbitrary list of terms, which are added to the list of arguments specified in the child specification. If the start function is specified as {M, F, A}, the child process is started by calling @@ -429,17 +438,17 @@ supervisor:start_child(Sup, List)

For example, adding a child to simple_sup above:

supervisor:start_child(Pid, [id1]) -

results in the child process being started by calling +

The result is that the child process is started by calling apply(call, start_link, []++[id1]), or actually:

call:start_link(id1) -

A child under a simple_one_for_one supervisor can be terminated - with

+

A child under a simple_one_for_one supervisor can be + terminated with the following:

supervisor:terminate_child(Sup, Pid) -

where Sup is the pid, or name, of the supervisor and +

Sup is the pid, or name, of the supervisor and Pid is the pid of the child.

-

Because a simple_one_for_one supervisor could have many +

Because a simple_one_for_one supervisor can have many children, it shuts them all down asynchronously. This means that the children will do their cleanup in parallel and therefore the order in which they are stopped is not defined.

@@ -447,11 +456,11 @@ supervisor:terminate_child(Sup, Pid)
Stopping -

Since the supervisor is part of a supervision tree, it will - automatically be terminated by its supervisor. When asked to - shutdown, it will terminate all child processes in reversed start +

Since the supervisor is part of a supervision tree, it is + automatically terminated by its supervisor. When asked to + shut down, it terminates all child processes in reversed start order according to the respective shutdown specifications, and - then terminate itself.

+ then terminates itself.

-- cgit v1.2.3