diff options
author | Sverker Eriksson <[email protected]> | 2019-02-06 19:10:26 +0100 |
---|---|---|
committer | Sverker Eriksson <[email protected]> | 2019-02-06 19:10:26 +0100 |
commit | 98cfd6016f8b40fc97e03b31177d14318349040f (patch) | |
tree | c0fcdd768071c36bfbcbf186d369d9ca14c47421 /system | |
parent | e2ca71b6e7172b320b5b171359d53a161383fb19 (diff) | |
parent | 3825199794da28d79b21052a2e69e2335921d55e (diff) | |
download | otp-98cfd6016f8b40fc97e03b31177d14318349040f.tar.gz otp-98cfd6016f8b40fc97e03b31177d14318349040f.tar.bz2 otp-98cfd6016f8b40fc97e03b31177d14318349040f.zip |
Merge tag 'OTP-21.2' into sverker/map-from-ks-vs-bug
Diffstat (limited to 'system')
92 files changed, 3243 insertions, 1876 deletions
diff --git a/system/COPYRIGHT b/system/COPYRIGHT index ef76b66f6b..91cf0bbfb3 100644 --- a/system/COPYRIGHT +++ b/system/COPYRIGHT @@ -5,7 +5,7 @@ This software is subject to the following Copyrights and Licenses: %CopyrightBegin% -Copyright Ericsson AB 1997-2016. All Rights Reserved. +Copyright Ericsson AB 1997-2018. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -22,6 +22,17 @@ limitations under the License. %CopyrightEnd% --------------------------------------------------------------------------- +[stdlib, compiler] + +* assert.hrl is Copyright (C) 2004-1016 Richard Carlsson, Mickaël Rémond +* array.erl is Copyright (C) 2006-2016 Richard Carlsson and Ericsson AB +* gb_trees.erl is Copyright (C) 1999-2001 Sven-Olof Nyström, Richard Carlsson +* gb_sets.erl is Copyright (C) 1999-2001 Richard Carlsson, Sven-Olof Nyström +* proplists.erl is Copyright (C) 2000-2003 Richard Carlsson +* cerl{_trees,_clauses}.erl are Copyright (C) 1999-2002 Richard Carlsson +* cerl_inline.erl is Copyright (C) 1999-2002 Richard Carlsson + +--------------------------------------------------------------------------- [PCRE] PCRE LICENCE @@ -30,12 +41,15 @@ PCRE LICENCE PCRE is a library of functions to support regular expressions whose syntax and semantics are as close as possible to those of the Perl 5 language. -Release 7 of PCRE is distributed under the terms of the "BSD" licence, as +Release 8 of PCRE is distributed under the terms of the "BSD" licence, as specified below. The documentation for PCRE, supplied in the "doc" -directory, is distributed under the same terms as the software itself. +directory, is distributed under the same terms as the software itself. The data +in the testdata directory is not copyrighted and is in the public domain. The basic library functions are written in C and are freestanding. Also -included in the distribution is a set of C++ wrapper functions. +included in the distribution is a set of C++ wrapper functions, and a +just-in-time compiler that can be used to optimize pattern matching. These +are both optional features that can be omitted when the library is built. THE BASIC LIBRARY FUNCTIONS @@ -48,7 +62,29 @@ Email domain: cam.ac.uk University of Cambridge Computing Service, Cambridge, England. -Copyright (c) 1997-2008 University of Cambridge +Copyright (c) 1997-2018 University of Cambridge +All rights reserved. + + +PCRE JUST-IN-TIME COMPILATION SUPPORT +------------------------------------- + +Written by: Zoltan Herczeg +Email local part: hzmester +Emain domain: freemail.hu + +Copyright(c) 2010-2018 Zoltan Herczeg +All rights reserved. + + +STACK-LESS JUST-IN-TIME COMPILER +-------------------------------- + +Written by: Zoltan Herczeg +Email local part: hzmester +Emain domain: freemail.hu + +Copyright(c) 2009-2018 Zoltan Herczeg All rights reserved. @@ -57,7 +93,7 @@ THE C++ WRAPPER FUNCTIONS Contributed by: Google Inc. -Copyright (c) 2007-2008, Google Inc. +Copyright (c) 2007-2012, Google Inc. All rights reserved. @@ -190,32 +226,91 @@ terms specified in this license. */ --------------------------------------------------------------------------- +[dialyzer] + +%% Copyright 1997-2016 Tobias Lindahl, Stavros Aronis, Kostis Sagonas, +%% Richard Carlsson, et al. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + +--------------------------------------------------------------------------- +[hipe] + +%% Copyright 1997-2016 Erik Stenman (Johansson), Kostis Sagonas, +%% Richard Carlsson, Tobias Lindahl, Per Gustafsson, et al. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + +--------------------------------------------------------------------------- [edoc, syntax_tools] -%% ===================================================================== -%% This library is free software; you can redistribute it and/or modify -%% it under the terms of the GNU Lesser General Public License as -%% published by the Free Software Foundation; either version 2 of the -%% License, or (at your option) any later version. +%% Copyright 1997-2016 Richard Carlsson <[email protected]> +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); you may +%% not use this file except in compliance with the License. You may obtain +%% a copy of the License at <http://www.apache.org/licenses/LICENSE-2.0> +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% -%% This library is distributed in the hope that it will be useful, but -%% WITHOUT ANY WARRANTY; without even the implied warranty of -%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%% Lesser General Public License for more details. +%% Alternatively, you may use this file under the terms of the GNU Lesser +%% General Public License (the "LGPL") as published by the Free Software +%% Foundation; either version 2.1, or (at your option) any later version. +%% If you wish to allow use of your version of this file only under the +%% terms of the LGPL, you should delete the provisions above and replace +%% them with the notice and other provisions required by the LGPL; see +%% <http://www.gnu.org/licenses/>. If you do not delete the provisions +%% above, a recipient may use your version of this file under the terms of +%% either the Apache License or the LGPL. + +--------------------------------------------------------------------------- +[eunit] + +%% Copyright 2004-2016 Richard Carlsson <[email protected]>, +%% Mickaël Rémond <[email protected]> %% -%% You should have received a copy of the GNU Lesser General Public -%% License along with this library; if not, write to the Free Software -%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -%% USA +%% Licensed under the Apache License, Version 2.0 (the "License"); you may +%% not use this file except in compliance with the License. You may obtain +%% a copy of the License at <http://www.apache.org/licenses/LICENSE-2.0> %% -%% $Id$ +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% -%% @copyright 2001-2003 Richard Carlsson -%% @author Richard Carlsson <[email protected]> -%% [http://www.csd.uu.se/~richardc/] -%% @version {@vsn} -%% @end -%% ===================================================================== +%% Alternatively, you may use this file under the terms of the GNU Lesser +%% General Public License (the "LGPL") as published by the Free Software +%% Foundation; either version 2.1, or (at your option) any later version. +%% If you wish to allow use of your version of this file only under the +%% terms of the LGPL, you should delete the provisions above and replace +%% them with the notice and other provisions required by the LGPL; see +%% <http://www.gnu.org/licenses/>. If you do not delete the provisions +%% above, a recipient may use your version of this file under the terms of +%% either the Apache License or the LGPL. --------------------------------------------------------------------------- [leex] diff --git a/system/doc/Makefile b/system/doc/Makefile index eb900b933f..0c4adf6554 100644 --- a/system/doc/Makefile +++ b/system/doc/Makefile @@ -9,11 +9,11 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# +# # The Initial Developer of the Original Code is Ericsson Utvecklings AB. # Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings # AB. All Rights Reserved.'' -# +# # $Id$ # include $(ERL_TOP)/make/target.mk @@ -38,10 +38,9 @@ SUB_DIRECTORIES = design_principles \ # pics \ -SPECIAL_TARGETS = +SPECIAL_TARGETS = # # Default Subdir Targets # include $(ERL_TOP)/make/otp_subdir.mk - diff --git a/system/doc/design_principles/Makefile b/system/doc/design_principles/Makefile index 937b3e28c8..242bf1c9a4 100644 --- a/system/doc/design_principles/Makefile +++ b/system/doc/design_principles/Makefile @@ -1,8 +1,8 @@ # # %CopyrightBegin% -# -# Copyright Ericsson AB 1997-2016. All Rights Reserved. -# +# +# Copyright Ericsson AB 1997-2018. All Rights Reserved. +# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -14,10 +14,11 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# +# # %CopyrightEnd% # # + include $(ERL_TOP)/make/target.mk include $(ERL_TOP)/make/$(TARGET)/otp.mk @@ -27,6 +28,8 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk include $(ERL_TOP)/erts/vsn.mk APPLICATION=otp-system-documentation +XMLDIR := $(XMLDIR)/design_principles + # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- @@ -37,7 +40,7 @@ RELSYSDIR = "$(RELEASE_PATH)/doc/design_principles" # ---------------------------------------------------- XML_PART_FILES = part.xml -include xmlfiles.mk +include xmlfiles.mk XML_CHAPTER_FILES=$(DESIGN_PRINCIPLES_CHAPTER_FILES) @@ -46,7 +49,6 @@ TOPDOCDIR=.. BOOK_FILES = book.xml GIF_FILES = \ - note.gif \ clientserver.gif \ dist1.gif \ dist2.gif \ @@ -58,15 +60,15 @@ GIF_FILES = \ sup5.gif \ sup6.gif -PNG_FILES = \ - code_lock.png \ - code_lock_2.png +SVG_FILES = \ + code_lock.svg \ + code_lock_2.svg -IMAGE_FILES = $(GIF_FILES) $(PNG_FILES) +IMAGE_FILES = $(GIF_FILES) $(SVG_FILES) XML_FILES = \ $(BOOK_FILES) $(XML_CHAPTER_FILES) \ - $(XML_PART_FILES) + $(XML_PART_FILES) # ---------------------------------------------------- @@ -78,10 +80,10 @@ HTMLDIR = ../html/design_principles HTML_UG_FILE = $(HTMLDIR)/users_guide.html # ---------------------------------------------------- -# FLAGS +# FLAGS # ---------------------------------------------------- -XML_FLAGS += -DVIPS_FLAGS += +XML_FLAGS += +DVIPS_FLAGS += # ---------------------------------------------------- # Targets @@ -91,7 +93,7 @@ _create_dirs := $(shell mkdir -p $(HTMLDIR)) $(HTMLDIR)/%.gif: %.gif $(INSTALL_DATA) $< $@ -$(HTMLDIR)/%.png: %.png +$(HTMLDIR)/%.svg: %.svg $(INSTALL_DATA) $< $@ docs: html @@ -102,16 +104,17 @@ html: $(HTML_UG_FILE) images images: $(IMAGE_FILES:%=$(HTMLDIR)/%) -debug opt: +debug opt: clean clean_docs: rm -rf $(HTMLDIR) + rm -rf $(XMLDIR) rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) - rm -f errs core *~ + rm -f errs core *~ # ---------------------------------------------------- # Release Target -# ---------------------------------------------------- +# ---------------------------------------------------- include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs @@ -123,5 +126,3 @@ release_docs_spec: docs release_spec: - - diff --git a/system/doc/design_principles/applications.xml b/system/doc/design_principles/applications.xml index 0a1b65ea8e..3b7b8fdaee 100644 --- a/system/doc/design_principles/applications.xml +++ b/system/doc/design_principles/applications.xml @@ -4,14 +4,14 @@ <chapter> <header> <copyright> - <year>1997</year><year>2016</year> + <year>1997</year><year>2018</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software @@ -19,7 +19,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - + </legalnotice> <title>Applications</title> @@ -172,31 +172,136 @@ ch_app:stop([])</code> </section> <section> - <marker id="app_dir"></marker> - <title>Directory Structure</title> - <p>When packaging code using <c>systools</c>, the code for each - application is placed in a separate directory, - <c>lib/Application-Vsn</c>, where <c>Vsn</c> is the version number.</p> - <p>This can be useful to know, even if <c>systools</c> is not used, - since Erlang/OTP is packaged according to the OTP principles - and thus comes with this directory structure. The code server - (see the <c>code(3)</c> 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.</p> - <p>The application directory structure can also be used in the - development environment. The version number can then - be omitted from the name.</p> - <p>The application directory has the following sub-directories:</p> - <list type="bulleted"> - <item><c>src</c> - Contains the Erlang source code.</item> - <item><c>ebin</c> - Contains the Erlang object code, the - <c>beam</c> files. The <c>.app</c> file is also placed here.</item> - <item><c>priv</c> - Used for application specific files. For - example, C executables are placed here. The function - <c>code:priv_dir/1</c> is to be used to access this directory.</item> - <item><c>include</c> - Used for include files.</item> - </list> + <marker id="app_dir"></marker> + <title>Directory Structure</title> + <p>When packaging code using <c>systools</c>, the code for each + application is placed in a separate directory, + <c>lib/Application-Vsn</c>, where <c>Vsn</c> is the version number.</p> + <p>This can be useful to know, even if <c>systools</c> is not used, + since Erlang/OTP is packaged according to the OTP principles + and thus comes with a specific directory structure. The code server + (see the <seealso marker="kernel:code"><c>code(3)</c></seealso> 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.</p> + <section> + <title>Directory Structure guidelines for a Development Environment</title> + <p>Any directory structure for development will suffice as long as the released directory structure + adhere to the <seealso marker="#app_dir_released">description below</seealso>, + but it is encouraged that the same directory structure + also be used in a development environment. The version number should be omitted from the + application directory name since this is an artifact of the release step. + </p> + <p> Some sub-directories are <em>required</em>. Some sub-directories are <em>optional</em>, meaning that it should + only be used if the application itself requires it. Finally, some sub-directories are <em>recommended</em>, + meaning it is encouraged that it is used and used as described here. For example, both documentation + and tests are encouraged to exist in an application for it to be deemed a proper OTP application.</p> +<code type="none"> + ─ ${application} + ├── doc + │ ├── internal + │ ├── examples + │ └── src + ├── include + ├── priv + ├── src + │ └── ${application}.app.src + └── test +</code> + <list type="bulleted"> + <item><c>src</c> - Required. Contains the Erlang source code, the source of the <c>.app</c> file + and internal include files used by the application itself. Additional sub-directories within + <c>src</c> can be used as namespaces to organize source files. These directories should never + be deeper than one level.</item> + <item><c>priv</c> - Optional. Used for application specific files. </item> + <item><c>include</c> - Optional. Used for public include files that must be reachable from + other applications.</item> + <item><c>doc</c> - Recommended. Any source documentation should be placed in sub-directories here.</item> + <item><c>doc/internal</c> - Recommended. Any documentation that describes implementation details about + this application, not intended for publication, should be placed here.</item> + <item><c>doc/examples</c> - Recommended. Source code for examples on how to use this application should + be placed here. It is encouraged that examples are sourced to the public documentation from + this directory.</item> + <item><c>doc/src</c> - Recommended. All source files for documentation, such as Markdown, AsciiDoc or + XML-files, should be placed here.</item> + <item><c>test</c> - Recommended. All files regarding tests, such as test suites and test specifications, + should be placed here. </item> + </list> + + <p>Other directories in the development environment may be needed. If source code from languages other + than Erlang is used, for instance C-code for NIFs, that code should be placed in a separate directory. + By convention it is recommended to prefix such directories with the language name, for example + <c>c_src</c> for C, <c>java_src</c> for Java or <c>go_src</c> for Go. Directories with <c>_src</c> + suffix indicates that it is a part of the application and the compilation step. The final build artifacts + should target the <c>priv/lib</c> or <c>priv/bin</c> directories.</p> + <p>The <c>priv</c> directory holds assets that the application needs during runtime. Executables should + reside in <c>priv/bin</c> and dynamically-linked libraries should reside in <c>priv/lib</c>. Other assets + are free to reside within the <c>priv</c> directory but it is recommended it does so in a structured manner.</p> + <p>Source files from other languages that generate Erlang code, such as ASN.1 or Mibs, should be placed + in directories, at the top level or in <c>src</c>, with the same name as the source language, for example + <c>asn1</c> and <c>mibs</c>. Build artifacts should be placed in their respective language directory, + such as <c>src</c> for Erlang code or <c>java_src</c> for Java code.</p> + <p>The <c>.app</c> file for release may reside in the <c>ebin</c>-directory in a development environment + but it is encouraged that this is an artifact of the build step. By convention a <c>.app.src</c> file + is used, which resides in the <c>src</c> directory. This file is nearly identical as the + <c>.app</c> file but certain fields may be replaced during the build step, such as the application version.</p> + <p>Directory names should not be capitalized.</p> + <p>It is encouraged to omit empty directories.</p> + + </section> + + <section> + <marker id="app_dir_released"></marker> + <title>Directory Structure for a Released System</title> + <p>A released application must follow a certain structure. + </p> +<code type="none"> + ─ ${application}-${version} + ├── bin + ├── doc + │ ├── html + │ ├── man[1-9] + │ ├── pdf + │ ├── internal + │ └── examples + ├── ebin + │ └── ${application}.app + ├── include + ├── priv + │ ├── lib + │ └── bin + └── src +</code> + <list type="bulleted"> + <item><c>src</c> - Optional. Contains the Erlang source code and internal include files + used by the application itself. This directory is no longer required in a released application.</item> + <item><c>ebin</c> - Required. Contains the Erlang object code, the <c>beam</c> files. + The <c>.app</c> file must also be placed here.</item> + <item><c>priv</c> - Optional. Used for application specific files. <c>code:priv_dir/1</c> + is to be used to access this directory.</item> + <item><c>priv/lib</c> - Recommended. Any shared-object files that are used by the application, + such as NIFs or linked-in-drivers, should be placed here.</item> + <item><c>priv/bin</c> - Recommended. Any executable that is used by the application, + such as port-programs, should be placed here.</item> + <item><c>include</c> - Optional. Used for public include files that must be reachable from + other applications.</item> + <item><c>bin</c> - Optional. Any executable that is a product of the application, + such as escripts or shell-scripts, should be placed here.</item> + <item><c>doc</c> - Optional. Any released documentation should be placed in + sub-directories here.</item> + <item><c>doc/man1</c> - Recommended. Man pages for Application executables.</item> + <item><c>doc/man3</c> - Recommended. Man pages for module APIs.</item> + <item><c>doc/man6</c> - Recommended. Man pages for Application overview.</item> + <item><c>doc/html</c> - Optional. HTML pages for the entire Application.</item> + <item><c>doc/pdf</c> - Optional. PDF documentation for the entire Application.</item> + </list> + + <p>The <c>src</c> directory could be useful to release for debugging purposes but is not required. + The <c>include</c> directory should only be released if the applications has public include files.</p> + <p>The only documentation that is recommended to be released in this way are the man pages. HTML and PDF + will normally be distributed in some other manner.</p> + <p>It is encouraged to omit empty directories.</p> + </section> </section> <section> @@ -258,9 +363,13 @@ ok application are running.</p> <marker id="application_master"></marker> <p>The application controller then creates an - <em>application master</em> 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 + <em>application master</em> for the application. The application + master becomes the group leader of all the processes in the + application. I/O is forwarded to the previous group leader, + though, this is just a way to identify processes that belong to + the application. Used for example to find itself from any process, + or, reciprocally, to kill them all when it terminates.</p> + <p>The application master starts the application by calling the application callback function <c>start/2</c> in the module, and with the start argument, defined by the <c>mod</c> key in the <c>.app</c> file.</p> @@ -381,4 +490,3 @@ application:start(Application, Type)</code> <c>shutdown</c>, not <c>normal</c>.</p> </section> </chapter> - diff --git a/system/doc/design_principles/code_lock.dia b/system/doc/design_principles/code_lock.dia Binary files differindex 8e6ff8a898..fe43d6da2c 100644 --- a/system/doc/design_principles/code_lock.dia +++ b/system/doc/design_principles/code_lock.dia diff --git a/system/doc/design_principles/code_lock.png b/system/doc/design_principles/code_lock.png Binary files differdeleted file mode 100644 index 745fd91920..0000000000 --- a/system/doc/design_principles/code_lock.png +++ /dev/null diff --git a/system/doc/design_principles/code_lock.svg b/system/doc/design_principles/code_lock.svg new file mode 100644 index 0000000000..223e121486 --- /dev/null +++ b/system/doc/design_principles/code_lock.svg @@ -0,0 +1,132 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd"> +<svg width="41cm" height="52cm" viewBox="-2 -2 806 1023" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="492.782,860 600,860 600,900 "/> + <polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="380,900 380,900 380,931.6 "/> + <polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="640,560 640,580 640,580 640,600 "/> + <polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="492.782,300 640,300 640,340 "/> + <g> + <path style="fill: #d5d5f7" d="M 289.774 260 L 470.226,260 C 492.782,276 500,284 500,300 C 500,316 492.782,324 470.226,340 L 289.774,340 C 267.218,324 260,316 260,300 C 260,284 267.218,276 289.774,260z"/> + <path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" d="M 289.774 260 L 470.226,260 C 492.782,276 500,284 500,300 C 500,316 492.782,324 470.226,340 L 289.774,340 C 267.218,324 260,316 260,300 C 260,284 267.218,276 289.774,260"/> + <text font-size="27.0933" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="380" y="308.467"> + <tspan x="380" y="308.467">locked</tspan> + </text> + </g> + <g> + <path style="fill: #d5d5f7" d="M 289.774 820 L 470.226,820 C 492.782,836 500,844 500,860 C 500,876 492.782,884 470.226,900 L 289.774,900 C 267.218,884 260,876 260,860 C 260,844 267.218,836 289.774,820z"/> + <path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" d="M 289.774 820 L 470.226,820 C 492.782,836 500,844 500,860 C 500,876 492.782,884 470.226,900 L 289.774,900 C 267.218,884 260,876 260,860 C 260,844 267.218,836 289.774,820"/> + <text font-size="27.0933" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="380" y="868.467"> + <tspan x="380" y="868.467">open</tspan> + </text> + </g> + <g> + <polygon style="fill: #aad7aa" points="520,340 760,340 736,360 760,380 520,380 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="520,340 760,340 736,360 760,380 520,380 "/> + <text font-size="20.32" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:italic;font-weight:normal" x="546" y="366.35"> + <tspan x="546" y="366.35">{button,Button}</tspan> + </text> + </g> + <g> + <polygon style="fill: #f3cccc" points="640,480 800,520 640,560 480,520 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="640,480 800,520 640,560 480,520 "/> + <text font-size="20.32" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="643.2" y="527.15"> + <tspan x="643.2" y="527.15">Correct Code?</tspan> + </text> + </g> + <g> + <polygon style="fill: #ffff8f" points="0,940 160,940 160,980 0,980 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="0,940 160,940 160,980 0,980 "/> + <text font-size="20.32" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="80" y="966.35"> + <tspan x="80" y="966.35">do_lock()</tspan> + </text> + </g> + <g> + <polygon style="fill: #aad7aa" points="280,931.6 480,931.6 460,960 480,988.4 280,988.4 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="280,931.6 480,931.6 460,960 480,988.4 280,988.4 "/> + <text font-size="20.32" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:italic;font-weight:normal" x="380" y="966.35"> + <tspan x="380" y="966.35">state_timeout</tspan> + </text> + </g> + <g> + <ellipse style="fill: #d5d5f7" cx="380" cy="40" rx="40" ry="40"/> + <ellipse style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" cx="380" cy="40" rx="40" ry="40"/> + <text font-size="27.0933" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="380" y="48.4667"> + <tspan x="380" y="48.4667">init</tspan> + </text> + </g> + <g> + <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="380.719" y1="180" x2="380.087" y2="250.264"/> + <polygon style="fill: #000000" points="380.02,257.764 375.11,247.72 380.087,250.264 385.11,247.809 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="380.02,257.764 375.11,247.72 380.087,250.264 385.11,247.809 "/> + </g> + <g> + <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="640.438" y1="440" x2="640.106" y2="470.265"/> + <polygon style="fill: #000000" points="640.024,477.764 635.134,467.71 640.106,470.265 645.134,467.819 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="640.024,477.764 635.134,467.71 640.106,470.265 645.134,467.819 "/> + </g> + <polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="640,700 640,740 380,740 380,740 "/> + <text font-size="20.32" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="640" y="578.9"> + <tspan x="640" y="578.9">Y</tspan> + </text> + <text font-size="20.32" style="fill: #000000;text-anchor:end;font-family:sans-serif;font-style:normal;font-weight:normal" x="480" y="538.9"> + <tspan x="480" y="538.9">N</tspan> + </text> + <g> + <polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="80,940 80,220 370.623,220 "/> + <polygon style="fill: #000000" points="378.123,220 368.123,225 370.623,220 368.123,215 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="378.123,220 368.123,225 370.623,220 368.123,215 "/> + </g> + <g> + <polygon style="fill: #aad7aa" points="500,900 700,900 680,920 700,940 500,940 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="500,900 700,900 680,920 700,940 500,940 "/> + <text font-size="20.32" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:italic;font-weight:normal" x="522" y="926.35"> + <tspan x="522" y="926.35">{button,Digit}</tspan> + </text> + </g> + <g> + <polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="600,940 600,980 760,980 760,780 389.736,780 "/> + <polygon style="fill: #000000" points="382.236,780 392.236,775 389.736,780 392.236,785 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="382.236,780 392.236,775 389.736,780 392.236,785 "/> + </g> + <g> + <polygon style="fill: #ffff8f" points="260,120 501.438,120 501.438,180 260,180 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="260,120 501.438,120 501.438,180 260,180 "/> + <text font-size="20.32" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="286.144" y="143.65"> + <tspan x="286.144" y="143.65">do_lock()</tspan> + <tspan x="286.144" y="169.05">Clear Buttons</tspan> + </text> + </g> + <g> + <polygon style="fill: #ffff8f" points="500,600 780,600 780,700 500,700 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="500,600 780,600 780,700 500,700 "/> + <text font-size="20.32" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="530" y="630.95"> + <tspan x="530" y="630.95">do_unlock()</tspan> + <tspan x="530" y="656.35">Clear Buttons</tspan> + <tspan x="530" y="681.75">state_timeout 10 s</tspan> + </text> + </g> + <g> + <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="380" y1="80" x2="380.544" y2="110.266"/> + <polygon style="fill: #000000" points="380.679,117.764 375.5,107.856 380.544,110.266 385.498,107.676 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="380.679,117.764 375.5,107.856 380.544,110.266 385.498,107.676 "/> + </g> + <g> + <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="380" y1="740" x2="380" y2="810.264"/> + <polygon style="fill: #000000" points="380,817.764 375,807.764 380,810.264 385,807.764 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="380,817.764 375,807.764 380,810.264 385,807.764 "/> + </g> + <polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="380,988.4 380,1020 80,1020 80,980 "/> + <g> + <polygon style="fill: #ffff8f" points="540,400 740.875,400 740.875,440 540,440 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="540,400 740.875,400 740.875,440 540,440 "/> + <text font-size="20.32" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="640.438" y="426.35"> + <tspan x="640.438" y="426.35">Collect Buttons</tspan> + </text> + </g> + <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="640" y1="380" x2="640.438" y2="400"/> + <g> + <polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="480,520 380,520 380,351 "/> + <polygon style="fill: #000000" points="385,351 380,341 375,351 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="385,351 380,341 375,351 "/> + </g> +</svg> diff --git a/system/doc/design_principles/code_lock_2.dia b/system/doc/design_principles/code_lock_2.dia Binary files differindex 142909a2f5..31eb0fb6eb 100644 --- a/system/doc/design_principles/code_lock_2.dia +++ b/system/doc/design_principles/code_lock_2.dia diff --git a/system/doc/design_principles/code_lock_2.png b/system/doc/design_principles/code_lock_2.png Binary files differdeleted file mode 100644 index ecf7b0d799..0000000000 --- a/system/doc/design_principles/code_lock_2.png +++ /dev/null diff --git a/system/doc/design_principles/code_lock_2.svg b/system/doc/design_principles/code_lock_2.svg new file mode 100644 index 0000000000..d3e15e7577 --- /dev/null +++ b/system/doc/design_principles/code_lock_2.svg @@ -0,0 +1,140 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd"> +<svg width="41cm" height="52cm" viewBox="-1 0 806 1021" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="380,300.55 380,300 140,300 140,360 "/> + <polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="412.782,900 412.782,900 560,900 560,940 "/> + <g> + <polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="560,980 560,1020 0,1020 0,120.55 370.264,120.55 "/> + <polygon style="fill: #000000" points="377.764,120.55 367.764,125.55 370.264,120.55 367.764,115.55 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="377.764,120.55 367.764,125.55 370.264,120.55 367.764,115.55 "/> + </g> + <polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="640,680 640,720 300,720 300,760 "/> + <polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="492.782,300.55 492.782,300 640,300 640,360 "/> + <g> + <path style="fill: #d5d5f7" d="M 289.774 261.1 L 470.226,261.1 C 492.782,276.88 500,284.77 500,300.55 C 500,316.33 492.782,324.22 470.226,340 L 289.774,340 C 267.218,324.22 260,316.33 260,300.55 C 260,284.77 267.218,276.88 289.774,261.1z"/> + <path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" d="M 289.774 261.1 L 470.226,261.1 C 492.782,276.88 500,284.77 500,300.55 C 500,316.33 492.782,324.22 470.226,340 L 289.774,340 C 267.218,324.22 260,316.33 260,300.55 C 260,284.77 267.218,276.88 289.774,261.1"/> + <text font-size="27.0933" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="380" y="309.017"> + <tspan x="380" y="309.017">locked</tspan> + </text> + </g> + <g> + <path style="fill: #d5d5f7" d="M 209.774 860 L 390.226,860 C 412.782,876 420,884 420,900 C 420,916 412.782,924 390.226,940 L 209.774,940 C 187.218,924 180,916 180,900 C 180,884 187.218,876 209.774,860z"/> + <path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" d="M 209.774 860 L 390.226,860 C 412.782,876 420,884 420,900 C 420,916 412.782,924 390.226,940 L 209.774,940 C 187.218,924 180,916 180,900 C 180,884 187.218,876 209.774,860"/> + <text font-size="27.0933" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:700" x="300" y="908.467"> + <tspan x="300" y="908.467">open</tspan> + </text> + </g> + <g> + <polygon style="fill: #aad7aa" points="520,360 760,360 736,380 760,400 520,400 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="520,360 760,360 736,380 760,400 520,400 "/> + <text font-size="20.32" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:italic;font-weight:normal" x="546" y="386.35"> + <tspan x="546" y="386.35">{button,Button}</tspan> + </text> + </g> + <g> + <polygon style="fill: #ffff8f" points="140,760 460,760 460,816.8 140,816.8 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="140,760 460,760 460,816.8 140,816.8 "/> + <text font-size="20.32" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="174" y="782.05"> + <tspan x="174" y="782.05">do_unlock()</tspan> + <tspan x="174" y="807.45">state_timeout 10 s</tspan> + </text> + </g> + <g> + <polygon style="fill: #ffff8f" points="260,160 500,160 500,222.2 260,222.2 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="260,160 500,160 500,222.2 260,222.2 "/> + <text font-size="20.32" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="380" y="184.75"> + <tspan x="380" y="184.75">do_lock()</tspan> + <tspan x="380" y="210.15">Clear Buttons</tspan> + </text> + </g> + <g> + <polygon style="fill: #aad7aa" points="460,940 660,940 640,960 660,980 460,980 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="460,940 660,940 640,960 660,980 460,980 "/> + <text font-size="20.32" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:italic;font-weight:normal" x="560" y="966.35"> + <tspan x="560" y="966.35">state_timeout</tspan> + </text> + </g> + <g> + <ellipse style="fill: #d5d5f7" cx="380" cy="41.1" rx="40" ry="40"/> + <ellipse style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" cx="380" cy="41.1" rx="40" ry="40"/> + <text font-size="27.0933" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="380" y="49.5667"> + <tspan x="380" y="49.5667">init</tspan> + </text> + </g> + <g> + <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="380" y1="81.1" x2="380" y2="150.264"/> + <polygon style="fill: #000000" points="380,157.764 375,147.764 380,150.264 385,147.764 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="380,157.764 375,147.764 380,150.264 385,147.764 "/> + </g> + <g> + <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="380" y1="222.2" x2="380" y2="251.364"/> + <polygon style="fill: #000000" points="380,258.864 375,248.864 380,251.364 385,248.864 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="380,258.864 375,248.864 380,251.364 385,248.864 "/> + </g> + <g> + <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="300" y1="816.8" x2="300" y2="850.264"/> + <polygon style="fill: #000000" points="300,857.764 295,847.764 300,850.264 305,847.764 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="300,857.764 295,847.764 300,850.264 305,847.764 "/> + </g> + <g> + <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="380" y1="560" x2="380" y2="349.736"/> + <polygon style="fill: #000000" points="380,342.236 385,352.236 380,349.736 375,352.236 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="380,342.236 385,352.236 380,349.736 375,352.236 "/> + </g> + <g> + <polygon style="fill: #ffff8f" points="240,560 520,560 520,600 240,600 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="240,560 520,560 520,600 240,600 "/> + <text font-size="20.32" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="380" y="586.35"> + <tspan x="380" y="586.35">state_timeout 30 s</tspan> + </text> + </g> + <g> + <polygon style="fill: #aad7aa" points="40,360 240,360 220,380 240,400 40,400 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="40,360 240,360 220,380 240,400 40,400 "/> + <text font-size="20.32" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:italic;font-weight:normal" x="62" y="386.35"> + <tspan x="62" y="386.35">state_timeout</tspan> + </text> + </g> + <g> + <polygon style="fill: #ffff8f" points="540,440 741.438,440 741.438,480 540,480 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="540,440 741.438,440 741.438,480 540,480 "/> + <text font-size="20.32" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="640.719" y="466.35"> + <tspan x="640.719" y="466.35">Collect Buttons</tspan> + </text> + </g> + <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="640" y1="400" x2="640.719" y2="440"/> + <g> + <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="640.611" y1="480.995" x2="640.056" y2="589"/> + <polygon style="fill: #000000" points="635.057,588.974 640.005,599 645.056,589.026 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="635.057,588.974 640.005,599 645.056,589.026 "/> + </g> + <g> + <polygon style="fill: #ffff8f" points="40,440 240,440 240,480 40,480 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="40,440 240,440 240,480 40,480 "/> + <text font-size="20.32" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="140" y="466.35"> + <tspan x="140" y="466.35">Clear Buttons</tspan> + </text> + </g> + <polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="480,640 380,640 380,600 "/> + <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="140" y1="400" x2="140" y2="440"/> + <g> + <g> + <polygon style="fill: #f3cccc" points="640,600 800,640 640,680 480,640 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="640,600 800,640 640,680 480,640 "/> + <text font-size="20.32" style="fill: #000000;text-anchor:middle;font-family:sans-serif;font-style:normal;font-weight:normal" x="643.2" y="647.15"> + <tspan x="643.2" y="647.15">Correct Code?</tspan> + </text> + </g> + <text font-size="20.32" style="fill: #000000;text-anchor:end;font-family:sans-serif;font-style:normal;font-weight:normal" x="480" y="658.9"> + <tspan x="480" y="658.9">N</tspan> + </text> + <text font-size="20.32" style="fill: #000000;text-anchor:start;font-family:sans-serif;font-style:normal;font-weight:normal" x="640" y="698.9"> + <tspan x="640" y="698.9">Y</tspan> + </text> + </g> + <g> + <polyline style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="140,480 140,516 369,516 "/> + <polygon style="fill: #000000" points="369,521 379,516 369,511 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="369,521 379,516 369,511 "/> + </g> +</svg> diff --git a/system/doc/design_principles/des_princ.xml b/system/doc/design_principles/des_princ.xml index 8ab8661c2d..2bfb8eb3c7 100644 --- a/system/doc/design_principles/des_princ.xml +++ b/system/doc/design_principles/des_princ.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1997</year><year>2016</year> + <year>1997</year><year>2018</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -64,7 +64,7 @@ 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.</p> + finite-state machines, or event handlers.</p> <p><em>Behaviours</em> are formalizations of these common patterns. The idea is to divide the code for a process in a generic part (a behaviour module) and a specific part (a @@ -225,10 +225,8 @@ free(Ch, {Alloc, Free} = Channels) -> <list type="bulleted"> <item><p><seealso marker="gen_server_concepts">gen_server</seealso></p> <p>For implementing the server of a client-server relation</p></item> - <item><p><seealso marker="fsm">gen_fsm</seealso></p> - <p>For implementing finite-state machines (Old)</p></item> <item><p><seealso marker="statem">gen_statem</seealso></p> - <p>For implementing state machines (New)</p></item> + <p>For implementing state machines</p></item> <item><p><seealso marker="events">gen_event</seealso></p> <p>For implementing event handling functionality</p></item> <item><p><seealso marker="sup_princ">supervisor</seealso></p> diff --git a/system/doc/design_principles/fsm.xml b/system/doc/design_principles/fsm.xml deleted file mode 100644 index 4f2b75e6e8..0000000000 --- a/system/doc/design_principles/fsm.xml +++ /dev/null @@ -1,338 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<!DOCTYPE chapter SYSTEM "chapter.dtd"> - -<chapter> - <header> - <copyright> - <year>1997</year><year>2016</year> - <holder>Ericsson AB. All Rights Reserved.</holder> - </copyright> - <legalnotice> - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - </legalnotice> - - <title>gen_fsm Behaviour</title> - <prepared></prepared> - <docno></docno> - <date></date> - <rev></rev> - <file>fsm.xml</file> - </header> - <marker id="gen_fsm behaviour"></marker> - <note> - <p> - There is a new behaviour - <seealso marker="statem"><c>gen_statem</c></seealso> - that is intended to replace <c>gen_fsm</c> for new code. - It has the same features and add some really useful. - This module will not be removed for the foreseeable future - to keep old state machine implementations running. - </p> - </note> - <p>This section is to be read with the <c>gen_fsm(3)</c> manual page - in STDLIB, where all interface functions and callback - functions are described in detail.</p> - - <section> - <title>Finite-State Machines</title> - <p>A Finite-State Machine (FSM) can be described as a set of - relations of the form:</p> - <pre> -State(S) x Event(E) -> Actions(A), State(S')</pre> - <p>These relations are interpreted as meaning:</p> - <quote> - <p>If we are in state <c>S</c> and event <c>E</c> occurs, we - are to perform actions <c>A</c> and make a transition to - state <c>S'</c>.</p> - </quote> - <p>For an FSM implemented using the <c>gen_fsm</c> behaviour, - the state transition rules are written as a number of Erlang - functions, which conform to the following convention:</p> - <pre> -StateName(Event, StateData) -> - .. code for actions here ... - {next_state, StateName', StateData'}</pre> - </section> - - <section> - <title>Example</title> - <p>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 can be correct, incomplete, or wrong.</p> - <p>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.</p> - <p>Implementing the code lock FSM using <c>gen_fsm</c> results in - the following callback module:</p> - <marker id="ex"></marker> - <code type="none"><![CDATA[ --module(code_lock). --behaviour(gen_fsm). - --export([start_link/1]). --export([button/1]). --export([init/1, locked/2, open/2]). - -start_link(Code) -> - gen_fsm:start_link({local, code_lock}, code_lock, lists:reverse(Code), []). - -button(Digit) -> - gen_fsm:send_event(code_lock, {button, Digit}). - -init(Code) -> - {ok, locked, {[], Code}}. - -locked({button, Digit}, {SoFar, Code}) -> - case [Digit|SoFar] of - Code -> - do_unlock(), - {next_state, open, {[], Code}, 30000}; - Incomplete when length(Incomplete)<length(Code) -> - {next_state, locked, {Incomplete, Code}}; - _Wrong -> - {next_state, locked, {[], Code}} - end. - -open(timeout, State) -> - do_lock(), - {next_state, locked, State}.]]></code> - <p>The code is explained in the next sections.</p> - </section> - - <section> - <title>Starting gen_fsm</title> - <p>In the example in the previous section, the <c>gen_fsm</c> is - started by calling <c>code_lock:start_link(Code)</c>:</p> - <code type="none"> -start_link(Code) -> - gen_fsm:start_link({local, code_lock}, code_lock, lists:reverse(Code), []). - </code> - <p><c>start_link</c> calls the function <c>gen_fsm:start_link/4</c>, - which spawns and links to a new process, a <c>gen_fsm</c>.</p> - <list type="bulleted"> - <item> - <p>The first argument, <c>{local, code_lock}</c>, specifies - the name. In this case, the <c>gen_fsm</c> is locally - registered as <c>code_lock</c>.</p> - <p>If the name is omitted, the <c>gen_fsm</c> is not registered. - Instead its pid must be used. The name can also be given - as <c>{global, Name}</c>, in which case the <c>gen_fsm</c> is - registered using <c>global:register_name/2</c>.</p> - </item> - <item> - <p>The second argument, <c>code_lock</c>, is the name of - the callback module, that is, the module where the callback - functions are located.</p> - <p>The interface functions (<c>start_link</c> and <c>button</c>) - are then located in the same module as the callback - functions (<c>init</c>, <c>locked</c>, and <c>open</c>). This - is normally good programming practice, to have the code - corresponding to one process contained in one module.</p> - </item> - <item> - <p>The third argument, <c>Code</c>, is a list of digits that - which is passed reversed to the callback function <c>init</c>. - Here, <c>init</c> - gets the correct code for the lock as indata.</p> - </item> - <item> - <p>The fourth argument, <c>[]</c>, is a list of options. See - the <c>gen_fsm(3)</c> manual page for available options.</p> - </item> - </list> - <p>If name registration succeeds, the new <c>gen_fsm</c> process calls - the callback function <c>code_lock:init(Code)</c>. This function - is expected to return <c>{ok, StateName, StateData}</c>, where - <c>StateName</c> is the name of the initial state of the - <c>gen_fsm</c>. In this case <c>locked</c>, assuming the door is - locked to begin with. <c>StateData</c> is the internal state of - the <c>gen_fsm</c>. (For <c>gen_fsm</c>, 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.</p> - <code type="none"> -init(Code) -> - {ok, locked, {[], Code}}.</code> - <p><c>gen_fsm:start_link</c> is synchronous. It does not return until - the <c>gen_fsm</c> has been initialized and is ready to - receive notifications.</p> - <p><c>gen_fsm:start_link</c> must be used if the <c>gen_fsm</c> is - part of a supervision tree, that is, started by a supervisor. There - is another function, <c>gen_fsm:start</c>, to start a standalone - <c>gen_fsm</c>, that is, a <c>gen_fsm</c> that is not part of a - supervision tree.</p> - </section> - - <section> - <title>Notifying about Events</title> - <p>The function notifying the code lock about a button event is - implemented using <c>gen_fsm:send_event/2</c>:</p> - <code type="none"> -button(Digit) -> - gen_fsm:send_event(code_lock, {button, Digit}).</code> - <p><c>code_lock</c> is the name of the <c>gen_fsm</c> and must - agree with the name used to start it. - <c>{button, Digit}</c> is the actual event.</p> - <p>The event is made into a message and sent to the <c>gen_fsm</c>. - When the event is received, the <c>gen_fsm</c> calls - <c>StateName(Event, StateData)</c>, which is expected to return a - tuple <c>{next_state,StateName1,StateData1}</c>. - <c>StateName</c> is the name of the current state and - <c>StateName1</c> is the name of the next state to go to. - <c>StateData1</c> is a new value for the state data of - the <c>gen_fsm</c>.</p> - <code type="none"><![CDATA[ -locked({button, Digit}, {SoFar, Code}) -> - case [Digit|SoFar] of - Code -> - do_unlock(), - {next_state, open, {[], Code}, 30000}; - Incomplete when length(Incomplete)<length(Code) -> - {next_state, locked, {Incomplete, Code}}; - _Wrong -> - {next_state, locked, {[], Code}}; - end. - -open(timeout, State) -> - do_lock(), - {next_state, locked, State}.]]></code> - <p>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 <c>gen_fsm</c> goes to state <c>open</c>, or the door - remains in state <c>locked</c>.</p> - </section> - - <section> - <title>Time-Outs</title> - <p>When a correct code has been given, the door is unlocked and - the following tuple is returned from <c>locked/2</c>:</p> - <code type="none"> -{next_state, open, {[], Code}, 30000};</code> - <p>30,000 is a time-out value in milliseconds. After this time, - that is, 30 seconds, a time-out occurs. Then, - <c>StateName(timeout, StateData)</c> is called. The time-out - then occurs when the door has been in state <c>open</c> for 30 - seconds. After that the door is locked again:</p> - <code type="none"> -open(timeout, State) -> - do_lock(), - {next_state, locked, State}.</code> - </section> - - <section> - <title>All State Events</title> - <p>Sometimes an event can arrive at any state of the <c>gen_fsm</c>. - Instead of sending the message with <c>gen_fsm:send_event/2</c> - and writing one clause handling the event for each state function, - the message can be sent with <c>gen_fsm:send_all_state_event/2</c> - and handled with <c>Module:handle_event/3</c>:</p> - <code type="none"> --module(code_lock). -... --export([stop/0]). -... - -stop() -> - gen_fsm:send_all_state_event(code_lock, stop). - -... - -handle_event(stop, _StateName, StateData) -> - {stop, normal, StateData}.</code> - </section> - - <section> - <title>Stopping</title> - - <section> - <title>In a Supervision Tree</title> - <p>If the <c>gen_fsm</c> is part of a supervision tree, no stop - function is needed. The <c>gen_fsm</c> is automatically - terminated by its supervisor. Exactly how this is done is - defined by a - <seealso marker="sup_princ#shutdown">shutdown strategy</seealso> - set in the supervisor.</p> - <p>If it is necessary to clean up before termination, the shutdown - strategy must be a time-out value and the <c>gen_fsm</c> must be - set to trap exit signals in the <c>init</c> function. When ordered - to shutdown, the <c>gen_fsm</c> then calls the callback function - <c>terminate(shutdown, StateName, StateData)</c>:</p> - <code type="none"> -init(Args) -> - ..., - process_flag(trap_exit, true), - ..., - {ok, StateName, StateData}. - -... - -terminate(shutdown, StateName, StateData) -> - ..code for cleaning up here.. - ok.</code> - </section> - - <section> - <title>Standalone gen_fsm</title> - <p>If the <c>gen_fsm</c> is not part of a supervision tree, a stop - function can be useful, for example:</p> - <code type="none"> -... --export([stop/0]). -... - -stop() -> - gen_fsm:send_all_state_event(code_lock, stop). -... - -handle_event(stop, _StateName, StateData) -> - {stop, normal, StateData}. - -... - -terminate(normal, _StateName, _StateData) -> - ok.</code> - <p>The callback function handling the <c>stop</c> event returns a - tuple, <c>{stop,normal,StateData1}</c>, where <c>normal</c> - specifies that it is a normal termination and <c>StateData1</c> - is a new value for the state data of the <c>gen_fsm</c>. This - causes the <c>gen_fsm</c> to call - <c>terminate(normal,StateName,StateData1)</c> and then - it terminates gracefully:</p> - </section> - </section> - - <section> - <title>Handling Other Messages</title> - <p>If the <c>gen_fsm</c> is to be able to receive other messages - than events, the callback function - <c>handle_info(Info, StateName, StateData)</c> must be implemented - to handle them. Examples of - other messages are exit messages, if the <c>gen_fsm</c> is linked to - other processes (than the supervisor) and trapping exit signals.</p> - <code type="none"> -handle_info({'EXIT', Pid, Reason}, StateName, StateData) -> - ..code to handle exits here.. - {next_state, StateName1, StateData1}.</code> - <p>The code_change method must also be implemented.</p> - <code type="none"> -code_change(OldVsn, StateName, StateData, Extra) -> - ..code to convert state (and more) during code change - {ok, NextStateName, NewStateData}</code> - </section> -</chapter> - diff --git a/system/doc/design_principles/gen_server_concepts.xml b/system/doc/design_principles/gen_server_concepts.xml index c1b98189d5..06413a3d93 100644 --- a/system/doc/design_principles/gen_server_concepts.xml +++ b/system/doc/design_principles/gen_server_concepts.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1997</year><year>2016</year> + <year>1997</year><year>2018</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -32,7 +32,7 @@ <marker id="gen_server"></marker> <p>This section is to be read with the <seealso marker="stdlib:gen_server">gen_server(3)</seealso> - manual page in <c>stdblib</c>, where all interface functions and + manual page in <c>stdlib</c>, where all interface functions and callback functions are described in detail.</p> <section> diff --git a/system/doc/design_principles/note.gif b/system/doc/design_principles/note.gif Binary files differdeleted file mode 100644 index 6fffe30419..0000000000 --- a/system/doc/design_principles/note.gif +++ /dev/null diff --git a/system/doc/design_principles/part.xml b/system/doc/design_principles/part.xml index 6495211e04..899c7f2afe 100644 --- a/system/doc/design_principles/part.xml +++ b/system/doc/design_principles/part.xml @@ -4,7 +4,7 @@ <part xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>1997</year><year>2016</year> + <year>1997</year><year>2017</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -30,7 +30,6 @@ </header> <xi:include href="des_princ.xml"/> <xi:include href="gen_server_concepts.xml"/> - <xi:include href="fsm.xml"/> <xi:include href="statem.xml"/> <xi:include href="events.xml"/> <xi:include href="sup_princ.xml"/> diff --git a/system/doc/design_principles/release_structure.xml b/system/doc/design_principles/release_structure.xml index a0ab21c43f..e8dfcad805 100644 --- a/system/doc/design_principles/release_structure.xml +++ b/system/doc/design_principles/release_structure.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2016</year> + <year>2003</year><year>2018</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -208,8 +208,8 @@ releases/ch_rel-1.rel</pre> is therefore now instead duplicated in the tar file so no manual copying is necessary.</p> <p>If a <c>relup</c> file and/or a system configuration file called - <c>sys.config</c> is found, these files are also included in - the release package. See + <c>sys.config</c>, or a <c>sys.config.src</c>, is found, these files + are also included in the release package. See <seealso marker="release_handling#req">Release Handling</seealso>.</p> <p>Options can be set to make the release package include source code and the ERTS binary as well.</p> @@ -240,7 +240,7 @@ $ROOT/lib/App1-AVsn1/ebin <item><c>erts-EVsn/bin</c> - Erlang runtime system executables</item> <item><c>releases/Vsn</c> - <c>.rel</c> file and boot script <c>start.boot</c>; if present in the release package, <c>relup</c> - and/or <c>sys.config</c></item> + and/or <c>sys.config</c> or <c>sys.config.src</c></item> <item><c>bin</c> - Top-level Erlang runtime system executables</item> </list> <p>Applications are not required to be located under directory diff --git a/system/doc/design_principles/spec_proc.xml b/system/doc/design_principles/spec_proc.xml index 5b156ac263..65f5492bdd 100644 --- a/system/doc/design_principles/spec_proc.xml +++ b/system/doc/design_principles/spec_proc.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1997</year><year>2016</year> + <year>1997</year><year>2018</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -45,61 +45,63 @@ <p>The <c>sys</c> module has functions for simple debugging of processes implemented using behaviours. The <c>code_lock</c> example from - <seealso marker="fsm#ex">gen_fsm Behaviour</seealso> + <seealso marker="statem#Example">gen_statem Behaviour</seealso> is used to illustrate this:</p> <pre> -% <input>erl</input> -Erlang (BEAM) emulator version 5.2.3.6 [hipe] [threads:0] +Erlang/OTP 20 [DEVELOPMENT] [erts-9.0] [source-5ace45e] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [hipe] [kernel-poll:false] -Eshell V5.2.3.6 (abort with ^G) -1> <input>code_lock:start_link([1,2,3,4]).</input> -{ok,<0.32.0>} -2> <input>sys:statistics(code_lock, true).</input> +Eshell V9.0 (abort with ^G) +1> code_lock:start_link([1,2,3,4]). +Lock +{ok,<0.63.0>} +2> sys:statistics(code_lock, true). ok -3> <input>sys:trace(code_lock, true).</input> +3> sys:trace(code_lock, true). ok -4> <input>code_lock:button(4).</input> -*DBG* code_lock got event {button,4} in state closed +4> code_lock:button(1). +*DBG* code_lock receive cast {button,1} in state locked ok -*DBG* code_lock switched to state closed -5> <input>code_lock:button(3).</input> -*DBG* code_lock got event {button,3} in state closed +*DBG* code_lock consume cast {button,1} in state locked +5> code_lock:button(2). +*DBG* code_lock receive cast {button,2} in state locked ok -*DBG* code_lock switched to state closed -6> <input>code_lock:button(2).</input> -*DBG* code_lock got event {button,2} in state closed +*DBG* code_lock consume cast {button,2} in state locked +6> code_lock:button(3). +*DBG* code_lock receive cast {button,3} in state locked ok -*DBG* code_lock switched to state closed -7> <input>code_lock:button(1).</input> -*DBG* code_lock got event {button,1} in state closed +*DBG* code_lock consume cast {button,3} in state locked +7> code_lock:button(4). +*DBG* code_lock receive cast {button,4} in state locked ok -OPEN DOOR -*DBG* code_lock switched to state open -*DBG* code_lock got event timeout in state open -CLOSE DOOR -*DBG* code_lock switched to state closed -8> <input>sys:statistics(code_lock, get).</input> -{ok,[{start_time,{{2003,6,12},{14,11,40}}}, - {current_time,{{2003,6,12},{14,12,14}}}, - {reductions,333}, +Unlock +*DBG* code_lock consume cast {button,4} in state locked +*DBG* code_lock receive state_timeout lock in state open +Lock +*DBG* code_lock consume state_timeout lock in state open +8> sys:statistics(code_lock, get). +{ok,[{start_time,{{2017,4,21},{16,8,7}}}, + {current_time,{{2017,4,21},{16,9,42}}}, + {reductions,2973}, {messages_in,5}, {messages_out,0}]} -9> <input>sys:statistics(code_lock, false).</input> +9> sys:statistics(code_lock, false). ok -10> <input>sys:trace(code_lock, false).</input> +10> sys:trace(code_lock, false). ok -11> <input>sys:get_status(code_lock).</input> -{status,<0.32.0>, - {module,gen_fsm}, - [[{'$ancestors',[<0.30.0>]}, - {'$initial_call',{gen,init_it, - [gen_fsm,<0.30.0>,<0.30.0>, - {local,code_lock}, - code_lock, - [1,2,3,4], - []]}}], - running,<0.30.0>,[], - [code_lock,closed,{[],[1,2,3,4]},code_lock,infinity]]}</pre> +11> sys:get_status(code_lock). +{status,<0.63.0>, + {module,gen_statem}, + [[{'$initial_call',{code_lock,init,1}}, + {'$ancestors',[<0.61.0>]}], + running,<0.61.0>,[], + [{header,"Status for state machine code_lock"}, + {data,[{"Status",running}, + {"Parent",<0.61.0>}, + {"Logged Events",[]}, + {"Postponed",[]}]}, + {data,[{"State", + {locked,#{code => [1,2,3,4],remaining => [1,2,3,4]}}}]}]]} + </pre> </section> <section> @@ -310,7 +312,7 @@ sys:handle_debug(Deb, Func, Info, Event) => Deb1</code> 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 <c>{in,Msg[,From]}</c> and <c>{out,Msg,To}</c>, + the tuples <c>{in,Msg[,From]}</c> and <c>{out,Msg,To[,State]}</c>, respectively.</item> </list> <p><c>handle_debug</c> returns an updated debug structure diff --git a/system/doc/design_principles/statem.xml b/system/doc/design_principles/statem.xml index 57e47431b8..16a901b3a5 100644 --- a/system/doc/design_principles/statem.xml +++ b/system/doc/design_principles/statem.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2016</year> + <year>2016</year><year>2018</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -29,27 +29,18 @@ <rev></rev> <file>statem.xml</file> </header> - <marker id="gen_statem behaviour"></marker> + <marker id="gen_statem Behaviour" /> <p> This section is to be read with the <seealso marker="stdlib:gen_statem"><c>gen_statem(3)</c></seealso> manual page in STDLIB, where all interface functions and callback functions are described in detail. </p> - <note> - <p> - This is a new behavior in Erlang/OTP 19.0. - It has been thoroughly reviewed, is stable enough - to be used by at least two heavy OTP applications, and is here to stay. - Depending on user feedback, we do not expect - but can find it necessary to make minor - not backward compatible changes into Erlang/OTP 20.0. - </p> - </note> <!-- =================================================================== --> <section> + <marker id="Event-Driven State Machines" /> <title>Event-Driven State Machines</title> <p> Established Automata Theory does not deal much with @@ -71,13 +62,14 @@ State(S) x Event(E) -> Actions(A), State(S')</pre> <p>These relations are interpreted as follows: if we are in state <c>S</c> and event <c>E</c> occurs, we are to perform actions <c>A</c> and make a transition to - state <c>S'</c>. Notice that <c>S'</c> can be equal to <c>S</c>. + state <c>S'</c>. Notice that <c>S'</c> can be equal to <c>S</c> + and that <c>A</c> can be empty. </p> <p> As <c>A</c> and <c>S'</c> depend only on <c>S</c> and <c>E</c>, the kind of state machine described - here is a Mealy Machine - (see, for example, the corresponding Wikipedia article). + here is a Mealy machine + (see, for example, the Wikipedia article "Mealy machine"). </p> <p> Like most <c>gen_</c> behaviors, <c>gen_statem</c> keeps @@ -87,61 +79,172 @@ State(S) x Event(E) -> Actions(A), State(S')</pre> or on the number of distinct input events, a state machine implemented with this behavior is in fact Turing complete. - But it feels mostly like an Event-Driven Mealy Machine. + But it feels mostly like an Event-Driven Mealy machine. + </p> + </section> + +<!-- =================================================================== --> + + <section> + <marker id="When to use gen_statem" /> + <title>When to use gen_statem</title> + <p> + If your process logic is convenient to describe as a state machine, + and you want any of these <c>gen_statem</c> key features: + </p> + <list type="bulleted"> + <item> + Co-located callback code for each state, + regardless of + <seealso marker="#Event Types">Event Type</seealso> + (such as <em>call</em>, <em>cast</em> and <em>info</em>) + </item> + <item> + <seealso marker="#Postponing Events"> + Postponing Events + </seealso> + (a substitute for selective receive) + </item> + <item> + <seealso marker="#Inserted Events"> + Inserted Events + </seealso> + that is: events from the state machine to itself + (in particular purely internal events) + </item> + <item> + <seealso marker="#State Enter Calls"> + State Enter Calls + </seealso> + (callback on state entry co-located with the rest + of each state's callback code) + </item> + <item> + Easy-to-use timeouts + (<seealso marker="#State Time-Outs">State Time-Outs</seealso>, + <seealso marker="#Event Time-Outs">Event Time-Outs</seealso> + and + <seealso marker="#Generic Time-Outs">Generic Time-outs</seealso> + (named time-outs)) + </item> + </list> + <p> + If so, or if possibly needed in future versions, + then you should consider using <c>gen_statem</c> over + <seealso marker="stdlib:gen_server"><c>gen_server</c></seealso>. + </p> + <p> + For simple state machines not needing these features + <seealso marker="stdlib:gen_server"><c>gen_server</c></seealso> + works just fine. + It also has got smaller call overhead, + but we are talking about something like 2 vs 3.3 microseconds + call roundtrip time here, so if the server callback + does just a little bit more than just replying, + or if the call is not extremely frequent, + that difference will be hard to notice. + </p> + </section> + +<!-- =================================================================== --> + + <section> + <marker id="Callback Module" /> + <title>Callback Module</title> + <p> + The callback module contains functions that implement + the state machine. + When an event occurs, + the <c>gen_statem</c> behaviour engine + calls a function in the callback module with the event, + current state and server data. + This function performs the actions for this event, + and returns the new state and server data + and also actions to be performed by the behaviour engine. + </p> + <p> + The behaviour engine holds the state machine state, + server data, timer references, a queue of posponed messages + and other metadata. It receives all process messages, + handles the system messages, and calls the callback module + with machine specific events. </p> </section> <!-- =================================================================== --> <section> - <marker id="callback_modes" /> + <marker id="Callback Modes" /> <title>Callback Modes</title> <p> The <c>gen_statem</c> behavior supports two callback modes: </p> - <list type="bulleted"> + <taglist> + <tag> + <seealso marker="stdlib:gen_statem#type-callback_mode"> + <c>state_functions</c> + </seealso> + </tag> <item> <p> - In mode - <seealso marker="stdlib:gen_statem#type-callback_mode"><c>state_functions</c></seealso>, - the state transition rules are written as some Erlang - functions, which conform to the following convention: - </p> - <pre> -StateName(EventType, EventContent, Data) -> - .. code for actions here ... - {next_state, NewStateName, NewData}.</pre> + Events are handled by one callback function per state. + </p> </item> + <tag> + <seealso marker="stdlib:gen_statem#type-callback_mode"> + <c>handle_event_function</c> + </seealso> + </tag> <item> <p> - In mode - <seealso marker="stdlib:gen_statem#type-callback_mode"><c>handle_event_function</c></seealso>, - only one Erlang function provides all state transition rules: - </p> - <pre> -handle_event(EventType, EventContent, State, Data) -> - .. code for actions here ... - {next_state, NewState, NewData}</pre> + Events are handled by one single callback function. + </p> </item> - </list> + </taglist> + <p> + The callback mode is selected at server start + and may be changed with a code upgrade/downgrade. + </p> + <p> + See the section + <seealso marker="#Event Handler">Event Handler</seealso> + that describes the event handling callback function(s). + </p> + <p> + The callback mode is selected by implementing + a mandatory callback function + <seealso marker="stdlib:gen_statem#Module:callback_mode/0"> + <c>Module:callback_mode()</c> + </seealso> + that returns one of the callback modes. + </p> <p> - Both these modes allow other return tuples; see - <seealso marker="stdlib:gen_statem#Module:StateName/3"><c>Module:StateName/3</c></seealso> - in the <c>gen_statem</c> manual page. - These other return tuples can, for example, stop the machine, - execute state transition actions on the machine engine itself, - and send replies. + The + <seealso marker="stdlib:gen_statem#Module:callback_mode/0"> + <c>Module:callback_mode()</c> + </seealso> + function may also return a list containing the callback mode + and the atom <c>state_enter</c> in which case + <seealso marker="#State Enter Calls">State Enter Calls</seealso> + are activated for the callback mode. </p> <section> + <marker id="Choosing the Callback Mode" /> <title>Choosing the Callback Mode</title> <p> + The short version: choose <c>state_functions</c> - + it is the one most like <c>gen_fsm</c>. + But if you do not want the restriction that the state + must be an atom, or if you do not want to write + one event handler function per state; please read on... + </p> + <p> The two - <seealso marker="#callback_modes">callback modes</seealso> - give different possibilities - and restrictions, but one goal remains: - you want to handle all possible combinations of - events and states. + <seealso marker="#Callback Modes">Callback Modes</seealso> + give different possibilities and restrictions, + with one common goal: + to handle all possible combinations of events and states. </p> <p> This can be done, for example, by focusing on one state at the time @@ -154,7 +257,7 @@ handle_event(EventType, EventContent, State, Data) -> With <c>state_functions</c>, you are restricted to use atom-only states, and the <c>gen_statem</c> engine branches depending on state name for you. - This encourages the callback module to gather + This encourages the callback module to co-locate the implementation of all event actions particular to one state in the same place in the code, hence to focus on one state at the time. @@ -173,13 +276,17 @@ handle_event(EventType, EventContent, State, Data) -> This mode works equally well when you want to focus on one event at the time or on one state at the time, but function - <seealso marker="stdlib:gen_statem#Module:handle_event/4"><c>Module:handle_event/4</c></seealso> + <seealso marker="stdlib:gen_statem#Module:handle_event/4"> + <c>Module:handle_event/4</c> + </seealso> quickly grows too large to handle without branching to helper functions. </p> <p> The mode enables the use of non-atom states, for example, complex states or even hierarchical states. + See section + <seealso marker="#Complex State">Complex State</seealso>. If, for example, a state diagram is largely alike for the client side and the server side of a protocol, you can have a state <c>{StateName,server}</c> or @@ -195,33 +302,471 @@ handle_event(EventType, EventContent, State, Data) -> <!-- =================================================================== --> <section> - <title>Example</title> + <marker id="Event Handler" /> + <title>Event Handler</title> + <p> + Which callback function that handles an event + depends on the callback mode: + </p> + <taglist> + <tag><c>state_functions</c></tag> + <item> + The event is handled by:<br /> + <seealso marker="stdlib:gen_statem#Module:StateName/3"> + <c>Module:StateName(EventType, EventContent, Data)</c> + </seealso> + <p> + This form is the one mostly used in the + <seealso marker="#Example">Example</seealso> + section. + </p> + </item> + <tag><c>handle_event_function</c></tag> + <item> + The event is handled by:<br /> + <seealso marker="stdlib:gen_statem#Module:handle_event/4"> + <c>Module:handle_event(EventType, EventContent, State, Data)</c> + </seealso> + <p> + See section + <seealso marker="#One Event Handler">One Event Handler</seealso> + for an example. + </p> + </item> + </taglist> + <p> + The state is either the name of the function itself or an argument to it. + The other arguments are the <c>EventType</c> described in section + <seealso marker="#Event Types">Event Types</seealso>, + the event dependent <c>EventContent</c>, and the current server <c>Data</c>. + </p> + <p> + State enter calls are also handled by the event handler and have + slightly different arguments. See the section + <seealso marker="#State Enter Calls">State Enter Calls</seealso>. + </p> + <p> + The event handler return values are defined in the description of + <seealso marker="stdlib:gen_statem#Module:StateName/3"> + <c>Module:StateName/3</c> + </seealso> + in the <c>gen_statem</c> manual page, but here is + a more readable list: + </p> + <taglist> + <tag> + <c>{next_state, NextState, NewData, Actions}</c><br /> + <c>{next_state, NextState, NewData}</c> + </tag> + <item> + <p> + Set next state and update the server data. + If the <c>Actions</c> field is used, execute state transition actions. + An empty <c>Actions</c> list is equivalent to not returning the field. + </p> + <p> + See section + <seealso marker="#State Transition Actions"> + State Transition Actions + </seealso> + for a list of possible + state transition actions. + </p> + <p> + If <c>NextState =/= State</c> the state machine changes + to a new state. A + <seealso marker="#State Enter Calls">state enter call</seealso> + is performed if enabled and all + <seealso marker="#Postponing Events">postponed events</seealso> + are retried. + </p> + </item> + <tag> + <c>{keep_state, NewData, Actions}</c><br /> + <c>{keep_state, NewData}</c> + </tag> + <item> + <p> + Same as the <c>next_state</c> values with + <c>NextState =:= State</c>, that is, no state change. + </p> + </item> + <tag> + <c>{keep_state_and_data, Actions}</c><br /> + <c>keep_state_and_data</c> + </tag> + <item> + <p> + Same as the <c>keep_state</c> values with + <c>NextData =:= Data</c>, that is, no change in server data. + </p> + </item> + <tag> + <c>{repeat_state, NewData, Actions}</c><br /> + <c>{repeat_state, NewData}</c><br /> + <c>{repeat_state_and_data, Actions}</c><br /> + <c>repeat_state_and_data</c> + </tag> + <item> + <p> + Same as the <c>keep_state</c> or <c>keep_state_and_data</c> values, + and if + <seealso marker="#State Enter Calls"> + State Enter Calls + </seealso> + are enabled, repeat the state enter call + as if this state was entered again. + </p> + </item> + <tag> + <c>{stop, Reason, NewData}</c><br /> + <c>{stop, Reason}</c> + </tag> + <item> + <p> + Stop the server with reason <c>Reason</c>. + If the <c>NewData</c> field is used, first update the server data. + </p> + </item> + <tag> + <c>{stop_and_reply, Reason, NewData, ReplyActions}</c><br /> + <c>{stop_and_reply, Reason, ReplyActions}</c> + </tag> + <item> + <p> + Same as the <c>stop</c> values, but first execute the given + state transition actions that may only be reply actions. + </p> + </item> + </taglist> + + <section> + <marker id="The First State" /> + <title>The First State</title> + <p> + To decide the first state the + <seealso marker="stdlib:gen_statem#Module:init/1"> + <c>Module:init(Args)</c> + </seealso> + callback function is called before any + <seealso marker="#Event Handler">Event Handler</seealso> + is called. This function behaves like an event handler + function, but gets its only argument <c>Args</c> from + the <c>gen_statem</c> + <seealso marker="stdlib:gen_statem#start/3"> + <c>start/3,4</c> + </seealso> + or + <seealso marker="stdlib:gen_statem#start_link/3"> + <c>start_link/3,4</c> + </seealso> + function, and returns <c>{ok, State, Data}</c> + or <c>{ok, State, Data, Actions}</c>. + If you use the + <seealso marker="#Postponing Events"><c>postpone</c></seealso> + action from this function, that action is ignored, + since there is no event to postpone. + </p> + </section> + + </section> + +<!-- =================================================================== --> + + <section> + <marker id="State Transition Actions" /> + <title>State Transition Actions</title> + <p> + In the first section + <seealso marker="#Event-Driven State Machines"> + Event-Driven State Machines + </seealso> + actions were mentioned as a part of + the general state machine model. These general actions + are implemented with the code that callback module + <c>gen_statem</c> executes in an event-handling + callback function before returning + to the <c>gen_statem</c> engine. + </p> <p> - This example starts off as equivalent to the example in section - <seealso marker="fsm"><c>gen_fsm</c> Behavior</seealso>. - In later sections, additions and tweaks are made - using features in <c>gen_statem</c> that <c>gen_fsm</c> does not have. - The end of this chapter provides the example again - with all the added features. + There are more specific state-transition actions + that a callback function can command the <c>gen_statem</c> + engine to do after the callback function return. + These are commanded by returning a list of + <seealso marker="stdlib:gen_statem#type-action">actions</seealso> + in the + <seealso marker="stdlib:gen_statem#type-state_callback_result"> + return value + </seealso> + from the + <seealso marker="stdlib:gen_statem#Module:StateName/3">callback function</seealso>. + These are the possible state transition actions: </p> + <taglist> + <tag> + <seealso marker="stdlib:gen_statem#type-postpone"> + <c>postpone</c> + </seealso> + <br /> + <c>{postpone, Boolean}</c> + </tag> + <item> + If set postpone the current event, see section + <seealso marker="#Postponing Events">Postponing Events</seealso> + </item> + <tag> + <seealso marker="stdlib:gen_statem#type-hibernate"> + <c>hibernate</c> + </seealso> + <br /> + <c>{hibernate, Boolean}</c> + </tag> + <item> + If set hibernate the <c>gen_statem</c>, treated in section + <seealso marker="#Hibernation">Hibernation</seealso> + </item> + <tag> + <seealso marker="stdlib:gen_statem#type-state_timeout"> + <c>{state_timeout, Time}</c> + </seealso> + <br /> + <c>{state_timeout, Time, Opts}</c> + </tag> + <item> + Start a state time-out, read more in section + <seealso marker="#State Time-Outs">State Time-Outs</seealso> + </item> + <tag> + <seealso marker="stdlib:gen_statem#type-generic_timeout"> + <c>{{timeout, Name}, Time}</c> + </seealso> + <br /> + <c>{{timeout, Name}, Time, Opts}</c> + </tag> + <item> + Start a generic time-out, read more in section + <seealso marker="#Generic Time-Outs">Generic Time-Outs</seealso> + </item> + <tag> + <seealso marker="stdlib:gen_statem#type-event_timeout"> + <c>{timeout, Time}</c> + </seealso> + <br /> + <c>{timeout, Time, Opts}</c><br /> + <c>Time</c> + </tag> + <item> + Start an event time-out, see more in section + <seealso marker="#Event Time-Outs">Event Time-Outs</seealso> + </item> + <tag> + <seealso marker="stdlib:gen_statem#type-reply_action"> + <c>{reply, From, Reply}</c> + </seealso> + </tag> + <item> + Reply to a caller, mentioned at the end of section + <seealso marker="#All State Events">All State Events</seealso> + </item> + <tag> + <seealso marker="stdlib:gen_statem#type-action"> + <c>{next_event, EventType, EventContent}</c> + </seealso> + </tag> + <item> + Generate the next event to handle, see section + <seealso marker="#Inserted Events">Inserted Events</seealso> + </item> + </taglist> + <p> + For details, see the <c>gen_statem(3)</c> + manual page for type + <seealso marker="stdlib:gen_statem#type-action"><c>action()</c></seealso>. + You can, for example, reply to many callers, + generate multiple next events, + and set a time-out to use absolute instead of relative time + (using the <c>Opts</c> field). + </p> + </section> + +<!-- =================================================================== --> + + <section> + <marker id="Event Types" /> + <title>Event Types</title> + <p> + Events are categorized in different + <seealso marker="stdlib:gen_statem#type-event_type">event types</seealso>. + Events of all types are for a given state + handled in the same callback function, and that function gets + <c>EventType</c> and <c>EventContent</c> as arguments. + </p> + <p> + The following is a complete list of event types and where + they come from: + </p> + <taglist> + <tag> + <seealso marker="stdlib:gen_statem#type-external_event_type"> + <c>cast</c> + </seealso> + </tag> + <item> + Generated by + <seealso marker="stdlib:gen_statem#cast/2"><c>gen_statem:cast</c></seealso>. + </item> + <tag> + <seealso marker="stdlib:gen_statem#type-external_event_type"> + <c>{call,From}</c> + </seealso> + </tag> + <item> + Generated by + <seealso marker="stdlib:gen_statem#call/2"><c>gen_statem:call</c></seealso>, + where <c>From</c> is the reply address to use + when replying either through the state transition action + <c>{reply,From,Msg}</c> or by calling + <seealso marker="stdlib:gen_statem#reply/1"><c>gen_statem:reply</c></seealso>. + </item> + <tag> + <seealso marker="stdlib:gen_statem#type-external_event_type"> + <c>info</c> + </seealso> + </tag> + <item> + Generated by any regular process message sent to + the <c>gen_statem</c> process. + </item> + <tag> + <seealso marker="stdlib:gen_statem#type-timeout_event_type"> + <c>state_timeout</c> + </seealso> + </tag> + <item> + Generated by state transition action + <seealso marker="stdlib:gen_statem#type-state_timeout"> + <c>{state_timeout,Time,EventContent}</c> + </seealso> + state timer timing out. + </item> + <tag> + <seealso marker="stdlib:gen_statem#type-timeout_event_type"> + <c>{timeout,Name}</c> + </seealso> + </tag> + <item> + Generated by state transition action + <seealso marker="stdlib:gen_statem#type-generic_timeout"> + <c>{{timeout,Name},Time,EventContent}</c> + </seealso> + generic timer timing out. + </item> + <tag> + <seealso marker="stdlib:gen_statem#type-timeout_event_type"> + <c>timeout</c> + </seealso> + </tag> + <item> + Generated by state transition action + <seealso marker="stdlib:gen_statem#type-event_timeout"> + <c>{timeout,Time,EventContent}</c> + </seealso> + (or its short form <c>Time</c>) + event timer timing out. + </item> + <tag> + <seealso marker="stdlib:gen_statem#type-event_type"> + <c>internal</c> + </seealso> + </tag> + <item> + Generated by state transition + <seealso marker="stdlib:gen_statem#type-action">action</seealso> + <c>{next_event,internal,EventContent}</c>. + All event types above can also be generated using + <c>{next_event,EventType,EventContent}</c>. + </item> + </taglist> + </section> + +<!-- =================================================================== --> + + <section> + <marker id="State Enter Calls" /> + <title>State Enter Calls</title> + <p> + The <c>gen_statem</c> behavior can if this is enabled, + regardless of callback mode, + automatically + <seealso marker="stdlib:gen_statem#type-state_enter"> + call the state callback + </seealso> + with special arguments whenever the state changes + so you can write state enter actions + near the rest of the state transition rules. + It typically looks like this: + </p> + <pre> +StateName(enter, OldState, Data) -> + ... code for state enter actions here ... + {keep_state, NewData}; +StateName(EventType, EventContent, Data) -> + ... code for actions here ... + {next_state, NewStateName, NewData}.</pre> + <p> + Since the state enter call is not an event there are restrictions + on the allowed return value and + <seealso marker="#State Transition Actions">State Transition Actions</seealso>. + You may not change the state, + <seealso marker="#Postponing Events">postpone</seealso> + this non-event, or + <seealso marker="#Inserted Events">insert events</seealso>. + </p> + <p> + The first state that is entered will get a state enter call + with <c>OldState</c> equal to the current state. + </p> + <p> + You may repeat the state enter call using the <c>{repeat_state,...}</c> + return value from the + <seealso marker="#Event Handler">Event Handler</seealso>. + In this case <c>OldState</c> will also be equal to the current state. + </p> + <p> + Depending on how your state machine is specified, + this can be a very useful feature, + but it forces you to handle the state enter calls in all states. + See also the + <seealso marker="#State Enter Actions"> + State Enter Actions + </seealso> + chapter. + </p> + </section> + +<!-- =================================================================== --> + + <section> + <marker id="Example" /> + <title>Example</title> <p> A door with a code lock can be seen as a state machine. Initially, the door is locked. When someone presses a button, an event is generated. - Depending on what buttons have been pressed before, - the sequence so far can be correct, incomplete, or wrong. - If correct, the door is unlocked for 10 seconds (10,000 milliseconds). - If incomplete, we wait for another button to be pressed. If - wrong, we start all over, waiting for a new button sequence. - </p> - <image file="../design_principles/code_lock.png"> + The pressed buttons are collected, up to the number of buttons + in the correct code. + If correct, the door is unlocked for 10 seconds. + If not correct, we wait for a new button to be pressed. + </p> + <!-- The image is edited with dia in a .dia file, + then exported to Scalable Vector Graphics. --> + <image file="../design_principles/code_lock.svg" width="80%"> <icaption>Code Lock State Diagram</icaption> </image> <p> This code lock state machine can be implemented using <c>gen_statem</c> with the following callback module: </p> - <marker id="ex"></marker> <code type="erl"><![CDATA[ -module(code_lock). -behaviour(gen_statem). @@ -229,43 +774,51 @@ handle_event(EventType, EventContent, State, Data) -> -export([start_link/1]). -export([button/1]). --export([init/1,callback_mode/0,terminate/3,code_change/4]). +-export([init/1,callback_mode/0,terminate/3]). -export([locked/3,open/3]). start_link(Code) -> gen_statem:start_link({local,?NAME}, ?MODULE, Code, []). -button(Digit) -> - gen_statem:cast(?NAME, {button,Digit}). +button(Button) -> + gen_statem:cast(?NAME, {button,Button}). init(Code) -> do_lock(), - Data = #{code => Code, remaining => Code}, - {ok,locked,Data}. + Data = #{code => Code, length => length(Code), buttons => []}, + {ok, locked, Data}. callback_mode() -> state_functions. - + ]]></code> + <code type="erl"><![CDATA[ locked( - cast, {button,Digit}, - #{code := Code, remaining := Remaining} = Data) -> - case Remaining of - [Digit] -> + cast, {button,Button}, + #{code := Code, length := Length, buttons := Buttons} = Data) -> + NewButtons = + if + length(Buttons) < Length -> + Buttons; + true -> + tl(Buttons) + end ++ [Button], + if + NewButtons =:= Code -> % Correct do_unlock(), - {next_state,open,Data#{remaining := Code},10000}; - [Digit|Rest] -> % Incomplete - {next_state,locked,Data#{remaining := Rest}}; - _Wrong -> - {next_state,locked,Data#{remaining := Code}} + {next_state, open, Data#{buttons := []}, + [{state_timeout,10000,lock}]}; % Time in milliseconds + true -> % Incomplete | Incorrect + {next_state, locked, Data#{buttons := NewButtons}} end. - -open(timeout, _, Data) -> + ]]></code> + <code type="erl"><![CDATA[ +open(state_timeout, lock, Data) -> do_lock(), - {next_state,locked,Data}; + {next_state, locked, Data}; open(cast, {button,_}, Data) -> - do_lock(), - {next_state,locked,Data}. - + {next_state, open, Data}. + ]]></code> + <code type="erl"><![CDATA[ do_lock() -> io:format("Lock~n", []). do_unlock() -> @@ -274,8 +827,6 @@ do_unlock() -> terminate(_Reason, State, _Data) -> State =/= locked andalso do_lock(), ok. -code_change(_Vsn, State, Data, _Extra) -> - {ok,State,Data}. ]]></code> <p>The code is explained in the next sections.</p> </section> @@ -283,6 +834,7 @@ code_change(_Vsn, State, Data, _Extra) -> <!-- =================================================================== --> <section> + <marker id="Starting gen_statem" /> <title>Starting gen_statem</title> <p> In the example in the previous section, <c>gen_statem</c> is @@ -345,22 +897,22 @@ start_link(Code) -> <p> If name registration succeeds, the new <c>gen_statem</c> process calls callback function <c>code_lock:init(Code)</c>. - This function is expected to return <c>{ok,State,Data}</c>, + This function is expected to return <c>{ok, State, Data}</c>, where <c>State</c> is the initial state of the <c>gen_statem</c>, in this case <c>locked</c>; assuming that the door is locked to begin with. <c>Data</c> is the internal server data of the <c>gen_statem</c>. Here the server data is a <seealso marker="stdlib:maps">map</seealso> - with key <c>code</c> that stores - the correct button sequence, and key <c>remaining</c> - that stores the remaining correct button sequence - (the same as the <c>code</c> to begin with). + with key <c>code</c> that stores the correct button sequence, + key <c>length</c> store its length, + and key <c>buttons</c> that stores the collected buttons + up to the same length. </p> <code type="erl"><![CDATA[ init(Code) -> do_lock(), - Data = #{code => Code, remaining => Code}, - {ok,locked,Data}. + Data = #{code => Code, length => length(Code), buttons => []}, + {ok, locked, Data}. ]]></code> <p>Function <seealso marker="stdlib:gen_statem#start_link/3"><c>gen_statem:start_link</c></seealso> @@ -378,115 +930,139 @@ init(Code) -> a <c>gen_statem</c> that is not part of a supervision tree. </p> - <code type="erl"><![CDATA[ -callback_mode() -> - state_functions. - ]]></code> <p> Function <seealso marker="stdlib:gen_statem#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso> selects the - <seealso marker="#callback_modes"><c>CallbackMode</c></seealso> + <seealso marker="#Callback Modes"><c>CallbackMode</c></seealso> for the callback module, in this case <seealso marker="stdlib:gen_statem#type-callback_mode"><c>state_functions</c></seealso>. - That is, each state has got its own handler function. + That is, each state has got its own handler function: </p> + <code type="erl"><![CDATA[ +callback_mode() -> + state_functions. + ]]></code> </section> <!-- =================================================================== --> <section> + <marker id="Handling Events" /> <title>Handling Events</title> <p>The function notifying the code lock about a button event is implemented using <seealso marker="stdlib:gen_statem#cast/2"><c>gen_statem:cast/2</c></seealso>: </p> <code type="erl"><![CDATA[ -button(Digit) -> - gen_statem:cast(?NAME, {button,Digit}). +button(Button) -> + gen_statem:cast(?NAME, {button,Button}). ]]></code> <p> The first argument is the name of the <c>gen_statem</c> and must agree with the name used to start it. So, we use the same macro <c>?NAME</c> as when starting. - <c>{button,Digit}</c> is the event content. + <c>{button,Button}</c> is the event content. </p> <p> - The event is made into a message and sent to the <c>gen_statem</c>. + The event is sent to the <c>gen_statem</c>. When the event is received, the <c>gen_statem</c> calls <c>StateName(cast, Event, Data)</c>, which is expected to - return a tuple <c>{next_state,NewStateName,NewData}</c>. + return a tuple <c>{next_state, NewStateName, NewData}</c>, + or <c>{next_state, NewStateName, NewData, Actions}</c>. <c>StateName</c> is the name of the current state and <c>NewStateName</c> is the name of the next state to go to. <c>NewData</c> is a new value for the server data of - the <c>gen_statem</c>. + the <c>gen_statem</c>, and <c>Actions</c> is a list of + actions to be performed by the <c>gen_statem</c> engine. </p> + <code type="erl"><![CDATA[ locked( - cast, {button,Digit}, - #{code := Code, remaining := Remaining} = Data) -> - case Remaining of - [Digit] -> % Complete + cast, {button,Button}, + #{code := Code, length := Length, buttons := Buttons} = Data) -> + NewButtons = + if + length(Buttons) < Length -> + Buttons; + true -> + tl(Buttons) + end ++ [Button], + if + NewButtons =:= Code -> % Correct do_unlock(), - {next_state,open,Data#{remaining := Code},10000}; - [Digit|Rest] -> % Incomplete - {next_state,locked,Data#{remaining := Rest}}; - [_|_] -> % Wrong - {next_state,locked,Data#{remaining := Code}} + {next_state, open, Data#{buttons := []}, + [{state_timeout,10000,lock}]}; % Time in milliseconds + true -> % Incomplete | Incorrect + {next_state, locked, Data#{buttons := NewButtons}} end. - -open(timeout, _, Data) -> - do_lock(), - {next_state,locked,Data}; -open(cast, {button,_}, Data) -> - do_lock(), - {next_state,locked,Data}. ]]></code> <p> - If the door is locked and a button is pressed, the pressed - button is compared with the next correct button. + In state <c>locked</c>, when a button is pressed, + it is collected with the last pressed buttons + up to the length of the correct code, + and compared with the correct code. Depending on the result, the door is either unlocked and the <c>gen_statem</c> goes to state <c>open</c>, or the door remains in state <c>locked</c>. </p> <p> - If the pressed button is incorrect, the server data - restarts from the start of the code sequence. + When changing to state <c>open</c>, the collected + buttons are reset, the lock unlocked, and a state timer + for 10 s is started. </p> + + <code type="erl"><![CDATA[ +open(cast, {button,_}, Data) -> + {next_state, open, Data}. + ]]></code> <p> - In state <c>open</c>, any button locks the door, as - any event cancels the event timer, so no - time-out event occurs after a button event. + In state <c>open</c>, a button event is ignored + by staying in the same state. This can also be done + by returning <c>{keep_state, Data}</c> or in this case + since <c>Data</c> unchanged even by returning + <c>keep_state_and_data</c>. </p> </section> <section> - <title>Event Time-Outs</title> + <marker id="State Time-Outs" /> + <title>State Time-Outs</title> <p> When a correct code has been given, the door is unlocked and the following tuple is returned from <c>locked/2</c>: </p> <code type="erl"><![CDATA[ -{next_state,open,Data#{remaining := Code},10000}; - ]]></code> +{next_state, open, Data#{buttons := []}, + [{state_timeout,10000,lock}]}; % Time in milliseconds + ]]></code> <p> 10,000 is a time-out value in milliseconds. After this time (10 seconds), a time-out occurs. - Then, <c>StateName(timeout, 10000, Data)</c> is called. + Then, <c>StateName(state_timeout, lock, Data)</c> is called. The time-out occurs when the door has been in state <c>open</c> for 10 seconds. After that the door is locked again: </p> <code type="erl"><![CDATA[ -open(timeout, _, Data) -> +open(state_timeout, lock, Data) -> do_lock(), - {next_state,locked,Data}; + {next_state, locked, Data}; ]]></code> + <p> + The timer for a state time-out is automatically cancelled + when the state machine changes states. You can restart + a state time-out by setting it to a new time, which cancels + the running timer and starts a new. This implies that + you can cancel a state time-out by restarting it with + time <c>infinity</c>. + </p> </section> <!-- =================================================================== --> <section> + <marker id="All State Events" /> <title>All State Events</title> <p> Sometimes events can arrive in any state of the <c>gen_statem</c>. @@ -495,10 +1071,9 @@ open(timeout, _, Data) -> </p> <p> Consider a <c>code_length/0</c> function that returns - the length of the correct code - (that should not be sensitive to reveal). + the length of the correct code. We dispatch all events that are not state-specific - to the common function <c>handle_event/3</c>: + to the common function <c>handle_common/3</c>: </p> <code type="erl"><![CDATA[ ... @@ -511,32 +1086,77 @@ code_length() -> ... locked(...) -> ... ; locked(EventType, EventContent, Data) -> - handle_event(EventType, EventContent, Data). + handle_common(EventType, EventContent, Data). ... open(...) -> ... ; open(EventType, EventContent, Data) -> - handle_event(EventType, EventContent, Data). + handle_common(EventType, EventContent, Data). -handle_event({call,From}, code_length, #{code := Code} = Data) -> - {keep_state,Data,[{reply,From,length(Code)}]}. +handle_common({call,From}, code_length, #{code := Code} = Data) -> + {keep_state, Data, + [{reply,From,length(Code)}]}. ]]></code> + + <p> + Another way to do it is through a convenience macro + <c>?HANDLE_COMMON/0</c>: + </p> + <code type="erl"><![CDATA[ +... +-export([button/1,code_length/0]). +... + +code_length() -> + gen_statem:call(?NAME, code_length). + +-define(HANDLE_COMMON, + ?FUNCTION_NAME(T, C, D) -> handle_common(T, C, D)). +%% +handle_common({call,From}, code_length, #{code := Code} = Data) -> + {keep_state, Data, + [{reply,From,length(Code)}]}. + +... +locked(...) -> ... ; +?HANDLE_COMMON. + +... +open(...) -> ... ; +?HANDLE_COMMON. +]]></code> + <p> This example uses <seealso marker="stdlib:gen_statem#call/2"><c>gen_statem:call/2</c></seealso>, which waits for a reply from the server. The reply is sent with a <c>{reply,From,Reply}</c> tuple - in an action list in the <c>{keep_state,...}</c> tuple - that retains the current state. + in an action list in the <c>{keep_state, ...}</c> tuple + that retains the current state. This return form is convenient + when you want to stay in the current state but do not know or + care about what it is. + </p> + <p> + If the common event handler needs to know the current state + a function <c>handle_common/4</c> can be used instead: </p> + <code type="erl"><![CDATA[ +-define(HANDLE_COMMON, + ?FUNCTION_NAME(T, C, D) -> handle_common(T, C, ?FUNCTION_NAME, D)). + ]]></code> </section> <!-- =================================================================== --> <section> + <marker id="One Event Handler" /> <title>One Event Handler</title> <p> - If mode <c>handle_event_function</c> is used, + If + <seealso marker="#Callback Modes"> + Callback Mode + </seealso> + <c>handle_event_function</c> is used, all events are handled in <seealso marker="stdlib:gen_statem#Module:handle_event/4"><c>Module:handle_event/4</c></seealso> and we can (but do not have to) use an event-centered approach @@ -551,36 +1171,48 @@ handle_event({call,From}, code_length, #{code := Code} = Data) -> callback_mode() -> handle_event_function. -handle_event(cast, {button,Digit}, State, #{code := Code} = Data) -> +handle_event(cast, {button,Button}, State, #{code := Code} = Data) -> case State of locked -> - case maps:get(remaining, Data) of - [Digit] -> % Complete - do_unlock(), - {next_state,open,Data#{remaining := Code},10000}; - [Digit|Rest] -> % Incomplete - {keep_state,Data#{remaining := Rest}}; - [_|_] -> % Wrong - {keep_state,Data#{remaining := Code}} - end; + #{length := Length, buttons := Buttons} = Data, + NewButtons = + if + length(Buttons) < Length -> + Buttons; + true -> + tl(Buttons) + end ++ [Button], + if + NewButtons =:= Code -> % Correct + do_unlock(), + {next_state, open, Data#{buttons := []}, + [{state_timeout,10000,lock}]}; % Time in milliseconds + true -> % Incomplete | Incorrect + {keep_state, Data#{buttons := NewButtons}} + end; open -> - do_lock(), - {next_state,locked,Data} + keep_state_and_data end; -handle_event(timeout, _, open, Data) -> +handle_event(state_timeout, lock, open, Data) -> do_lock(), - {next_state,locked,Data}. + {next_state, locked, Data}; +handle_event( + {call,From}, code_length, _State, #{code := Code} = Data) -> + {keep_state, Data, + [{reply,From,length(Code)}]}. ... - ]]></code> +]]></code> </section> <!-- =================================================================== --> <section> + <marker id="Stopping" /> <title>Stopping</title> <section> + <marker id="In a Supervision Tree" /> <title>In a Supervision Tree</title> <p> If the <c>gen_statem</c> is part of a supervision tree, @@ -602,7 +1234,7 @@ init(Args) -> process_flag(trap_exit, true), do_lock(), ... - ]]></code> + ]]></code> <p> When ordered to shut down, the <c>gen_statem</c> then calls callback function <c>terminate(shutdown, State, Data)</c>. @@ -616,10 +1248,11 @@ init(Args) -> terminate(_Reason, State, _Data) -> State =/= locked andalso do_lock(), ok. - ]]></code> + ]]></code> </section> <section> + <marker id="Standalone gen_statem" /> <title>Standalone gen_statem</title> <p> If the <c>gen_statem</c> is not part of a supervision tree, @@ -634,7 +1267,7 @@ terminate(_Reason, State, _Data) -> ... stop() -> gen_statem:stop(?NAME). - ]]></code> + ]]></code> <p> This makes the <c>gen_statem</c> call callback function <c>terminate/3</c> just like for a supervised server @@ -646,156 +1279,188 @@ stop() -> <!-- =================================================================== --> <section> - <title>Actions</title> + <marker id="Event Time-Outs" /> + <title>Event Time-Outs</title> <p> - In the first sections actions were mentioned as a part of - the general state machine model. These general actions - are implemented with the code that callback module - <c>gen_statem</c> executes in an event-handling - callback function before returning - to the <c>gen_statem</c> engine. + A time-out feature inherited from <c>gen_statem</c>'s predecessor + <seealso marker="stdlib:gen_fsm"><c>gen_fsm</c></seealso>, + is an event time-out, that is, + if an event arrives the timer is cancelled. + You get either an event or a time-out, but not both. </p> <p> - There are more specific state-transition actions - that a callback function can order the <c>gen_statem</c> - engine to do after the callback function return. - These are ordered by returning a list of - <seealso marker="stdlib:gen_statem#type-action">actions</seealso> - in the - <seealso marker="stdlib:gen_statem#type-state_function_result">return tuple</seealso> - from the - <seealso marker="stdlib:gen_statem#Module:StateName/3">callback function</seealso>. - These state transition actions affect the <c>gen_statem</c> - engine itself and can do the following: + It is ordered by the state transition action + <c>{timeout,Time,EventContent}</c>, or just an integer <c>Time</c>, + even without the enclosing actions list + (the latter is a form inherited from <c>gen_fsm</c>. </p> - <list type="bulleted"> - <item>Postpone the current event</item> - <item>Hibernate the <c>gen_statem</c></item> - <item>Start an event time-out</item> - <item>Reply to a caller</item> - <item>Generate the next event to handle</item> - </list> <p> - In the example earlier was mentioned the event time-out - and replying to a caller. - An example of event postponing is included later in this chapter. - For details, see the - <seealso marker="stdlib:gen_statem#type-action"><c>gen_statem(3)</c></seealso> - manual page. - You can, for example, reply to many callers - and generate multiple next events to handle. + This type of time-out is useful for example to act on inactivity. + Let us restart the code sequence + if no button is pressed for say 30 seconds: </p> - </section> - -<!-- =================================================================== --> + <code type="erl"><![CDATA[ +... - <section> - <title>Event Types</title> +locked(timeout, _, Data) -> + {next_state, locked, Data#{buttons := []}}; +locked( + cast, {button,Button}, + #{code := Code, length := Length, buttons := Buttons} = Data) -> +... + true -> % Incomplete | Incorrect + {next_state, locked, Data#{buttons := NewButtons}, + 30000} % Time in milliseconds +... +]]></code> <p> - The previous sections mentioned a few - <seealso marker="stdlib:gen_statem#type-event_type">event types</seealso>. - Events of all types are handled in the same callback function, - for a given state, and the function gets - <c>EventType</c> and <c>EventContent</c> as arguments. + Whenever we receive a button event we start an event time-out + of 30 seconds, and if we get an event type <c>timeout</c> + we reset the remaining code sequence. </p> <p> - The following is a complete list of event types and where - they come from: + An event time-out is cancelled by any other event so you either + get some other event or the time-out event. It is therefore + not possible nor needed to cancel or restart an event time-out. + Whatever event you act on has already cancelled + the event time-out... + </p> + <p> + Note that an event time-out does not work well with + when you have for example a status call as in + <seealso marker="#All State Events">All State Events</seealso>, + or handle unknown events, since all kinds of events + will cancel the event time-out. </p> - <taglist> - <tag><c>cast</c></tag> - <item> - Generated by - <seealso marker="stdlib:gen_statem#cast/2"><c>gen_statem:cast</c></seealso>. - </item> - <tag><c>{call,From}</c></tag> - <item> - Generated by - <seealso marker="stdlib:gen_statem#call/2"><c>gen_statem:call</c></seealso>, - where <c>From</c> is the reply address to use - when replying either through the state transition action - <c>{reply,From,Msg}</c> or by calling - <seealso marker="stdlib:gen_statem#reply/1"><c>gen_statem:reply</c></seealso>. - </item> - <tag><c>info</c></tag> - <item> - Generated by any regular process message sent to - the <c>gen_statem</c> process. - </item> - <tag><c>timeout</c></tag> - <item> - Generated by state transition action - <c>{timeout,Time,EventContent}</c> (or its short form <c>Time</c>) - timer timing out. - </item> - <tag><c>internal</c></tag> - <item> - Generated by state transition action - <c>{next_event,internal,EventContent}</c>. - All event types above can also be generated using - <c>{next_event,EventType,EventContent}</c>. - </item> - </taglist> </section> <!-- =================================================================== --> <section> - <title>State Time-Outs</title> + <marker id="Generic Time-Outs" /> + <title>Generic Time-Outs</title> <p> - The time-out event generated by state transition action - <c>{timeout,Time,EventContent}</c> is an event time-out, - that is, if an event arrives the timer is cancelled. - You get either an event or a time-out, but not both. + The previous example of state time-outs only work if + the state machine stays in the same state during the + time-out time. And event time-outs only work if no + disturbing unrelated events occur. + </p> + <p> + You may want to start a timer in one state and respond + to the time-out in another, maybe cancel the time-out + without changing states, or perhaps run multiple + time-outs in parallel. All this can be accomplished with + <seealso marker="stdlib:gen_statem#type-generic_timeout">generic time-outs</seealso>. + They may look a little bit like + <seealso marker="stdlib:gen_statem#type-event_timeout">event time-outs</seealso> + but contain a name to allow for any number of them simultaneously + and they are not automatically cancelled. + </p> + <p> + Here is how to accomplish the state time-out + in the previous example by instead using a generic time-out + named for example <c>open</c>: + </p> + <code type="erl"><![CDATA[ +... +locked( + cast, {button,Button}, + #{code := Code, length := Length, buttons := Buttons} = Data) -> +... + if + NewButtons =:= Code -> % Correct + do_unlock(), + {next_state, open, Data#{buttons := []}, + [{{timeout,open},10000,lock}]}; % Time in milliseconds +... + +open({timeout,open}, lock, Data) -> + do_lock(), + {next_state,locked,Data}; +open(cast, {button,_}, Data) -> + {keep_state,Data}; +... +]]></code> + <p> + Specific generic time-outs can just as + <seealso marker="#State Time-Outs">State Time-Outs</seealso> + be restarted or cancelled + by setting it to a new time or <c>infinity</c>. + </p> + <p> + In this particular case we do not need to cancel the timeout + since the timeout event is the only possible reason to + change the state from <c>open</c> to <c>locked</c>. </p> <p> - Often you want a timer not to be cancelled by any event - or you want to start a timer in one state and respond - to the time-out in another. This can be accomplished - with a regular Erlang timer: - <seealso marker="erts:erlang#start_timer/4"><c>erlang:start_timer</c></seealso>. + Instead of bothering with when to cancel a time-out, + a late time-out event can be handled by ignoring it + if it arrives in a state where it is known to be late. </p> + </section> + +<!-- =================================================================== --> + + <section> + <marker id="Erlang Timers" /> + <title>Erlang Timers</title> <p> - For the example so far in this chapter: using the - <c>gen_statem</c> event timer has the consequence that - if a button event is generated while in the <c>open</c> state, - the time-out is cancelled and the button event is delivered. - So, we choose to lock the door if this occurred. + The most versatile way to handle time-outs is to use + Erlang Timers; see + <seealso marker="erts:erlang#start_timer/4"><c>erlang:start_timer/3,4</c></seealso>. + Most time-out tasks can be performed with the + time-out features in <c>gen_statem</c>, + but an example of one that can not is if you should need + the return value from + <seealso marker="erts:erlang#cancel_timer/2"><c>erlang:cancel_timer(Tref)</c></seealso>, that is; the remaining time of the timer. </p> <p> - Suppose that we do not want a button to lock the door, - instead we want to ignore button events in the <c>open</c> state. - Then we start a timer when entering the <c>open</c> state - and wait for it to expire while ignoring button events: + Here is how to accomplish the state time-out + in the previous example by instead using an Erlang Timer: </p> <code type="erl"><![CDATA[ ... locked( - cast, {button,Digit}, - #{code := Code, remaining := Remaining} = Data) -> - case Remaining of - [Digit] -> + cast, {button,Button}, + #{code := Code, length := Length, buttons := Buttons} = Data) -> +... + if + NewButtons =:= Code -> % Correct do_unlock(), - Tref = erlang:start_timer(10000, self(), lock), - {next_state,open,Data#{remaining := Code, timer := Tref}}; + Tref = + erlang:start_timer( + 10000, self(), lock), % Time in milliseconds + {next_state, open, Data#{buttons := [], timer => Tref}}; ... open(info, {timeout,Tref,lock}, #{timer := Tref} = Data) -> do_lock(), - {next_state,locked,Data}; + {next_state,locked,maps:remove(timer, Data)}; open(cast, {button,_}, Data) -> {keep_state,Data}; ... - ]]></code> +]]></code> + <p> + Removing the <c>timer</c> key from the map when we + change to state <c>locked</c> is not strictly + necessary since we can only get into state <c>open</c> + with an updated <c>timer</c> map value. But it can be nice + to not have outdated values in the state <c>Data</c>! + </p> <p> If you need to cancel a timer because of some other event, you can use <seealso marker="erts:erlang#cancel_timer/2"><c>erlang:cancel_timer(Tref)</c></seealso>. - Notice that a time-out message cannot arrive after this, - unless you have postponed it (see the next section) before, + Note that a time-out message cannot arrive after this, + unless you have postponed it before (see the next section), so ensure that you do not accidentally postpone such messages. + Also note that a time-out message may have arrived + just before you cancelling it, so you may have to read out + such a message from the process mailbox depending on + the return value from + <seealso marker="erts:erlang#cancel_timer/2"><c>erlang:cancel_timer(Tref)</c></seealso>. </p> <p> - Another way to cancel a timer is not to cancel it, + Another way to handle a late time-out can be to not cancel it, but to ignore it if it arrives in a state where it is known to be late. </p> @@ -804,6 +1469,7 @@ open(cast, {button,_}, Data) -> <!-- =================================================================== --> <section> + <marker id="Postponing Events" /> <title>Postponing Events</title> <p> If you want to ignore a particular event in the current state @@ -813,7 +1479,9 @@ open(cast, {button,_}, Data) -> </p> <p> Postponing is ordered by the state transition - <seealso marker="stdlib:gen_statem#type-action">action</seealso> + <seealso marker="#State Transition Actions"> + State Transition Action + </seealso> <c>postpone</c>. </p> <p> @@ -826,22 +1494,33 @@ open(cast, {button,_}, Data) -> open(cast, {button,_}, Data) -> {keep_state,Data,[postpone]}; ... - ]]></code> - <p> - The fact that a postponed event is only retried after a state change - translates into a requirement on the event and state space. - If you have a choice between storing a state data item - in the <c>State</c> or in the <c>Data</c>: - if a change in the item value affects which events that - are handled, then this item is to be part of the state. - </p> - <p> - You want to avoid that you maybe much later decide - to postpone an event in one state and by misfortune it is never retried, - as the code only changes the <c>Data</c> but not the <c>State</c>. +]]></code> + <p> + Since a postponed event is only retried after a state change, + you have to think about where to keep a state data item. + You can keep it in the server <c>Data</c> + or in the <c>State</c> itself, + for example by having two more or less identical states + to keep a boolean value, or by using a complex state + (see section + <seealso marker="#Complex State">Complex State</seealso>) + with + <seealso marker="#Callback Modes">Callback Mode</seealso> + <seealso marker="stdlib:gen_statem#type-callback_mode"><c>handle_event_function</c></seealso>. + If a change in the value changes the set of events that is handled, + then the value should be kept in the State. + Otherwise no postponed events will be retried + since only the server Data changes. + </p> + <p> + This is not important if you do not postpone events. + But if you later decide to start postponing some events, + then the design flaw of not having separate states + when they should be, might become a hard to find bug. </p> <section> + <marker id="Fuzzy State Diagrams" /> <title>Fuzzy State Diagrams</title> <p> It is not uncommon that a state diagram does not specify @@ -858,6 +1537,7 @@ open(cast, {button,_}, Data) -> </section> <section> + <marker id="Selective Receive" /> <title>Selective Receive</title> <p> Erlang's selective receive statement is often used to @@ -875,28 +1555,38 @@ start_link(Code) -> fun () -> true = register(?NAME, self()), do_lock(), - locked(Code, Code) + locked(Code, length(Code), []) end). -button(Digit) -> - ?NAME ! {button,Digit}. - -locked(Code, [Digit|Remaining]) -> +button(Button) -> + ?NAME ! {button,Button}. + ]]></code> + <code type="erl"><![CDATA[ +locked(Code, Length, Buttons) -> receive - {button,Digit} when Remaining =:= [] -> - do_unlock(), - open(Code); - {button,Digit} -> - locked(Code, Remaining); - {button,_} -> - locked(Code, Code) + {button,Button} -> + NewButtons = + if + length(Buttons) < Length -> + Buttons; + true -> + tl(Buttons) + end ++ [Button], + if + NewButtons =:= Code -> % Correct + do_unlock(), + open(Code, Length); + true -> % Incomplete | Incorrect + locked(Code, Length, NewButtons) + end end. - -open(Code) -> + ]]></code> + <code type="erl"><![CDATA[ +open(Code, Length) -> receive - after 10000 -> + after 10000 -> % Time in milliseconds do_lock(), - locked(Code, Code) + locked(Code, Length, []) end. do_lock() -> @@ -919,8 +1609,10 @@ do_unlock() -> passing non-system messages to the callback module. </p> <p> - The state transition - <seealso marker="stdlib:gen_statem#type-action">action</seealso> + The + <seealso marker="#State Transition Actions"> + State Transition Action + </seealso> <c>postpone</c> is designed to model selective receives. A selective receive implicitly postpones any not received events, but the <c>postpone</c> @@ -937,12 +1629,80 @@ do_unlock() -> <!-- =================================================================== --> <section> - <title>Self-Generated Events</title> + <marker id="State Enter Actions" /> + <title>State Enter Actions</title> + <p> + Say you have a state machine specification + that uses state enter actions. + Allthough you can code this using inserted events + (described in the next section), especially if just + one or a few states has got state enter actions, + this is a perfect use case for the built in + <seealso marker="#State Enter Calls">State Enter Calls</seealso>. + </p> + <p> + You return a list containing <c>state_enter</c> from your + <seealso marker="stdlib:gen_statem#Module:callback_mode/0"><c>callback_mode/0</c></seealso> + function and the <c>gen_statem</c> engine will call your + state callback once with the arguments + <c>(enter, OldState, ...)</c> whenever the state changes. + Then you just need to handle these event-like calls in all states. + </p> + <code type="erl"><![CDATA[ +... +init(Code) -> + process_flag(trap_exit, true), + Data = #{code => Code, length = length(Code)}, + {ok, locked, Data}. + +callback_mode() -> + [state_functions,state_enter]. + +locked(enter, _OldState, Data) -> + do_lock(), + {keep_state,Data#{buttons => []}}; +locked( + cast, {button,Button}, + #{code := Code, length := Length, buttons := Buttons} = Data) -> +... + if + NewButtons =:= Code -> % Correct + {next_state, open, Data}; +... + +open(enter, _OldState, _Data) -> + do_unlock(), + {keep_state_and_data, + [{state_timeout,10000,lock}]}; % Time in milliseconds +open(state_timeout, lock, Data) -> + {next_state, locked, Data}; +... +]]></code> + <p> + You can repeat the state enter code by returning one of + <c>{repeat_state, ...}</c>, <c>{repeat_state_and_data,_}</c> + or <c>repeat_state_and_data</c> that otherwise behaves + exactly like their <c>keep_state</c> siblings. + See the type + <seealso marker="stdlib:gen_statem#type-state_callback_result"> + <c>state_callback_result()</c> + </seealso> + in the reference manual. + </p> + </section> + +<!-- =================================================================== --> + + <section> + <marker id="Inserted Events" /> + <title>Inserted Events</title> <p> It can sometimes be beneficial to be able to generate events to your own state machine. - This can be done with the state transition - <seealso marker="stdlib:gen_statem#type-action">action</seealso> + This can be done with the + <seealso marker="#State Transition Actions"> + State Transition Action + </seealso> <c>{next_event,EventType,EventContent}</c>. </p> <p> @@ -954,79 +1714,106 @@ do_unlock() -> from your state machine to itself. </p> <p> - One example of using self-generated events can be when you have - a state machine specification that uses state entry actions. - You can code that using a dedicated function - to do the state transition. But if you want that code to be - visible besides the other state logic, you can insert - an <c>internal</c> event that does the entry actions. - This has the same unfortunate consequence as using - state transition functions: everywhere you go to - the state, you must explicitly - insert the <c>internal</c> event - or use a state transition function. + One example for this is to pre-process incoming data, for example + decrypting chunks or collecting characters up to a line break. + </p> + <p> + Purists may argue that this should be modelled with a separate + state machine that sends pre-processed events + to the main state machine, + but to decrease overhead the small pre-processing state machine + can be implemented in the common state event handling + of the main state machine using a few state data variables + that then sends the pre-processed events as internal events + to the main state machine. + Using internal events also can make it easier + to synchronize the state machines. + </p> + <p> + A variant of this is to use a + <seealso marker="#Complex State"> + Complex State + </seealso> + with + <seealso marker="#One Event Handler">One Event Handler</seealso>. + The state is then modeled with for example a tuple + <c>{MainFSMState,SubFSMState}</c>. </p> <p> - The following is an implementation of entry actions - using <c>internal</c> events with content <c>enter</c> - using a helper function <c>enter/3</c> for state entry: + To illustrate this we make up an example where the buttons + instead generate down and up (press and release) events, + and the lock responds to an up event only after + the corresponding down event. </p> <code type="erl"><![CDATA[ ... -init(Code) -> - process_flag(trap_exit, true), - Data = #{code => Code}, - enter(ok, locked, Data). +-export([down/1, up/1]). +... +down(Button) -> + gen_statem:cast(?NAME, {down,Button}). -callback_mode() -> - state_functions. +up(Button) -> + gen_statem:cast(?NAME, {up,Button}). + +... -locked(internal, enter, _Data) -> +locked(enter, _OldState, Data) -> do_lock(), - {keep_state,Data#{remaining => Code}}; + {keep_state,Data#{buttons => []}}; locked( - cast, {button,Digit}, - #{code := Code, remaining := Remaining} = Data) -> - case Remaining of - [Digit] -> - enter(next_state, open, Data); + internal, {button,Button}, + #{code := Code, length := Length, buttons := Buttons} = Data) -> ... - -open(internal, enter, _Data) -> - Tref = erlang:start_timer(10000, self(), lock), - do_unlock(), - {keep_state,Data#{timer => Tref}}; -open(info, {timeout,Tref,lock}, #{timer := Tref} = Data) -> - enter(next_state, locked, Data); +]]></code> + <code type="erl"><![CDATA[ +handle_common(cast, {down,Button}, Data) -> + {keep_state, Data#{button => Button}}; +handle_common(cast, {up,Button}, Data) -> + case Data of + #{button := Button} -> + {keep_state,maps:remove(button, Data), + [{next_event,internal,{button,Button}}]}; + #{} -> + keep_state_and_data + end; ... -enter(Tag, State, Data) -> - {Tag,State,Data,[{next_event,internal,enter}]}. - ]]></code> +open(internal, {button,_}, Data) -> + {keep_state,Data,[postpone]}; +... +]]></code> + <p> + If you start this program with <c>code_lock:start([17])</c> + you can unlock with <c>code_lock:down(17), code_lock:up(17).</c> + </p> </section> <!-- =================================================================== --> <section> + <marker id="Example Revisited" /> <title>Example Revisited</title> <p> - This section includes the example after all mentioned modifications - and some more using the entry actions, + This section includes the example after most of the mentioned + modifications and some more using state enter calls, which deserves a new state diagram: </p> - <image file="../design_principles/code_lock_2.png"> + <!-- The image is edited with dia in a .dia file, + then exported to Scalable Vector Graphics. --> + <image file="../design_principles/code_lock_2.svg" width="80%"> <icaption>Code Lock State Diagram Revisited</icaption> </image> <p> Notice that this state diagram does not specify how to handle a button event in the state <c>open</c>. So, you need to - read somewhere else that unspecified events - must be ignored as in not consumed but handled in some other state. + read in some side notes, that is, here: that unspecified events + shall be postponed (handled in some later state). Also, the state diagram does not show that the <c>code_length/0</c> call must be handled in every state. </p> <section> + <marker id="Callback Mode: state_functions" /> <title>Callback Mode: state_functions</title> <p> Using state functions: @@ -1037,8 +1824,8 @@ enter(Tag, State, Data) -> -define(NAME, code_lock_2). -export([start_link/1,stop/0]). --export([button/1,code_length/0]). --export([init/1,callback_mode/0,terminate/3,code_change/4]). +-export([down/1,up/1,code_length/0]). +-export([init/1,callback_mode/0,terminate/3]). -export([locked/3,open/3]). start_link(Code) -> @@ -1046,51 +1833,74 @@ start_link(Code) -> stop() -> gen_statem:stop(?NAME). -button(Digit) -> - gen_statem:cast(?NAME, {button,Digit}). +down(Button) -> + gen_statem:cast(?NAME, {down,Button}). +up(Button) -> + gen_statem:cast(?NAME, {up,Button}). code_length() -> gen_statem:call(?NAME, code_length). - + ]]></code> + <code type="erl"><![CDATA[ init(Code) -> process_flag(trap_exit, true), - Data = #{code => Code}, - enter(ok, locked, Data). + Data = #{code => Code, length => length(Code), buttons => []}, + {ok, locked, Data}. callback_mode() -> - state_functions. + [state_functions,state_enter]. -locked(internal, enter, #{code := Code} = Data) -> +-define(HANDLE_COMMON, + ?FUNCTION_NAME(T, C, D) -> handle_common(T, C, D)). +%% +handle_common(cast, {down,Button}, Data) -> + {keep_state, Data#{button => Button}}; +handle_common(cast, {up,Button}, Data) -> + case Data of + #{button := Button} -> + {keep_state, maps:remove(button, Data), + [{next_event,internal,{button,Button}}]}; + #{} -> + keep_state_and_data + end; +handle_common({call,From}, code_length, #{code := Code}) -> + {keep_state_and_data, + [{reply,From,length(Code)}]}. + ]]></code> + <code type="erl"><![CDATA[ +locked(enter, _OldState, Data) -> do_lock(), - {keep_state,Data#{remaining => Code}}; + {keep_state, Data#{buttons := []}}; +locked(state_timeout, button, Data) -> + {keep_state, Data#{buttons := []}}; locked( - cast, {button,Digit}, - #{code := Code, remaining := Remaining} = Data) -> - case Remaining of - [Digit] -> % Complete - enter(next_state, open, Data); - [Digit|Rest] -> % Incomplete - {keep_state,Data#{remaining := Rest}}; - [_|_] -> % Wrong - {keep_state,Data#{remaining := Code}} + internal, {button,Button}, + #{code := Code, length := Length, buttons := Buttons} = Data) -> + NewButtons = + if + length(Buttons) < Length -> + Buttons; + true -> + tl(Buttons) + end ++ [Button], + if + NewButtons =:= Code -> % Correct + {next_state, open, Data}; + true -> % Incomplete | Incorrect + {keep_state, Data#{buttons := NewButtons}, + [{state_timeout,30000,button}]} % Time in milliseconds end; -locked(EventType, EventContent, Data) -> - handle_event(EventType, EventContent, Data). - -open(internal, enter, Data) -> - Tref = erlang:start_timer(10000, self(), lock), +?HANDLE_COMMON. +]]></code> + <code type="erl"><![CDATA[ +open(enter, _OldState, _Data) -> do_unlock(), - {keep_state,Data#{timer => Tref}}; -open(info, {timeout,Tref,lock}, #{timer := Tref} = Data) -> - enter(next_state, locked, Data); -open(cast, {button,_}, _) -> - {keep_state_and_data,[postpone]}; -open(EventType, EventContent, Data) -> - handle_event(EventType, EventContent, Data). - -handle_event({call,From}, code_length, #{code := Code}) -> - {keep_state_and_data,[{reply,From,length(Code)}]}. -enter(Tag, State, Data) -> - {Tag,State,Data,[{next_event,internal,enter}]}. + {keep_state_and_data, + [{state_timeout,10000,lock}]}; % Time in milliseconds +open(state_timeout, lock, Data) -> + {next_state, locked, Data}; +open(internal, {button,_}, _) -> + {keep_state_and_data, [postpone]}; +?HANDLE_COMMON. do_lock() -> io:format("Locked~n", []). @@ -1100,64 +1910,85 @@ do_unlock() -> terminate(_Reason, State, _Data) -> State =/= locked andalso do_lock(), ok. -code_change(_Vsn, State, Data, _Extra) -> - {ok,State,Data}. - ]]></code> + ]]></code> </section> <section> + <marker id="Callback Mode: handle_event_function" /> <title>Callback Mode: handle_event_function</title> <p> This section describes what to change in the example to use one <c>handle_event/4</c> function. The previously used approach to first branch depending on event - does not work that well here because of the generated - entry actions, so this example first branches depending on state: + does not work that well here because of the state enter calls, + so this example first branches depending on state: </p> <code type="erl"><![CDATA[ -... -export([handle_event/4]). - -... +]]></code> + <code type="erl"><![CDATA[ callback_mode() -> - handle_event_function. - + [handle_event_function,state_enter]. + ]]></code> + <code type="erl"><![CDATA[ +%% %% State: locked -handle_event(internal, enter, locked, #{code := Code} = Data) -> +handle_event(enter, _OldState, locked, Data) -> do_lock(), - {keep_state,Data#{remaining => Code}}; + {keep_state, Data#{buttons := []}}; +handle_event(state_timeout, button, locked, Data) -> + {keep_state, Data#{buttons := []}}; handle_event( - cast, {button,Digit}, locked, - #{code := Code, remaining := Remaining} = Data) -> - case Remaining of - [Digit] -> % Complete - enter(next_state, open, Data); - [Digit|Rest] -> % Incomplete - {keep_state,Data#{remaining := Rest}}; - [_|_] -> % Wrong - {keep_state,Data#{remaining := Code}} + internal, {button,Button}, locked, + #{code := Code, length := Length, buttons := Buttons} = Data) -> + NewButtons = + if + length(Buttons) < Length -> + Buttons; + true -> + tl(Buttons) + end ++ [Button], + if + NewButtons =:= Code -> % Correct + {next_state, open, Data}; + true -> % Incomplete | Incorrect + {keep_state, Data#{buttons := NewButtons}, + [{state_timeout,30000,button}]} % Time in milliseconds end; + ]]></code> + <code type="erl"><![CDATA[ %% %% State: open -handle_event(internal, enter, open, Data) -> - Tref = erlang:start_timer(10000, self(), lock), +handle_event(enter, _OldState, open, _Data) -> do_unlock(), - {keep_state,Data#{timer => Tref}}; -handle_event(info, {timeout,Tref,lock}, open, #{timer := Tref} = Data) -> - enter(next_state, locked, Data); -handle_event(cast, {button,_}, open, _) -> + {keep_state_and_data, + [{state_timeout,10000,lock}]}; % Time in milliseconds +handle_event(state_timeout, lock, open, Data) -> + {next_state, locked, Data}; +handle_event(internal, {button,_}, open, _) -> {keep_state_and_data,[postpone]}; -%% -%% Any state -handle_event({call,From}, code_length, _State, #{code := Code}) -> - {keep_state_and_data,[{reply,From,length(Code)}]}. - -... - ]]></code> + ]]></code> + <code type="erl"><![CDATA[ +%% Common events +handle_event(cast, {down,Button}, _State, Data) -> + {keep_state, Data#{button => Button}}; +handle_event(cast, {up,Button}, _State, Data) -> + case Data of + #{button := Button} -> + {keep_state, maps:remove(button, Data), + [{next_event,internal,{button,Button}}, + {state_timeout,30000,button}]}; % Time in milliseconds + #{} -> + keep_state_and_data + end; +handle_event({call,From}, code_length, _State, #{length := Length}) -> + {keep_state_and_data, + [{reply,From,Length}]}. + ]]></code> </section> <p> - Notice that postponing buttons from the <c>locked</c> state - to the <c>open</c> state feels like the wrong thing to do + Notice that postponing buttons from the <c>open</c> state + to the <c>locked</c> state feels like a strange thing to do for a code lock, but it at least illustrates event postponing. </p> </section> @@ -1165,6 +1996,7 @@ handle_event({call,From}, code_length, _State, #{code := Code}) -> <!-- =================================================================== --> <section> + <marker id="Filter the State" /> <title>Filter the State</title> <p> The example servers so far in this chapter @@ -1193,7 +2025,7 @@ handle_event({call,From}, code_length, _State, #{code := Code}) -> </p> <code type="erl"><![CDATA[ ... --export([init/1,terminate/3,code_change/4,format_status/2]). +-export([init/1,terminate/3,format_status/2]). ... format_status(Opt, [_PDict,State,Data]) -> @@ -1201,7 +2033,6 @@ format_status(Opt, [_PDict,State,Data]) -> {State, maps:filter( fun (code, _) -> false; - (remaining, _) -> false; (_, _) -> true end, Data)}, @@ -1225,19 +2056,22 @@ format_status(Opt, [_PDict,State,Data]) -> <!-- =================================================================== --> <section> + <marker id="Complex State" /> <title>Complex State</title> <p> The callback mode <seealso marker="stdlib:gen_statem#type-callback_mode"><c>handle_event_function</c></seealso> enables using a non-atom state as described in section - <seealso marker="#callback_modes">Callback Modes</seealso>, + <seealso marker="#Callback Modes">Callback Modes</seealso>, for example, a complex state term like a tuple. </p> <p> - One reason to use this is when you have - a state item that affects the event handling, - in particular in combination with postponing events. - We complicate the previous example + One reason to use this is when you have a state item + that when changed should cancel the + <seealso marker="#State Time-Outs">State Time-Out</seealso>, + or one that affects the event handling + in combination with postponing events. + We will go for the latter and complicate the previous example by introducing a configurable lock button (this is the state item in question), which in the <c>open</c> state immediately locks the door, @@ -1246,33 +2080,33 @@ format_status(Opt, [_PDict,State,Data]) -> <p> Suppose now that we call <c>set_lock_button</c> while the door is open, - and have already postponed a button event - that until now was not the lock button. - The sensible thing can be to say that - the button was pressed too early so it is - not to be recognized as the lock button. - However, then it can be surprising that a button event - that now is the lock button event arrives (as retried postponed) - immediately after the state transits to <c>locked</c>. - </p> - <p> - So we make the <c>button/1</c> function synchronous - by using - <seealso marker="stdlib:gen_statem#call/2"><c>gen_statem:call</c></seealso> - and still postpone its events in the <c>open</c> state. - Then a call to <c>button/1</c> during the <c>open</c> - state does not return until the state transits to <c>locked</c>, - as it is there the event is handled and the reply is sent. - </p> - <p> - If a process now calls <c>set_lock_button/1</c> - to change the lock button while another process - hangs in <c>button/1</c> with the new lock button, - it can be expected that the hanging lock button call - immediately takes effect and locks the lock. - Therefore, we make the current lock button a part of the state, - so that when we change the lock button, the state changes - and all postponed events are retried. + and we have already postponed a button event + that was the new lock button: + </p> + <code type="erl"><![CDATA[ +1> code_lock:start_link([a,b,c], x). +{ok,<0.666.0>} +2> code_lock:button(a). +ok +3> code_lock:button(b). +ok +4> code_lock:button(c). +ok +Open +5> code_lock:button(y). +ok +6> code_lock:set_lock_button(y). +x +% What should happen here? Immediate lock or nothing? +]]></code> + <p> + We could say that the button was pressed too early + so it is not to be recognized as the lock button. + Or we can make the lock button part of the state so + when we then change the lock button in the locked state, + the change becomes a state change + and all postponed events are retried, + therefore the lock is immediately locked! </p> <p> We define the state as <c>{StateName,LockButton}</c>, @@ -1285,8 +2119,8 @@ format_status(Opt, [_PDict,State,Data]) -> -define(NAME, code_lock_3). -export([start_link/2,stop/0]). --export([button/1,code_length/0,set_lock_button/1]). --export([init/1,callback_mode/0,terminate/3,code_change/4,format_status/2]). +-export([button/1,set_lock_button/1]). +-export([init/1,callback_mode/0,terminate/3]). -export([handle_event/4]). start_link(Code, LockButton) -> @@ -1295,80 +2129,68 @@ start_link(Code, LockButton) -> stop() -> gen_statem:stop(?NAME). -button(Digit) -> - gen_statem:call(?NAME, {button,Digit}). -code_length() -> - gen_statem:call(?NAME, code_length). +button(Button) -> + gen_statem:cast(?NAME, {button,Button}). set_lock_button(LockButton) -> gen_statem:call(?NAME, {set_lock_button,LockButton}). - + ]]></code> + <code type="erl"><![CDATA[ init({Code,LockButton}) -> process_flag(trap_exit, true), - Data = #{code => Code, remaining => undefined, timer => undefined}, - enter(ok, {locked,LockButton}, Data, []). + Data = #{code => Code, length => length(Code), buttons => []}, + {ok, {locked,LockButton}, Data}. callback_mode() -> - handle_event_function. + [handle_event_function,state_enter]. +%% State: locked +handle_event(enter, _OldState, {locked,_}, Data) -> + do_lock(), + {keep_state, Data#{buttons := []}}; +handle_event(state_timeout, button, {locked,_}, Data) -> + {keep_state, Data#{buttons := []}}; handle_event( - {call,From}, {set_lock_button,NewLockButton}, - {StateName,OldLockButton}, Data) -> - {next_state,{StateName,NewLockButton},Data, - [{reply,From,OldLockButton}]}; -handle_event( - {call,From}, code_length, - {_StateName,_LockButton}, #{code := Code}) -> - {keep_state_and_data, - [{reply,From,length(Code)}]}; -handle_event( - EventType, EventContent, - {locked,LockButton}, #{code := Code, remaining := Remaining} = Data) -> - case {EventType,EventContent} of - {internal,enter} -> - do_lock(), - {keep_state,Data#{remaining := Code}}; - {{call,From},{button,Digit}} -> - case Remaining of - [Digit] -> % Complete - next_state( - {open,LockButton}, Data, - [{reply,From,ok}]); - [Digit|Rest] -> % Incomplete - {keep_state,Data#{remaining := Rest}, - [{reply,From,ok}]}; - [_|_] -> % Wrong - {keep_state,Data#{remaining := Code}, - [{reply,From,ok}]} - end + cast, {button,Button}, {locked,LockButton}, + #{code := Code, length := Length, buttons := Buttons} = Data) -> + NewButtons = + if + length(Buttons) < Length -> + Buttons; + true -> + tl(Buttons) + end ++ [Button], + if + NewButtons =:= Code -> % Correct + {next_state, {open,LockButton}, Data}; + true -> % Incomplete | Incorrect + {keep_state, Data#{buttons := NewButtons}, + [{state_timeout,30000,button}]} % Time in milliseconds end; + ]]></code> + <code type="erl"><![CDATA[ +%% +%% State: open +handle_event(enter, _OldState, {open,_}, _Data) -> + do_unlock(), + {keep_state_and_data, + [{state_timeout,10000,lock}]}; % Time in milliseconds +handle_event(state_timeout, lock, {open,LockButton}, Data) -> + {next_state, {locked,LockButton}, Data}; +handle_event(cast, {button,LockButton}, {open,LockButton}, Data) -> + {next_state, {locked,LockButton}, Data}; +handle_event(cast, {button,_}, {open,_}, _Data) -> + {keep_state_and_data,[postpone]}; + ]]></code> + <code type="erl"><![CDATA[ +%% +%% Common events handle_event( - EventType, EventContent, - {open,LockButton}, #{timer := Timer} = Data) -> - case {EventType,EventContent} of - {internal,enter} -> - Tref = erlang:start_timer(10000, self(), lock), - do_unlock(), - {keep_state,Data#{timer := Tref}}; - {info,{timeout,Timer,lock}} -> - next_state({locked,LockButton}, Data, []); - {{call,From},{button,Digit}} -> - if - Digit =:= LockButton -> - erlang:cancel_timer(Timer), - next_state( - {locked,LockButton}, Data, - [{reply,From,locked}]); - true -> - {keep_state_and_data, - [postpone]} - end - end. - -next_state(State, Data, Actions) -> - enter(next_state, State, Data, Actions). -enter(Tag, State, Data, Actions) -> - {Tag,State,Data,[{next_event,internal,enter}|Actions]}. - + {call,From}, {set_lock_button,NewLockButton}, + {StateName,OldLockButton}, Data) -> + {next_state, {StateName,NewLockButton}, Data, + [{reply,From,OldLockButton}]}. + ]]></code> + <code type="erl"><![CDATA[ do_lock() -> io:format("Locked~n", []). do_unlock() -> @@ -1377,34 +2199,13 @@ do_unlock() -> terminate(_Reason, State, _Data) -> State =/= locked andalso do_lock(), ok. -code_change(_Vsn, State, Data, _Extra) -> - {ok,State,Data}. -format_status(Opt, [_PDict,State,Data]) -> - StateData = - {State, - maps:filter( - fun (code, _) -> false; - (remaining, _) -> false; - (_, _) -> true - end, - Data)}, - case Opt of - terminate -> - StateData; - normal -> - [{data,[{"State",StateData}]}] - end. ]]></code> - <p> - It can be an ill-fitting model for a physical code lock - that the <c>button/1</c> call can hang until the lock - is locked. But for an API in general it is not that strange. - </p> </section> <!-- =================================================================== --> <section> + <marker id="Hibernation" /> <title>Hibernation</title> <p> If you have many servers in one node @@ -1430,20 +2231,19 @@ format_status(Opt, [_PDict,State,Data]) -> </p> <code type="erl"><![CDATA[ ... -handle_event( - EventType, EventContent, - {open,LockButton}, #{timer := Timer} = Data) -> - case {EventType,EventContent} of - {internal,enter} -> - Tref = erlang:start_timer(10000, self(), lock), - do_unlock(), - {keep_state,Data#{timer := Tref},[hibernate]}; +%% +%% State: open +handle_event(enter, _OldState, {open,_}, _Data) -> + do_unlock(), + {keep_state_and_data, + [{state_timeout,10000,lock}, % Time in milliseconds + hibernate]}; ... - ]]></code> +]]></code> <p> - The - <seealso marker="stdlib:gen_statem#type-hibernate"><c>[hibernate]</c></seealso> - action list on the last line + The atom + <seealso marker="stdlib:gen_statem#type-hibernate"><c>hibernate</c></seealso> + in the action list on the last line when entering the <c>{open,_}</c> state is the only change. If any event arrives in the <c>{open,_},</c> state, we do not bother to rehibernate, so the server stays @@ -1452,16 +2252,34 @@ handle_event( <p> To change that we would need to insert action <c>hibernate</c> in more places. - For example, for the state-independent <c>set_lock_button</c> - and <c>code_length</c> operations that then would have to - be aware of using <c>hibernate</c> while in the + For example, the state-independent <c>set_lock_button</c> + operation would have to use <c>hibernate</c> but only in the <c>{open,_}</c> state, which would clutter the code. </p> <p> - This server probably does not use + Another not uncommon scenario is to use the + <seealso marker="#Event Time-Outs">Event Time-Out</seealso> + to trigger hibernation after a certain time of inactivity. + There is also a server start option + <seealso marker="stdlib:gen_statem#type-hibernate_after_opt"> + <c>{hibernate_after, Timeout}</c> + </seealso> + for + <seealso marker="stdlib:gen_statem#start/3"> + <c>start/3,4</c> + </seealso> + or + <seealso marker="stdlib:gen_statem#start_link/3"> + <c>start_link/3,4</c> + </seealso> + that may be used to automatically hibernate the server. + </p> + <p> + This particular server probably does not use heap memory worth hibernating for. To gain anything from hibernation, your server would - have to produce some garbage during callback execution, + have to produce non-insignificant garbage + during callback execution, for which this example server can serve as a bad example. </p> </section> diff --git a/system/doc/design_principles/sup_princ.xml b/system/doc/design_principles/sup_princ.xml index 0a24e97950..321fa41e8d 100644 --- a/system/doc/design_principles/sup_princ.xml +++ b/system/doc/design_principles/sup_princ.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1997</year><year>2016</year> + <year>1997</year><year>2018</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -163,7 +163,9 @@ SupFlags = #{strategy => Strategy, ...}</code> SupFlags = #{intensity => MaxR, period => MaxT, ...}</code> <p>If more than <c>MaxR</c> number of restarts occur in the last <c>MaxT</c> seconds, the supervisor terminates all the child - processes and then itself.</p> + processes and then itself. + The termination reason for the supervisor itself in that case will be + <c>shutdown</c>.</p> <p>When the supervisor terminates, then the next higher-level supervisor takes some action. It either restarts the terminated supervisor or terminates itself.</p> @@ -173,6 +175,69 @@ SupFlags = #{intensity => MaxR, period => MaxT, ...}</code> <p>The keys <c>intensity</c> and <c>period</c> are optional in the supervisor flags map. If they are not given, they default to <c>1</c> and <c>5</c>, respectively.</p> + <section> + <title>Tuning the intensity and period</title> + <p>The default values are 1 restart per 5 seconds. This was chosen to + be safe for most systems, even with deep supervision hierarchies, + but you will probably want to tune the settings for your particular + use case.</p> + <p>First, the intensity decides how big bursts of restarts you want + to tolerate. For example, you might want to accept a burst of at + most 5 or 10 attempts, even within the same second, if it results + in a successful restart.</p> + <p>Second, you need to consider the sustained failure rate, if + crashes keep happening but not often enough to make the supervisor + give up. If you set intensity to 10 and set the period as low as 1, + the supervisor will allow child processes to keep restarting up to + 10 times per second, forever, filling your logs with crash reports + until someone intervenes manually.</p> + <p>You should therefore set the period to be long enough that you can + accept that the supervisor keeps going at that rate. For example, + if you have picked an intensity value of 5, then setting the period + to 30 seconds will give you at most one restart per 6 seconds for + any longer period of time, which means that your logs won't fill up + too quickly, and you will have a chance to observe the failures and + apply a fix.</p> + <p>These choices depend a lot on your problem domain. If you don't + have real time monitoring and ability to fix problems quickly, for + example in an embedded system, you might want to accept at most + one restart per minute before the supervisor should give up and + escalate to the next level to try to clear the error automatically. + On the other hand, if it is more important that you keep trying + even at a high failure rate, you might want a sustained rate of as + much as 1-2 restarts per second.</p> + <p>Avoiding common mistakes:</p> + <list type="bulleted"> + <item> + <p>Do not forget to consider the burst rate. If you set intensity + to 1 and period to 6, it gives the same sustained error rate as + 5/30 or 10/60, but will not allow even 2 restart attempts in + quick succession. This is probably not what you wanted.</p> + </item> + <item> + <p>Do not set the period to a very high value if you want to + tolerate bursts. If you set intensity to 5 and period to 3600 + (one hour), the supervisor will allow a short burst of 5 + restarts, but then gives up if it sees another single restart + almost an hour later. You probably want to regard those crashes + as separate incidents, so setting the period to 5 or 10 minutes + will be more reasonable.</p> + </item> + <item> + <p>If your application has multiple levels of supervision, then + do not simply set the restart intensities to the same values on + all levels. Keep in mind that the total number of restarts + (before the top level supervisor gives up and terminates the + application) will be the product of the intensity values of all + the supervisors above the failing child process.</p> + <p>For example, if the top level allows 10 restarts, and the next + level also allows 10, a crashing child below that level will be + restarted 100 times, which is probably excessive. Allowing at + most 3 restarts for the top level supervisor might be a better + choice in this case.</p> + </item> + </list> + </section> </section> <section> @@ -211,7 +276,6 @@ child_spec() = #{id => child_id(), % mandatory <list type="bulleted"> <item><c>supervisor:start_link</c></item> <item><c>gen_server:start_link</c></item> - <item><c>gen_fsm:start_link</c></item> <item><c>gen_statem:start_link</c></item> <item><c>gen_event:start_link</c></item> <item>A function compliant with these functions. For details, @@ -248,12 +312,17 @@ child_spec() = #{id => child_id(), % mandatory signal back. If no exit signal is received within the specified time, the child process is unconditionally terminated using <c>exit(Child, kill)</c>.</item> - <item>If the child process is another supervisor, it is to be + <item>If the child process is another supervisor, it must be set to <c>infinity</c> to give the subtree enough time to shut down. It is also allowed to set it to <c>infinity</c>, - if the child process is a worker. See the warning below:</item> + if the child process is a worker. See the warning below:</item> </list> <warning> + <p>Setting the shutdown time to anything other + than <c>infinity</c> for a child of type <c>supervisor</c> + can cause a race condition where the child in question + unlinks its own children, but fails to terminate them + before it is killed.</p> <p>Be careful when setting the shutdown time to <c>infinity</c> when the child process is a worker. Because, in this situation, the termination of the supervision tree depends on the @@ -276,7 +345,7 @@ child_spec() = #{id => child_id(), % mandatory <p><c>modules</c> are to be a list with one element <c>[Module]</c>, where <c>Module</c> is the name of the callback module, if the child process is a supervisor, - gen_server, gen_fsm or gen_statem. + gen_server, gen_statem. If the child process is a gen_event, the value shall be <c>dynamic</c>.</p> <p>This information is used by the release handler during diff --git a/system/doc/design_principles/warning.gif b/system/doc/design_principles/warning.gif Binary files differdeleted file mode 100644 index 96af52360e..0000000000 --- a/system/doc/design_principles/warning.gif +++ /dev/null diff --git a/system/doc/design_principles/xmlfiles.mk b/system/doc/design_principles/xmlfiles.mk index e476255d62..fbcaf9c7d9 100644 --- a/system/doc/design_principles/xmlfiles.mk +++ b/system/doc/design_principles/xmlfiles.mk @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2009-2016. All Rights Reserved. +# Copyright Ericsson AB 2009-2017. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,7 +24,6 @@ DESIGN_PRINCIPLES_CHAPTER_FILES = \ des_princ.xml \ distributed_applications.xml \ events.xml \ - fsm.xml \ statem.xml \ gen_server_concepts.xml \ included_applications.xml \ diff --git a/system/doc/efficiency_guide/Makefile b/system/doc/efficiency_guide/Makefile index 36e4cd00df..72bcd2ee73 100644 --- a/system/doc/efficiency_guide/Makefile +++ b/system/doc/efficiency_guide/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2016. All Rights Reserved. +# Copyright Ericsson AB 2001-2018. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ include $(ERL_TOP)/erts/vsn.mk #VSN=$(SYSTEM_VSN) APPLICATION=otp-system-documentation +XMLDIR := $(XMLDIR)/efficiency_guide # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- @@ -98,6 +99,7 @@ debug opt: clean clean_docs: rm -rf $(HTMLDIR) + rm -rf $(XMLDIR) rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) rm -f errs core *~ diff --git a/system/doc/efficiency_guide/advanced.xml b/system/doc/efficiency_guide/advanced.xml index eee2648f34..fe77ce8ea4 100644 --- a/system/doc/efficiency_guide/advanced.xml +++ b/system/doc/efficiency_guide/advanced.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2001</year><year>2016</year> + <year>2001</year><year>2018</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -87,15 +87,15 @@ </row> <row> <cell>Small Map</cell> - <cell>4 words + 2 words per entry (key and value) + the size of each key and value pair.</cell> + <cell>5 words + the size of all keys and values.</cell> </row> <row> - <cell>Large Map</cell> + <cell>Large Map (> 32 keys)</cell> <cell> - At least, 2 words + 2 x <c>N</c> words + 2 x log16(<c>N</c>) words + - the size of each key and value pair, where <c>N</c> is the number of pairs in the Map. - A large Map is represented as a tree internally where each node in the tree is a - "sparse tuple" of arity 16. + <c>N</c> x <c>F</c> words + the size of all keys and values.<br></br> + <c>N</c> is the number of keys in the Map.<br></br> + <c>F</c> is a sparsity factor that can vary between 1.6 and 1.8 + due to the probabilistic nature of the internal HAMT data structure. </cell> </row> <row> @@ -151,7 +151,7 @@ <row> <cell>Processes</cell> <cell>The maximum number of simultaneously alive Erlang processes - is by default 32,768. This limit can be configured at startup. + is by default 262,144. This limit can be configured at startup. For more information, see the <seealso marker="erts:erl#max_processes"><c>+P</c></seealso> command-line flag in the @@ -188,16 +188,9 @@ limit can be raised or lowered using the <c>+t</c> option.</cell> </row> <row> - <cell>Ets tables</cell> - <cell>Default is 1400. It can be changed with the environment - variable <c>ERL_MAX_ETS_TABLES</c>.</cell> - </row> - <row> <cell>Elements in a tuple</cell> - <cell>The maximum number of elements in a tuple is 67,108,863 - (26-bit unsigned integer). Clearly, other factors such as the - available memory can make it difficult to create a tuple of - that size.</cell> + <cell>The maximum number of elements in a tuple is 16,777,215 + (24-bit unsigned integer).</cell> </row> <row> <cell>Size of binary</cell> @@ -255,30 +248,36 @@ <cell><marker id="unique_references"/>Unique References on a Runtime System Instance</cell> <cell>Each scheduler thread has its own set of references, and all other threads have a shared set of references. Each set of references - consist of <c>2⁶⁴ - 1</c> unique references. That is the total + consist of <c>2⁶⁴ - 1</c> unique references. That is, the total amount of unique references that can be produced on a runtime - system instance is <c>(NoSchedulers + 1) * (2⁶⁴ - 1)</c>. If a - scheduler thread create a new reference each nano second, + system instance is <c>(NoSchedulers + 1) × (2⁶⁴ - 1)</c>. + <br/><br/> + If a scheduler thread create a new reference each nano second, references will at earliest be reused after more than 584 years. That is, for the foreseeable future they are unique enough.</cell> </row> <row> <cell><marker id="unique_integers"/>Unique Integers on a Runtime System Instance</cell> - <cell>There are two types of unique integers both created using the - <seealso marker="erts:erlang#unique_integer/1">erlang:unique_integer()</seealso> - BIF. Unique integers created: - <taglist> - <tag>with the <c>monotonic</c> modifier</tag> - <item>consist of a set of <c>2⁶⁴ - 1</c> unique integers.</item> - <tag>without the <c>monotonic</c> modifier</tag> - <item>consist of a set of <c>2⁶⁴ - 1</c> unique integers per scheduler - thread and a set of <c>2⁶⁴ - 1</c> unique integers shared by - other threads. That is the total amount of unique integers without - the <c>monotonic</c> modifier is <c>(NoSchedulers + 1) * (2⁶⁴ - 1)</c></item> - </taglist> - If a unique integer is created each nano second, unique integers - will at earliest be reused after more than 584 years. That is, for - the foreseeable future they are unique enough.</cell> + <cell> + There are two types of unique integers both created using the + <seealso marker="erts:erlang#unique_integer/1">erlang:unique_integer()</seealso> + BIF: + <br/><br/> + <em>1.</em> Unique integers created <em>with</em> the + <c>monotonic</c> modifier consist of a set of <c>2⁶⁴ - 1</c> + unique integers. + <br/><br/> + <em>2.</em> Unique integers created <em>without</em> the + <c>monotonic</c> modifier consist of a set of <c>2⁶⁴ - 1</c> + unique integers per scheduler thread and a set of <c>2⁶⁴ - 1</c> + unique integers shared by other threads. That is, the total + amount of unique integers without the <c>monotonic</c> modifier + is <c>(NoSchedulers + 1) × (2⁶⁴ - 1)</c>. + <br/><br/> + If a unique integer is created each nano second, unique integers + will at earliest be reused after more than 584 years. That is, for + the foreseeable future they are unique enough. + </cell> </row> <tcaption>System Limits</tcaption> </table> diff --git a/system/doc/efficiency_guide/bench.erl b/system/doc/efficiency_guide/bench.erl index 1f60e858f6..a1be24b051 100644 --- a/system/doc/efficiency_guide/bench.erl +++ b/system/doc/efficiency_guide/bench.erl @@ -355,7 +355,7 @@ create_html_report(ResultList) -> {ok, OutputFile} = file:open("index.html", [write]), - %% Create the begining of the result html-file. + %% Create the beginning of the result html-file. Head = Title = "Benchmark Results", io:put_chars(OutputFile, "<html>\n"), io:put_chars(OutputFile, "<head>\n"), diff --git a/system/doc/efficiency_guide/binaryhandling.xml b/system/doc/efficiency_guide/binaryhandling.xml index 0295d18644..b500329ef9 100644 --- a/system/doc/efficiency_guide/binaryhandling.xml +++ b/system/doc/efficiency_guide/binaryhandling.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>2007</year> - <year>2016</year> + <year>2018</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -32,12 +32,9 @@ <file>binaryhandling.xml</file> </header> - <p>In R12B, the most natural way to construct and match binaries is - significantly faster than in earlier releases.</p> + <p>Binaries can be efficiently built in the following way:</p> - <p>To construct a binary, you can simply write as follows:</p> - - <p><em>DO</em> (in R12B) / <em>REALLY DO NOT</em> (in earlier releases)</p> + <p><em>DO</em></p> <code type="erl"><![CDATA[ my_list_to_binary(List) -> my_list_to_binary(List, <<>>). @@ -47,21 +44,13 @@ my_list_to_binary([H|T], Acc) -> my_list_to_binary([], Acc) -> Acc.]]></code> - <p>In releases before R12B, <c>Acc</c> is copied in every iteration. - In R12B, <c>Acc</c> is copied only in the first iteration and extra - space is allocated at the end of the copied binary. In the next iteration, - <c>H</c> is written into the extra space. When the extra space runs out, - the binary is reallocated with more extra space. The extra space allocated - (or reallocated) is twice the size of the - existing binary data, or 256, whichever is larger.</p> - - <p>The most natural way to match binaries is now the fastest:</p> + <p>Binaries can be efficiently matched like this:</p> - <p><em>DO</em> (in R12B)</p> + <p><em>DO</em></p> <code type="erl"><![CDATA[ my_binary_to_list(<<H,T/binary>>) -> [H|my_binary_to_list(T)]; -my_binary_to_list(<<>>) -> [].]]></code> +my_binary_to_list(<<>>) -> [].]]></code> <section> <title>How Binaries are Implemented</title> @@ -138,10 +127,7 @@ my_binary_to_list(<<>>) -> [].]]></code> pointer to the binary data. For each field that is matched out of a binary, the position in the match context is incremented.</p> - <p>In R11B, a match context was only used during a binary matching - operation.</p> - - <p>In R12B, the compiler tries to avoid generating code that + <p>The compiler tries to avoid generating code that creates a sub binary, only to shortly afterwards create a new match context and discard the sub binary. Instead of creating a sub binary, the match context is kept.</p> @@ -155,7 +141,7 @@ my_binary_to_list(<<>>) -> [].]]></code> <section> <title>Constructing Binaries</title> - <p>In R12B, appending to a binary or bitstring + <p>Appending to a binary or bitstring is specially optimized by the <em>runtime system</em>:</p> <code type="erl"><![CDATA[ @@ -292,7 +278,7 @@ Bin = <<Bin1,...>> %% Bin1 will be COPIED <p>Let us revisit the example in the beginning of the previous section:</p> - <p><em>DO</em> (in R12B)</p> + <p><em>DO</em></p> <code type="erl"><![CDATA[ my_binary_to_list(<<H,T/binary>>) -> [H|my_binary_to_list(T)]; @@ -304,15 +290,14 @@ my_binary_to_list(<<>>) -> [].]]></code> byte of the binary. 1 byte is matched out and the match context is updated to point to the second byte in the binary.</p> - <p>In R11B, at this point a - <seealso marker="#sub_binary">sub binary</seealso> - would be created. In R12B, - the compiler sees that there is no point in creating a sub binary, - because there will soon be a call to a function (in this case, + <p>At this point it would make sense to create a + <seealso marker="#sub_binary">sub binary</seealso>, + but in this particular example the compiler sees that + there will soon be a call to a function (in this case, to <c>my_binary_to_list/1</c> itself) that immediately will create a new match context and discard the sub binary.</p> - <p>Therefore, in R12B, <c>my_binary_to_list/1</c> calls itself + <p>Therefore <c>my_binary_to_list/1</c> calls itself with the match context instead of with a sub binary. The instruction that initializes the matching operation basically does nothing when it sees that it was passed a match context instead of a binary.</p> @@ -321,34 +306,10 @@ my_binary_to_list(<<>>) -> [].]]></code> the match context will simply be discarded (removed in the next garbage collection, as there is no longer any reference to it).</p> - <p>To summarize, <c>my_binary_to_list/1</c> in R12B only needs to create - <em>one</em> match context and no sub binaries. In R11B, if the binary - contains <em>N</em> bytes, <em>N+1</em> match contexts and <em>N</em> - sub binaries are created.</p> + <p>To summarize, <c>my_binary_to_list/1</c> only needs to create + <em>one</em> match context and no sub binaries.</p> - <p>In R11B, the fastest way to match binaries is as follows:</p> - - <p><em>DO NOT</em> (in R12B)</p> - <code type="erl"><![CDATA[ -my_complicated_binary_to_list(Bin) -> - my_complicated_binary_to_list(Bin, 0). - -my_complicated_binary_to_list(Bin, Skip) -> - case Bin of - <<_:Skip/binary,Byte,_/binary>> -> - [Byte|my_complicated_binary_to_list(Bin, Skip+1)]; - <<_:Skip/binary>> -> - [] - end.]]></code> - - <p>This function cleverly avoids building sub binaries, but it cannot - avoid building a match context in each recursion step. - Therefore, in both R11B and R12B, - <c>my_complicated_binary_to_list/1</c> builds <em>N+1</em> match - contexts. (In a future Erlang/OTP release, the compiler might be able - to generate code that reuses the match context.)</p> - - <p>Returning to <c>my_binary_to_list/1</c>, notice that the match context + <p>Notice that the match context in <c>my_binary_to_list/1</c> was discarded when the entire binary had been traversed. What happens if the iteration stops before it has reached the end of the binary? Will the optimization still work?</p> @@ -396,25 +357,8 @@ all_but_zeroes_to_list(<<Byte,T/binary>>, Acc, Remaining) -> <c>Buffer</c> from a match context to a sub binary (or do nothing if <c>Buffer</c> is a binary already).</p> - <p>Before you begin to think that the compiler can optimize any binary - patterns, the following function cannot be optimized by the compiler - (currently, at least):</p> - - <code type="erl"><![CDATA[ -non_opt_eq([H|T1], <<H,T2/binary>>) -> - non_opt_eq(T1, T2); -non_opt_eq([_|_], <<_,_/binary>>) -> - false; -non_opt_eq([], <<>>) -> - true.]]></code> - - <p>It was mentioned earlier that the compiler can only delay creation of - sub binaries if it knows that the binary will not be shared. In this case, - the compiler cannot know.</p> - - <p>Soon it is shown how to rewrite <c>non_opt_eq/2</c> so that the delayed - sub binary optimization can be applied, and more importantly, it is shown - how you can find out whether your code can be optimized.</p> + <p>But in more complicated code, how can one know whether the + optimization is applied or not?</p> <section> <marker id="bin_opt_info"></marker> @@ -461,67 +405,6 @@ after_zero(<<>>) -> binary cannot be delayed, because it will be returned. The warning for the second clause says that a sub binary will not be created (yet).</p> - - <p>Let us revisit the earlier example of the code that could not - be optimized and find out why:</p> - - <code type="erl"><![CDATA[ -non_opt_eq([H|T1], <<H,T2/binary>>) -> - %% INFO: matching anything else but a plain variable to - %% the left of binary pattern will prevent delayed - %% sub binary optimization; - %% SUGGEST changing argument order - %% NOT OPTIMIZED: called function non_opt_eq/2 does not - %% begin with a suitable binary matching instruction - non_opt_eq(T1, T2); -non_opt_eq([_|_], <<_,_/binary>>) -> - false; -non_opt_eq([], <<>>) -> - true.]]></code> - - <p>The compiler emitted two warnings. The <c>INFO</c> warning refers - to the function <c>non_opt_eq/2</c> as a callee, indicating that any - function that call <c>non_opt_eq/2</c> cannot make delayed sub binary - optimization. There is also a suggestion to change argument order. - The second warning (that happens to refer to the same line) refers to - the construction of the sub binary itself.</p> - - <p>Soon another example will show the difference between the - <c>INFO</c> and <c>NOT OPTIMIZED</c> warnings somewhat clearer, but - let us first follow the suggestion to change argument order:</p> - - <code type="erl"><![CDATA[ -opt_eq(<<H,T1/binary>>, [H|T2]) -> - %% OPTIMIZED: creation of sub binary delayed - opt_eq(T1, T2); -opt_eq(<<_,_/binary>>, [_|_]) -> - false; -opt_eq(<<>>, []) -> - true.]]></code> - - <p>The compiler gives a warning for the following code fragment:</p> - - <code type="erl"><![CDATA[ -match_body([0|_], <<H,_/binary>>) -> - %% INFO: matching anything else but a plain variable to - %% the left of binary pattern will prevent delayed - %% sub binary optimization; - %% SUGGEST changing argument order - done; -...]]></code> - - <p>The warning means that <em>if</em> there is a call to <c>match_body/2</c> - (from another clause in <c>match_body/2</c> or another function), the - delayed sub binary optimization will not be possible. More warnings will - occur for any place where a sub binary is matched out at the end of and - passed as the second argument to <c>match_body/2</c>, for example:</p> - - <code type="erl"><![CDATA[ -match_head(List, <<_:10,Data/binary>>) -> - %% NOT OPTIMIZED: called function match_body/2 does not - %% begin with a suitable binary matching instruction - match_body(List, Data).]]></code> - </section> <section> @@ -544,5 +427,15 @@ count3(<<>>, Count) -> Count.]]></code> not matched out.</p> </section> </section> + + <section> + <title>Historical Note</title> + + <p>Binary handling was significantly improved in R12B. Because + code that was efficient in R11B might not be efficient in R12B, + and vice versa, earlier revisions of this Efficiency Guide contained + some information about binary handling in R11B.</p> + </section> + </chapter> diff --git a/system/doc/efficiency_guide/commoncaveats.xml b/system/doc/efficiency_guide/commoncaveats.xml index ecfeff0349..367da09ba3 100644 --- a/system/doc/efficiency_guide/commoncaveats.xml +++ b/system/doc/efficiency_guide/commoncaveats.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2001</year><year>2016</year> + <year>2001</year><year>2017</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -148,10 +148,10 @@ multiple_setelement(T0) -> <p><c>size/1</c> returns the size for both tuples and binaries.</p> - <p>Using the new BIFs <c>tuple_size/1</c> and <c>byte_size/1</c>, introduced - in R12B, gives the compiler and the runtime system more opportunities for - optimization. Another advantage is that the new BIFs can help Dialyzer to - find more bugs in your program.</p> + <p>Using the BIFs <c>tuple_size/1</c> and <c>byte_size/1</c> + gives the compiler and the runtime system more opportunities for + optimization. Another advantage is that the BIFs give Dialyzer more + type information.</p> </section> <section> @@ -169,53 +169,5 @@ multiple_setelement(T0) -> {Bin1,Bin2} = split_binary(Bin, Num)</code> </section> - <section> - <title>Operator "--"</title> - <p>The "<c>--</c>" operator has a complexity - proportional to the product of the length of its operands. - This means that the operator is very slow if both of its operands - are long lists:</p> - - <p><em>DO NOT</em></p> - <code type="none"><![CDATA[ - HugeList1 -- HugeList2]]></code> - - <p>Instead use the <seealso marker="stdlib:ordsets">ordsets</seealso> - module in STDLIB:</p> - - <p><em>DO</em></p> - <code type="none"> - HugeSet1 = ordsets:from_list(HugeList1), - HugeSet2 = ordsets:from_list(HugeList2), - ordsets:subtract(HugeSet1, HugeSet2)</code> - - <p>Obviously, that code does not work if the original order - of the list is important. If the order of the list must be - preserved, do as follows:</p> - - <p><em>DO</em></p> - <code type="none"><![CDATA[ - Set = gb_sets:from_list(HugeList2), - [E || E <- HugeList1, not gb_sets:is_element(E, Set)]]]></code> - - <note><p>This code behaves differently from "<c>--</c>" - if the lists contain duplicate elements (one occurrence - of an element in HugeList2 removes <em>all</em> - occurrences in HugeList1.)</p> - <p>Also, this code compares lists elements using the - "<c>==</c>" operator, while "<c>--</c>" uses the "<c>=:=</c>" operator. - If that difference is important, <c>sets</c> can be used instead of - <c>gb_sets</c>, but <c>sets:from_list/1</c> is much - slower than <c>gb_sets:from_list/1</c> for long lists.</p></note> - - <p>Using the "<c>--</c>" operator to delete an element - from a list is not a performance problem:</p> - - <p><em>OK</em></p> - <code type="none"> - HugeList1 -- [Element]</code> - - </section> - </chapter> diff --git a/system/doc/efficiency_guide/efficiency_guide.erl b/system/doc/efficiency_guide/efficiency_guide.erl index e982bdae65..c57785aaa3 100644 --- a/system/doc/efficiency_guide/efficiency_guide.erl +++ b/system/doc/efficiency_guide/efficiency_guide.erl @@ -1,5 +1,5 @@ -module(efficiency_guide). --compile(export_all). +-compile([export_all,nowarn_export_all). %% DO NOT naive_reverse([H|T]) -> @@ -71,28 +71,6 @@ all_but_zeroes_to_list(<<0,T/binary>>, Acc, Remaining) -> all_but_zeroes_to_list(<<Byte,T/binary>>, Acc, Remaining) -> all_but_zeroes_to_list(T, [Byte|Acc], Remaining-1). -non_opt_eq([H|T1], <<H,T2/binary>>) -> - non_opt_eq(T1, T2); -non_opt_eq([_|_], <<_,_/binary>>) -> - false; -non_opt_eq([], <<>>) -> - true. - -opt_eq(<<H,T1/binary>>, [H|T2]) -> - opt_eq(T1, T2); -opt_eq(<<_,_/binary>>, [_|_]) -> - false; -opt_eq(<<>>, []) -> - true. - -match_head(List, <<_:10,Data/binary>>) -> - match_body(List, Data). - -match_body([0|_], <<H,_/binary>>) -> - done; -match_body([H|T1], <<H,T2/binary>>) -> - {T1,T2}. - count1(<<_,T/binary>>, Count) -> count1(T, Count+1); count1(<<>>, Count) -> Count. diff --git a/system/doc/efficiency_guide/functions.xml b/system/doc/efficiency_guide/functions.xml index 4a8248e65c..0a8ee7eb34 100644 --- a/system/doc/efficiency_guide/functions.xml +++ b/system/doc/efficiency_guide/functions.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2001</year><year>2016</year> + <year>2001</year><year>2017</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -65,7 +65,7 @@ atom_map1(six) -> 6.</code> thus, quite efficient even if there are many values) to select which one of the first three clauses to execute (if any).</item> - <item>>If none of the first three clauses match, the fourth clause + <item>If none of the first three clauses match, the fourth clause match as a variable always matches.</item> <item>If the guard test <c>is_integer(Int)</c> succeeds, the fourth @@ -183,15 +183,6 @@ explicit_map_pairs(Map, Xs0, Ys0) -> A fun contains an (indirect) pointer to the function that implements the fun.</p> - <warning><p><em>Tuples are not fun(s)</em>. - A "tuple fun", <c>{Module,Function}</c>, is not a fun. - The cost for calling a "tuple fun" is similar to that - of <c>apply/3</c> or worse. - Using "tuple funs" is <em>strongly discouraged</em>, - as they might not be supported in a future Erlang/OTP release, - and because there exists a superior alternative from R10B, - namely the <c>fun Module:Function/Arity</c> syntax.</p></warning> - <p><c>apply/3</c> must look up the code for the function to execute in a hash table. It is therefore always slower than a direct call or a fun call.</p> diff --git a/system/doc/efficiency_guide/introduction.xml b/system/doc/efficiency_guide/introduction.xml index ca4a41c798..dca2dec95e 100644 --- a/system/doc/efficiency_guide/introduction.xml +++ b/system/doc/efficiency_guide/introduction.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2001</year><year>2016</year> + <year>2001</year><year>2017</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -46,14 +46,6 @@ to find out where the performance bottlenecks are and optimize only the bottlenecks. Let other code stay as clean as possible.</p> - <p>Fortunately, compiler and runtime optimizations introduced in - Erlang/OTP R12B makes it easier to write code that is both clean and - efficient. For example, the ugly workarounds needed in R11B and earlier - releases to get the most speed out of binary pattern matching are - no longer necessary. In fact, the ugly code is slower - than the clean code (because the clean code has become faster, not - because the uglier code has become slower).</p> - <p>This Efficiency Guide cannot really teach you how to write efficient code. It can give you a few pointers about what to avoid and what to use, and some understanding of how certain language features are implemented. diff --git a/system/doc/efficiency_guide/listhandling.xml b/system/doc/efficiency_guide/listhandling.xml index 2ebc877820..4f2497359d 100644 --- a/system/doc/efficiency_guide/listhandling.xml +++ b/system/doc/efficiency_guide/listhandling.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2001</year><year>2016</year> + <year>2001</year><year>2017</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -90,7 +90,7 @@ tail_recursive_fib(N, Current, Next, Fibs) -> <p>Lists comprehensions still have a reputation for being slow. They used to be implemented using funs, which used to be slow.</p> - <p>In recent Erlang/OTP releases (including R12B), a list comprehension:</p> + <p>A list comprehension:</p> <code type="erl"><![CDATA[ [Expr(E) || E <- List]]]></code> @@ -102,7 +102,7 @@ tail_recursive_fib(N, Current, Next, Fibs) -> [Expr(E)|'lc^0'(Tail, Expr)]; 'lc^0'([], _Expr) -> [].</code> - <p>In R12B, if the result of the list comprehension will <em>obviously</em> + <p>If the result of the list comprehension will <em>obviously</em> not be used, a list will not be constructed. For example, in this code:</p> <code type="erl"><![CDATA[ @@ -131,6 +131,14 @@ some_function(...), 'lc^0'(Tail, Expr); 'lc^0'([], _Expr) -> [].</code> + <p>The compiler also understands that assigning to '_' means that + the value will not used. Therefore, the code in the following example + will also be optimized:</p> + + <code type="erl"><![CDATA[ +_ = [io:put_chars(E) || E <- List], +ok.]]></code> + </section> <section> @@ -209,11 +217,11 @@ some_function(...), <section> <title>Recursive List Functions</title> - <p>In Section 7.2, the following myth was exposed: + <p>In section about myths, the following myth was exposed: <seealso marker="myths#tail_recursive">Tail-Recursive Functions are Much Faster Than Recursive Functions</seealso>.</p> - <p>To summarize, in R12B there is usually not much difference between + <p>There is usually not much difference between a body-recursive list function and tail-recursive function that reverses the list at the end. Therefore, concentrate on writing beautiful code and forget about the performance of your list functions. In the diff --git a/system/doc/efficiency_guide/myths.xml b/system/doc/efficiency_guide/myths.xml index 5d3ad78b23..778cd06c09 100644 --- a/system/doc/efficiency_guide/myths.xml +++ b/system/doc/efficiency_guide/myths.xml @@ -24,7 +24,7 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>The Eight Myths of Erlang Performance</title> + <title>The Seven Myths of Erlang Performance</title> <prepared>Bjorn Gustavsson</prepared> <docno></docno> <date>2007-11-10</date> @@ -35,80 +35,33 @@ <marker id="myths"></marker> <p>Some truths seem to live on well beyond their best-before date, perhaps because "information" spreads faster from person-to-person - than a single release note that says, for example, that funs - have become faster.</p> + than a single release note that says, for example, that body-recursive + calls have become faster.</p> <p>This section tries to kill the old truths (or semi-truths) that have become myths.</p> <section> - <title>Myth: Funs are Slow</title> - <p>Funs used to be very slow, slower than <c>apply/3</c>. - Originally, funs were implemented using nothing more than - compiler trickery, ordinary tuples, <c>apply/3</c>, and a great - deal of ingenuity.</p> - - <p>But that is history. Funs was given its own data type - in R6B and was further optimized in R7B. - Now the cost for a fun call falls roughly between the cost for a call - to a local function and <c>apply/3</c>.</p> - </section> - - <section> - <title>Myth: List Comprehensions are Slow</title> - - <p>List comprehensions used to be implemented using funs, and in the - old days funs were indeed slow.</p> - - <p>Nowadays, the compiler rewrites list comprehensions into an ordinary - recursive function. Using a tail-recursive function with - a reverse at the end would be still faster. Or would it? - That leads us to the next myth.</p> - </section> - - <section> <title>Myth: Tail-Recursive Functions are Much Faster Than Recursive Functions</title> <p><marker id="tail_recursive"></marker>According to the myth, - recursive functions leave references - to dead terms on the stack and the garbage collector has to copy - all those dead terms, while tail-recursive functions immediately - discard those terms.</p> - - <p>That used to be true before R7B. In R7B, the compiler started - to generate code that overwrites references to terms that will never - be used with an empty list, so that the garbage collector would not - keep dead values any longer than necessary.</p> - - <p>Even after that optimization, a tail-recursive function is - still most of the times faster than a body-recursive function. Why?</p> - - <p>It has to do with how many words of stack that are used in each - recursive call. In most cases, a recursive function uses more words - on the stack for each recursion than the number of words a tail-recursive - would allocate on the heap. As more memory is used, the garbage - collector is invoked more frequently, and it has more work traversing - the stack.</p> - - <p>In R12B and later releases, there is an optimization that - in many cases reduces the number of words used on the stack in - body-recursive calls. A body-recursive list function and a - tail-recursive function that calls <seealso - marker="stdlib:lists#reverse/1">lists:reverse/1</seealso> at - the end will use the same amount of memory. - <c>lists:map/2</c>, <c>lists:filter/2</c>, list comprehensions, - and many other recursive functions now use the same amount of space - as their tail-recursive equivalents.</p> - - <p>So, which is faster? - It depends. On Solaris/Sparc, the body-recursive function seems to - be slightly faster, even for lists with a lot of elements. On the x86 - architecture, tail-recursion was up to about 30% faster.</p> - - <p>So, the choice is now mostly a matter of taste. If you really do need - the utmost speed, you must <em>measure</em>. You can no longer be - sure that the tail-recursive list function always is the fastest.</p> + using a tail-recursive function that builds a list in reverse + followed by a call to <c>lists:reverse/1</c> is faster than + a body-recursive function that builds the list in correct order; + the reason being that body-recursive functions use more memory than + tail-recursive functions.</p> + + <p>That was true to some extent before R12B. It was even more true + before R7B. Today, not so much. A body-recursive function + generally uses the same amount of memory as a tail-recursive + function. It is generally not possible to predict whether the + tail-recursive or the body-recursive version will be + faster. Therefore, use the version that makes your code cleaner + (hint: it is usually the body-recursive version).</p> + + <p>For a more thorough discussion about tail and body recursion, + see <url href="http://ferd.ca/erlang-s-tail-recursion-is-not-a-silver-bullet.html">Erlang's Tail Recursion is Not a Silver Bullet</url>.</p> <note><p>A tail-recursive function that does not need to reverse the list at the end is faster than a body-recursive function, @@ -199,6 +152,29 @@ vanilla_reverse([], Acc) -> <p>That was once true, but from R6B the BEAM compiler can see that a variable is not used.</p> + + <p>Similarly, trivial transformations on the source-code level + such as converting a <c>case</c> statement to clauses at the + top-level of the function seldom makes any difference to the + generated code.</p> + </section> + + <section> + <title>Myth: A NIF Always Speeds Up Your Program</title> + + <p>Rewriting Erlang code to a NIF to make it faster should be + seen as a last resort. It is only guaranteed to be dangerous, + but not guaranteed to speed up the program.</p> + + <p>Doing too much work in each NIF call will + <seealso marker="erts:erl_nif#WARNING">degrade responsiveness + of the VM</seealso>. Doing too little work may mean that + the gain of the faster processing in the NIF is eaten up by + the overhead of calling the NIF and checking the arguments.</p> + + <p>Be sure to read about + <seealso marker="erts:erl_nif#lengthy_work">Long-running NIFs</seealso> + before writing a NIF.</p> </section> </chapter> diff --git a/system/doc/efficiency_guide/part.xml b/system/doc/efficiency_guide/part.xml index 6e10a0c031..5673ddd320 100644 --- a/system/doc/efficiency_guide/part.xml +++ b/system/doc/efficiency_guide/part.xml @@ -39,5 +39,6 @@ <xi:include href="drivers.xml"/> <xi:include href="advanced.xml"/> <xi:include href="profiling.xml"/> + <xi:include href="retired_myths.xml"/> </part> diff --git a/system/doc/efficiency_guide/processes.xml b/system/doc/efficiency_guide/processes.xml index f2d9712f51..3b64c863ff 100644 --- a/system/doc/efficiency_guide/processes.xml +++ b/system/doc/efficiency_guide/processes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2001</year><year>2016</year> + <year>2001</year><year>2017</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -146,14 +146,14 @@ loop() -> <section> <title>Constant Pool</title> - <p>Constant Erlang terms (also called <em>literals</em>) are now + <p>Constant Erlang terms (also called <em>literals</em>) are kept in constant pools; each loaded module has its own pool. - The following function does no longer build the tuple every time + The following function does not build the tuple every time it is called (only to have it discarded the next time the garbage collector was run), but the tuple is located in the module's constant pool:</p> - <p><em>DO</em> (in R12B and later)</p> + <p><em>DO</em></p> <code type="erl"> days_in_month(M) -> element(M, {31,28,31,30,31,30,31,31,30,31,30,31}).</code> @@ -222,7 +222,7 @@ kilo_byte(N, Acc) -> <pre> 4> <input>T = ets:new(tab, []).</input> -17 +#Ref<0.1662103692.2407923716.214181> 5> <input>ets:insert(T, {key,efficiency_guide:kilo_byte()}).</input> true 6> <input>erts_debug:size(element(2, hd(ets:lookup(T, key)))).</input> @@ -235,9 +235,7 @@ true return the same value. Sharing has been lost.</p> <p>In a future Erlang/OTP release, it might be implemented a - way to (optionally) preserve sharing. There are no plans to make - preserving of sharing the default behaviour, as that would - penalize the vast majority of Erlang applications.</p> + way to (optionally) preserve sharing.</p> </section> </section> @@ -261,10 +259,6 @@ true The estone benchmark, for example, is entirely sequential. So is the most common implementation of the "ring benchmark"; usually one process is active, while the others wait in a <c>receive</c> statement.</p> - - <p>The <seealso marker="percept:percept">percept</seealso> application - can be used to profile your application to see how much potential (or lack - thereof) it has for concurrency.</p> </section> </chapter> diff --git a/system/doc/efficiency_guide/profiling.xml b/system/doc/efficiency_guide/profiling.xml index bf50a03fa6..5ec1f1be6e 100644 --- a/system/doc/efficiency_guide/profiling.xml +++ b/system/doc/efficiency_guide/profiling.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2001</year><year>2016</year> + <year>2001</year><year>2018</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -41,30 +41,87 @@ <p>Erlang/OTP contains several tools to help finding bottlenecks:</p> <list type="bulleted"> - <item><c>fprof</c> provides the most detailed information about - where the program time is spent, but it significantly slows down the - program it profiles.</item> - - <item><p><c>eprof</c> provides time information of each function - used in the program. No call graph is produced, but <c>eprof</c> has - considerable less impact on the program it profiles.</p> - <p>If the program is too large to be profiled by <c>fprof</c> or - <c>eprof</c>, the <c>cover</c> and <c>cprof</c> tools can be used - to locate code parts that are to be more thoroughly profiled using - <c>fprof</c> or <c>eprof</c>.</p></item> - - <item><c>cover</c> provides execution counts per line per - process, with less overhead than <c>fprof</c>. Execution counts - can, with some caution, be used to locate potential performance - bottlenecks.</item> - - <item><c>cprof</c> is the most lightweight tool, but it only - provides execution counts on a function basis (for all processes, - not per process).</item> + <item><p><seealso marker="tools:fprof"><c>fprof</c></seealso> provides + the most detailed information about where the program time is spent, + but it significantly slows down the program it profiles.</p></item> + + <item><p><seealso marker="tools:eprof"><c>eprof</c></seealso> provides + time information of each function used in the program. No call graph is + produced, but <c>eprof</c> has considerable less impact on the program it + profiles.</p> + <p>If the program is too large to be profiled by <c>fprof</c> or + <c>eprof</c>, <c>cprof</c> can be used to locate code parts that + are to be more thoroughly profiled using <c>fprof</c> or <c>eprof</c>.</p></item> + + <item><p><seealso marker="tools:cprof"><c>cprof</c></seealso> is the + most lightweight tool, but it only provides execution counts on a + function basis (for all processes, not per process).</p></item> + + <item><p><seealso marker="runtime_tools:dbg"><c>dbg</c></seealso> is the + generic erlang tracing frontend. By using the <c>timestamp</c> or + <c>cpu_timestamp</c> options it can be used to time how long function + calls in a live system take.</p></item> + + <item><p><seealso marker="tools:lcnt"><c>lcnt</c></seealso> is used + to find contention points in the Erlang Run-Time System's internal + locking mechanisms. It is useful when looking for bottlenecks in + interaction between process, port, ets tables and other entities + that can be run in parallel.</p></item> + </list> <p>The tools are further described in <seealso marker="#profiling_tools">Tools</seealso>.</p> + + <p>There are also several open source tools outside of Erlang/OTP + that can be used to help profiling. Some of them are:</p> + + <list type="bulleted"> + <item><url href="https://github.com/isacssouza/erlgrind">erlgrind</url> + can be used to visualize fprof data in kcachegrind.</item> + <item><url href="https://github.com/proger/eflame">eflame</url> + is an alternative to fprof that displays the profiling output as a flamegraph.</item> + <item><url href="https://ferd.github.io/recon/index.html">recon</url> + is a collection of Erlang profiling and debugging tools. + This tool comes with an accompanying E-book called + <url href="https://www.erlang-in-anger.com/">Erlang in Anger</url>.</item> + </list> + </section> + + <section> + <title>Memory profiling</title> + <pre>eheap_alloc: Cannot allocate 1234567890 bytes of memory (of type "heap").</pre> + <p>The above slogan is one of the more common reasons for Erlang to terminate. + For unknown reasons the Erlang Run-Time System failed to allocate memory to + use. When this happens a crash dump is generated that contains information + about the state of the system as it ran out of memory. Use the + <seealso marker="observer:cdv"><c>crashdump_viewer</c></seealso> to get a + view of the memory is being used. Look for processes with large heaps or + many messages, large ets tables, etc.</p> + <p>When looking at memory usage in a running system the most basic function + to get information from is <seealso marker="erts:erlang#memory/0"><c> + erlang:memory()</c></seealso>. It returns the current memory usage + of the system. <seealso marker="tools:instrument"><c>instrument(3)</c></seealso> + can be used to get a more detailed breakdown of where memory is used.</p> + <p>Processes, ports and ets tables can then be inspecting using their + respective info functions, i.e. + <seealso marker="erts:erlang#process_info_memory"><c>erlang:process_info/2 + </c></seealso>, + <seealso marker="erts:erlang#port_info_memory"><c>erlang:port_info/2 + </c></seealso> and + <seealso marker="stdlib:ets#info/1"><c>ets:info/1</c></seealso>. + </p> + <p>Sometimes the system can enter a state where the reported memory + from <c>erlang:memory(total)</c> is very different from the + memory reported by the OS. This can be because of internal + fragmentation within the Erlang Run-Time System. Data about + how memory is allocated can be retrieved using + <seealso marker="erts:erlang#system_info_allocator"> + <c>erlang:system_info(allocator)</c></seealso>. + The data you get from that function is very raw and not very plesant to read. + <url href="http://ferd.github.io/recon/recon_alloc.html">recon_alloc</url> + can be used to extract useful information from system_info + statistics counters.</p> </section> <section> @@ -80,6 +137,22 @@ tools on the whole system. Instead you want to concentrate on central processes and modules, which contribute for a big part of the execution.</p> + + <p>There are also some tools that can be used to get a view of the + whole system with more or less overhead.</p> + <list type="bulleted"> + <item><seealso marker="observer:observer"><c>observer</c></seealso> + is a GUI tool that can connect to remote nodes and display a + variety of information about the running system.</item> + <item><seealso marker="observer:etop"><c>etop</c></seealso> + is a command line tool that can connect to remote nodes and + display information similar to what the UNIX tool top shows.</item> + <item><seealso marker="runtime_tools:msacc"><c>msacc</c></seealso> + allows the user to get a view of what the Erlang Run-Time system + is spending its time doing. Has a very low overhead, which makes it + useful to run in heavily loaded systems to get some idea of where + to start doing more granular profiling.</item> + </list> </section> <section> @@ -128,7 +201,7 @@ performance impact. Using <c>fprof</c> is just a matter of calling a few library functions, see the <seealso marker="tools:fprof">fprof</seealso> manual page in - Tools .<c>fprof</c> was introduced in R8.</p> + Tools.</p> </section> <section> @@ -142,20 +215,6 @@ </section> <section> - <title>cover</title> - <p>The primary use of <c>cover</c> is coverage analysis to verify - test cases, making sure that all relevant code is covered. - <c>cover</c> counts how many times each executable line of code - is executed when a program is run, on a per module basis.</p> - <p>Clearly, this information can be used to determine what - code is run very frequently and can therefore be subject for - optimization. Using <c>cover</c> is just a matter of calling a - few library functions, see the - <seealso marker="tools:cover">cover</seealso> manual page in - Tools.</p> - </section> - - <section> <title>cprof</title> <p><c>cprof</c> is something in between <c>fprof</c> and <c>cover</c> regarding features. It counts how many times each @@ -202,16 +261,6 @@ <cell>No</cell> </row> <row> - <cell><c>cover</c></cell> - <cell>Per module to screen/file</cell> - <cell>Small</cell> - <cell>Moderate slowdown</cell> - <cell>Yes, per line</cell> - <cell>No</cell> - <cell>No</cell> - <cell>No</cell> - </row> - <row> <cell><c>cprof</c></cell> <cell>Per module to caller</cell> <cell>Small</cell> @@ -224,6 +273,37 @@ <tcaption>Tool Summary</tcaption> </table> </section> + + <section> + <title>dbg</title> + <p><c>dbg</c> is a generic Erlang trace tool. By using the + <c>timestamp</c> or <c>cpu_timestamp</c> options it can be used + as a precision instrument to profile how long time a function + call takes for a specific process. This can be very useful when + trying to understand where time is spent in a heavily loaded + system as it is possible to limit the scope of what is profiled + to be very small. + For more information, see the + <seealso marker="runtime_tools:dbg">dbg</seealso> manual page in + Runtime Tools.</p> + </section> + + <section> + <title>lcnt</title> + <p><c>lcnt</c> is used to profile interactions inbetween + entities that run in parallel. For example if you have + a process that all other processes in the system needs + to interact with (maybe it has some global configuration), + then <c>lcnt</c> can be used to figure out if the interaction + with that process is a problem.</p> + <p>In the Erlang Run-time System entities are only run in parallel + when there are multiple schedulers. Therefore <c>lcnt</c> will + show more contention points (and thus be more useful) on systems + using many schedulers on many cores.</p> + <p>For more information, see the + <seealso marker="tools:lcnt">lcnt</seealso> manual page in Tools.</p> + </section> + </section> <section> @@ -282,4 +362,3 @@ </list> </section> </chapter> - diff --git a/system/doc/efficiency_guide/retired_myths.xml b/system/doc/efficiency_guide/retired_myths.xml new file mode 100644 index 0000000000..144c942c2b --- /dev/null +++ b/system/doc/efficiency_guide/retired_myths.xml @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>2016</year> + <year>2017</year> + <holder>Ericsson AB, All Rights Reserved</holder> + </copyright> + <legalnotice> + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + The Initial Developer of the Original Code is Ericsson AB. + </legalnotice> + <title>Retired Myths</title> + <prepared>Bjorn Gustavsson</prepared> + <docno></docno> + <date>2016-06-07</date> + <rev></rev> + <file>retired_myths.xml</file> + </header> + + <p>We belive that the truth finally has caught with the following, + retired myths.</p> + + <section> + <marker id="retired_myths"/> + <title>Myth: Funs are Slow</title> + <p>Funs used to be very slow, slower than <c>apply/3</c>. + Originally, funs were implemented using nothing more than + compiler trickery, ordinary tuples, <c>apply/3</c>, and a great + deal of ingenuity.</p> + + <p>But that is history. Funs was given its own data type + in R6B and was further optimized in R7B. + Now the cost for a fun call falls roughly between the cost for a call + to a local function and <c>apply/3</c>.</p> + </section> + + <section> + <title>Myth: List Comprehensions are Slow</title> + + <p>List comprehensions used to be implemented using funs, and in the + old days funs were indeed slow.</p> + + <p>Nowadays, the compiler rewrites list comprehensions into an ordinary + recursive function. Using a tail-recursive function with + a reverse at the end would be still faster. Or would it? + That leads us to the myth that tail-recursive functions are faster + than body-recursive functions.</p> + </section> + + <section> + <title>Myth: List subtraction ("--" operator) is slow</title> + + <p>List subtraction used to have a run-time complexity proportional to the + product of the length of its operands, so it was extremely slow when both + lists were long.</p> + + <p>As of OTP 22 the run-time complexity is "n log n" and the operation will + complete quickly even when both lists are very long. In fact, it is + faster and uses less memory than the commonly used workaround to convert + both lists to ordered sets before subtracting them with + <c>ordsets:subtract/2</c>.</p> + </section> +</chapter> diff --git a/system/doc/efficiency_guide/xmlfiles.mk b/system/doc/efficiency_guide/xmlfiles.mk index 88df9417f5..e275823dd1 100644 --- a/system/doc/efficiency_guide/xmlfiles.mk +++ b/system/doc/efficiency_guide/xmlfiles.mk @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2009-2016. All Rights Reserved. +# Copyright Ericsson AB 2009-2018. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -29,5 +29,5 @@ EFF_GUIDE_CHAPTER_FILES = \ processes.xml \ profiling.xml \ tablesDatabases.xml \ - drivers.xml - + drivers.xml \ + retired_myths.xml diff --git a/system/doc/embedded/Makefile b/system/doc/embedded/Makefile index 40a1b1fb23..396aef276b 100644 --- a/system/doc/embedded/Makefile +++ b/system/doc/embedded/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2016. All Rights Reserved. +# Copyright Ericsson AB 1997-2018. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ include $(ERL_TOP)/erts/vsn.mk #VSN=$(SYSTEM_VSN) APPLICATION=otp-system-documentation +XMLDIR := $(XMLDIR)/embedded # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- @@ -86,6 +87,7 @@ debug opt: clean clean_docs: rm -rf $(HTMLDIR) + rm -rf $(XMLDIR) rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) rm -f errs core *~ diff --git a/system/doc/embedded/note.gif b/system/doc/embedded/note.gif Binary files differdeleted file mode 100644 index 6fffe30419..0000000000 --- a/system/doc/embedded/note.gif +++ /dev/null diff --git a/system/doc/embedded/warning.gif b/system/doc/embedded/warning.gif Binary files differdeleted file mode 100644 index 96af52360e..0000000000 --- a/system/doc/embedded/warning.gif +++ /dev/null diff --git a/system/doc/getting_started/Makefile b/system/doc/getting_started/Makefile index 1fe3d39e4e..cdf1e121c2 100644 --- a/system/doc/getting_started/Makefile +++ b/system/doc/getting_started/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2016. All Rights Reserved. +# Copyright Ericsson AB 1996-2018. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ include $(ERL_TOP)/erts/vsn.mk #VSN=$(SYSTEM_VSN) APPLICATION=otp-system-documentation +XMLDIR := $(XMLDIR)/getting_started # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- @@ -85,6 +86,7 @@ debug opt: clean clean_docs: rm -rf $(HTMLDIR) + rm -rf $(XMLDIR) rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) rm -f errs core *~ diff --git a/system/doc/getting_started/conc_prog.xml b/system/doc/getting_started/conc_prog.xml index f3136898ad..4374a59e04 100644 --- a/system/doc/getting_started/conc_prog.xml +++ b/system/doc/getting_started/conc_prog.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2016</year> + <year>2003</year><year>2018</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -355,8 +355,8 @@ pong ! {ping, self()},</code> <p>Let us rewrite the ping pong program with "ping" and "pong" on different computers. First a few things are needed to set up to get this to work. The distributed Erlang - implementation provides a basic security mechanism to prevent - unauthorized access to an Erlang system on another computer. + implementation provides a very basic authentication mechanism to prevent + unintentional access to an Erlang system on another computer. Erlang systems which talk to each other must have the same <em>magic cookie</em>. The easiest way to achieve this is by having a file called <c>.erlang.cookie</c> in your home @@ -627,7 +627,7 @@ ping finished</pre> %%% Change the function below to return the name of the node where the %%% messenger server runs server_node() -> - messenger@bill. + messenger@super. %%% This is the server process for the "messenger" %%% the user list has the format [{ClientPid1, Name1},{ClientPid22, Name2},...] diff --git a/system/doc/getting_started/seq_prog.xml b/system/doc/getting_started/seq_prog.xml index 6b7e1cd24f..2b0750ff80 100644 --- a/system/doc/getting_started/seq_prog.xml +++ b/system/doc/getting_started/seq_prog.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2016</year> + <year>2003</year><year>2017</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -271,11 +271,12 @@ convert(N, centimeter) -> {'EXIT',{function_clause,[{tut2,convert, [3,miles], [{file,"tut2.erl"},{line,4}]}, - {erl_eval,do_apply,5,[{file,"erl_eval.erl"},{line,482}]}, - {shell,exprs,7,[{file,"shell.erl"},{line,666}]}, - {shell,eval_exprs,7,[{file,"shell.erl"},{line,621}]}, - {shell,eval_loop,3,[{file,"shell.erl"},{line,606}]}]}}</pre> - + {erl_eval,do_apply,6, + [{file,"erl_eval.erl"},{line,677}]}, + {shell,exprs,7,[{file,"shell.erl"},{line,687}]}, + {shell,eval_exprs,7,[{file,"shell.erl"},{line,642}]}, + {shell,eval_loop,3, + [{file,"shell.erl"},{line,627}]}]}}</pre> </section> <section> diff --git a/system/doc/installation_guide/Makefile b/system/doc/installation_guide/Makefile index 673c203422..4a1335cf31 100644 --- a/system/doc/installation_guide/Makefile +++ b/system/doc/installation_guide/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2016. All Rights Reserved. +# Copyright Ericsson AB 1996-2018. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -28,6 +28,8 @@ include $(ERL_TOP)/erts/vsn.mk #VSN=$(SYSTEM_VSN) APPLICATION=otp-system-documentation +XMLDIR := $(XMLDIR)/installation_guide + # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- @@ -43,6 +45,8 @@ include xmlfiles.mk XML_CHAPTER_FILES=$(INST_GUIDE_CHAPTER_FILES) +# ---------------------------------------------------- + TOPDOCDIR=.. BOOK_FILES = book.xml @@ -55,12 +59,7 @@ XML_FILES = \ $(BOOK_FILES) $(XML_CHAPTER_FILES) \ $(XML_PART_FILES) -# ---------------------------------------------------- -GENERATED_XML_FILES = \ - INSTALL.xml \ - INSTALL-CROSS.xml \ - INSTALL-WIN32.xml \ - OTP-PATCH-APPLY.xml +XML_GEN_FILES = $(INST_GUIDE_CHAPTER_GEN_FILES:%=$(XMLDIR)/%) # ---------------------------------------------------- @@ -88,7 +87,7 @@ DVIPS_FLAGS += # Targets # ---------------------------------------------------- -%.xml: $(ERL_TOP)/HOWTO/%.md $(ERL_TOP)/make/emd2exml +$(XMLDIR)/%.xml: $(ERL_TOP)/HOWTO/%.md $(ERL_TOP)/make/emd2exml $(ERL_TOP)/make/emd2exml $< $@ $(REDIRECT_HTML_DIR)/%.html: Makefile @@ -102,18 +101,19 @@ $(REDIRECT_HTML_DIR)/%.html: Makefile echo "This <a href=\"../"$(notdir $@)"\">link</a> will" >> $@ echo "take you there immediately.</p></body></html>" >> $@ -docs: $(GENERATED_XML_FILES) html +docs: $(XML_GEN_FILES) html local_docs: PDFDIR=../../pdf -local_docs: $(GENERATED_XML_FILES) +local_docs: $(XML_GEN_FILES) -html: $(REDIRECT_HTML_FILES) $(GENERATED_XML_FILES) $(GIF_FILES) $(HTML_UG_FILE) +html: $(REDIRECT_HTML_FILES) $(XML_GEN_FILES) $(GIF_FILES) $(HTML_UG_FILE) debug opt: clean clean_docs: rm -f $(GENERATED_XML_FILES) rm -rf $(HTMLDIR) + rm -rf $(XMLDIR) rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) rm -f errs core *~ diff --git a/system/doc/installation_guide/note.gif b/system/doc/installation_guide/note.gif Binary files differdeleted file mode 100644 index 6fffe30419..0000000000 --- a/system/doc/installation_guide/note.gif +++ /dev/null diff --git a/system/doc/installation_guide/warning.gif b/system/doc/installation_guide/warning.gif Binary files differdeleted file mode 100644 index 96af52360e..0000000000 --- a/system/doc/installation_guide/warning.gif +++ /dev/null diff --git a/system/doc/installation_guide/xmlfiles.mk b/system/doc/installation_guide/xmlfiles.mk index 3f720e1ee5..f005c8404b 100644 --- a/system/doc/installation_guide/xmlfiles.mk +++ b/system/doc/installation_guide/xmlfiles.mk @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2009-2016. All Rights Reserved. +# Copyright Ericsson AB 2009-2018. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,7 +18,9 @@ # %CopyrightEnd% # INST_GUIDE_CHAPTER_FILES = \ - install-binary.xml \ + install-binary.xml + +INST_GUIDE_CHAPTER_GEN_FILES = \ INSTALL.xml \ INSTALL-CROSS.xml \ INSTALL-WIN32.xml \ diff --git a/system/doc/oam/Makefile b/system/doc/oam/Makefile index 9095744423..147f56f885 100644 --- a/system/doc/oam/Makefile +++ b/system/doc/oam/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2016. All Rights Reserved. +# Copyright Ericsson AB 1997-2018. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ include $(ERL_TOP)/erts/vsn.mk #VSN=$(SYSTEM_VSN) APPLICATION=otp-system-documentation +XMLDIR := $(XMLDIR)/oam # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- @@ -87,6 +88,7 @@ debug opt: clean clean_docs: rm -rf $(HTMLDIR) + rm -rf $(XMLDIR) rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) rm -f errs core *~ diff --git a/system/doc/oam/note.gif b/system/doc/oam/note.gif Binary files differdeleted file mode 100644 index 6fffe30419..0000000000 --- a/system/doc/oam/note.gif +++ /dev/null diff --git a/system/doc/oam/oam_intro.xml b/system/doc/oam/oam_intro.xml index 8b8d69e638..ead8c026b9 100644 --- a/system/doc/oam/oam_intro.xml +++ b/system/doc/oam/oam_intro.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1997</year><year>2016</year> + <year>1997</year><year>2017</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -211,7 +211,7 @@ snmp:c("MY-MIB", [{il, ["sasl/priv/mibs"]}]).</code> <p>The following MIBs are defined in the OTP system:</p> <list type="bulleted"> - <item><p><c>OTP-REG)</c> (in SASL) contains the top-level + <item><p><c>OTP-REG</c> (in SASL) contains the top-level OTP registration objects, used by all other MIBs.</p></item> <item><p><c>OTP-TC</c> (in SASL) contains the general Textual Conventions, which can be used by any other MIB.</p></item> @@ -243,8 +243,8 @@ snmp:c("MY-MIB", [{il, ["sasl/priv/mibs"]}]).</code> loading the MIBs into the agent. Some MIB implementations are code-only, while others need a server. One way, used by the code-only MIB implementations, is for the user to call a - function such as <c>otp_mib:init(Agent)</c> to load the MIB, - and <c>otp_mib:stop(Agent)</c> to unload the MIB. See the + function such as <c>otp_mib:load(Agent)</c> to load the MIB, + and <c>otp_mib:unload(Agent)</c> to unload the MIB. See the manual page for each application for a description of how to load each MIB.</p> </section> diff --git a/system/doc/oam/warning.gif b/system/doc/oam/warning.gif Binary files differdeleted file mode 100644 index 96af52360e..0000000000 --- a/system/doc/oam/warning.gif +++ /dev/null diff --git a/system/doc/programming_examples/Makefile b/system/doc/programming_examples/Makefile index 237076d770..e4737ba069 100644 --- a/system/doc/programming_examples/Makefile +++ b/system/doc/programming_examples/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2003-2016. All Rights Reserved. +# Copyright Ericsson AB 2003-2018. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ include $(ERL_TOP)/erts/vsn.mk #VSN=$(SYSTEM_VSN) APPLICATION=otp-system-documentation +XMLDIR := $(XMLDIR)/programming_examples # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- @@ -52,7 +53,9 @@ PS_FILES = XML_FILES = \ $(BOOK_FILES) $(XML_CHAPTER_FILES) \ - $(XML_PART_FILES) + $(XML_PART_FILES) + +XML_GEN_FILES = $(PROG_EX_CHAPTER_GEN_FILES:%=$(XMLDIR)/%) # ---------------------------------------------------- HTML_FILES = \ @@ -82,6 +85,7 @@ debug opt: clean clean_docs: rm -rf $(HTMLDIR) + rm -rf $(XMLDIR) rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) rm -f errs core *~ diff --git a/system/doc/programming_examples/bit_syntax.xml b/system/doc/programming_examples/bit_syntax.xml index 0af295b7b7..d1dd52c5ab 100644 --- a/system/doc/programming_examples/bit_syntax.xml +++ b/system/doc/programming_examples/bit_syntax.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2015</year> + <year>2003</year><year>2017</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -32,6 +32,8 @@ <section> <title>Introduction</title> + <p>The complete specification for the bit syntax appears in the + <seealso marker="doc/reference_manual:expressions#bit_syntax">Reference Manual</seealso>.</p> <p>In Erlang, a Bin is used for constructing binaries and matching binary patterns. A Bin is written with the following syntax:</p> <code type="none"><![CDATA[ @@ -45,7 +47,7 @@ Bin = <<E1, E2, ... En>>]]></code> <<E1, E2, ... En>> = Bin ]]></code> <p>Here, <c>Bin</c> is bound and the elements are bound or unbound, as in any match.</p> - <p>Since Erlang R12B, a Bin does not need to consist of a whole number of bytes.</p> + <p>A Bin does not need to consist of a whole number of bytes.</p> <p>A <em>bitstring</em> is a sequence of zero or more bits, where the number of bits does not need to be divisible by 8. If the number @@ -165,8 +167,9 @@ end.]]></code> separated by hyphens.</p> <taglist> <tag>Type</tag> - <item>The type can be <c>integer</c>, <c>float</c>, or - <c>binary</c>.</item> + <item>The most commonly used types are <c>integer</c>, <c>float</c>, and <c>binary</c>. + See <seealso marker="doc/reference_manual:expressions#bit_syntax">Bit Syntax Expressions in the Reference Manual</seealso> for a complete description. +</item> <tag>Signedness</tag> <item>The signedness specification can be either <c>signed</c> or <c>unsigned</c>. Notice that signedness only matters for @@ -181,7 +184,7 @@ end.]]></code> <item>The unit size is given as <c>unit:IntegerLiteral</c>. The allowed range is 1-256. It is multiplied by the <c>Size</c> specifier to give the effective size of - the segment. Since Erlang R12B, the unit size specifies the alignment + the segment. The unit size specifies the alignment for binary segments without size.</item> </taglist> <p><em>Example:</em></p> @@ -319,21 +322,15 @@ foo(<<A:8,Rest/bitstring>>) ->]]></code> <section> <title>Appending to a Binary</title> - <p>Since Erlang R12B, the following function for creating a binary out of - a list of triples of integers is efficient:</p> + <p>Appending to a binary in an efficient way can be done as follows:</p> <code type="none"><![CDATA[ triples_to_bin(T) -> triples_to_bin(T, <<>>). triples_to_bin([{X,Y,Z} | T], Acc) -> - triples_to_bin(T, <<Acc/binary,X:32,Y:32,Z:32>>); % inefficient before R12B + triples_to_bin(T, <<Acc/binary,X:32,Y:32,Z:32>>); triples_to_bin([], Acc) -> Acc.]]></code> - <p>In previous releases, this function was highly inefficient, because - the binary constructed so far (<c>Acc</c>) was copied in each recursion step. - That is no longer the case. For more information, see - <seealso marker="doc/efficiency_guide:introduction"> - Efficiency Guide</seealso>.</p> </section> </chapter> diff --git a/system/doc/programming_examples/xmlfiles.mk b/system/doc/programming_examples/xmlfiles.mk index 5129e488f4..e457ca0cce 100644 --- a/system/doc/programming_examples/xmlfiles.mk +++ b/system/doc/programming_examples/xmlfiles.mk @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2009-2016. All Rights Reserved. +# Copyright Ericsson AB 2009-2018. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,6 +19,8 @@ # PROG_EX_CHAPTER_FILES = \ bit_syntax.xml \ - funs.xml \ list_comprehensions.xml \ records.xml + +PROG_EX_CHAPTER_GEN_FILES = \ + funs.xml diff --git a/system/doc/reference_manual/Makefile b/system/doc/reference_manual/Makefile index e14a056979..d034ad2ff8 100644 --- a/system/doc/reference_manual/Makefile +++ b/system/doc/reference_manual/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2003-2016. All Rights Reserved. +# Copyright Ericsson AB 2003-2018. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ include $(ERL_TOP)/erts/vsn.mk #VSN=$(SYSTEM_VSN) APPLICATION=otp-system-documentation +XMLDIR := $(XMLDIR)/reference_manual # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- @@ -95,6 +96,7 @@ debug opt: clean clean_docs: rm -rf $(HTMLDIR) + rm -rf $(XMLDIR) rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) rm -f errs core *~ diff --git a/system/doc/reference_manual/character_set.xml b/system/doc/reference_manual/character_set.xml index d25f2c001d..ef14bf1372 100644 --- a/system/doc/reference_manual/character_set.xml +++ b/system/doc/reference_manual/character_set.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2014</year><year>2015</year> + <year>2014</year><year>2017</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -32,9 +32,9 @@ <section> <title>Character Set</title> - <p>Since Erlang 4.8/OTP R5A, the syntax of Erlang tokens is extended to - allow the use of the full ISO-8859-1 (Latin-1) character set. This - is noticeable in the following ways:</p> + <p>The syntax of Erlang tokens allow the use of the full + ISO-8859-1 (Latin-1) character set. This is noticeable in the + following ways:</p> <list type="bulleted"> <item> <p>All the Latin-1 printable characters can be used and are @@ -102,13 +102,16 @@ <tcaption>Character Classes</tcaption> </table> <p>In Erlang/OTP R16B the syntax of Erlang tokens was extended to - handle Unicode. The support is limited to - string literals and comments. Atoms, module names, and - function names are restricted to the ISO-Latin-1 range. + handle Unicode. The support was limited to + string literals and comments. More about the usage of Unicode in Erlang source files can be found in <seealso marker="stdlib:unicode_usage#unicode_in_erlang">STDLIB's User's Guide</seealso>.</p> + <p>From Erlang/OTP 20, atoms and function names are also allowed + to contain Unicode characters outside the ISO-Latin-1 range. + Module names, application names, and node names are still + restricted to the ISO-Latin-1 range.</p> </section> <section> <title>Source File Encoding</title> diff --git a/system/doc/reference_manual/code_loading.xml b/system/doc/reference_manual/code_loading.xml index f6fd2911fa..f5e5e74841 100644 --- a/system/doc/reference_manual/code_loading.xml +++ b/system/doc/reference_manual/code_loading.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2015</year> + <year>2003</year><year>2016</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/system/doc/reference_manual/data_types.xml b/system/doc/reference_manual/data_types.xml index e63825b97d..93c679357b 100644 --- a/system/doc/reference_manual/data_types.xml +++ b/system/doc/reference_manual/data_types.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2015</year> + <year>2003</year><year>2017</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -50,10 +50,7 @@ <item><em><c>base</c></em><c>#</c><em><c>value</c></em> <br></br> Integer with the base <em><c>base</c></em>, that must be an - integer in the range 2..36. <br></br> - - In Erlang 5.2/OTP R9B and earlier versions, the allowed range - is 2..16.</item> + integer in the range 2..36.</item> </list> <p><em>Examples:</em></p> <pre> @@ -455,7 +452,7 @@ hello <<"77">> 16> <input>float_to_binary(7.0).</input> <<"7.00000000000000000000e+00">> -17> <input>binary_to_float(<<"7.000e+00>>").</input> +17> <input>binary_to_float(<<"7.000e+00">>).</input> 7.0</pre> </section> </chapter> diff --git a/system/doc/reference_manual/distributed.xml b/system/doc/reference_manual/distributed.xml index 0a4a323fe9..b519609717 100644 --- a/system/doc/reference_manual/distributed.xml +++ b/system/doc/reference_manual/distributed.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2015</year> + <year>2003</year><year>2017</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -42,6 +42,19 @@ <p>The distribution mechanism is implemented using TCP/IP sockets. How to implement an alternative carrier is described in the <seealso marker="erts:alt_dist">ERTS User's Guide</seealso>.</p> + <warning> + <p> + Starting a distributed node without also specifying + <seealso marker="erts:erl#proto_dist"><c>-proto_dist inet_tls</c></seealso> + will expose the node to attacks that may give the attacker + complete access to the node and in extension the cluster. + When using un-secure distributed nodes, make sure that the + network is configured to keep potential attackers out. + See the <seealso marker="ssl:ssl_distribution"> + Using SSL for Erlang Distribution</seealso> User's Guide + for details on how to setup a secure distributed node. + </p> + </warning> </section> <section> diff --git a/system/doc/reference_manual/errors.xml b/system/doc/reference_manual/errors.xml index e764cf431f..16d3e7590e 100644 --- a/system/doc/reference_manual/errors.xml +++ b/system/doc/reference_manual/errors.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2015</year> + <year>2003</year><year>2017</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -49,8 +49,7 @@ The Erlang programming language has built-in features for handling of run-time errors.</p> <p>A run-time error can also be emulated by calling - <c>erlang:error(Reason)</c> or <c>erlang:error(Reason, Args)</c> - (those appeared in Erlang 5.4/OTP-R10).</p> + <c>erlang:error(Reason)</c> or <c>erlang:error(Reason, Args)</c>.</p> <p>A run-time error is another name for an exception of class <c>error</c>. </p> @@ -79,7 +78,6 @@ <p>Exceptions are run-time errors or generated errors and are of three different classes, with different origins. The <seealso marker="expressions#try">try</seealso> expression - (new in Erlang 5.4/OTP R10B) can distinguish between the different classes, whereas the <seealso marker="expressions#catch">catch</seealso> expression cannot. They are described in @@ -94,7 +92,7 @@ <cell align="left" valign="middle"><c>error</c></cell> <cell align="left" valign="middle">Run-time error, for example, <c>1+a</c>, or the process called - <c>erlang:error/1,2</c> (new in Erlang 5.4/OTP R10B)</cell> + <c>erlang:error/1,2</c></cell> </row> <row> <cell align="left" valign="middle"><c>exit</c></cell> @@ -110,14 +108,55 @@ (see <seealso marker="#exit_reasons">Exit Reason</seealso>), and a stack trace (which aids in finding the code location of the exception).</p> - <p>The stack trace can be retrieved using - <c>erlang:get_stacktrace/0</c> (new in Erlang 5.4/OTP R10B) - from within a <c>try</c> expression, and is returned for + <p>The stack trace can be be bound to a variable from within + a <c>try</c> expression, and is returned for exceptions of class <c>error</c> from a <c>catch</c> expression.</p> <p>An exception of class <c>error</c> is also known as a run-time error.</p> + + <section> + <title>The call-stack back trace (stacktrace)</title> + <p>The stack back-trace (<em>stacktrace</em>) is a list of + <c>{Module,Function,Arity,Location}</c> + tuples. The field <c>Arity</c> in the first tuple can be the + argument list of that function call instead of an arity integer, + depending on the exception.</p> + + <p><c>Location</c> is a (possibly empty) list of two-tuples + that can indicate the location in the source code of the + function. The first element is an atom describing the type of + information in the second element. The following items can + occur:</p> + <taglist> + <tag><c>file</c></tag> + <item>The second element of the tuple is a string (list of + characters) representing the filename of the source file + of the function. + </item> + <tag><c>line</c></tag> + <item>The second element of the tuple is the line number + (an integer > 0) in the source file + where the exception occurred or the function was called. + </item> + </taglist> + <warning><p>Developers should rely on stacktrace entries only for + debugging purposes.</p> + <p>The VM performs tail call optimization, which + does not add new entries to the stacktrace, and also limits stacktraces + to a certain depth. Furthermore, compiler options, optimizations and + future changes may add or remove stacktrace entries, causing any code + that expects the stacktrace to be in a certain order or contain specific + items to fail.</p> + <p>The only exception to this rule is the class <c>error</c> with the + reason <c>undef</c> which is guaranteed to include the <c>Module</c>, + <c>Function</c> and <c>Arity</c> of the attempted + function as the first stacktrace entry.</p> + </warning> + </section> + </section> + <section> <title>Handling of Run-time Errors in Erlang</title> diff --git a/system/doc/reference_manual/expressions.xml b/system/doc/reference_manual/expressions.xml index 1a3d19aed1..76b3e92937 100644 --- a/system/doc/reference_manual/expressions.xml +++ b/system/doc/reference_manual/expressions.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2016</year> + <year>2003</year><year>2018</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -123,10 +123,9 @@ member(_Elem, []) -> or <c>receive</c> expression must be bound in all branches to have a value outside the expression. Otherwise they are regarded as 'unsafe' outside the expression.</p> - <p>For the <c>try</c> expression introduced in - Erlang 5.4/OTP R10B, variable scoping is limited so that + <p>For the <c>try</c> expression variable scoping is limited so that variables bound in the expression are always 'unsafe' outside - the expression. This is to be improved.</p> + the expression.</p> </section> <section> @@ -189,7 +188,6 @@ f([$p,$r,$e,$f,$i,$x | Str]) -> ...</pre> <pre> case {Value, Result} of {?THRESHOLD+1, ok} -> ...</pre> - <p>This feature was added in Erlang 5.0/OTP R7.</p> </section> </section> @@ -569,6 +567,10 @@ Expr1 <input>op</input> Expr2</pre> order is defined:</p> <pre> number < atom < reference < fun < port < pid < tuple < map < nil < list < bit string</pre> + <p><c>nil</c> in the previous expression represents the empty list + (<c>[]</c>), which is regarded as a separate type from + <c>list/0</c>. That is why <c>nil < list</c>. + </p> <p>Lists are compared element by element. Tuples are ordered by size, two tuples with the same size are compared element by element.</p> @@ -1342,14 +1344,14 @@ hello</pre> <code type="none"> try Exprs catch - [Class1:]ExceptionPattern1 [when ExceptionGuardSeq1] -> + Class1:ExceptionPattern1[:Stacktrace] [when ExceptionGuardSeq1] -> ExceptionBody1; - [ClassN:]ExceptionPatternN [when ExceptionGuardSeqN] -> + ClassN:ExceptionPatternN[:Stacktrace] [when ExceptionGuardSeqN] -> ExceptionBodyN end</code> <p>This is an enhancement of - <seealso marker="#catch">catch</seealso> that appeared in - Erlang 5.4/OTP R10B. It gives the possibility to:</p> + <seealso marker="#catch">catch</seealso>. + It gives the possibility to:</p> <list type="bulleted"> <item>Distinguish between different exception classes.</item> <item>Choose to handle only the desired ones.</item> @@ -1364,10 +1366,12 @@ end</code> the evaluation. In that case the exception is caught and the patterns <c>ExceptionPattern</c> with the right exception class <c>Class</c> are sequentially matched against the caught - exception. An omitted <c>Class</c> is shorthand for <c>throw</c>. - If a match succeeds and the optional guard sequence + exception. If a match succeeds and the optional guard sequence <c>ExceptionGuardSeq</c> is true, the corresponding <c>ExceptionBody</c> is evaluated to become the return value.</p> + <p><c>Stacktrace</c>, if specified, must be the name of a variable + (not a pattern). The stack trace is bound to the variable when + the corresponding <c>ExceptionPattern</c> matches.</p> <p>If an exception occurs during evaluation of <c>Exprs</c> but there is no matching <c>ExceptionPattern</c> of the right <c>Class</c> with a true guard sequence, the exception is passed @@ -1375,6 +1379,18 @@ end</code> expression.</p> <p>If an exception occurs during evaluation of <c>ExceptionBody</c>, it is not caught.</p> + <p>It is allowed to omit <c>Class</c> and <c>Stacktrace</c>. + An omitted <c>Class</c> is shorthand for <c>throw</c>:</p> + + <code type="none"> +try Exprs +catch + ExceptionPattern1 [when ExceptionGuardSeq1] -> + ExceptionBody1; + ExceptionPatternN [when ExceptionGuardSeqN] -> + ExceptionBodyN +end</code> + <p>The <c>try</c> expression can have an <c>of</c> section: </p> @@ -1386,10 +1402,10 @@ try Exprs of PatternN [when GuardSeqN] -> BodyN catch - [Class1:]ExceptionPattern1 [when ExceptionGuardSeq1] -> + Class1:ExceptionPattern1[:Stacktrace] [when ExceptionGuardSeq1] -> ExceptionBody1; ...; - [ClassN:]ExceptionPatternN [when ExceptionGuardSeqN] -> + ClassN:ExceptionPatternN[:Stacktrace] [when ExceptionGuardSeqN] -> ExceptionBodyN end</code> <p>If the evaluation of <c>Exprs</c> succeeds without an exception, @@ -1410,10 +1426,10 @@ try Exprs of PatternN [when GuardSeqN] -> BodyN catch - [Class1:]ExceptionPattern1 [when ExceptionGuardSeq1] -> + Class1:ExceptionPattern1[:Stacktrace] [when ExceptionGuardSeq1] -> ExceptionBody1; ...; - [ClassN:]ExceptionPatternN [when ExceptionGuardSeqN] -> + ClassN:ExceptionPatternN[:Stacktrace] [when ExceptionGuardSeqN] -> ExceptionBodyN after AfterBody @@ -1472,7 +1488,7 @@ try Expr catch throw:Term -> Term; exit:Reason -> {'EXIT',Reason} - error:Reason -> {'EXIT',{Reason,erlang:get_stacktrace()}} + error:Reason:Stk -> {'EXIT',{Reason,Stk}} end</code> </section> diff --git a/system/doc/reference_manual/introduction.xml b/system/doc/reference_manual/introduction.xml index abb4ed407d..69e52e0b37 100644 --- a/system/doc/reference_manual/introduction.xml +++ b/system/doc/reference_manual/introduction.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2015</year> + <year>2003</year><year>2018</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -48,7 +48,7 @@ System Principles</seealso></p> <p>Starting and stopping, boot scripts, code loading, <seealso marker="doc/system_principles:error_logging"> - error logging</seealso>, + logging</seealso>, <seealso marker="doc/system_principles:create_target"> creating target systems</seealso></p> </item> @@ -80,14 +80,14 @@ <item>A <em>list</em> is any number of items. For example, an argument list can consist of zero, one, or more arguments.</item> </list> - <p>If a feature has been added recently, in Erlang 5.0/OTP R7 or - later, this is mentioned in the text.</p> + <p>If a feature has been added in R13A or later, + this is mentioned in the text.</p> </section> <section> <title>Complete List of BIFs</title> <p>For a complete list of BIFs, their arguments and return values, - see <seealso marker="erts:erlang#process_flag/2">erlang(3)</seealso> + see <seealso marker="erts:erlang">erlang(3)</seealso> manual page in ERTS.</p> </section> diff --git a/system/doc/reference_manual/macros.xml b/system/doc/reference_manual/macros.xml index 350bb1d123..8a8d5f3a4c 100644 --- a/system/doc/reference_manual/macros.xml +++ b/system/doc/reference_manual/macros.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2015</year> + <year>2003</year><year>2018</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -150,6 +150,11 @@ bar(X) -> <item>The name of the current function.</item> <tag><c>?FUNCTION_ARITY</c></tag> <item>The arity (number of arguments) for the current function.</item> + <tag><c>?OTP_RELEASE</c></tag> + <item>The OTP release that the currently executing ERTS + application is part of, as an integer. For details, see + <seealso marker="erts:erlang#system_info/1"><c>erlang:system_info(otp_release)</c></seealso>. + This macro was introduced in OTP release 21.</item> </taglist> </section> @@ -202,8 +207,16 @@ f() -> directive. If that condition is false, the lines following <c>else</c> are evaluated instead.</item> <tag><c>-endif.</c></tag> - <item>Specifies the end of an <c>ifdef</c> or <c>ifndef</c> - directive.</item> + <item>Specifies the end of an <c>ifdef</c>, an <c>ifndef</c> + directive, or the end of an <c>if</c> or <c>elif</c> directive.</item> + <tag><c>-if(Condition).</c></tag> + <item>Evaluates the following lines only if <c>Condition</c> + evaluates to true.</item> + <tag><c>-elif(Condition).</c></tag> + <item>Only allowed after an <c>if</c> or another <c>elif</c> directive. + If the preceding <c>if</c> or <c>elif</c> directives do not + evaluate to true, and the <c>Condition</c> evaluates to true, + the lines following the <c>elif</c> are evaluated instead.</item> </taglist> <note> <p>The macro directives cannot be used inside functions.</p> @@ -231,6 +244,24 @@ or {ok,m}</pre> <p><c>?LOG(Arg)</c> is then expanded to a call to <c>io:format/2</c> and provide the user with some simple trace output.</p> + + <p><em>Example:</em></p> + <code type="none"> +-module(m) +... +-ifdef(OTP_RELEASE). + %% OTP 21 or higher + -if(?OTP_RELEASE >= 22). + %% Code that will work in OTP 22 or higher + -elif(?OTP_RELEASE >= 21). + %% Code that will work in OTP 21 or higher + -endif. +-else. + %% OTP 20 or lower. +-endif. +...</code> + <p>The code uses the <c>OTP_RELEASE</c> macro to conditionally + select code depending on release.</p> </section> <section> @@ -286,7 +317,6 @@ t.erl:5: Warning: -warning("Macro VERSION not defined -- using default version." argument, is expanded to a string containing the tokens of the argument. This is similar to the <c>#arg</c> stringifying construction in C.</p> - <p>The feature was added in Erlang 5.0/OTP R7.</p> <p><em>Example:</em></p> <code type="none"> -define(TESTCALL(Call), io:format("Call ~s: ~w~n", [??Call, Call])). diff --git a/system/doc/reference_manual/modules.xml b/system/doc/reference_manual/modules.xml index 96968b547e..6f93198ec1 100644 --- a/system/doc/reference_manual/modules.xml +++ b/system/doc/reference_manual/modules.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2016</year> + <year>2003</year><year>2018</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -143,7 +143,6 @@ fact(0) -> % | standard behaviours:</p> <list type="bulleted"> <item><c>gen_server</c></item> - <item><c>gen_fsm</c></item> <item><c>gen_statem</c></item> <item><c>gen_event</c></item> <item><c>supervisor</c></item> @@ -308,6 +307,12 @@ behaviour_info(callbacks) -> Callbacks.</pre> all functions in the module.</p> </item> + <tag><c>nifs</c></tag> + <item> + <p>Returns a list of <c>{Name,Arity}</c> tuples with + all NIF functions in the module.</p> + </item> + <tag><c>native</c></tag> <item> <p>Return <c>true</c> if the module has native compiled code. diff --git a/system/doc/reference_manual/records.xml b/system/doc/reference_manual/records.xml index 12a3e697cd..6b26e2c242 100644 --- a/system/doc/reference_manual/records.xml +++ b/system/doc/reference_manual/records.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2015</year> + <year>2003</year><year>2017</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -72,9 +72,9 @@ <pre> #Name{Field1=Expr1,...,FieldK=ExprK, _=ExprL}</pre> <p>Omitted fields then get the value of evaluating <c>ExprL</c> - instead of their default values. This feature was added in - Erlang 5.1/OTP R8 and is primarily intended to be used to create - patterns for ETS and Mnesia match functions.</p> + instead of their default values. This feature is primarily + intended to be used to create patterns for ETS and Mnesia match + functions.</p> <p><em>Example:</em></p> <pre> -record(person, {name, phone, address}). diff --git a/system/doc/reference_manual/typespec.xml b/system/doc/reference_manual/typespec.xml index 1899efd5f3..27cd0ba83d 100644 --- a/system/doc/reference_manual/typespec.xml +++ b/system/doc/reference_manual/typespec.xml @@ -4,14 +4,14 @@ <chapter> <header> <copyright> - <year>2003</year><year>2016</year> + <year>2003</year><year>2018</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software @@ -63,7 +63,7 @@ Types consist of, and are built from, a set of predefined types, for example, <c>integer()</c>, <c>atom()</c>, and <c>pid()</c>. Predefined types represent a typically infinite set of Erlang terms that - belong to this type. For example, the type <c>atom()</c> stands for the + belong to this type. For example, the type <c>atom()</c> denotes the set of all Erlang atoms. </p> <p> @@ -113,8 +113,8 @@ | Erlang_Atom %% 'foo', 'bar', ... Bitstring :: <<>> - | <<_:M>> %% M is a positive integer - | <<_:_*N>> %% N is a positive integer + | <<_:M>> %% M is an Integer_Value that evaluates to a positive integer + | <<_:_*N>> %% N is an Integer_Value that evaluates to a positive integer | <<_:M, _:_*N>> Fun :: fun() %% any function @@ -123,27 +123,36 @@ | fun((TList) -> Type) Integer :: integer() - | Erlang_Integer %% ..., -1, 0, 1, ... 42 ... - | Erlang_Integer..Erlang_Integer %% specifies an integer range + | Integer_Value + | Integer_Value..Integer_Value %% specifies an integer range + + Integer_Value :: Erlang_Integer %% ..., -1, 0, 1, ... 42 ... + | Erlang_Character %% $a, $b ... + | Integer_Value BinaryOp Integer_Value + | UnaryOp Integer_Value + + BinaryOp :: '*' | 'div' | 'rem' | 'band' | '+' | '-' | 'bor' | 'bxor' | 'bsl' | 'bsr' + + UnaryOp :: '+' | '-' | 'bnot' List :: list(Type) %% Proper list ([]-terminated) | maybe_improper_list(Type1, Type2) %% Type1=contents, Type2=termination | nonempty_improper_list(Type1, Type2) %% Type1 and Type2 as above | nonempty_list(Type) %% Proper non-empty list - Map :: map() %% stands for a map of any size - | #{} %% stands for the empty map - | #{PairList} + Map :: map() %% denotes a map of any size + | #{} %% denotes the empty map + | #{AssociationList} - Tuple :: tuple() %% stands for a tuple of any size + Tuple :: tuple() %% denotes a tuple of any size | {} | {TList} - PairList :: Pair - | Pair, PairList + AssociationList :: Association + | Association, AssociationList - Pair :: Type := Type %% denotes a pair that must be present - | Type => Type + Association :: Type := Type %% denotes a mandatory association + | Type => Type %% denotes an optional association TList :: Type | Type, TList @@ -151,8 +160,13 @@ Union :: Type1 | Type2 ]]></pre> <p> + Integer values are either integer or character literals or expressions + consisting of possibily nested unary or binary operations that evaluate to + an integer. Such expressions can also be used in bit strings and ranges. + </p> + <p> The general form of bit strings is <c><<_:M, _:_*N>></c>, - where <c>M</c> and <c>N</c> are positive integers. It denotes a + where <c>M</c> and <c>N</c> must evaluate to positive integers. It denotes a bit string that is <c>M + (k*N)</c> bits long (that is, a bit string that starts with <c>M</c> bits and continues with <c>k</c> segments of <c>N</c> bits each, where <c>k</c> is also a positive integer). @@ -161,7 +175,7 @@ that <c>M</c> or <c>N</c>, or both, are zero. </p> <p> - Because lists are commonly used, they have shorthand type notations. + Because lists are commonly used, they have shorthand type notations. The types <c>list(T)</c> and <c>nonempty_list(T)</c> have the shorthands <c>[T]</c> and <c>[T,...]</c>, respectively. The only difference between the two shorthands is that <c>[T]</c> can be an @@ -169,14 +183,21 @@ </p> <p> Notice that the shorthand for <c>list()</c>, that is, the list of - elements of unknown type, is <c>[_]</c> (or <c>[any()]</c>), not <c>[]</c>. + elements of unknown type, is <c>[_]</c> (or <c>[any()]</c>), not <c>[]</c>. The notation <c>[]</c> specifies the singleton type for the empty list. </p> <p> - The general form of maps is <c>#{PairList}</c>. The key types in - <c>PairList</c> are allowed to overlap, and if they do, the - leftmost pair takes precedence. A map pair has a key in - <c>PairList</c> if it belongs to this type. + The general form of map types is <c>#{AssociationList}</c>. + The key types in + <c>AssociationList</c> are allowed to overlap, and if they do, the + leftmost association takes precedence. A map association has a key in + <c>AssociationList</c> if it belongs to this type. + <c>AssociationList</c> can contain both mandatory and optional + association types. + If an association type is mandatory, an association with that type + is to be present. + In the case of an optional association type it is not required for + the key type to be present. </p> <p> Notice that the syntactic representation of <c>map()</c> is @@ -184,8 +205,8 @@ The notation <c>#{}</c> specifies the singleton type for the empty map. </p> <p> - For convenience, the following types are also built-in. - They can be thought as predefined aliases for the type unions also shown in + For convenience, the following types are also built-in. + They can be thought as predefined aliases for the type unions also shown in the table. </p> <table> @@ -201,37 +222,37 @@ <row> <cell><c>bitstring()</c></cell><cell><c><<_:_*1>></c></cell> </row> - <row> + <row> <cell><c>boolean()</c></cell><cell><c>'false' | 'true'</c></cell> </row> - <row> + <row> <cell><c>byte()</c></cell><cell><c>0..255</c></cell> </row> <row> <cell><c>char()</c></cell><cell><c>0..16#10ffff</c></cell> </row> - <row> + <row> <cell><c>nil()</c></cell><cell><c>[]</c></cell> </row> <row> <cell><c>number()</c></cell><cell><c>integer() | float()</c></cell> </row> - <row> + <row> <cell><c>list()</c></cell><cell><c>[any()]</c></cell> </row> - <row> + <row> <cell><c>maybe_improper_list()</c></cell><cell><c>maybe_improper_list(any(), any())</c></cell> </row> - <row> + <row> <cell><c>nonempty_list()</c></cell><cell><c>nonempty_list(any())</c></cell> </row> <row> <cell><c>string()</c></cell><cell><c>[char()]</c></cell> </row> - <row> + <row> <cell><c>nonempty_string()</c></cell><cell><c>[char(),...]</c></cell> </row> - <row> + <row> <cell><c>iodata()</c></cell><cell><c>iolist() | binary()</c></cell> </row> <row> @@ -243,7 +264,7 @@ <row> <cell><c>module()</c></cell><cell><c>atom()</c></cell> </row> - <row> + <row> <cell><c>mfa()</c></cell><cell><c>{module(),atom(),arity()}</c></cell> </row> <row> @@ -259,7 +280,7 @@ <cell><c>timeout()</c></cell><cell><c>'infinity' | non_neg_integer()</c></cell> </row> <row> - <cell><c>no_return()</c></cell><cell><c>none()</c></cell> + <cell><c>no_return()</c></cell><cell><c>none()</c></cell> </row> <tcaption>Built-in types, predefined aliases</tcaption> </table> @@ -284,11 +305,11 @@ </row> <tcaption>Additional built-in types</tcaption> </table> - + <p> Users are not allowed to define types with the same names as the predefined or built-in ones. This is checked by the compiler and - its violation results in a compilation error. + its violation results in a compilation error. </p> <note> <p> @@ -394,13 +415,13 @@ <pre> -record(rec, {field1 :: Type1, field2, field3 :: Type3}).</pre> <p> - For fields without type annotations, their type defaults to any(). + For fields without type annotations, their type defaults to any(). That is, the previous example is a shorthand for the following: </p> <pre> -record(rec, {field1 :: Type1, field2 :: any(), field3 :: Type3}).</pre> <p> - In the presence of initial values for fields, + In the presence of initial values for fields, the type must be declared after the initialization, as follows: </p> <pre> @@ -409,11 +430,13 @@ The initial values for fields are to be compatible with (that is, a member of) the corresponding types. This is checked by the compiler and results in a compilation error - if a violation is detected. For fields without initial values, - the singleton type <c>'undefined'</c> is added to all declared types. - In other words, the following two record declarations have identical - effects: + if a violation is detected. </p> + <note> + <p>Before Erlang/OTP 19, for fields without initial values, + the singleton type <c>'undefined'</c> was added to all declared types. + In other words, the following two record declarations had identical + effects:</p> <pre> -record(rec, {f1 = 42 :: integer(), f2 :: float(), @@ -423,26 +446,27 @@ f2 :: 'undefined' | float(), f3 :: 'undefined' | 'a' | 'b'}).</pre> <p> - For this reason, it is recommended that records contain initializers, - whenever possible. + This is no longer the case. If you require <c>'undefined'</c> in your record field + type, you must explicitly add it to the typespec, as in the 2nd example. </p> + </note> <p> - Any record, containing type information or not, once defined, + Any record, containing type information or not, once defined, can be used as a type using the following syntax: </p> <pre> #rec{}</pre> <p> - In addition, the record fields can be further specified when using + In addition, the record fields can be further specified when using a record type by adding type information about the field as follows: </p> <pre> #rec{some_field :: Type}</pre> <p> - Any unspecified fields are assumed to have the type in the original + Any unspecified fields are assumed to have the type in the original record declaration. </p> </section> - + <section> <title>Specifications for Functions</title> <p> @@ -456,9 +480,9 @@ else a compilation error occurs. </p> <p> - This form can also be used in header files (.hrl) to declare type - information for exported functions. - Then these header files can be included in files that (implicitly or + This form can also be used in header files (.hrl) to declare type + information for exported functions. + Then these header files can be included in files that (implicitly or explicitly) import these functions. </p> <p> @@ -472,14 +496,14 @@ <pre> -spec Function(ArgName1 :: Type1, ..., ArgNameN :: TypeN) -> RT.</pre> <p> - A function specification can be overloaded. + A function specification can be overloaded. That is, it can have several types, separated by a semicolon (<c>;</c>): </p> <pre> -spec foo(T1, T2) -> T3 ; (T4, T5) -> T6.</pre> <p> - A current restriction, which currently results in a warning + A current restriction, which currently results in a warning (not an error) by the compiler, is that the domains of the argument types cannot overlap. For example, the following specification results in a warning: @@ -488,9 +512,9 @@ -spec foo(pos_integer()) -> pos_integer() ; (integer()) -> integer().</pre> <p> - Type variables can be used in specifications to specify relations for - the input and output arguments of a function. - For example, the following specification defines the type of a + Type variables can be used in specifications to specify relations for + the input and output arguments of a function. + For example, the following specification defines the type of a polymorphic identity function: </p> <pre> @@ -505,8 +529,8 @@ <p> Currently, the <c>::</c> constraint (read as «is a subtype of») is - the only guard constraint that can be used in the <c>'when'</c> - part of a <c>'-spec'</c> attribute. + the only guard constraint that can be used in the <c>when</c> + part of a <c>-spec</c> attribute. </p> <note> <p> @@ -539,8 +563,8 @@ -spec foo({X, integer()}) -> X when X :: atom() ; ([Y]) -> Y when Y :: number().</pre> <p> - Some functions in Erlang are not meant to return; - either because they define servers or because they are used to + Some functions in Erlang are not meant to return; + either because they define servers or because they are used to throw exceptions, as in the following function: </p> <pre> my_error(Err) -> erlang:throw({error, Err}).</pre> @@ -552,4 +576,3 @@ <pre> -spec my_error(term()) -> no_return().</pre> </section> </chapter> - diff --git a/system/doc/reference_manual/xmlfiles.mk b/system/doc/reference_manual/xmlfiles.mk index 61637ae701..92d232b628 100644 --- a/system/doc/reference_manual/xmlfiles.mk +++ b/system/doc/reference_manual/xmlfiles.mk @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2009-2016. All Rights Reserved. +# Copyright Ericsson AB 2009-2018. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -30,5 +30,6 @@ REF_MAN_CHAPTER_FILES = \ processes.xml \ distributed.xml \ code_loading.xml \ - ports.xml - + ports.xml \ + character_set.xml \ + typespec.xml diff --git a/system/doc/system_architecture_intro/Makefile b/system/doc/system_architecture_intro/Makefile index 446e66205c..eb885a744d 100644 --- a/system/doc/system_architecture_intro/Makefile +++ b/system/doc/system_architecture_intro/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2016. All Rights Reserved. +# Copyright Ericsson AB 1997-2018. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ include $(ERL_TOP)/erts/vsn.mk #VSN=$(SYSTEM_VSN) APPLICATION=otp-system-documentation +XMLDIR := $(XMLDIR)/system_architecture_intro # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- @@ -80,6 +81,7 @@ debug opt: clean clean_docs: rm -rf $(HTMLDIR) + rm -rf $(XMLDIR) rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) rm -f errs core *~ diff --git a/system/doc/system_architecture_intro/note.gif b/system/doc/system_architecture_intro/note.gif Binary files differdeleted file mode 100644 index 6fffe30419..0000000000 --- a/system/doc/system_architecture_intro/note.gif +++ /dev/null diff --git a/system/doc/system_architecture_intro/sys_arch_intro.xml b/system/doc/system_architecture_intro/sys_arch_intro.xml index cf75e1f100..e8ada6427b 100644 --- a/system/doc/system_architecture_intro/sys_arch_intro.xml +++ b/system/doc/system_architecture_intro/sys_arch_intro.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2000</year><year>2016</year> + <year>2000</year><year>2018</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -98,20 +98,6 @@ </list> </item> <item> - <p>CORBA services and IDL compiler.</p> - <list type="bulleted"> - <item><em>cosEvent</em> Orber OMG Event Service.</item> - <item><em>cosNotification</em> Orber OMG Notification - Service.</item> - <item><em>cosTime</em> Orber OMG Timer and TimerEvent - Services.</item> - <item><em>cosTransactions</em> Orber OMG Transaction - Service.</item> - <item><em>IC</em> IDL compiler</item> - <item><em>Orber</em> A CORBA object request broker.</item> - </list> - </item> - <item> <p>Tools.</p> <list type="bulleted"> <item><em>Appmon</em> A utility used to view OTP applications.</item> diff --git a/system/doc/system_architecture_intro/warning.gif b/system/doc/system_architecture_intro/warning.gif Binary files differdeleted file mode 100644 index 96af52360e..0000000000 --- a/system/doc/system_architecture_intro/warning.gif +++ /dev/null diff --git a/system/doc/system_principles/Makefile b/system/doc/system_principles/Makefile index 77edea8f58..1979deda4c 100644 --- a/system/doc/system_principles/Makefile +++ b/system/doc/system_principles/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2016. All Rights Reserved. +# Copyright Ericsson AB 1996-2018. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ include $(ERL_TOP)/erts/vsn.mk #VSN=$(SYSTEM_VSN) APPLICATION=otp-system-documentation +XMLDIR := $(XMLDIR)/system_principles # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- @@ -52,6 +53,8 @@ XML_FILES = \ $(BOOK_FILES) $(XML_CHAPTER_FILES) \ $(XML_PART_FILES) +XML_GEN_FILES = $(SYSTEM_PRINCIPLES_CHAPTER_GEN_FILES:%=$(XMLDIR)/%) + # ---------------------------------------------------- HTMLDIR = ../html/system_principles @@ -79,6 +82,7 @@ debug opt: clean clean_docs: rm -rf $(HTMLDIR) + rm -rf $(XMLDIR) rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) rm -f errs core *~ diff --git a/system/doc/system_principles/create_target.xmlsrc b/system/doc/system_principles/create_target.xmlsrc index f9b27ffc35..47b84e5760 100644 --- a/system/doc/system_principles/create_target.xmlsrc +++ b/system/doc/system_principles/create_target.xmlsrc @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2002</year><year>2016</year> + <year>2002</year><year>2018</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -263,6 +263,14 @@ os> <input>/usr/local/erl-target/bin/erl -boot /usr/local/erl-target/releases/FI current directory create not only the file <c>mysystem.rel</c>, but also file <c>sys.config</c>, the latter file is tacitly put in the appropriate directory.</p> + <p>However, it can also be convenient to replace variables in + within a <c>sys.config</c> on the target after unpacking but + before running the release. If you have a <c>sys.config.src</c> + it will be included and is not required to be a valid Erlang term + file like <c>sys.config</c>. Before running the release you must + have a valid <c>sys.config</c> in the same directory, so using + <c>sys.config.src</c> requires having some tool to populate what is + needed and write <c>sys.config</c> to disk before booting the release.</p> </section> <section> diff --git a/system/doc/system_principles/error_logging.xml b/system/doc/system_principles/error_logging.xml index c99d59ddb7..9cbf7a2e94 100644 --- a/system/doc/system_principles/error_logging.xml +++ b/system/doc/system_principles/error_logging.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2016</year> + <year>2003</year><year>2018</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -39,80 +39,71 @@ <code type="none"><![CDATA[ =ERROR REPORT==== 9-Dec-2003::13:25:02 === Error in process <0.27.0> with exit value: {{badmatch,[1,2,3]},[{m,f,1},{shell,eval_loop,2}]}]]></code> - <p>The error information is handled by the <em>error logger</em>, a - system process registered as <c>error_logger</c>. This process - receives all error messages from the Erlang runtime system as - well as from the standard behaviours and different Erlang/OTP - applications.</p> + <p>The error information is handled by Logger, which is part of + the Kernel application.</p> <p>The exit reasons (such as <c>badarg</c>) used by the runtime system are described in <seealso marker="doc/reference_manual:errors#exit_reasons"> Errors and Error Handling</seealso>.</p> - <p>For information about the process <c>error_logger</c> and its user - interface (with the same name), see the - <seealso marker="kernel:error_logger">error_logger(3)</seealso> - manual page in Kernel. The system can be configured so that - error information - is written to file or to tty, or both. In addition, user-defined - applications can send and format error information using - <c>error_logger</c>.</p> + <p>For information about Logger and its user + interface, see the + <seealso marker="kernel:logger">logger(3)</seealso> + manual page and + the <seealso marker="kernel:logger_chapter">Logging</seealso> + section in the Kernel User's Guide. The system can be configured so that + log events are + written to file or to tty, or both. In addition, user-defined + applications can send and format log events using + Logger.</p> </section> <section> - <title>SASL Error Logging</title> + <title>Log events from OTP behaviours</title> <p>The standard behaviours (<c>supervisor</c>, <c>gen_server</c>, - and so on) send progress and error information to <c>error_logger</c>. - If the SASL application is started, this information is - written to tty as well. For more information, see + and so on) send progress and error information to + Logger. Progress reports are by default not logged, but can be + enabled by setting the primary log level to <c>info</c>, for + example by using the Kernel configuration + parameter <c>logger_level</c>. Supervisor reports, crash reports + and other error and information reports are by default logged + through the log handler which is set up when the Kernel + application is started.</p> + <p>Prior to Erlang/OTP 21.0, supervisor, crash, and progress + reports were only logged when the SASL application was + running. This behaviour can, for backwards compatibility, be + enabled by setting the Kernel configuration + parameter <seealso marker="kernel:kernel_app#logger_sasl_compatible"> + <c>logger_sasl_compatible</c></seealso> + to <c>true</c>. For more information, see <seealso marker="sasl:error_logging">SASL Error Logging</seealso> in the SASL User's Guide.</p> <pre> -% <input>erl -boot start_sasl</input> -Erlang (BEAM) emulator version 5.4.13 [hipe] [threads:0] [kernel-poll] +% <input>erl -kernel logger_level info</input> +Erlang/OTP 21 [erts-10.0] [source-13c50db] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] - -=PROGRESS REPORT==== 31-Mar-2006::12:45:58 === - supervisor: {local,sasl_safe_sup} - started: [{pid,<0.33.0>}, - {name,alarm_handler}, - {mfa,{alarm_handler,start_link,[]}}, - {restart_type,permanent}, - {shutdown,2000}, - {child_type,worker}] - -=PROGRESS REPORT==== 31-Mar-2006::12:45:58 === - supervisor: {local,sasl_safe_sup} - started: [{pid,<0.34.0>}, - {name,overload}, - {mfa,{overload,start_link,[]}}, - {restart_type,permanent}, - {shutdown,2000}, - {child_type,worker}] - -=PROGRESS REPORT==== 31-Mar-2006::12:45:58 === - supervisor: {local,sasl_sup} - started: [{pid,<0.32.0>}, - {name,sasl_safe_sup}, - {mfa,{supervisor, - start_link, - [{local,sasl_safe_sup},sasl,safe]}}, - {restart_type,permanent}, - {shutdown,infinity}, - {child_type,supervisor}] - -=PROGRESS REPORT==== 31-Mar-2006::12:45:58 === - supervisor: {local,sasl_sup} - started: [{pid,<0.35.0>}, - {name,release_handler}, - {mfa,{release_handler,start_link,[]}}, - {restart_type,permanent}, - {shutdown,2000}, - {child_type,worker}] - -=PROGRESS REPORT==== 31-Mar-2006::12:45:58 === - application: sasl - started_at: nonode@nohost -Eshell V5.4.13 (abort with ^G) +=PROGRESS REPORT==== 8-Jun-2018::16:54:19.916404 === + application: kernel + started_at: nonode@nohost +=PROGRESS REPORT==== 8-Jun-2018::16:54:19.922908 === + application: stdlib + started_at: nonode@nohost +=PROGRESS REPORT==== 8-Jun-2018::16:54:19.925755 === + supervisor: {local,kernel_safe_sup} + started: [{pid,<0.74.0>}, + {id,disk_log_sup}, + {mfargs,{disk_log_sup,start_link,[]}}, + {restart_type,permanent}, + {shutdown,1000}, + {child_type,supervisor}] +=PROGRESS REPORT==== 8-Jun-2018::16:54:19.926056 === + supervisor: {local,kernel_safe_sup} + started: [{pid,<0.75.0>}, + {id,disk_log_server}, + {mfargs,{disk_log_server,start_link,[]}}, + {restart_type,permanent}, + {shutdown,2000}, + {child_type,worker}] +Eshell V10.0 (abort with ^G) 1> </pre> </section> </chapter> diff --git a/system/doc/system_principles/misc.xml b/system/doc/system_principles/misc.xml new file mode 100644 index 0000000000..dd6c2a1336 --- /dev/null +++ b/system/doc/system_principles/misc.xml @@ -0,0 +1,198 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>2018</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + </legalnotice> + + <title>Support, Compatibility, Deprecations, and Removal</title> + <prepared></prepared> + <responsible></responsible> + <docno></docno> + <approved></approved> + <checked></checked> + <date>2018-05-21</date> + <rev></rev> + <file>misc.xml</file> + </header> + + <section> + <marker id="supported_releases"/> + <title>Supported Releases</title> + <p> + In general, bugs are only fixed on the latest + <seealso marker="versions#releases_and_patches">release</seealso>, + and new features are introduced in the upcoming release that is + under development. However, when we, due to internal reasons, fix + bugs on older releases, these will be available and announced as well. + </p> + <p> + Due to the above, pull requests are only accepted on the + <c>maint</c> and the <c>master</c> branches in our + <url href="https://github.com/erlang/otp">git repository</url>. + The <c>maint</c> branch contains changes planned for the next + <seealso marker="versions#releases_and_patches">maintenance patch package</seealso> + on the latest OTP release and the <c>master</c> branch contain + changes planned for the upcoming OTP release. + </p> + </section> + + <section> + <marker id="compatibility"/> + <title>Compatibility</title> + <p> + We always strive to remain as compatible as possible + even in the cases where we give no compatibility guarantees. + </p> + <p> + Different parts of the system will be handled differently + regarding compatibility. The following items describe how + different parts of the system are handled. + </p> + <taglist> + <tag>Erlang Distribution</tag> + <item> + <p> + Erlang nodes can communicate across at least + two preceding and two subsequent releases. + </p> + </item> + <tag>Compiled BEAM Code, NIF Libraries and Drivers</tag> + <item> + <p> + Compiled code can be loaded on at least two + subsequent releases. + </p> + <p> + Loading on previous releases is <em>not</em> supported. + </p> + </item> + <tag>Compiled HiPE Code</tag> + <item> + <p> + Compiled HiPE code can be loaded on the exact same build + of ERTS that was used when compiling the code. It might + however work on other builds, the emulator verifies + checksums in order to determine if it can load the code + or not. Note that HiPE has some limitations. For more + information see the documentation of the + <seealso marker="hipe:HiPE_app">HiPE</seealso> application. + </p> + </item> + <tag>APIs</tag> + <item> + <p>Compatible between releases.</p> + </item> + <tag>Compiler Warnings</tag> + <item> + <p>New warnings may be issued between releases.</p> + </item> + <tag>Command Line Arguments</tag> + <item> + <p>Incompatible changes may occur between releases.</p> + </item> + <tag>OTP Build Procedures</tag> + <item><p>Incompatible changes may occur between releases.</p></item> + </taglist> + <p> + Under certain circumstances incompatible changes might be + introduced even in parts of the system that should be compatible + between releases. Things that might trigger incompatible changes + like this are: + </p> + <taglist> + <tag>Security Issues</tag> + <item> + <p> + It might be necessary to introduce incompatible changes + in order to solve a security issue. This kind of + incompatibility might occur in a patch. + </p> + </item> + <tag>Bug Fixes</tag> + <item> + <p> + We will not be bug-compatible. A bug fix might introduce + incompatible changes. This kind of incompatibility + might occur in a patch. + </p> + </item> + <tag>Severe Previous Design Issues</tag> + <item> + <p> + Some parts of OTP were designed a very long time ago and + did not necessarily take today's computing environments into + account. In some cases the consequences of those design + decisions are too severe. This may be performance wise, + scalability wise, etc. If we deem the consequences too + severe, we might introduce incompatible changes. This kind + of incompatibility will not be introduced in a patch, but + instead in the next release. + </p> + </item> + </taglist> + <p> + Peripheral, trace, and debug functionality is at greater + risk of being changed in an incompatible way than functionality + in the language itself and core libraries used during operation. + </p> + </section> + + <section> + <marker id="deprecation"/> + <title>Deprecation</title> + <p> + Functionality is deprecated when new functionality is + introduced that is preferred to be used instead of the + old functionality that is being deprecated. The deprecation + does <em>not</em> imply removal of the functionality unless + an upcoming removal is explicitly stated in the deprecation. + </p> + <p> + Deprecated functionality will be documented as deprecated, and + compiler warnings will be issued, when appropriate, as + early as possible. That is, the new preferred functionality + will appear at the same time as the deprecation is issued. + A new deprecation will at least be announced in a release + note and the documentation. + </p> + </section> + + <section> + <marker id="removal"/> + <title>Removal</title> + <p> + Legacy solutions may eventually need to be removed. In such + cases, they will be phased out on a long enough time period + to give users the time to adapt. Before removal of + functionality it will be deprecated at least during one + release with an explicit announcement about + the upcoming removal. A new deprecation will at least be + announced in a release note and the documentation. + </p> + <p> + Peripheral, trace, and debug functionality is at greater + risk of removal than functionality in the language itself + and core libraries used during operation. + </p> + </section> + +</chapter> + diff --git a/system/doc/system_principles/part.xml b/system/doc/system_principles/part.xml index 1b87ecd350..6699389eec 100644 --- a/system/doc/system_principles/part.xml +++ b/system/doc/system_principles/part.xml @@ -4,7 +4,7 @@ <part xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>1997</year><year>2016</year> + <year>1997</year><year>2018</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -34,4 +34,5 @@ <xi:include href="create_target.xml"/> <xi:include href="upgrade.xml"/> <xi:include href="versions.xml"/> + <xi:include href="misc.xml"/> </part> diff --git a/system/doc/system_principles/system_principles.xml b/system/doc/system_principles/system_principles.xml index 3605df3ade..500522d778 100644 --- a/system/doc/system_principles/system_principles.xml +++ b/system/doc/system_principles/system_principles.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1996</year><year>2015</year> + <year>1996</year><year>2018</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -93,7 +93,7 @@ init:stop()</pre> {progress,kernel_load_completed} {progress,modules_loaded} {start,heart} -{start,error_logger} +{start,logger} ...</pre> <p>For a detailed description of the syntax and contents of the boot script, see the <c>script(4)</c> manual page in SASL.</p> diff --git a/system/doc/system_principles/versions.xml b/system/doc/system_principles/versions.xml index b9f7fa4bf6..fbdcc6b2b0 100644 --- a/system/doc/system_principles/versions.xml +++ b/system/doc/system_principles/versions.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2014</year><year>2016</year> + <year>2014</year><year>2018</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -32,9 +32,9 @@ <rev></rev> <file>versions.xml</file> </header> - <marker id="versions section"></marker> <section> + <marker id="versions section"></marker> <title>OTP Version</title> <p>As of OTP release 17, the OTP release number corresponds to the major part of the OTP version. The OTP version as a concept was @@ -136,8 +136,8 @@ </section> <section> - <title>Version Scheme</title> <marker id="version_scheme"/> + <title>Version Scheme</title> <note><p>The version scheme was changed as of OTP 17.0. This implies that application versions used prior to OTP 17.0 do not adhere to this version scheme. <seealso marker="#otp_17_0_app_versions">A list of @@ -181,7 +181,7 @@ goes for application versions.</p> <p>In general, versions can have more than three parts. The versions are then only partially ordered. Such - versions are only used in exceptional cases. When an extra + versions are only used when branching off from another branch. When an extra part (out of the normal three parts) is added to a version number, a new branch of versions is made. The new branch has a linear order against the base version. However, versions on different @@ -207,8 +207,68 @@ </section> <section> - <title>OTP 17.0 Application Versions</title> + <marker id="releases_and_patches"/> + <title>Releases and Patches</title> + <p> + When a new OTP release is released it will have an OTP + version on the form <c><Major>.0</c> where the + major OTP version number equals the release number. + The major version number is increased one step since the + last major version. All other OTP versions with the same + major OTP version number are patches on that OTP release. + </p> + <p> + Patches are either released as maintenance patch packages + or emergency patch packages. The only difference is that + maintenance patch packages are planned and usually contain + more changes than emergency patch packages. Emergency patch + packages are released to solve one or more specific issues + when such are discovered. + </p> + <p> + The release of a maintenance patch package usually imply + an increase of the OTP <c><Minor></c> version while + the release of an emergency patch package usually imply an + increase of the OTP <c><Patch></c> version. This is + however not necessarily always the case since changes of + OTP versions are based on the actual changes in the code + and not based on whether the patch was planned or not. + For more information see the + <seealso marker="#version_scheme">Version Scheme</seealso> + section above. + </p> + </section> + + <section> + <marker id="otp_versions_tree"/> + <title>OTP Versions Tree</title> + <p> + All released OTP versions can be found in the + <url href="http://www.erlang.org/download/otp_versions_tree.html">OTP + Versions Tree</url> which is automatically updated whenever + we release a new OTP version. Note that every version number as + such explicitly define its position in the version tree. Nothing + more than the version numbers are needed in order to construct + the tree. The root of the tree is OTP version 17.0 which is when + we introduced the new + <seealso marker="#version_scheme">version scheme</seealso>. The + green versions are normal versions released on the main track. + Old <seealso marker="#releases_and_patches">OTP releases</seealso> + will be maintained for a while on <c>maint</c> branches that have + branched off from the main track. Old <c>maint</c> branches always + branch off from the main track when the next OTP release is + introduced into the main track. Versions on these old <c>maint</c> + branches are marked blue. Besides the green and blue versions, + there are also gray versions. These are versions on branches + introduced in order to fix a specific problem for a specific + customer on a specific base version. Branches with gray versions + will typically become dead ends very quickly if not immediately. + </p> + </section> + + <section> <marker id="otp_17_0_app_versions"/> + <title>OTP 17.0 Application Versions</title> <p>The following list details the application versions that were part of OTP 17.0. If the normal part of an application version number compares diff --git a/system/doc/system_principles/warning.gif b/system/doc/system_principles/warning.gif Binary files differdeleted file mode 100644 index 96af52360e..0000000000 --- a/system/doc/system_principles/warning.gif +++ /dev/null diff --git a/system/doc/system_principles/xmlfiles.mk b/system/doc/system_principles/xmlfiles.mk index c3c3bb4731..353c2c7f7f 100644 --- a/system/doc/system_principles/xmlfiles.mk +++ b/system/doc/system_principles/xmlfiles.mk @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2009-2016. All Rights Reserved. +# Copyright Ericsson AB 2009-2018. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,6 +20,9 @@ SYSTEM_PRINCIPLES_CHAPTER_FILES = \ system_principles.xml \ error_logging.xml \ - create_target.xml \ upgrade.xml \ - versions.xml + versions.xml \ + misc.xml + +SYSTEM_PRINCIPLES_CHAPTER_GEN_FILES = \ + create_target.xml diff --git a/system/doc/top/Makefile b/system/doc/top/Makefile index 116ec688fa..0703b821f1 100644 --- a/system/doc/top/Makefile +++ b/system/doc/top/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1999-2016. All Rights Reserved. +# Copyright Ericsson AB 1999-2018. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -50,31 +50,48 @@ include ../tutorial/xmlfiles.mk include ../design_principles/xmlfiles.mk include ../oam/xmlfiles.mk -XML_FILES = \ - $(INST_GUIDE_CHAPTER_FILES:%=../installation_guide/%) \ - $(SYSTEM_PRINCIPLES_CHAPTER_FILES:%=../system_principles/%) \ - $(EMBEDDED_CHAPTER_FILES:%=../embedded/%) \ - $(GETTING_STARTED_CHAPTER_FILES:%=../getting_started/%) \ - $(REF_MAN_CHAPTER_FILES:%=../reference_manual/%) \ - $(PROG_EX_CHAPTER_FILES:%=../programming_examples/%) \ - $(EFF_GUIDE_CHAPTER_FILES:%=../efficiency_guide/%) \ - $(TUTORIAL_CHAPTER_FILES:%=../tutorial/%) \ - $(DESIGN_PRINCIPLES_CHAPTER_FILES:%=../design_principles/%) \ - $(OAM_CHAPTER_FILES:%=../oam/%) \ - ../installation_guide/part.xml \ - ../system_principles/part.xml \ - ../embedded/part.xml \ - ../getting_started/part.xml \ - ../reference_manual/part.xml \ - ../programming_examples/part.xml \ - ../efficiency_guide/part.xml \ - ../tutorial/part.xml \ - ../design_principles/part.xml \ - ../oam/part.xml - BOOK_FILES = book.xml -XMLLINT_SRCDIRS= ../installation_guide:../system_principles:../embedded:../getting_started:../reference_manual:../programming_examples:../efficiency_guide:../tutorial:../design_principles:../oam +XML_FILES = \ + $(BOOK_FILES) + +XML_GUIDE_FILES = \ + $(INST_GUIDE_CHAPTER_FILES:%=installation_guide/%) \ + $(INST_GUIDE_CHAPTER_GEN_FILES:%=installation_guide/%) \ + $(SYSTEM_PRINCIPLES_CHAPTER_FILES:%=system_principles/%) \ + $(SYSTEM_PRINCIPLES_CHAPTER_GEN_FILES:%=system_principles/%) \ + $(EMBEDDED_CHAPTER_FILES:%=embedded/%) \ + $(EMBEDDED_CHAPTER_GEN_FILES:%=embedded/%) \ + $(GETTING_STARTED_CHAPTER_FILES:%=getting_started/%) \ + $(GETTING_STARTED_CHAPTER_GEN_FILES:%=getting_started/%) \ + $(REF_MAN_CHAPTER_FILES:%=reference_manual/%) \ + $(REF_MAN_CHAPTER_GEN_FILES:%=reference_manual/%) \ + $(PROG_EX_CHAPTER_FILES:%=programming_examples/%) \ + $(PROG_EX_CHAPTER_GEN_FILES:%=programming_examples/%) \ + $(EFF_GUIDE_CHAPTER_FILES:%=efficiency_guide/%) \ + $(EFF_GUIDE_CHAPTER_GEN_FILES:%=efficiency_guide/%) \ + $(TUTORIAL_CHAPTER_FILES:%=tutorial/%) \ + $(TUTORIAL_CHAPTER_GEN_FILES:%=tutorial/%) \ + $(DESIGN_PRINCIPLES_CHAPTER_FILES:%=design_principles/%) \ + $(DESIGN_PRINCIPLES_CHAPTER_GEN_FILES:%=design_principles/%) \ + $(OAM_CHAPTER_FILES:%=oam/%) \ + $(OAM_CHAPTER_GEN_FILES:%=oam/%) + +XML_GEN_FILES = \ + $(XML_GUIDE_FILES:%=$(XMLDIR)/%) \ + $(XMLDIR)/installation_guide/part.xml \ + $(XMLDIR)/system_principles/part.xml \ + $(XMLDIR)/embedded/part.xml \ + $(XMLDIR)/getting_started/part.xml \ + $(XMLDIR)/reference_manual/part.xml \ + $(XMLDIR)/programming_examples/part.xml \ + $(XMLDIR)/efficiency_guide/part.xml \ + $(XMLDIR)/tutorial/part.xml \ + $(XMLDIR)/design_principles/part.xml \ + $(XMLDIR)/oam/part.xml + + +XMLLINT_SRCDIRS= $(XMLDIR)/installation_guide:$(XMLDIR)/system_principles:$(XMLDIR)/embedded:$(XMLDIR)/getting_started:$(XMLDIR)/reference_manual:$(XMLDIR)/programming_examples:$(XMLDIR)/efficiency_guide:$(XMLDIR)/tutorial:$(XMLDIR)/design_principles:$(XMLDIR)/oam HTMLDIR= ../html PDFREFDIR= pdf @@ -238,13 +255,11 @@ clean: rm -f $(INDEX_SCRIPT) $(GLOSSARY_SCRIPT) \ $(JAVASCRIPT_BUILD_SCRIPT) rm -f erl_crash.dump errs core *~ - # ---------------------------------------------------- # Release Target # ---------------------------------------------------- include $(ERL_TOP)/make/otp_release_targets.mk - release_docs_spec: docs $(INSTALL_DIR) "$(RELEASE_PATH)" $(INSTALL_DATA) $(INFO_FILES) "$(RELEASE_PATH)" diff --git a/system/doc/top/book.xml b/system/doc/top/book.xml index c94b0f24d6..61e75591ef 100644 --- a/system/doc/top/book.xml +++ b/system/doc/top/book.xml @@ -4,7 +4,7 @@ <book xmlns:xi="http://www.w3.org/2001/XInclude"> <header titlestyle="normal"> <copyright> - <year>1997</year><year>2016</year> + <year>1997</year><year>2018</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -36,16 +36,16 @@ <contents level="2"></contents> </preamble> <parts lift="no"> - <xi:include href="../installation_guide/part.xml"/> - <xi:include href="../system_principles/part.xml"/> - <xi:include href="../embedded/part.xml"/> - <xi:include href="../getting_started/part.xml"/> - <xi:include href="../reference_manual/part.xml"/> - <xi:include href="../programming_examples/part.xml"/> - <xi:include href="../efficiency_guide/part.xml"/> - <xi:include href="../tutorial/part.xml"/> - <xi:include href="../design_principles/part.xml"/> - <xi:include href="../oam/part.xml"/> + <xi:include href="../xml/installation_guide/part.xml"/> + <xi:include href="../xml/system_principles/part.xml"/> + <xi:include href="../xml/embedded/part.xml"/> + <xi:include href="../xml/getting_started/part.xml"/> + <xi:include href="../xml/reference_manual/part.xml"/> + <xi:include href="../xml/programming_examples/part.xml"/> + <xi:include href="../xml/efficiency_guide/part.xml"/> + <xi:include href="../xml/tutorial/part.xml"/> + <xi:include href="../xml/design_principles/part.xml"/> + <xi:include href="../xml/oam/part.xml"/> </parts> <listofterms></listofterms> <index></index> diff --git a/system/doc/top/src/erl_html_tools.erl b/system/doc/top/src/erl_html_tools.erl index d55c2e1164..28a0649658 100644 --- a/system/doc/top/src/erl_html_tools.erl +++ b/system/doc/top/src/erl_html_tools.erl @@ -387,9 +387,7 @@ subst("#copyright#", _Info, _Group) -> "copyright Copyright © 1991-2004"; subst("#groups#", Info, _Group) -> [ - "<table border=0 width=\"90%\" cellspacing=3 cellpadding=5>\n", - subst_groups(Info), - "</table>\n" + subst_groups(Info) ]; subst("#applinks#", Info, Group) -> subst_applinks(Info, Group); @@ -476,16 +474,10 @@ subst_unknown_groups([{_Group,Heading,Apps} | Groups], Text0, Left) -> group_table(Heading,Apps) -> - [ - " <tr>\n", - " <td colspan=2 class=header>\n", - " <font size=\"+1\"><b>",Heading,"</b></font>\n", - " </td>\n", - " </tr>\n", + ["<h2>",Heading,"</h2>", + "<table class=\"group-table\">\n", subst_apps(Apps), - " <tr>\n", - " <td colspan=2><font size=1> </font></td>\n", - " </tr>\n" + "</table>\n" ]. % Count and split the applications in half to get the right sort @@ -500,17 +492,11 @@ subst_apps([]) -> subst_app(App, [{VSN,_Path,Link,Text}]) -> [ " <tr class=app>\n", - " <td align=left valign=top>\n", - " <table border=0 width=\"100%\" cellspacing=0 cellpadding=0>\n", - " <tr class=app>\n", - " <td align=left valign=top>\n", + " <td>\n", " <a href=\"",Link,"\" target=\"_top\">",uc(App),"</a>\n", " <a href=\"",Link,"\" target=\"_top\">",VSN,"</a>\n", - " </td>\n", - " </tr>\n", - " </table>\n" " </td>\n", - " <td align=left valign=top>\n", + " <td>\n", Text,"\n", " </td>\n", " </tr>\n" @@ -518,27 +504,14 @@ subst_app(App, [{VSN,_Path,Link,Text}]) -> subst_app(App, [{VSN,_Path,Link,Text} | VerInfos]) -> [ " <tr class=app>\n", - " <td align=left valign=top>\n", - " <table border=0 width=\"100%\" cellspacing=0 cellpadding=0>\n", - " <tr class=app>\n", - " <td align=left valign=top>\n", + " <td>\n", " <a href=\"",Link,"\" target=\"_top\">",uc(App), - "</a> <br>\n", + "</a>\n", " <a href=\"",Link,"\" target=\"_top\">",VSN,"</a>\n", - " </td>\n", - " <td align=right valign=top width=50>\n", - " <table border=0 width=40 cellspacing=0 cellpadding=0>\n", - " <tr class=app>\n", - " <td align=left valign=top class=appnums>\n", + " <td class=appnums>\n", subst_vsn(VerInfos), - " </td>\n", - " </tr>\n", - " </table>\n" - " </td>\n", - " </tr>\n", - " </table>\n" " </td>\n", - " <td align=left valign=top>\n", + " <td>\n", Text,"\n", " </td>\n", " </tr>\n" diff --git a/system/doc/top/src/otp_man_index.erl b/system/doc/top/src/otp_man_index.erl index 12aaba1423..655d7265f7 100644 --- a/system/doc/top/src/otp_man_index.erl +++ b/system/doc/top/src/otp_man_index.erl @@ -154,10 +154,10 @@ gen_html(RefPages, OutFile)-> SortedPages = lists:sort(RefPages), lists:foreach(fun({_,Module, App, AppDocDir, RefPagePath}) -> - io:fwrite(Out, " <TR>\n",[]), - io:fwrite(Out, " <TD><A HREF=\"~s\">~s</A></TD>\n", + io:fwrite(Out, " <tr>\n",[]), + io:fwrite(Out, " <td><a href=\"~s\">~s</a></td>\n", [RefPagePath, Module]), - io:fwrite(Out, " <TD><A HREF=\"~s\">~s</A></TD>\n", + io:fwrite(Out, " <td><a HREF=\"~s\">~s</a></td>\n", [filename:join(AppDocDir, "index.html"), App]), io:fwrite(Out, " </TR>\n",[]) @@ -175,41 +175,40 @@ gen_html(RefPages, OutFile)-> html_header() -> "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n" "<!-- This file was generated by the otp_man_index -->\n" - "<HTML>\n" - "<HEAD>\n" + "<html>\n" + "<head>\n" " <link rel=\"stylesheet\" href=\"otp_doc.css\" type=\"text/css\"/>\n" - " <TITLE>Erlang/OTP Manual Page Index</TITLE>\n" - "</HEAD>\n" - "<BODY BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#0000FF\" VLINK=\"#FF00FF\" ALINK=\"#FF0000\">\n" - "<CENTER>\n" + " <title>Erlang/OTP Manual Page Index</title>\n" + "</head>\n" + "<body>\n" + "<center>\n" "<!-- A HREF=\"http://www.erlang.org/\">\n" "<img alt=\"Erlang logo\" src=\"erlang-logo.png\"/>\n" - "</A><BR -->\n" - "<SMALL>\n" - "[<A HREF=\"index.html\">Up</A> | <A HREF=\"http://www.erlang.org/\">Erlang</A>]\n" - "</SMALL><BR>\n" - "<P/><FONT SIZE=\"+4\">OTP Reference Page Index</FONT><BR>\n" - "</CENTER>\n" - "<CENTER>\n" - "<P/>\n" - "<TABLE BORDER=1>\n" - "<TR>\n" - " <TH>Manual Page</TH><TH>Application</TH>\n" - "</TR>\n". + "</a><br -->\n" + "<small>\n" + "[ <A HREF=\"index.html\">Up</A> | <A HREF=\"http://www.erlang.org/\">Homepage</A> ]\n" + "</small><br>\n" + "<h1>OTP Reference Page Index</h1>\n" + "</center>\n" + "<center>\n" + "<table class=\"man-index\">\n" + "<tr>\n" + " <th>Manual Page</th><th>Application</th>\n" + "</tr>\n". html_footer(Year) -> - "</TABLE>\n" - "</CENTER>\n" - "<P/>\n" - "<CENTER>\n" - "<HR/>\n" - "<SMALL>\n" + "</table>\n" + "</center>\n" + "<p/>\n" + "<center>\n" + "<hr/>\n" + "<small>\n" "Copyright © 1991-" ++ Year ++ "\n" "<a href=\"http://www.ericsson.com/technology/opensource/erlang/\">\n" "Ericsson AB</a>\n" - "</SMALL>\n" - "</CENTER>\n" - "</BODY>\n" - "</HTML>\n". + "</small>\n" + "</center>\n" + "</body>\n" + "</html>\n". diff --git a/system/doc/top/templates/applications.html.src b/system/doc/top/templates/applications.html.src index 1f73c44d69..7e939ddcd6 100644 --- a/system/doc/top/templates/applications.html.src +++ b/system/doc/top/templates/applications.html.src @@ -24,31 +24,53 @@ limitations under the License. <title>Erlang/OTP #version# Applications</title> <style type="text/css"> <!-- - BODY { background: white } + BODY { background: #fefefe; color: #111; } - BODY { font-family: Verdana, Arial, Helvetica, sans-serif } - TH { font-family: Verdana, Arial, Helvetica, sans-serif } - TD { font-family: Verdana, Arial, Helvetica, sans-serif } - P { font-family: Verdana, Arial, Helvetica, sans-serif } + BODY { font-family: sans-serif } - .header { background: #222; color: #fff } - .app { background: #ccc } + .header { background: #333; color: #fefefe; } - a.anum:link { color: green; text-decoration: none } - a.anum:active { color: green; text-decoration: none } - a.anum:visited { color: green; text-decoration: none } + a:link { color: #303f9f; text-decoration: none } + a:active { color: #303f9f; text-decoration: none } + a:visited { color: #303f9f; text-decoration: none } - a:link { text-decoration: none } - a:active { text-decoration: none } - a:visited { text-decoration: none } + h1,h2{ + text-align: center; + } + table { + margin: 1em 10%; + width: 80%; + border-collapse: collapse; + min-width: 50%; + } + + table, th, td { + border: 1px solid #666; + } + + th, td { + padding: 0.5em; + text-align: left; + } + + tr:hover { + background-color: #f5f5f5; + } + + tr:nth-child(even) { + background-color: #f2f2f2; + } + + th { + background-color: #777; + color: #fefefe; + } --> </style> </head> -<body bgcolor=white text="#000000" link="#0000ff" vlink="#ff00ff" - alink="#ff0000"> -<center> +<body> + <h1>Erlang/OTP Applications</h1> #groups# -</center> </body> </html> diff --git a/system/doc/top/templates/index.html.src b/system/doc/top/templates/index.html.src index d2a6736d34..747d19cf7e 100644 --- a/system/doc/top/templates/index.html.src +++ b/system/doc/top/templates/index.html.src @@ -2,7 +2,7 @@ <!-- %CopyrightBegin% -Copyright Ericsson AB 2009-2016. All Rights Reserved. +Copyright Ericsson AB 2009-2017. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -29,20 +29,24 @@ limitations under the License. </script> </head> -<body bgcolor=white text="#000000" link="#0000ff" vlink="#ff00ff" - alink="#ff0000"> +<body> <div id="container"> <div id="leftnav"> <div class="innertube"> -<img alt="Erlang logo" src="erlang-logo.png"/ > -<p/> -<small><a href="applications.html">Applications</a><br> -<a href="man_index.html">Modules</a></small> -<p/> -<a href="javascript:openAllFlips()">Expand All</a><br> -<a href="javascript:closeAllFlips()">Contract All</a> -<p/> +<div class="erlang-logo-wrapper"> + <img alt="Erlang logo" src="erlang-logo.png" class="erlang-logo"/ > +</div> + +<ul class="section-links"> + <li><a href="applications.html">Applications</a></li> + <li><a href="man_index.html" class="modules">Modules</a></li> +</ul> + +<ul class="expand-collapse-items"> + <li><a href="javascript:openAllFlips()">Expand All</a></li> + <li><a href="javascript:closeAllFlips()">Contract All</a></li> +</ul> <ul class="flipMenu"> <li>System Documentation @@ -60,8 +64,8 @@ limitations under the License. </ul> </li> </ul> -<b>Application Groups</b> -<br/> + +<h3>Application Groups</h3> <ul class="flipMenu"> #applinks# @@ -73,24 +77,12 @@ limitations under the License. <div id="content"> <div class="innertube"> -<center> -<font size="+1"><b>Erlang/OTP #otp_base_vsn#</b></font><br> -</center> -<center> -<p> -<font size="+1">Welcome to Erlang/OTP, a complete<br> -development environment<br> -for concurrent programming.</font> -</p> -</center> -<br> -<br> -<br> -<p><b> -<font size="+1"> -Some hints that may get you started faster -</font> -</b></p> + <h1 class="main-title">Erlang/OTP #otp_base_vsn#</h1> + <p class="main-description"> + Welcome to Erlang/OTP, a complete development environment for concurrent programming. + </p> + + <h2>Some hints that may get you started faster</h2> <ul> @@ -149,10 +141,11 @@ verification, comment support including paragraph filling, skeletons, tags support and more. See the <a href="#tools#/index.html"> Tools</a> application for details. <p> -There are also Erlang plugins for +There are also Erlang plugins for other code editors +<a href="http://github.com/vim-erlang">Vim (vim-erlang)</a> , +<a href="http://atom.io/packages/language-erlang"> Atom </a> , <a href="http://erlide.org/index.html">Eclipse (ErlIDE)</a> and -<a href="http://ignatov.github.io/intellij-erlang/">IntelliJ IDEA</a> -if you prefer a more graphical environment, which are both under active development. +<a href="http://ignatov.github.io/intellij-erlang/">IntelliJ IDEA</a>. <li>When developing with Erlang/OTP you usually test your programs from the interactive shell (see <a href="getting_started/users_guide.html"> Getting Started With Erlang</a>) where you can call individual diff --git a/system/doc/tutorial/Makefile b/system/doc/tutorial/Makefile index 5deea41f0a..5867096fc8 100644 --- a/system/doc/tutorial/Makefile +++ b/system/doc/tutorial/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2000-2016. All Rights Reserved. +# Copyright Ericsson AB 2000-2018. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ include $(ERL_TOP)/erts/vsn.mk #VSN=$(SYSTEM_VSN) APPLICATION=otp-system-documentation +XMLDIR := $(XMLDIR)/tutorial # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- @@ -53,6 +54,9 @@ XML_FILES = \ $(BOOK_FILES) $(XML_CHAPTER_FILES) \ $(XML_PART_FILES) +XML_GEN_FILES = \ + $(TUTORIAL_CHAPTER_GEN_FILES:%=$(XMLDIR)/%) + # ---------------------------------------------------- C_FILES = \ @@ -106,6 +110,7 @@ debug opt: clean clean_docs: rm -rf $(HTMLDIR) + rm -rf $(XMLDIR) rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) rm -f errs core *~ diff --git a/system/doc/tutorial/c_port.xmlsrc b/system/doc/tutorial/c_port.xmlsrc index 3c3bc48044..ff0997fb54 100644 --- a/system/doc/tutorial/c_port.xmlsrc +++ b/system/doc/tutorial/c_port.xmlsrc @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2000</year><year>2015</year> + <year>2000</year><year>2016</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/system/doc/tutorial/c_portdriver.xmlsrc b/system/doc/tutorial/c_portdriver.xmlsrc index 933e2395a3..c4cf6daa3b 100644 --- a/system/doc/tutorial/c_portdriver.xmlsrc +++ b/system/doc/tutorial/c_portdriver.xmlsrc @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2000</year><year>2015</year> + <year>2000</year><year>2017</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -161,8 +161,8 @@ decode([Int]) -> Int.</pre> <title>Running the Example</title> <p><em>Step 1.</em> Compile the C code:</p> <pre> -unix> <input>gcc -o exampledrv -fpic -shared complex.c port_driver.c</input> -windows> <input>cl -LD -MD -Fe exampledrv.dll complex.c port_driver.c</input></pre> +unix> <input>gcc -o example_drv.so -fpic -shared complex.c port_driver.c</input> +windows> <input>cl -LD -MD -Fe example_drv.dll complex.c port_driver.c</input></pre> <p><em>Step 2.</em> Start Erlang and compile the Erlang code:</p> <pre> > <input>erl</input> diff --git a/system/doc/tutorial/complex6_nif.c b/system/doc/tutorial/complex6_nif.c index b656ed43ce..f6c06e94f4 100644 --- a/system/doc/tutorial/complex6_nif.c +++ b/system/doc/tutorial/complex6_nif.c @@ -1,4 +1,4 @@ -#include "erl_nif.h" +#include <erl_nif.h> extern int foo(int x); extern int bar(int y); diff --git a/system/doc/tutorial/erl_interface.xmlsrc b/system/doc/tutorial/erl_interface.xmlsrc index de50af42cf..bf9bd36597 100644 --- a/system/doc/tutorial/erl_interface.xmlsrc +++ b/system/doc/tutorial/erl_interface.xmlsrc @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2000</year><year>2015</year> + <year>2000</year><year>2017</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -162,9 +162,9 @@ int main() { the include files <c>erl_interface.h</c> and <c>ei.h</c>, and also to the libraries <c>erl_interface</c> and <c>ei</c>:</p> <pre> -unix> <input>gcc -o extprg -I/usr/local/otp/lib/erl_interface-3.2.1/include \\ </input> -<input> -L/usr/local/otp/lib/erl_interface-3.2.1/lib \\ </input> -<input> complex.c erl_comm.c ei.c -lerl_interface -lei</input></pre> +unix> <input>gcc -o extprg -I/usr/local/otp/lib/erl_interface-3.9.2/include \\ </input> +<input> -L/usr/local/otp/lib/erl_interface-3.9.2/lib \\ </input> +<input> complex.c erl_comm.c ei.c -lerl_interface -lei -lpthread</input></pre> <p>In Erlang/OTP R5B and later versions of OTP, the <c>include</c> and <c>lib</c> directories are situated under <c>OTPROOT/lib/erl_interface-VSN</c>, where <c>OTPROOT</c> is @@ -184,7 +184,7 @@ Eshell V4.9.1.2 (abort with ^G) {ok,complex2}</pre> <p><em>Step 3.</em> Run the example:</p> <pre> -2> <input>complex2:start("extprg").</input> +2> <input>complex2:start("./extprg").</input> <0.34.0> 3> <input>complex2:foo(3).</input> 4 diff --git a/system/doc/tutorial/port_driver.c b/system/doc/tutorial/port_driver.c index 37de67310f..8b441733ed 100644 --- a/system/doc/tutorial/port_driver.c +++ b/system/doc/tutorial/port_driver.c @@ -51,8 +51,7 @@ ErlDrvEntry example_driver_entry = { queue */ NULL, /* F_PTR call, much like control, sync call to driver */ - NULL, /* F_PTR event, called when an event selected - by driver_event() occurs. */ + NULL, /* unused */ ERL_DRV_EXTENDED_MARKER, /* int extended marker, Should always be set to indicate driver versioning */ ERL_DRV_EXTENDED_MAJOR_VERSION, /* int major_version, should always be diff --git a/system/doc/tutorial/xmlfiles.mk b/system/doc/tutorial/xmlfiles.mk index f8ed7be064..74e174f6d4 100644 --- a/system/doc/tutorial/xmlfiles.mk +++ b/system/doc/tutorial/xmlfiles.mk @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2009-2016. All Rights Reserved. +# Copyright Ericsson AB 2009-2018. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,13 +19,16 @@ # TUTORIAL_CHAPTER_FILES = \ introduction.xml\ + overview.xml + +TUTORIAL_CHAPTER_GEN_FILES = \ cnode.xml\ c_port.xml\ erl_interface.xml \ c_portdriver.xml \ example.xml\ - overview.xml\ nif.xml + # appendix.xml # distribution.xml (to be part of tutorial later) |