aboutsummaryrefslogtreecommitdiffstats
path: root/system/doc
diff options
context:
space:
mode:
Diffstat (limited to 'system/doc')
-rw-r--r--system/doc/Makefile8
-rw-r--r--system/doc/design_principles/Makefile44
-rw-r--r--system/doc/design_principles/applications.xml14
-rw-r--r--system/doc/design_principles/code_lock.diabin2945 -> 2605 bytes
-rw-r--r--system/doc/design_principles/code_lock.pngbin59827 -> 0 bytes
-rw-r--r--system/doc/design_principles/code_lock.svg132
-rw-r--r--system/doc/design_principles/code_lock_2.diabin2956 -> 2854 bytes
-rw-r--r--system/doc/design_principles/code_lock_2.pngbin55553 -> 0 bytes
-rw-r--r--system/doc/design_principles/code_lock_2.svg140
-rw-r--r--system/doc/design_principles/des_princ.xml8
-rw-r--r--system/doc/design_principles/fsm.xml338
-rw-r--r--system/doc/design_principles/gen_server_concepts.xml4
-rw-r--r--system/doc/design_principles/note.gifbin1539 -> 0 bytes
-rw-r--r--system/doc/design_principles/part.xml3
-rw-r--r--system/doc/design_principles/release_structure.xml8
-rw-r--r--system/doc/design_principles/spec_proc.xml90
-rw-r--r--system/doc/design_principles/statem.xml1843
-rw-r--r--system/doc/design_principles/sup_princ.xml81
-rw-r--r--system/doc/design_principles/warning.gifbin1498 -> 0 bytes
-rw-r--r--system/doc/design_principles/xmlfiles.mk3
-rw-r--r--system/doc/efficiency_guide/Makefile7
-rw-r--r--system/doc/efficiency_guide/advanced.xml57
-rw-r--r--system/doc/efficiency_guide/bench.erl2
-rw-r--r--system/doc/efficiency_guide/binaryhandling.xml173
-rw-r--r--system/doc/efficiency_guide/commoncaveats.xml65
-rw-r--r--system/doc/efficiency_guide/efficiency_guide.erl24
-rw-r--r--system/doc/efficiency_guide/functions.xml13
-rw-r--r--system/doc/efficiency_guide/introduction.xml10
-rw-r--r--system/doc/efficiency_guide/listhandling.xml18
-rw-r--r--system/doc/efficiency_guide/myths.xml108
-rw-r--r--system/doc/efficiency_guide/part.xml1
-rw-r--r--system/doc/efficiency_guide/processes.xml18
-rw-r--r--system/doc/efficiency_guide/profiling.xml173
-rw-r--r--system/doc/efficiency_guide/retired_myths.xml77
-rw-r--r--system/doc/efficiency_guide/xmlfiles.mk6
-rw-r--r--system/doc/embedded/Makefile7
-rw-r--r--system/doc/embedded/note.gifbin1539 -> 0 bytes
-rw-r--r--system/doc/embedded/starting.xml2
-rw-r--r--system/doc/embedded/warning.gifbin1498 -> 0 bytes
-rw-r--r--system/doc/general_info/Makefile99
-rw-r--r--system/doc/general_info/book.xml44
-rw-r--r--system/doc/general_info/deprecations.xml98
-rw-r--r--system/doc/general_info/part.xml34
-rw-r--r--system/doc/general_info/scheduled_for_removal.xml61
-rw-r--r--system/doc/general_info/xmlfiles.mk22
-rw-r--r--system/doc/getting_started/Makefile7
-rw-r--r--system/doc/getting_started/conc_prog.xml8
-rw-r--r--system/doc/getting_started/seq_prog.xml13
-rw-r--r--system/doc/html/design_principles/.gitignore0
-rw-r--r--system/doc/html/efficiency_guide/.gitignore0
-rw-r--r--system/doc/html/embedded/.gitignore0
-rw-r--r--system/doc/html/general_info/.gitignore0
-rw-r--r--system/doc/html/getting_started/.gitignore0
-rw-r--r--system/doc/html/installation_guide/.gitignore0
-rw-r--r--system/doc/html/installation_guide/source/.gitignore0
-rw-r--r--system/doc/html/js/.gitignore0
-rw-r--r--system/doc/html/oam/.gitignore0
-rw-r--r--system/doc/html/programming_examples/.gitignore0
-rw-r--r--system/doc/html/reference_manual/.gitignore0
-rw-r--r--system/doc/html/system_architecture_intro/.gitignore0
-rw-r--r--system/doc/html/system_principles/.gitignore0
-rw-r--r--system/doc/html/tutorial/.gitignore0
-rw-r--r--system/doc/installation_guide/Makefile25
-rw-r--r--system/doc/installation_guide/note.gifbin1539 -> 0 bytes
-rw-r--r--system/doc/installation_guide/warning.gifbin1498 -> 0 bytes
-rw-r--r--system/doc/installation_guide/xmlfiles.mk6
-rw-r--r--system/doc/oam/Makefile9
-rw-r--r--system/doc/oam/note.gifbin1539 -> 0 bytes
-rw-r--r--system/doc/oam/oam_intro.xml55
-rw-r--r--system/doc/oam/warning.gifbin1498 -> 0 bytes
-rw-r--r--system/doc/programming_examples/Makefile11
-rw-r--r--system/doc/programming_examples/bit_syntax.xml23
-rw-r--r--system/doc/programming_examples/xmlfiles.mk6
-rw-r--r--system/doc/reference_manual/Makefile7
-rw-r--r--system/doc/reference_manual/character_set.xml17
-rw-r--r--system/doc/reference_manual/data_types.xml9
-rw-r--r--system/doc/reference_manual/distributed.xml15
-rw-r--r--system/doc/reference_manual/errors.xml55
-rw-r--r--system/doc/reference_manual/expressions.xml57
-rw-r--r--system/doc/reference_manual/introduction.xml10
-rw-r--r--system/doc/reference_manual/macros.xml38
-rw-r--r--system/doc/reference_manual/modules.xml9
-rw-r--r--system/doc/reference_manual/records.xml8
-rw-r--r--system/doc/reference_manual/typespec.xml68
-rw-r--r--system/doc/reference_manual/xmlfiles.mk7
-rw-r--r--system/doc/system_architecture_intro/Makefile7
-rw-r--r--system/doc/system_architecture_intro/note.gifbin1539 -> 0 bytes
-rw-r--r--system/doc/system_architecture_intro/sys_arch_intro.xml16
-rw-r--r--system/doc/system_architecture_intro/warning.gifbin1498 -> 0 bytes
-rw-r--r--system/doc/system_principles/Makefile9
-rw-r--r--system/doc/system_principles/create_target.xmlsrc12
-rw-r--r--system/doc/system_principles/error_logging.xml117
-rw-r--r--system/doc/system_principles/misc.xml206
-rw-r--r--system/doc/system_principles/part.xml3
-rw-r--r--system/doc/system_principles/system_principles.xml7
-rw-r--r--system/doc/system_principles/versions.xml70
-rw-r--r--system/doc/system_principles/warning.gifbin1498 -> 0 bytes
-rw-r--r--system/doc/system_principles/xmlfiles.mk9
-rw-r--r--system/doc/top/Makefile159
-rw-r--r--system/doc/top/book.xml23
-rw-r--r--system/doc/top/src/erl_html_tools.erl49
-rw-r--r--system/doc/top/src/erlresolvelinks.erl90
-rw-r--r--system/doc/top/src/otp_man_index.erl61
-rw-r--r--system/doc/top/templates/applications.html.src56
-rw-r--r--system/doc/top/templates/index.html.src63
-rw-r--r--system/doc/tutorial/Makefile12
-rw-r--r--system/doc/tutorial/complex6_nif.c2
-rw-r--r--system/doc/tutorial/erl_interface.xmlsrc10
-rw-r--r--system/doc/tutorial/overview.xml6
-rw-r--r--system/doc/tutorial/port_driver.c3
-rw-r--r--system/doc/tutorial/xmlfiles.mk7
-rw-r--r--system/doc/xml/design_principles/.gitignore0
-rw-r--r--system/doc/xml/efficiency_guide/.gitignore0
-rw-r--r--system/doc/xml/embedded/.gitignore0
-rw-r--r--system/doc/xml/general_info/.gitignore0
-rw-r--r--system/doc/xml/getting_started/.gitignore0
-rw-r--r--system/doc/xml/installation_guide/.gitignore0
-rw-r--r--system/doc/xml/oam/.gitignore0
-rw-r--r--system/doc/xml/programming_examples/.gitignore0
-rw-r--r--system/doc/xml/reference_manual/.gitignore0
-rw-r--r--system/doc/xml/system_architecture_intro/.gitignore0
-rw-r--r--system/doc/xml/system_principles/.gitignore0
-rw-r--r--system/doc/xml/tutorial/.gitignore0
123 files changed, 3348 insertions, 1947 deletions
diff --git a/system/doc/Makefile b/system/doc/Makefile
index eb900b933f..8f2faeee04 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
@@ -24,6 +24,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
#
SUB_DIRECTORIES = design_principles \
+ general_info \
getting_started \
system_architecture_intro \
embedded \
@@ -38,10 +39,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..2fbd7d087f 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,20 +80,19 @@ HTMLDIR = ../html/design_principles
HTML_UG_FILE = $(HTMLDIR)/users_guide.html
# ----------------------------------------------------
-# FLAGS
+# FLAGS
# ----------------------------------------------------
-XML_FLAGS +=
-DVIPS_FLAGS +=
+XML_FLAGS +=
+DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-_create_dirs := $(shell mkdir -p $(HTMLDIR))
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
-$(HTMLDIR)/%.png: %.png
+$(HTMLDIR)/%.svg: %.svg
$(INSTALL_DATA) $< $@
docs: html
@@ -102,16 +103,17 @@ html: $(HTML_UG_FILE) images
images: $(IMAGE_FILES:%=$(HTMLDIR)/%)
-debug opt:
+debug opt:
clean clean_docs:
- rm -rf $(HTMLDIR)
+ rm -f $(XMLDIR)/*.xml
+ rm -f $(HTMLDIR)/*.gif $(HTMLDIR)/*.html
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 +125,3 @@ release_docs_spec: docs
release_spec:
-
-
diff --git a/system/doc/design_principles/applications.xml b/system/doc/design_principles/applications.xml
index c673fde07e..8f1969ff29 100644
--- a/system/doc/design_principles/applications.xml
+++ b/system/doc/design_principles/applications.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>
@@ -185,7 +185,7 @@ ch_app:stop([])</code>
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>
+ <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
@@ -363,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>
diff --git a/system/doc/design_principles/code_lock.dia b/system/doc/design_principles/code_lock.dia
index eaa2aca5b0..fe43d6da2c 100644
--- a/system/doc/design_principles/code_lock.dia
+++ b/system/doc/design_principles/code_lock.dia
Binary files differ
diff --git a/system/doc/design_principles/code_lock.png b/system/doc/design_principles/code_lock.png
deleted file mode 100644
index 40bd35fc74..0000000000
--- a/system/doc/design_principles/code_lock.png
+++ /dev/null
Binary files differ
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
index 3b9ba554d8..31eb0fb6eb 100644
--- a/system/doc/design_principles/code_lock_2.dia
+++ b/system/doc/design_principles/code_lock_2.dia
Binary files differ
diff --git a/system/doc/design_principles/code_lock_2.png b/system/doc/design_principles/code_lock_2.png
deleted file mode 100644
index 3aca9dd5aa..0000000000
--- a/system/doc/design_principles/code_lock_2.png
+++ /dev/null
Binary files differ
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
deleted file mode 100644
index 6fffe30419..0000000000
--- a/system/doc/design_principles/note.gif
+++ /dev/null
Binary files differ
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,&lt;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,&lt;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,&lt;0.32.0>,
- {module,gen_fsm},
- [[{'$ancestors',[&lt;0.30.0>]},
- {'$initial_call',{gen,init_it,
- [gen_fsm,&lt;0.30.0>,&lt;0.30.0>,
- {local,code_lock},
- code_lock,
- [1,2,3,4],
- []]}}],
- running,&lt;0.30.0>,[],
- [code_lock,closed,{[],[1,2,3,4]},code_lock,infinity]]}</pre>
+11> sys:get_status(code_lock).
+{status,&lt;0.63.0>,
+ {module,gen_statem},
+ [[{'$initial_call',{code_lock,init,1}},
+ {'$ancestors',[&lt;0.61.0>]}],
+ running,&lt;0.61.0>,[],
+ [{header,"Status for state machine code_lock"},
+ {data,[{"Status",running},
+ {"Parent",&lt;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 22b622ec5f..23e9054547 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>2017</year>
+ <year>2016</year><year>2019</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -36,16 +36,6 @@
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>
<!-- =================================================================== -->
@@ -54,31 +44,44 @@
<title>Event-Driven State Machines</title>
<p>
Established Automata Theory does not deal much with
- how a state transition is triggered,
+ how a <em>state transition</em> is triggered,
but assumes that the output is a function
of the input (and the state) and that they are
some kind of values.
</p>
<p>
For an Event-Driven State Machine, the input is an event
- that triggers a state transition and the output
- is actions executed during the state transition.
+ that triggers a <em>state transition</em> and the output
+ is actions executed during the <em>state transition</em>.
It can analogously to the mathematical model of a
- Finite-State Machine be described as
+ Finite State Machine be described as
a set of relations of the following form:
</p>
<pre>
State(S) x Event(E) -> Actions(A), State(S')</pre>
- <p>These relations are interpreted as follows:
+ <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>
+ In <c>gen_statem</c> we define
+ a <em>state change</em> as a <em>state transition</em>
+ in which the new state <c>S'</c> is different from
+ the current state <c>S</c>, where "different" means
+ Erlang's strict inequality: <c>=/=</c>
+ also know as "does not match".
+ During a <em>state changes</em>,
+ <c>gen_statem</c> does more things
+ than during other <em>state transitions</em>.
</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
@@ -88,7 +91,95 @@ 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,
+ for all
+ <seealso marker="#Event Types"><em>Event Types</em></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">
+ <em>State Enter Calls</em>
+ </seealso>
+ (callback on state entry co-located with the rest
+ of each state's callback code)
+ </item>
+ <item>
+ Easy-to-use time-outs
+ (<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 <em>callback module</em> contains functions that implement
+ the state machine.
+ When an event occurs,
+ the <c>gen_statem</c> behaviour engine
+ calls a function in the <em>callback module</em> 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 <em>callback module</em>
+ with machine specific events.
</p>
</section>
@@ -98,63 +189,76 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
<marker id="Callback Modes" />
<title>Callback Modes</title>
<p>
- The <c>gen_statem</c> behavior supports two callback modes:
+ The <c>gen_statem</c> behavior supports two <em>callback modes</em>:
</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>
- <p>
- This form is used in most examples here for example in section
- <seealso marker="#Example">Example</seealso>.
+ 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>
- <p>
- See section
- <seealso marker="#One Event Handler">One Event Handler</seealso>
- for an example.
+ Events are handled by one single callback function.
</p>
</item>
- </list>
+ </taglist>
+ <p>
+ The <em>callback mode</em> is selected at server start
+ and may be changed with a code upgrade/downgrade.
+ </p>
+ <p>
+ See the section
+ <seealso marker="#State Callback"><em>State Callback</em></seealso>
+ that describes the event handling callback function(s).
+ </p>
+ <p>
+ The <em>callback mode</em> 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 <em>callback modes</em>.
+ </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 <em>callback mode</em>
+ and the atom <c>state_enter</c> in which case
+ <seealso marker="#State Enter Calls">
+ <em>state enter calls</em>
+ </seealso>
+ are activated for the <em>callback mode</em>.
</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 <em>state callback</em> 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"><em>callback modes</em></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
@@ -167,7 +271,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 <em>callback module</em> 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.
@@ -186,13 +290,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
@@ -208,43 +316,200 @@ handle_event(EventType, EventContent, State, Data) ->
<!-- =================================================================== -->
<section>
- <marker id="State Enter Calls" />
- <title>State Enter Calls</title>
+ <marker id="State Callback" />
+ <title>State Callback</title>
<p>
- The <c>gen_statem</c> behavior can 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 entry actions
- near the rest of the state transition rules.
- It typically looks like this:
+ The <em>state callback</em> is the callback function
+ that handles an event in the current state,
+ and which function that is depends on the <em>callback mode</em>:
</p>
- <pre>
-StateName(enter, _OldState, Data) ->
- ... code for state entry actions here ...
- {keep_state, NewData};
-StateName(EventType, EventContent, Data) ->
- ... code for actions here ...
- {next_state, NewStateName, NewData}.</pre>
+ <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 State Callback">
+ <em>One State Callback</em>
+ </seealso>
+ for an example.
+ </p>
+ </item>
+ </taglist>
<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 Entry Actions">
- State Entry Actions
+ 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>
+ <em>State enter calls</em> are also handled by the event handler
+ and have slightly different arguments. See section
+ <seealso marker="#State Enter Calls">State Enter Calls</seealso>.
+ </p>
+ <p>
+ The <em>state callback</em> return values
+ are defined in the description of
+ <seealso marker="stdlib:gen_statem#Module:StateName/3">
+ <c>Module:StateName/3</c>
</seealso>
- chapter.
+ 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 <em>transition actions</em>.
+ An empty <c>Actions</c> list is equivalent to
+ not returning the field.
+ </p>
+ <p>
+ See section
+ <seealso marker="#Transition Actions">
+ <em>Transition Actions</em>
+ </seealso>
+ for a list of possible
+ <em>transition actions</em>.
+ </p>
+ <p>
+ If <c>NextState =/= State</c> this is a <em>state change</em>
+ so the extra things <c>gen_statem</c> does are: the event queue
+ is restarted from the oldest
+ <seealso marker="#Postponing Events">postponed event</seealso>,
+ any current
+ <seealso marker="#State Time-Outs">state time-out</seealso>
+ is cancelled, and a
+ <seealso marker="#State Enter Calls">state enter call</seealso>
+ is performed, if enabled.
+ </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 <em>state change</em>.
+ </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 <em>state enter call</em>
+ as if this state was entered again.
+ </p>
+ <p>
+ If these return values are used from a
+ <em>state enter call</em> the <c>OldState</c> does not change,
+ but if used from an event handling <em>state callback</em>
+ the new <em>state enter call's</em> <c>OldState</c>
+ will be the current state.
+ </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
+ <seealso marker="#Transition Actions">
+ <em>transition actions</em>
+ </seealso>
+ 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="#State Callback"><em>state callback</em></seealso>
+ is called. This function behaves like an <em>state callback</em>
+ 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="Actions" />
- <title>Actions</title>
+ <marker id="Transition Actions" />
+ <title>Transition Actions</title>
<p>
In the first section
<seealso marker="#Event-Driven State Machines">
@@ -252,76 +517,112 @@ StateName(EventType, EventContent, Data) ->
</seealso>
actions were mentioned as a part of
the general state machine model. These general actions
- are implemented with the code that callback module
+ are implemented with the code that <em>callback module</em>
<c>gen_statem</c> executes in an event-handling
callback function before returning
to the <c>gen_statem</c> engine.
</p>
<p>
- There are more specific state-transition actions
- that a callback function can order the <c>gen_statem</c>
+ There are more specific <em>transition actions</em>
+ that a callback function can command the <c>gen_statem</c>
engine to do after the callback function return.
- These are ordered by returning a list of
+ 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 tuple</seealso>
+ <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 state transition actions affect the <c>gen_statem</c>
- engine itself and can do the following:
+ These are the possible <em>transition actions</em>:
</p>
- <list type="bulleted">
- <item>
+ <taglist>
+ <tag>
<seealso marker="stdlib:gen_statem#type-postpone">
- Postpone
+ <c>postpone</c>
</seealso>
- the current event, see section
- <seealso marker="#Postponing Events">Postponing Events</seealso>
- </item>
+ <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">
- Hibernate
+ <c>hibernate</c>
</seealso>
- the <c>gen_statem</c>, treated in
- <seealso marker="#Hibernation">Hibernation</seealso>
- </item>
+ <br />
+ <c>{hibernate, Boolean}</c>
+ </tag>
<item>
- Start a
+ 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">
- state time-out</seealso>,
- read more in section
- <seealso marker="#State Time-Outs">State Time-Outs</seealso>
+ <c>{state_timeout, EventContent, Time}</c>
+ </seealso>
+ <br />
+ <c>{state_timeout, EventContent, Time, Opts}</c>
+ </tag>
+ <item>
+ Start a state time-out, read more in sections
+ <seealso marker="#Time-Outs">Time-Outs</seealso> and
+ <seealso marker="#State Time-Outs">State Time-Outs</seealso>.
</item>
+ <tag>
+ <seealso marker="stdlib:gen_statem#type-generic_timeout">
+ <c>{{timeout, Name}, EventContent, Time}</c>
+ </seealso>
+ <br />
+ <c>{{timeout, Name}, EventContent, Time, Opts}</c>
+ </tag>
<item>
- Start an
- <seealso marker="stdlib:gen_statem#type-event_timeout">event time-out</seealso>,
- see more in section
- <seealso marker="#Event Time-Outs">Event Time-Outs</seealso>
+ Start a generic time-out, read more in sections
+ <seealso marker="#Time-Outs">Time-Outs</seealso> and
+ <seealso marker="#Generic Time-Outs">Generic Time-Outs</seealso>.
</item>
+ <tag>
+ <seealso marker="stdlib:gen_statem#type-event_timeout">
+ <c>{timeout, EventContent, Time}</c>
+ </seealso>
+ <br />
+ <c>{timeout, EventContent, Time, Opts}</c><br />
+ <c>Time</c>
+ </tag>
<item>
+ Start an event time-out, see more in sections
+ <seealso marker="#Time-Outs">Time-Outs</seealso> and
+ <seealso marker="#Event Time-Outs">Event Time-Outs</seealso>.
+ </item>
+ <tag>
<seealso marker="stdlib:gen_statem#type-reply_action">
- Reply
+ <c>{reply, From, Reply}</c>
</seealso>
- to a caller, mentioned at the end of section
- <seealso marker="#All State Events">All State Events</seealso>
- </item>
+ </tag>
<item>
- Generate the
+ 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">
- next event
+ <c>{next_event, EventType, EventContent}</c>
</seealso>
- to handle, see section
- <seealso marker="#Self-Generated Events">Self-Generated Events</seealso>
+ </tag>
+ <item>
+ Generate the next event to handle, see section
+ <seealso marker="#Inserted Events">Inserted Events</seealso>.
</item>
- </list>
+ </taglist>
<p>
- 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.
+ 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>
@@ -332,58 +633,100 @@ StateName(EventType, EventContent, Data) ->
<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 handled in the same callback function,
- for a given state, and the function gets
+ <seealso marker="stdlib:gen_statem#type-event_type"><em>event types</em></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
+ The following is a complete list of <em>event types</em> and where
they come from:
</p>
<taglist>
- <tag><c>cast</c></tag>
+ <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><c>{call,From}</c></tag>
+ <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
+ when replying either through the <em>transition action</em>
<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>
+ <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><c>state_timeout</c></tag>
+ <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">
+ Generated by <em>transition action</em>
+ <seealso marker="stdlib:gen_statem#type-timeout_action">
<c>{state_timeout,Time,EventContent}</c>
</seealso>
- state timer timing out.
+ state timer timing out. Read more in sections
+ <seealso marker="#Time-Outs">Time-Outs</seealso> and
+ <seealso marker="#State Time-Outs">State Time-Outs</seealso>.
</item>
- <tag><c>timeout</c></tag>
+ <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-event_timeout">
+ Generated by <em>transition action</em>
+ <seealso marker="stdlib:gen_statem#type-timeout_action">
+ <c>{{timeout,Name},Time,EventContent}</c>
+ </seealso>
+ generic timer timing out. Read more in sections
+ <seealso marker="#Time-Outs">Time-Outs</seealso> and
+ <seealso marker="#Generic Time-Outs">Generic Time-Outs</seealso>.
+ </item>
+ <tag>
+ <seealso marker="stdlib:gen_statem#type-timeout_event_type">
+ <c>timeout</c>
+ </seealso>
+ </tag>
+ <item>
+ Generated by <em>transition action</em>
+ <seealso marker="stdlib:gen_statem#type-timeout_action">
<c>{timeout,Time,EventContent}</c>
</seealso>
(or its short form <c>Time</c>)
- event timer timing out.
+ event timer timing out. Read more in sections
+ <seealso marker="#Time-Outs">Time-Outs</seealso> and
+ <seealso marker="#Event Time-Outs">Event Time-Outs</seealso>.
</item>
- <tag><c>internal</c></tag>
+ <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
+ Generated by <em>transition action</em>
+ <seealso marker="stdlib:gen_statem#type-enter_action"><c>{next_event,internal,EventContent}</c></seealso>.
+ All <em>event types</em> above can also be generated using
+ the <c>next_event</c> action:
<c>{next_event,EventType,EventContent}</c>.
</item>
</taglist>
@@ -392,32 +735,184 @@ StateName(EventType, EventContent, Data) ->
<!-- =================================================================== -->
<section>
- <marker id="Example" />
- <title>Example</title>
+ <marker id="State Enter Calls" />
+ <title>State Enter Calls</title>
<p>
- This example starts off as equivalent to the example in section
- <seealso marker="fsm"><c>gen_fsm</c>&nbsp;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.
+ The <c>gen_statem</c> behavior can if this is enabled,
+ regardless of <em>callback mode</em>,
+ 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 <em>state transition</em> 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 <em>state enter call</em> is not an event there are restrictions
+ on the allowed return value and
+ <seealso marker="#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 any events</seealso>.
+ </p>
+ <p>
+ The first state that is entered
+ will get a <em>state enter call</em>
+ with <c>OldState</c> equal to the current state.
+ </p>
+ <p>
+ You may repeat the <em>state enter call</em>
+ using the <c>{repeat_state,...}</c>
+ return value from the
+ <seealso marker="#State Callback">state callback</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 <em>state enter calls</em> in all states.
+ See also the
+ <seealso marker="#State Enter Actions">
+ State Enter Actions
+ </seealso>
+ section.
+ </p>
+ </section>
+
+<!-- =================================================================== -->
+
+ <section>
+ <marker id="Time-Outs" />
+ <title>Time-outs</title>
+ <p>
+ Time-outs in <c>gen_statem</c> are started from a
+ <seealso marker="#Transition Actions">
+ <em>transition action</em>
+ </seealso>
+ during a state transition that is when exiting from the
+ <seealso marker="#State Callback"><em>state callback</em></seealso>.
</p>
<p>
+ There are 3 types of time-outs in <c>gen_statem</c>:
+ </p>
+ <taglist>
+ <tag>
+ <seealso marker="stdlib:gen_statem#type-state_timeout">
+ <c>state_timeout</c>
+ </seealso>
+ </tag>
+ <item>
+ There is one
+ <seealso marker="#State Time-Outs">State Time-Out</seealso>
+ that is automatically cancelled by a <em>state change</em>.
+ </item>
+ <tag>
+ <seealso marker="stdlib:gen_statem#type-generic_timeout">
+ <c>{timeout, Name}</c>
+ </seealso>
+ </tag>
+ <item>
+ There are any number of
+ <seealso marker="#Generic Time-Outs">Generic Time-Outs</seealso>
+ differing by their <c>Name</c>.
+ They have no automatic cancelling.
+ </item>
+ <tag>
+ <seealso marker="stdlib:gen_statem#type-event_timeout">
+ <c>timeout</c>
+ </seealso>
+ </tag>
+ <item>
+ There is one
+ <seealso marker="#Event Time-Outs">Event Time-Out</seealso>
+ that is automatically cancelled by any event.
+ Note that
+ <seealso marker="#Postponing Events">postponed </seealso>
+ and
+ <seealso marker="#Inserted Events">inserted</seealso>
+ events cancel this timeout just as external events.
+ </item>
+ </taglist>
+ <p>
+ When a time-out is started any running time-out with the same tag,
+ <c>state_timeout</c>, <c>{timeout, Name}</c> or <c>timeout</c>,
+ is cancelled, that is the time-out is restarted with the new time.
+ </p>
+ <p>
+ All time-outs has got an <c>EventContent</c> that is part of the
+ <seealso marker="#Transition Actions">
+ <em>transition action</em>
+ </seealso>
+ that starts the time-out.
+ Different <c>EventContent</c>s does not create different time-outs.
+ The <c>EventContent</c> is delivered to the
+ <seealso marker="#State Callback"><em>state callback</em></seealso>
+ when the time-out expires.
+ </p>
+ <section>
+ <marker id="Cancelling a Time-Out" />
+ <title>Cancelling a Time-Out</title>
+ <p>
+ If a time-out is started with the time <c>infinity</c> it will
+ never time out, in fact it will not even be started, and any
+ running time-out with the same tag will be cancelled.
+ The <c>EventContent</c> will in this case be ignored,
+ so why not set it to <c>undefined</c>.
+ </p>
+ </section>
+ <section>
+ <marker id="Time-Out Zero" />
+ <title>Time-Out Zero</title>
+ <p>
+ If a time-out is started with the time <c>0</c> it will
+ actually not be started. Instead the time-out event will
+ immediately be inserted to be processed after any events
+ already enqueued, and before any not yet received external events.
+ Note that some time-outs are automatically cancelled
+ so if you for example combine
+ <seealso marker="#Postponing Events">postponing</seealso>
+ an event in a <em>state change</em> with starting an
+ <seealso marker="#Event Time-Outs">event time-out</seealso>
+ with time <c>0</c> there will be no timeout event inserted
+ since the event time-out is cancelled by the postponed
+ event that is delivered due to the state change.
+ </p>
+ </section>
+ </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:
+ <c>gen_statem</c> with the following <em>callback module</em>:
</p>
<code type="erl"><![CDATA[
-module(code_lock).
@@ -426,43 +921,51 @@ StateName(EventType, EventContent, 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},
+ 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},
- [{state_timeout,10000,lock}];
- [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.
-
+ ]]></code>
+ <code type="erl"><![CDATA[
open(state_timeout, lock, Data) ->
do_lock(),
{next_state, locked, Data};
open(cast, {button,_}, Data) ->
{next_state, open, Data}.
-
+ ]]></code>
+ <code type="erl"><![CDATA[
do_lock() ->
io:format("Lock~n", []).
do_unlock() ->
@@ -471,8 +974,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>
@@ -514,7 +1015,8 @@ start_link(Code) ->
<item>
<p>
The second argument, <c>?MODULE</c>, is the name of
- the callback module, that is, the module where the callback
+ the <em>callback module</em>, that is,
+ the module where the callback
functions are located, which is this module.
</p>
<p>
@@ -548,17 +1050,17 @@ start_link(Code) ->
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>
@@ -576,19 +1078,19 @@ 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>
- for the callback module, in this case
+ for the <em>callback module</em>, 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>
@@ -602,17 +1104,17 @@ callback_mode() ->
<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>,
@@ -621,44 +1123,48 @@ button(Digit) ->
<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>, and <c>Actions</c> is a list of
- actions on the <c>gen_statem</c> engine.
+ 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},
- [{state_timeout,10000,lock}]};
- [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(state_timeout, lock, Data) ->
- do_lock(),
- {next_state, locked, Data};
-open(cast, {button,_}, Data) ->
- {next_state, open, 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.
- </p>
- <p>
- If the whole code is correct, the server changes states
- to <c>open</c>.
+ 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>, a button event is ignored
by staying in the same state. This can also be done
@@ -676,9 +1182,9 @@ open(cast, {button,_}, Data) ->
the following tuple is returned from <c>locked/2</c>:
</p>
<code type="erl"><![CDATA[
-{next_state, open, Data#{remaining := Code},
- [{state_timeout,10000,lock}]};
- ]]></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.
@@ -693,11 +1199,11 @@ open(state_timeout, lock, 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>.
+ when the state machine does a <em>state change</em>.
+ 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>
@@ -713,10 +1219,9 @@ open(state_timeout, lock, 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[
...
@@ -729,16 +1234,46 @@ 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>,
@@ -749,15 +1284,27 @@ handle_event({call,From}, code_length, #{code := Code} = Data) ->
when you want to stay in the current state but do not know or
care about what it is.
</p>
+ <p>
+ If the common <em>state callback</em> 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>
+ <marker id="One State Callback" />
+ <title>One State Callback</title>
<p>
- If mode <c>handle_event_function</c> is used,
+ If
+ <seealso marker="#Callback Modes">
+ <em>callback mode</em>
+ </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
@@ -772,28 +1319,38 @@ 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},
- [{state_timeout,10000,lock}};
- [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 ->
keep_state_and_data
end;
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>
<!-- =================================================================== -->
@@ -825,7 +1382,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>.
@@ -839,7 +1396,7 @@ init(Args) ->
terminate(_Reason, State, _Data) ->
State =/= locked andalso do_lock(),
ok.
- ]]></code>
+ ]]></code>
</section>
<section>
@@ -858,7 +1415,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
@@ -873,57 +1430,66 @@ stop() ->
<marker id="Event Time-Outs" />
<title>Event Time-Outs</title>
<p>
- A timeout feature inherited from <c>gen_statem</c>'s predecessor
+ 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>
- It is ordered by the state transition action
- <c>{timeout,Time,EventContent}</c>, or just <c>Time</c>,
- or even just <c>Time</c> instead of an action list
+ It is ordered by the
+ <seealso marker="#Transition Actions">
+ <em>transition action</em>
+ </seealso>
+ <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>
<p>
- This type of time-out is useful to for example act on inactivity.
+ 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>
<code type="erl"><![CDATA[
...
+locked(timeout, _, Data) ->
+ {next_state, locked, Data#{buttons := []}};
locked(
- timeout, _,
- #{code := Code, remaining := Remaining} = Data) ->
- {next_state, locked, Data#{remaining := Code}};
-locked(
- cast, {button,Digit},
- #{code := Code, remaining := Remaining} = Data) ->
+ cast, {button,Button},
+ #{code := Code, length := Length, buttons := Buttons} = Data) ->
...
- [Digit|Rest] -> % Incomplete
- {next_state, locked, Data#{remaining := Rest}, 30000};
+ true -> % Incomplete | Incorrect
+ {next_state, locked, Data#{buttons := NewButtons},
+ 30000} % Time in milliseconds
...
- ]]></code>
+]]></code>
<p>
- Whenever we receive a button event we start an event timeout
- of 30 seconds, and if we get an event type <c>timeout</c>
+ Whenever we receive a button event we start an event time-out
+ of 30 seconds, and if we get an <em>event type</em> of <c>timeout</c>
we reset the remaining code sequence.
</p>
<p>
- An event timeout is cancelled by any other event so you either
- get some other event or the timeout event. It is therefore
- not possible nor needed to cancel or restart an event timeout.
+ 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 timeout...
+ 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 section
+ <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>
</section>
<!-- =================================================================== -->
<section>
- <marker id="Erlang Timers" />
- <title>Erlang Timers</title>
+ <marker id="Generic Time-Outs" />
+ <title>Generic Time-Outs</title>
<p>
The previous example of state time-outs only work if
the state machine stays in the same state during the
@@ -934,24 +1500,88 @@ locked(
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 Erlang Timers:
- <seealso marker="erts:erlang#start_timer/4"><c>erlang:start_timer3,4</c></seealso>.
+ 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 insted using an Erlang Timer:
+ 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,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}};
+ {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 time-out
+ since the time-out event is the only possible reason to
+ do a <em>state change</em> from <c>open</c> to <c>locked</c>.
+ </p>
+ <p>
+ 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>
+ 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 cannot 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>
+ 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,Button},
+ #{code := Code, length := Length, buttons := Buttons} = Data) ->
+...
+ if
+ NewButtons =:= Code -> % Correct
+ do_unlock(),
+ 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) ->
@@ -960,10 +1590,10 @@ open(info, {timeout,Tref,lock}, #{timer := Tref} = 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
+ do a <em>state change</em> to <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>!
@@ -995,12 +1625,14 @@ open(cast, {button,_}, Data) ->
<p>
If you want to ignore a particular event in the current state
and handle it in a future state, you can postpone the event.
- A postponed event is retried after the state has
- changed, that is, <c>OldState =/= NewState</c>.
+ A postponed event is retried after a <em>state change</em>,
+ that is, <c>OldState =/= NewState</c>.
</p>
<p>
- Postponing is ordered by the state transition
- <seealso marker="stdlib:gen_statem#type-action">action</seealso>
+ Postponing is ordered by the
+ <seealso marker="#Transition Actions">
+ <em>transition action</em>
+ </seealso>
<c>postpone</c>.
</p>
<p>
@@ -1013,15 +1645,19 @@ open(cast, {button,_}, Data) ->
open(cast, {button,_}, Data) ->
{keep_state,Data,[postpone]};
...
- ]]></code>
+]]></code>
<p>
- Since a postponed event is only retried after a state change,
+ Since a postponed event is only retried
+ after a <em>state change</em>,
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 with
- <seealso marker="#Callback Modes">callback mode</seealso>
+ 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"><em>callback mode</em></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.
@@ -1071,28 +1707,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() ->
@@ -1112,15 +1758,17 @@ do_unlock() ->
<seealso marker="stdlib:sys"><c>sys</c></seealso>
compatible behaviors must respond to system messages and therefore
do that in their engine receive loop,
- passing non-system messages to the callback module.
+ passing non-system messages to the <em>callback module</em>.
</p>
<p>
- The state transition
- <seealso marker="stdlib:gen_statem#type-action">action</seealso>
+ The
+ <seealso marker="#Transition Actions">
+ <em>transition action</em>
+ </seealso>
<c>postpone</c> is designed to model
selective receives. A selective receive implicitly postpones
any not received events, but the <c>postpone</c>
- state transition action explicitly postpones one received event.
+ <em>transition action</em> explicitly postpones one received event.
</p>
<p>
Both mechanisms have the same theoretical
@@ -1133,30 +1781,33 @@ do_unlock() ->
<!-- =================================================================== -->
<section>
- <marker id="State Entry Actions" />
- <title>State Entry Actions</title>
+ <marker id="State Enter Actions" />
+ <title>State Enter Actions</title>
<p>
Say you have a state machine specification
- that uses state entry actions.
- Allthough you can code this using self-generated events
+ 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 entry actions,
+ 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>.
+ <seealso marker="#State Enter Calls"><em>state enter calls</em></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>
+ <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.
+ <em>state callback</em> once with an event
+ <c>(enter, OldState, ...)</c>
+ whenever it does a <em>state change</em>.
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},
+ Data = #{code => Code, length = length(Code)},
{ok, locked, Data}.
callback_mode() ->
@@ -1164,24 +1815,26 @@ callback_mode() ->
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] ->
- {next_state, open, Data};
+ 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}]};
+ {keep_state_and_data,
+ [{state_timeout,10000,lock}]}; % Time in milliseconds
open(state_timeout, lock, Data) ->
{next_state, locked, Data};
...
- ]]></code>
+]]></code>
<p>
- You can repeat the state entry code by returning one of
+ 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.
@@ -1196,13 +1849,15 @@ open(state_timeout, lock, Data) ->
<!-- =================================================================== -->
<section>
- <marker id="Self-Generated Events" />
- <title>Self-Generated Events</title>
+ <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="#Transition Actions">
+ <em>transition action</em>
+ </seealso>
<c>{next_event,EventType,EventContent}</c>.
</p>
<p>
@@ -1216,58 +1871,73 @@ open(state_timeout, lock, Data) ->
<p>
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
+ 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 State Callback"><em>one state callback</em></seealso>.
+ The state is then modeled with for example a tuple
+ <c>{MainFSMState,SubFSMState}</c>.
</p>
<p>
- The following example uses an input model where you give the lock
- characters with <c>put_chars(Chars)</c> and then call
- <c>enter()</c> to finish the input.
+ 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[
...
--export(put_chars/1, enter/0).
+-export([down/1, up/1]).
...
-put_chars(Chars) when is_binary(Chars) ->
- gen_statem:call(?NAME, {chars,Chars}).
+down(Button) ->
+ gen_statem:cast(?NAME, {down,Button}).
-enter() ->
- gen_statem:call(?NAME, enter).
+up(Button) ->
+ gen_statem:cast(?NAME, {up,Button}).
...
locked(enter, _OldState, Data) ->
do_lock(),
- {keep_state,Data#{remaining => Code, buf => []}};
+ {keep_state,Data#{buttons => []}};
+locked(
+ internal, {button,Button},
+ #{code := Code, length := Length, buttons := Buttons} = Data) ->
...
-
-handle_event({call,From}, {chars,Chars}, #{buf := Buf} = Data) ->
- {keep_state, Data#{buf := [Chars|Buf],
- [{reply,From,ok}]};
-handle_event({call,From}, enter, #{buf := Buf} = Data) ->
- Chars = unicode:characters_to_binary(lists:reverse(Buf)),
- try binary_to_integer(Chars) of
- Digit ->
- {keep_state, Data#{buf := []},
- [{reply,From,ok},
- {next_event,internal,{button,Chars}}]}
- catch
- error:badarg ->
- {keep_state, Data#{buf := []},
- [{reply,From,{error,not_an_integer}}]}
+]]></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;
...
- ]]></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:put_chars(&lt;&lt;"001">>),
- code_lock:put_chars(&lt;&lt;"7">>), code_lock:enter()</c>.
+ you can unlock with <c>code_lock:down(17), code_lock:up(17).</c>
</p>
</section>
@@ -1278,17 +1948,19 @@ handle_event({call,From}, enter, #{buf := Buf} = Data) ->
<title>Example Revisited</title>
<p>
This section includes the example after most of the mentioned
- modifications and some more using state enter calls,
+ modifications and some more using <em>state enter calls</em>,
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>
@@ -1305,8 +1977,8 @@ handle_event({call,From}, enter, #{buf := Buf} = 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) ->
@@ -1314,52 +1986,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},
+ Data = #{code => Code, length => length(Code), buttons => []},
{ok, locked, Data}.
callback_mode() ->
[state_functions,state_enter].
-locked(enter, _OldState, #{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(
- timeout, _,
- #{code := Code, remaining := Remaining} = Data) ->
- {keep_state, Data#{remaining := Code}};
-locked(
- cast, {button,Digit},
- #{code := Code, remaining := Remaining} = Data) ->
- case Remaining of
- [Digit] -> % Complete
+ 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};
- [Digit|Rest] -> % Incomplete
- {keep_state, Data#{remaining := Rest}, 30000};
- [_|_] -> % Wrong
- {keep_state, Data#{remaining := Code}}
+ 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).
-
+?HANDLE_COMMON.
+]]></code>
+ <code type="erl"><![CDATA[
open(enter, _OldState, _Data) ->
do_unlock(),
- {keep_state_and_data, [{state_timeout,10000,lock}]};
+ {keep_state_and_data,
+ [{state_timeout,10000,lock}]}; % Time in milliseconds
open(state_timeout, lock, Data) ->
{next_state, locked, Data};
-open(cast, {button,_}, _) ->
+open(internal, {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)}]}.
+?HANDLE_COMMON.
do_lock() ->
io:format("Locked~n", []).
@@ -1369,9 +2063,7 @@ 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>
@@ -1381,58 +2073,76 @@ code_change(_Vsn, State, Data, _Extra) ->
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 state enter calls,
+ does not work that well here
+ because of the <em>state enter calls</em>,
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,state_enter].
-
+ ]]></code>
+ <code type="erl"><![CDATA[
+%%
%% State: locked
-handle_event(
- enter, _OldState, 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(
- timeout, _, locked,
- #{code := Code, remaining := Remaining} = Data) ->
- {keep_state, Data#{remaining := Code}};
-handle_event(
- cast, {button,Digit}, locked,
- #{code := Code, remaining := Remaining} = Data) ->
- case Remaining of
- [Digit] -> % Complete
+ 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};
- [Digit|Rest] -> % Incomplete
- {keep_state, Data#{remaining := Rest}, 30000};
- [_|_] -> % Wrong
- {keep_state, Data#{remaining := Code}}
+ 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}]};
+ {keep_state_and_data,
+ [{state_timeout,10000,lock}]}; % Time in milliseconds
handle_event(state_timeout, lock, open, Data) ->
{next_state, locked, Data};
-handle_event(cast, {button,_}, open, _) ->
+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 a strange 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>
@@ -1469,7 +2179,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]) ->
@@ -1477,7 +2187,6 @@ format_status(Opt, [_PDict,State,Data]) ->
{State,
maps:filter(
fun (code, _) -> false;
- (remaining, _) -> false;
(_, _) -> true
end,
Data)},
@@ -1504,17 +2213,19 @@ format_status(Opt, [_PDict,State,Data]) ->
<marker id="Complex State" />
<title>Complex State</title>
<p>
- The callback mode
+ The <em>callback mode</em>
<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>,
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,
@@ -1523,33 +2234,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 <em>state change</em>
+ and all postponed events are retried,
+ therefore the lock is immediately locked!
</p>
<p>
We define the state as <c>{StateName,LockButton}</c>,
@@ -1562,8 +2273,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) ->
@@ -1572,77 +2283,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},
+ Data = #{code => Code, length => length(Code), buttons => []},
{ok, {locked,LockButton}, Data}.
callback_mode() ->
[handle_event_function,state_enter].
-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)}]};
-%%
%% 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(
- EventType, EventContent,
- {locked,LockButton}, #{code := Code, remaining := Remaining} = Data) ->
- case {EventType, EventContent} of
- {enter, _OldState} ->
- do_lock(),
- {keep_state, Data#{remaining := Code}};
- {timeout, _} ->
- {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, 30000},
- [{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}, Data) ->
- case {EventType, EventContent} of
- {enter, _OldState} ->
- do_unlock(),
- {keep_state_and_data, [{state_timeout,10000,lock}]};
- {state_timeout, lock} ->
- {next_state, {locked,LockButton}, Data};
- {{call,From}, {button,Digit}} ->
- if
- Digit =:= LockButton ->
- {next_state, {locked,LockButton}, Data,
- [{reply,From,locked}]);
- true ->
- {keep_state_and_data,
- [postpone]}
- end
- end.
-
+ {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() ->
@@ -1651,29 +2353,7 @@ 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>
<!-- =================================================================== -->
@@ -1705,17 +2385,15 @@ format_status(Opt, [_PDict,State,Data]) ->
</p>
<code type="erl"><![CDATA[
...
+%%
%% State: open
-handle_event(
- EventType, EventContent,
- {open,LockButton}, Data) ->
- case {EventType, EventContent} of
- {enter, _OldState} ->
- do_unlock(),
- {keep_state_and_data,
- [{state_timeout,10000,lock},hibernate]};
+handle_event(enter, _OldState, {open,_}, _Data) ->
+ do_unlock(),
+ {keep_state_and_data,
+ [{state_timeout,10000,lock}, % Time in milliseconds
+ hibernate]};
...
- ]]></code>
+]]></code>
<p>
The atom
<seealso marker="stdlib:gen_statem#type-hibernate"><c>hibernate</c></seealso>
@@ -1728,20 +2406,35 @@ 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>
- Another not uncommon scenario is to use the event time-out
- to triger hibernation after a certain time of inactivity.
+ 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-enter_loop_opt">
+ <c>{hibernate_after, Timeout}</c>
+ </seealso>
+ for
+ <seealso marker="stdlib:gen_statem#start/3"><c>start/3,4</c></seealso>,
+ <seealso marker="stdlib:gen_statem#start_link/3">
+ <c>start_link/3,4</c>
+ </seealso>
+ or
+ <seealso marker="stdlib:gen_statem#enter_loop/4">
+ <c>enter_loop/4,5,6</c>
+ </seealso>
+ that may be used to automatically hibernate the server.
</p>
<p>
- This server probably does not use
+ 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
deleted file mode 100644
index 96af52360e..0000000000
--- a/system/doc/design_principles/warning.gif
+++ /dev/null
Binary files differ
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..a2742a1354 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
# ----------------------------------------------------
@@ -86,7 +87,6 @@ DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-_create_dirs := $(shell mkdir -p $(HTMLDIR))
docs: html
@@ -97,7 +97,8 @@ html: $(GIF_FILES) $(HTML_UG_FILE)
debug opt:
clean clean_docs:
- rm -rf $(HTMLDIR)
+ rm -f $(XMLDIR)/*.xml
+ rm -f $(HTMLDIR)/*.gif $(HTMLDIR)/*.html
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 e1760d0ded..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>
@@ -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..d92da17390 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>
@@ -440,8 +384,8 @@ export ERL_COMPILER_OPTIONS=bin_opt_info]]></code>
<p>The warnings look as follows:</p>
<code type="erl"><![CDATA[
-./efficiency_guide.erl:60: Warning: NOT OPTIMIZED: sub binary is used or returned
-./efficiency_guide.erl:62: Warning: OPTIMIZED: creation of sub binary delayed]]></code>
+./efficiency_guide.erl:60: Warning: NOT OPTIMIZED: binary is returned from the function
+./efficiency_guide.erl:62: Warning: OPTIMIZED: match context reused]]></code>
<p>To make it clearer exactly what code the warnings refer to, the
warnings in the following examples are inserted as comments
@@ -449,10 +393,10 @@ export ERL_COMPILER_OPTIONS=bin_opt_info]]></code>
<code type="erl"><![CDATA[
after_zero(<<0,T/binary>>) ->
- %% NOT OPTIMIZED: sub binary is used or returned
+ %% BINARY CREATED: binary is returned from the function
T;
after_zero(<<_,T/binary>>) ->
- %% OPTIMIZED: creation of sub binary delayed
+ %% OPTIMIZED: match context reused
after_zero(T);
after_zero(<<>>) ->
<<>>.]]></code>
@@ -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..7b00fa5d63 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>
@@ -36,10 +36,9 @@
<title>Timer Module</title>
<p>Creating timers using <seealso
- marker="erts:erlang#erlang:send_after/3">erlang:send_after/3</seealso>
+ marker="erts:erlang#send_after/3">erlang:send_after/3</seealso>
and
- <seealso marker="erts:erlang#erlang:start_timer/3">erlang:start_timer/3</seealso>
-,
+ <seealso marker="erts:erlang#start_timer/3">erlang:start_timer/3</seealso>,
is much more efficient than using the timers provided by the
<seealso marker="stdlib:timer">timer</seealso> module in STDLIB.
The <c>timer</c> module uses a separate process to manage the timers.
@@ -63,7 +62,7 @@
dangerous in a system that runs continuously.
If only certain well-defined atoms are allowed as input,
<seealso marker="erts:erlang#list_to_existing_atom/1">list_to_existing_atom/1</seealso>
- can be used to
+ can be used
to guard against a denial-of-service attack. (All atoms that are allowed
must have been created earlier, for example, by simply using all of them
in a module and loading that module.)</p>
@@ -148,10 +147,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 +168,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&lt;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..1604075312 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
# ----------------------------------------------------
@@ -74,7 +75,6 @@ DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-_create_dirs := $(shell mkdir -p $(HTMLDIR))
docs: html
@@ -85,7 +85,8 @@ html: $(GIF_FILES) $(HTML_UG_FILE)
debug opt:
clean clean_docs:
- rm -rf $(HTMLDIR)
+ rm -f $(XMLDIR)/*.xml
+ rm -f $(HTMLDIR)/*.gif $(HTMLDIR)/*.html
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
deleted file mode 100644
index 6fffe30419..0000000000
--- a/system/doc/embedded/note.gif
+++ /dev/null
Binary files differ
diff --git a/system/doc/embedded/starting.xml b/system/doc/embedded/starting.xml
index 11bf9b412a..6888f9c959 100644
--- a/system/doc/embedded/starting.xml
+++ b/system/doc/embedded/starting.xml
@@ -231,7 +231,7 @@ exec $BINDIR/erlexec -boot $RELDIR/$VSN/start -config $RELDIR/$VSN/sys $* <
<p>If a diskless and/or read-only client node with the SASL
configuration parameter <c>static_emulator</c> set to <c>true</c>
is about to start the <c>-boot</c> and <c>-config</c> flags must be
- changed. As such a client can not read a new <c>start_erl.data</c>
+ changed. As such a client cannot read a new <c>start_erl.data</c>
file (the file is not possible to change dynamically) the boot and
config files is always fetched from the same place (but with a new
contents if a new release has been installed). The
diff --git a/system/doc/embedded/warning.gif b/system/doc/embedded/warning.gif
deleted file mode 100644
index 96af52360e..0000000000
--- a/system/doc/embedded/warning.gif
+++ /dev/null
Binary files differ
diff --git a/system/doc/general_info/Makefile b/system/doc/general_info/Makefile
new file mode 100644
index 0000000000..539075280e
--- /dev/null
+++ b/system/doc/general_info/Makefile
@@ -0,0 +1,99 @@
+#
+# %CopyrightBegin%
+#
+# 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.
+# 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.
+#
+# %CopyrightEnd%
+#
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include $(ERL_TOP)/erts/vsn.mk
+#VSN=$(SYSTEM_VSN)
+
+APPLICATION=otp-system-documentation
+XMLDIR := $(XMLDIR)/general_info
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = "$(RELEASE_PATH)/doc/general_info"
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+XML_PART_FILES = part.xml
+
+include xmlfiles.mk
+
+XML_CHAPTER_FILES=$(GENERAL_INFO_CHAPTER_FILES)
+
+TOPDOCDIR=..
+
+BOOK_FILES = book.xml
+
+GIF_FILES =
+
+XML_FILES = \
+ $(BOOK_FILES) $(XML_CHAPTER_FILES) \
+ $(XML_PART_FILES)
+
+# ----------------------------------------------------
+
+HTMLDIR = ../html/general_info
+
+HTML_UG_FILE = $(HTMLDIR)/users_guide.html
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+XML_FLAGS +=
+DVIPS_FLAGS +=
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+docs: html
+
+local_docs: PDFDIR=../../pdf
+
+html: $(GIF_FILES) $(HTML_UG_FILE)
+
+debug opt:
+
+clean clean_docs:
+ rm -f $(XMLDIR)/*.xml
+ rm -f $(HTMLDIR)/*.gif $(HTMLDIR)/*.html
+ rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
+ rm -f errs core *~
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_docs_spec: docs
+# $(INSTALL_DIR) "$(RELEASE_PATH)/pdf"
+# $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELEASE_PATH)/pdf"
+ $(INSTALL_DIR) $(RELSYSDIR)
+ $(INSTALL_DATA) $(GIF_FILES) $(HTMLDIR)/*.html \
+ $(RELSYSDIR)
+
+release_spec:
+
diff --git a/system/doc/general_info/book.xml b/system/doc/general_info/book.xml
new file mode 100644
index 0000000000..ca9b5fae39
--- /dev/null
+++ b/system/doc/general_info/book.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE book SYSTEM "book.dtd">
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header titlestyle="normal">
+ <copyright>
+ <year>2019</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>General Information</title>
+ <prepared>OTP Team</prepared>
+ <docno></docno>
+ <date>2019-02-23</date>
+ <rev></rev>
+ <file>book.xml</file>
+ </header>
+ <insidecover>
+ </insidecover>
+ <pagetext>General Information</pagetext>
+ <preamble>
+ <contents level="2"></contents>
+ </preamble>
+ <parts lift="no">
+ <xi:include href="part.xml"/>
+ </parts>
+ <listofterms></listofterms>
+ <index></index>
+</book>
+
diff --git a/system/doc/general_info/deprecations.xml b/system/doc/general_info/deprecations.xml
new file mode 100644
index 0000000000..c748e60059
--- /dev/null
+++ b/system/doc/general_info/deprecations.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2019</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>Deprecations</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ <file>deprecations.xml</file>
+ </header>
+ <section>
+ <title>Introduction</title>
+ <p>This document aim to list all deprecated functionality in Erlang/OTP. It
+ was introduced as of OTP 22, and have not yet been updated with all old
+ deprecations. Deprecations made in other parts of the documentation are of
+ course still valid. For more information regarding the strategy regarding
+ deprecations see the documentation of
+ <seealso marker="../system_principles/misc#deprecation">Support, Compatibility,
+ Deprecations, and Removal</seealso>.</p>
+ </section>
+ <section>
+ <marker id="OTP-22"/>
+ <title>OTP 22</title>
+ <section>
+ <title>VxWorks Support</title>
+ <p>Some parts of OTP has had limited VxWorks support, such as for
+ example <seealso marker="erl_interface:index"><c>erl_interface</c></seealso>.
+ This support is now deprecated and has also been
+ <seealso marker="scheduled_for_removal#OTP-23">scheduled for removal</seealso>.</p>
+ </section>
+ <section>
+ <title>Legacy parts of erl_interface</title>
+ <p>The old legacy <seealso marker="erl_interface:index"><c>erl_interface</c></seealso>
+ library (functions with prefix <c>erl_</c>) is deprecated as of OTP 22. These
+ parts of <c>erl_interface</c> has been informally deprecated
+ for a very long time. You typically want to replace the usage of
+ the <c>erl_interface</c> library with the use of the <c>ei</c> library
+ which also is part of the <c>erl_interface</c> application. The old legacy
+ <seealso marker="erl_interface:index"><c>erl_interface</c></seealso>
+ library has also been <seealso marker="scheduled_for_removal#OTP-23">scheduled
+ for removal</seealso>.</p>
+ </section>
+ <section>
+ <title>System Events</title>
+ <p>
+ The format of "System Events" as defined in the man page for
+ <seealso marker="stdlib:sys">sys</seealso>
+ has been clarified and cleaned up.
+ Due to this, code that relied on the internal badly
+ documented previous (before this change) format
+ of OTP's "System Events", needs to be changed.
+ </p>
+ <p>
+ In the wake of this the function
+ <seealso marker="stdlib:sys#get_debug/3">sys:get_debug/3</seealso>
+ that returns data with undocumented and internal format
+ (and therefore is practically useless) has been deprecated,
+ and a new function
+ <seealso marker="stdlib:sys#get_log/1">sys:get_log/1</seealso>
+ has been added,
+ that hopefully does what the deprecated function was intended for.
+ </p>
+ </section>
+ </section>
+ <section>
+ <marker id="OTP-18"/>
+ <title>OTP 18</title>
+ <section>
+ <title>erlang:now()</title>
+ <p>New time functionality and a new time API was introduced. For more information
+ see the <seealso marker="erts:time_correction">Time and Time Correction</seealso>
+ chapter in the ERTS User's guide and specifically the
+ <seealso marker="erts:time_correction#Dos_and_Donts">Dos and Donts</seealso>
+ section on how to replace usage of <c>erlang:now()</c>.</p>
+ </section>
+ </section>
+</chapter>
diff --git a/system/doc/general_info/part.xml b/system/doc/general_info/part.xml
new file mode 100644
index 0000000000..fead7d58e7
--- /dev/null
+++ b/system/doc/general_info/part.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE part SYSTEM "part.dtd">
+
+<part xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>2019</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>General Information</title>
+ <prepared>OTP Team</prepared>
+ <docno></docno>
+ <date>2019-02-23</date>
+ <rev></rev>
+ <file>part.xml</file>
+ </header>
+ <xi:include href="deprecations.xml"/>
+ <xi:include href="scheduled_for_removal.xml"/>
+</part>
diff --git a/system/doc/general_info/scheduled_for_removal.xml b/system/doc/general_info/scheduled_for_removal.xml
new file mode 100644
index 0000000000..a9421df385
--- /dev/null
+++ b/system/doc/general_info/scheduled_for_removal.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2019</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>Scheduled for Removal</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ <file>scheduled_for_removal.xml</file>
+ </header>
+ <section>
+ <title>Introduction</title>
+ <p>This document list all functionality in Erlang/OTP that currently are
+ scheduled for removal. For more information regarding the strategy regarding
+ removal of functionality see the documentation of
+ <seealso marker="../system_principles/misc#removal">Support, Compatibility,
+ Deprecations, and Removal</seealso>.</p>
+ </section>
+ <section>
+ <marker id="OTP-23"/>
+ <title>OTP 23</title>
+ <section>
+ <title>VxWorks Support</title>
+ <p>Some parts of OTP has had limited VxWorks support, such as for
+ example <seealso marker="erl_interface:index"><c>erl_interface</c></seealso>.
+ This support will be removed as of OTP 23. This limited support
+ was formally deprecated as of OTP 22</p>
+ </section>
+ <section>
+ <title>Legacy parts of erl_interface</title>
+ <p>The old legacy <seealso marker="erl_interface:index"><c>erl_interface</c></seealso>
+ library (functions with prefix <c>erl_</c>) will be removed as of
+ OTP 23. These parts of <c>erl_interface</c> has been informally deprecated
+ for a very long time, and was formally deprecated in OTP 22. You typically
+ want to replace the usage of the <c>erl_interface</c> library with the use
+ of the <c>ei</c> library which also is part of the <c>erl_interface</c>
+ application.</p>
+ </section>
+ </section>
+</chapter>
diff --git a/system/doc/general_info/xmlfiles.mk b/system/doc/general_info/xmlfiles.mk
new file mode 100644
index 0000000000..eab1632a4f
--- /dev/null
+++ b/system/doc/general_info/xmlfiles.mk
@@ -0,0 +1,22 @@
+#
+# %CopyrightBegin%
+#
+# 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.
+# 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.
+#
+# %CopyrightEnd%
+#
+GENERAL_INFO_CHAPTER_FILES = \
+ deprecations.xml \
+ scheduled_for_removal.xml
diff --git a/system/doc/getting_started/Makefile b/system/doc/getting_started/Makefile
index 1fe3d39e4e..1c917895d5 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
# ----------------------------------------------------
@@ -73,7 +74,6 @@ DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-_create_dirs := $(shell mkdir -p $(HTMLDIR))
docs: html
@@ -84,7 +84,8 @@ html: $(GIF_FILES) $(HTML_UG_FILE)
debug opt:
clean clean_docs:
- rm -rf $(HTMLDIR)
+ rm -f $(XMLDIR)/*.xml
+ rm -f $(HTMLDIR)/*.gif $(HTMLDIR)/*.html
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/html/design_principles/.gitignore b/system/doc/html/design_principles/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/design_principles/.gitignore
diff --git a/system/doc/html/efficiency_guide/.gitignore b/system/doc/html/efficiency_guide/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/efficiency_guide/.gitignore
diff --git a/system/doc/html/embedded/.gitignore b/system/doc/html/embedded/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/embedded/.gitignore
diff --git a/system/doc/html/general_info/.gitignore b/system/doc/html/general_info/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/general_info/.gitignore
diff --git a/system/doc/html/getting_started/.gitignore b/system/doc/html/getting_started/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/getting_started/.gitignore
diff --git a/system/doc/html/installation_guide/.gitignore b/system/doc/html/installation_guide/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/installation_guide/.gitignore
diff --git a/system/doc/html/installation_guide/source/.gitignore b/system/doc/html/installation_guide/source/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/installation_guide/source/.gitignore
diff --git a/system/doc/html/js/.gitignore b/system/doc/html/js/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/js/.gitignore
diff --git a/system/doc/html/oam/.gitignore b/system/doc/html/oam/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/oam/.gitignore
diff --git a/system/doc/html/programming_examples/.gitignore b/system/doc/html/programming_examples/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/programming_examples/.gitignore
diff --git a/system/doc/html/reference_manual/.gitignore b/system/doc/html/reference_manual/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/reference_manual/.gitignore
diff --git a/system/doc/html/system_architecture_intro/.gitignore b/system/doc/html/system_architecture_intro/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/system_architecture_intro/.gitignore
diff --git a/system/doc/html/system_principles/.gitignore b/system/doc/html/system_principles/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/system_principles/.gitignore
diff --git a/system/doc/html/tutorial/.gitignore b/system/doc/html/tutorial/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/html/tutorial/.gitignore
diff --git a/system/doc/installation_guide/Makefile b/system/doc/installation_guide/Makefile
index 673c203422..c5234c1c9a 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,11 +87,10 @@ 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
- test -d $(REDIRECT_HTML_DIR) || $(INSTALL_DIR) $(REDIRECT_HTML_DIR)
echo "<html><head><meta HTTP-EQUIV=\"REFRESH\"" > $@
echo " content=\"5; url=../"$(notdir $@)"\">" >> $@
echo "<title>This page has moved</title></head><body>" >> $@
@@ -102,17 +100,20 @@ $(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 -f $(XMLDIR)/*.xml
+ rm -f $(HTMLDIR)/*.gif $(HTMLDIR)/*.html
+ rm -f $(XML_GEN_FILES)
rm -rf $(HTMLDIR)
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
deleted file mode 100644
index 6fffe30419..0000000000
--- a/system/doc/installation_guide/note.gif
+++ /dev/null
Binary files differ
diff --git a/system/doc/installation_guide/warning.gif b/system/doc/installation_guide/warning.gif
deleted file mode 100644
index 96af52360e..0000000000
--- a/system/doc/installation_guide/warning.gif
+++ /dev/null
Binary files differ
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..2eb429e04d 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
# ----------------------------------------------------
@@ -70,10 +71,9 @@ DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-_create_dirs := $(shell mkdir -p $(HTMLDIR))
$(HTMLDIR)/%.gif: %.gif
- $(INSTALL_DATA) $< $@
+ $(CP) $< $@
docs: html
@@ -86,7 +86,8 @@ gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
debug opt:
clean clean_docs:
- rm -rf $(HTMLDIR)
+ rm -f $(XMLDIR)/*.xml
+ rm -f $(HTMLDIR)/*.gif $(HTMLDIR)/*.html
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
deleted file mode 100644
index 6fffe30419..0000000000
--- a/system/doc/oam/note.gif
+++ /dev/null
Binary files differ
diff --git a/system/doc/oam/oam_intro.xml b/system/doc/oam/oam_intro.xml
index 8b8d69e638..3d08a5f3b1 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>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -178,7 +178,7 @@
<section>
<title>MIB Structure</title>
<p>The top-level OTP MIB is called <c>OTP-REG</c> and it is
- included in the SASL application. All other OTP MIBs
+ included in the SNMP application. All other OTP MIBs
import some objects from this MIB.</p>
<p>Each MIB is contained in one application. The MIB text
@@ -186,67 +186,44 @@
the application directory. The generated <c>.hrl</c> files
with constant declarations are stored under
<c><![CDATA[include/<MIB>.hrl]]></c>, and the compiled MIBs
- are stored under <c><![CDATA[priv/mibs/<MIB>.bin]]></c>.
- For example, the <c>OTP-MIB</c> is included in the
- SASL application:</p>
+ are stored under <c><![CDATA[priv/mibs/<MIB>.bin]]></c>. </p>
- <code type="none">
-sasl-1.3/mibs/OTP-MIB.mib
-include/OTP-MIB.hrl
-priv/mibs/OTP-MIB.bin</code>
-
- <p>An application that needs to import this MIB into another
+ <p>An application that needs to import an MIB into another
MIB is to use the <c>il</c> option to the SNMP MIB compiler:</p>
<code type="none">
-snmp:c("MY-MIB", [{il, ["sasl/priv/mibs"]}]).</code>
+snmp:c("MY-MIB", [{il, ["snmp/priv/mibs"]}]).</code>
- <p>If the application needs to include the generated
+ <p>If the application needs to include a generated
<c>.hrl</c> file, it is to use the <c>-include_lib</c>
directive to the Erlang compiler:</p>
<code type="none">
-module(my_mib).
--include_lib("sasl/include/OTP-MIB.hrl").</code>
+-include_lib("snmp/include/OTP-REG.hrl").</code>
- <p>The following MIBs are defined in the OTP system:</p>
+ <p>Here is a list of some of the MIBs 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 SNMP) 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
+ <item><p><c>OTP-TC</c> (in SNMP) contains the general
Textual Conventions, which can be used by any other MIB.</p></item>
- <item><p><c>OTP-MIB</c> (in SASL) contains objects for
- instrumentation of the Erlang nodes, the Erlang machines,
- and the applications in the system.</p></item>
- <item><p><c>OTP-OS-MON-MIB</c> (in <c>oc_mon</c>) contains
- objects for instrumentation of disk, memory, and CPU use
- of the nodes in the system.</p></item>
<item><p><c>OTP-SNMPEA-MIB</c> (in <c>snmp</c>)
contains objects for instrumentation and control of the extensible
SNMP agent itself. The agent also implements the standard SNMPv2-MIB
(or v1 part of MIB-II, if SNMPv1 is used).</p></item>
- <item><p><c>OTP-EVA-MIB</c> (in <c>eva</c>) contains objects
- for instrumentation and control of the events and alarms in
- the system.</p></item>
- <item><p><c>OTP-LOG-MIB</c> (in <c>eva</c>) contains objects
- for instrumentation and control of the logs and FTP transfer of
- logs.</p></item>
- <item><p><c>OTP-EVA-LOG-MIB</c> (in <c>eva</c>) contains objects
- for instrumentation and control of the events and alarm logs
- in the system.</p></item>
- <item><p><c>OTP-SNMPEA-LOG-MIB</c> (in <c>eva</c>) contains
- objects for instrumentation and control of the SNMP audit
- trail log in the system.</p></item>
</list>
<p>The different applications use different strategies for
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
- manual page for each application for a description of how
- to load each MIB.</p>
+ function such as
+ <c>snmpa:unload_mibs(Agent, [Mib])</c>
+ to load the MIB, and
+ <c>snmpa:unload_mibs(Agent, [Mib])</c>
+ to unload the MIB. See the manual page for each application for
+ a description of how to load each MIB.</p>
</section>
</section>
</chapter>
diff --git a/system/doc/oam/warning.gif b/system/doc/oam/warning.gif
deleted file mode 100644
index 96af52360e..0000000000
--- a/system/doc/oam/warning.gif
+++ /dev/null
Binary files differ
diff --git a/system/doc/programming_examples/Makefile b/system/doc/programming_examples/Makefile
index 237076d770..9c67c24b64 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 = \
@@ -71,7 +74,6 @@ DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-_create_dirs := $(shell mkdir -p $(HTMLDIR))
docs: html
local_docs: PDFDIR=../../pdf
@@ -81,7 +83,8 @@ html: $(GIF_FILES) $(HTML_UG_FILE)
debug opt:
clean clean_docs:
- rm -rf $(HTMLDIR)
+ rm -f $(XMLDIR)/*.xml
+ rm -f $(HTMLDIR)/*.gif $(HTMLDIR)/*.html
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..809eb2c979 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
# ----------------------------------------------------
@@ -83,7 +84,6 @@ DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-_create_dirs := $(shell mkdir -p $(HTMLDIR))
docs: html
@@ -94,7 +94,8 @@ html: $(GIF_FILES) $(HTML_UG_FILE)
debug opt:
clean clean_docs:
- rm -rf $(HTMLDIR)
+ rm -f $(XMLDIR)/*.xml
+ rm -f $(HTMLDIR)/*.gif $(HTMLDIR)/*.html
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/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
&lt;&lt;"77">>
16> <input>float_to_binary(7.0).</input>
&lt;&lt;"7.00000000000000000000e+00">>
-17> <input>binary_to_float(&lt;&lt;"7.000e+00>>").</input>
+17> <input>binary_to_float(&lt;&lt;"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 &gt; 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..ea3b2159fc 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 &lt; atom &lt; reference &lt; fun &lt; port &lt; pid &lt; tuple &lt; map &lt; nil &lt; list &lt; 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 &lt; 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>
@@ -576,6 +578,7 @@ number &lt; atom &lt; reference &lt; fun &lt; port &lt; pid &lt; tuple &lt; map
ascending term order and then by values in key order.
In maps key order integers types are considered less than floats types.
</p>
+ <p>Atoms are compared using their string value, codepoint by codepoint.</p>
<p>When comparing an integer to a float, the term with the lesser
precision is converted into the type of the other term, unless the
operator is one of <c>=:=</c> or <c>=/=</c>. A float is more precise than
@@ -1218,10 +1221,10 @@ Ei = Value |
&lt;&lt;1,17,0,42&gt;&gt;
10> <input>H.</input>
&lt;&lt;17,0,42&gt;&gt;
-11> <input>&lt;&lt;G,H/bitstring&gt;&gt; = &lt;&lt;1,17,42:12&gt;&gt;.</input>
-&lt;&lt;1,17,1,10:4&gt;&gt;
-12> <input>H.</input>
-&lt;&lt;17,1,10:4&gt;&gt;
+11> <input>&lt;&lt;G,J/bitstring&gt;&gt; = &lt;&lt;1,17,42:12&gt;&gt;.</input>
+&lt;&lt;1,17,2,10:4&gt;&gt;
+12> <input>J.</input>
+&lt;&lt;17,2,10:4&gt;&gt;
13> <input>&lt;&lt;1024/utf8&gt;&gt;.</input>
&lt;&lt;208,128&gt;&gt;
</pre>
@@ -1342,14 +1345,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 +1367,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 +1380,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 +1403,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 +1427,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 +1489,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 5f24473557..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>2016</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 a0ea41cb3b..f517259a64 100644
--- a/system/doc/reference_manual/typespec.xml
+++ b/system/doc/reference_manual/typespec.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2016</year>
+ <year>2003</year><year>2019</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -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,35 @@
| 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() %% denotes a map of any size
- | #{} %% denotes the empty map
- | #{PairList}
+ Map :: #{} %% denotes the empty map
+ | #{AssociationList}
Tuple :: tuple() %% denotes a tuple of any size
| {}
| {TList}
- PairList :: Pair
- | Pair, PairList
+ AssociationList :: Association
+ | Association, AssociationList
- Pair :: Type := Type %% denotes a mandatory pair
- | Type => Type %% denotes an optional pair
+ Association :: Type := Type %% denotes a mandatory association
+ | Type => Type %% denotes an optional association
TList :: Type
| Type, TList
@@ -151,8 +159,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>&lt;&lt;_:M, _:_*N&gt;&gt;</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).
@@ -173,19 +186,21 @@
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. A <c>PairList</c> may contain
- both 'mandatory' and 'optional' pairs where 'mandatory' denotes that
- a key type, and its associated value type, must be present.
- In the case of an 'optional' pair it is not required for the key type to
- be present.
+ 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 <c>(:=)</c> and
+ optional <c>(=&gt;)</c> association types.
+ If an association type is mandatory, an association with that type
+ needs 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
- <c>#{any() =&gt; any()}</c> (or <c>#{_ =&gt; _}</c>), not <c>#{}</c>.
The notation <c>#{}</c> specifies the singleton type for the empty map.
+ Note that this notation is not a shorthand for the <c>map()</c> type.
</p>
<p>
For convenience, the following types are also built-in.
@@ -242,6 +257,9 @@
<cell><c>iolist()</c></cell><cell><c>maybe_improper_list(byte() | binary() | iolist(), binary() | [])</c></cell>
</row>
<row>
+ <cell><c>map()</c></cell><cell><c>#{any() =&gt; any()}</c></cell>
+ </row>
+ <row>
<cell><c>function()</c></cell><cell><c>fun()</c></cell>
</row>
<row>
@@ -512,8 +530,8 @@
<p>
Currently, the <c>::</c> constraint
(read as &laquo;is a subtype of&raquo;) 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>
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..ea9ee85105 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
# ----------------------------------------------------
@@ -68,7 +69,6 @@ DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-_create_dirs := $(shell mkdir -p $(HTMLDIR))
docs: html
@@ -79,7 +79,8 @@ html: $(GIF_FILES) $(HTML_UG_FILE)
debug opt:
clean clean_docs:
- rm -rf $(HTMLDIR)
+ rm -f $(XMLDIR)/*.xml
+ rm -f $(HTMLDIR)/*.gif $(HTMLDIR)/*.html
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
deleted file mode 100644
index 6fffe30419..0000000000
--- a/system/doc/system_architecture_intro/note.gif
+++ /dev/null
Binary files differ
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
deleted file mode 100644
index 96af52360e..0000000000
--- a/system/doc/system_architecture_intro/warning.gif
+++ /dev/null
Binary files differ
diff --git a/system/doc/system_principles/Makefile b/system/doc/system_principles/Makefile
index 77edea8f58..5110b73373 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
@@ -67,7 +70,6 @@ DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-_create_dirs := $(shell mkdir -p $(HTMLDIR))
docs: html
@@ -78,7 +80,8 @@ html: $(GIF_FILES) $(HTML_UG_FILE)
debug opt:
clean clean_docs:
- rm -rf $(HTMLDIR)
+ rm -f $(XMLDIR)/*.xml
+ rm -f $(HTMLDIR)/*.gif $(HTMLDIR)/*.html
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..ea0d938936 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>
@@ -344,7 +352,7 @@ fi
START_ERL_DATA=${1:-$RELDIR/start_erl.data}
$ROOTDIR/bin/run_erl -daemon /tmp/ $ROOTDIR/log "exec $ROOTDIR/bin/start_erl $ROOTDIR\
-$RELDIR $START_ERL_DATA -heart</code>
+$RELDIR $START_ERL_DATA -heart"</code>
<p>We use the simplest possible <c>sys.config</c>, which we
store in <c>releases/FIRST</c>:</p>
<code type="none">
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,&lt;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,&lt;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,&lt;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,&lt;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,&lt;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,&lt;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..ed3675d180
--- /dev/null
+++ b/system/doc/system_principles/misc.xml
@@ -0,0 +1,206 @@
+<?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>
+ <title>Introduction</title>
+ <p>This document describes strategy regarding supported Releases,
+ compatibility, deprecations and removal of functionality. This
+ document was introduced in OTP 21. Actions taken regarding these
+ issues before OTP 21 did not adhere this document.</p>
+ </section>
+
+ <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..6986d93ed9 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>
@@ -156,7 +156,8 @@ init:stop()</pre>
command-line flag <c>-mode</c>.</p>
<pre>
% <input>erl -mode embedded</input></pre>
- <p>Default mode is <c>interactive</c>.</p>
+ <p>Default mode is <c>interactive</c> and extra <c>-mode</c> flags are
+ ignored.</p>
<p>The mode properties are as follows:</p>
<list type="bulleted">
<item>In embedded mode, all code is loaded during system startup
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>&lt;Major&gt;.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>&lt;Minor&gt;</c> version while
+ the release of an emergency patch package usually imply an
+ increase of the OTP <c>&lt;Patch&gt;</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
deleted file mode 100644
index 96af52360e..0000000000
--- a/system/doc/system_principles/warning.gif
+++ /dev/null
Binary files differ
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..e3f9c4710a 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.
@@ -39,6 +39,7 @@ INFO_FILES = ../../../README.md ../../COPYRIGHT PR.template
TOPDOCDIR=.
+include ../general_info/xmlfiles.mk
include ../installation_guide/xmlfiles.mk
include ../system_principles/xmlfiles.mk
include ../embedded/xmlfiles.mk
@@ -50,37 +51,75 @@ 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 = \
+ $(GENERAL_INFO_CHAPTER_FILES:%=general_info/%) \
+ $(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)/general_info/part.xml \
+ $(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)/general_info:$(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
TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
TOPDOC=true
+ifdef RELEASE_PATH
+INST_TYPE=rel
+INST_TYPE_SRC_DIR=$(RELEASE_PATH)
+# We build to the 'temporary' dir in order to be able to install
+# results using INSTALL_DATA (in order to get correct access
+# rights on installed files)
+INST_TYPE_DEST_DIR=$(RELSYSDIR)/temporary
+INST_TYPE_DEST_DIR_DEP=$(INST_TYPE_DEST_DIR)
+INST_TYPE_JS_DEST_DIR=$(INST_TYPE_DEST_DIR)
+INST_TYPE_VSN_FILE=$(INST_TYPE_DEST_DIR)/OTP_VERSION
+else
+INST_TYPE=src
+INST_TYPE_SRC_DIR=$(ERL_TOP)
+INST_TYPE_DEST_DIR=$(HTMLDIR)
+INST_TYPE_DEST_DIR_DEP=
+INST_TYPE_JS_DEST_DIR=$(INST_TYPE_DEST_DIR)/js
+INST_TYPE_VSN_FILE=$(ERL_TOP)/OTP_VERSION
+endif
+
#--------------------------------------------------------------------------
# We generate the index page from the installed system. This make
# it important that this is done last. The file index.html.src
@@ -90,17 +129,18 @@ EBIN = ebin
INDEX_SCRIPT = $(EBIN)/erl_html_tools.$(EMULATOR)
INDEX_SRC = src/erl_html_tools.erl
-INDEX_FILES = \
- $(HTMLDIR)/index.html \
- $(HTMLDIR)/applications.html
-JAVASCRIPT = $(HTMLDIR)/js/erlresolvelinks.js
+INDEX_HTML=$(INST_TYPE_DEST_DIR)/index.html
+APPLICATIONS_HTML=$(INST_TYPE_DEST_DIR)/applications.html
+INDEX_FILES = $(INDEX_HTML) $(APPLICATIONS_HTML)
+
+JAVASCRIPT = $(INST_TYPE_JS_DEST_DIR)/erlresolvelinks.js
JAVASCRIPT_BUILD_SCRIPT = $(EBIN)/erlresolvelinks.$(EMULATOR)
JAVASCRIPT_BUILD_SCRIPT_SRC = src/erlresolvelinks.erl
MAN_INDEX_SCRIPT = $(EBIN)/otp_man_index.$(EMULATOR)
MAN_INDEX_SRC = src/otp_man_index.erl
-MAN_INDEX = $(HTMLDIR)/man_index.html
+MAN_INDEX = $(INST_TYPE_DEST_DIR)/man_index.html
GLOSSARY = $(HTMLDIR)/glossary.html
GLOSSARY_SRC = $(ERL_TOP)/system/internal_tools/doctools/src/glossary.erl
@@ -115,45 +155,38 @@ TEMPLATES = \
$(INDEX_SCRIPT): $(INDEX_SRC)
$(ERLC) -o$(EBIN) +warn_unused_vars $<
-# We don't list toc_*.html as targets because we don't know
-$(HTMLDIR)/index.html + $(HTMLDIR)/applications.html: $(INDEX_SCRIPT) $(TEMPLATES)
- echo "Generating index $@"
-# Check if we are building the index from source or an installed release
- if test "$$RELEASE_ROOT" = "" ; then \
- $(ERL) -noshell -pa $(EBIN) -s erl_html_tools top_index src $(ERL_TOP) \
- $(HTMLDIR) `cat "$(ERL_TOP)/OTP_VERSION"` -s erlang halt ;\
+$(INST_TYPE_DEST_DIR)/OTP_VERSION: $(INST_TYPE_DEST_DIR_DEP)
+ if test -f "$(RELEASE_PATH)/releases/$(SYSTEM_VSN)/OTP_VERSION"; then \
+ $(CP) "$(RELEASE_PATH)/releases/$(SYSTEM_VSN)/OTP_VERSION" $@; \
else \
- $(ERL) -noshell -pa $(EBIN) -s erl_html_tools top_index rel $(RELEASE_ROOT) \
- $(HTMLDIR) `cat "$(RELEASE_ROOT)/releases/$(SYSTEM_VSN)/OTP_VERSION"` \
- -s erlang halt ;\
+ $(CP) $(ERL_TOP)/OTP_VERSION $@; \
fi
+# We don't list toc_*.html as targets because we don't know
+$(INDEX_HTML) + $(APPLICATIONS_HTML): $(INST_TYPE_DEST_DIR_DEP) $(INDEX_SCRIPT) $(TEMPLATES) $(INST_TYPE_VSN_FILE)
+ echo "Generating index $@"
+ $(ERL) -noshell -pa $(EBIN) -s erl_html_tools top_index $(INST_TYPE) \
+ $(INST_TYPE_SRC_DIR) $(INST_TYPE_DEST_DIR) \
+ `cat "$(INST_TYPE_VSN_FILE)"` -s erlang halt
+
#--------------------------------------------------------------------------
$(JAVASCRIPT_BUILD_SCRIPT): $(JAVASCRIPT_BUILD_SCRIPT_SRC)
$(ERLC) -o$(EBIN) +warn_unused_vars $<
-$(JAVASCRIPT): $(JAVASCRIPT_BUILD_SCRIPT)
- erl -noshell -pa $(EBIN) -s erlresolvelinks make -s erlang halt
- $(INSTALL_DIR) $(HTMLDIR)/js
- $(INSTALL_DATA) erlresolvelinks.js $(JAVASCRIPT)
+$(JAVASCRIPT): $(INST_TYPE_DEST_DIR_DEP) $(JAVASCRIPT_BUILD_SCRIPT)
+ erl -noshell -pa $(EBIN) -run erlresolvelinks make $(ERL_TOP) \
+ $(INST_TYPE_SRC_DIR) $(INST_TYPE_JS_DEST_DIR) -s erlang halt
#--------------------------------------------------------------------------
$(MAN_INDEX_SCRIPT): $(MAN_INDEX_SRC)
$(ERLC) -o$(EBIN) +warn_unused_vars $<
-$(MAN_INDEX): $(MAN_INDEX_SCRIPT)
-# Check if we are building the index from source or an installed release
- if test "$$RELEASE_ROOT" = "" ; then \
- $(ERL) -noshell -pa $(EBIN) -s otp_man_index gen src $(ERL_TOP) $@ \
- -s erlang halt ;\
- else \
- $(ERL) -noshell -pa $(EBIN) -s otp_man_index gen rel $(RELEASE_ROOT) $@ \
- -s erlang halt ;\
- fi
-
+$(MAN_INDEX): $(INST_TYPE_DEST_DIR_DEP) $(MAN_INDEX_SCRIPT)
+ $(ERL) -noshell -pa $(EBIN) -s otp_man_index gen $(INST_TYPE) \
+ $(INST_TYPE_SRC_DIR) $@ -s erlang halt
#--------------------------------------------------------------------------
@@ -231,19 +264,21 @@ html: $(INDEX_FILES) \
debug opt:
clean:
- rm -rf ../html/js
- rm -f PR.template
- rm -f $(INDEX_FILES) $(MAN_INDEX)
- rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
- rm -f $(INDEX_SCRIPT) $(GLOSSARY_SCRIPT) \
- $(JAVASCRIPT_BUILD_SCRIPT)
- rm -f erl_crash.dump errs core *~
+ $(RM) ../html/js/*.js
+ $(RM) PR.template
+ $(RM) $(XMLDIR)/*.xml
+ $(RM) $(INDEX_FILES) $(MAN_INDEX)
+ $(RM) $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
+ $(RM) $(INDEX_SCRIPT) $(GLOSSARY_SCRIPT) $(JAVASCRIPT_BUILD_SCRIPT)
+ $(RM) erl_crash.dump errs core *~
# ----------------------------------------------------
# Release Target
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_release_targets.mk
+$(RELSYSDIR)/temporary:
+ $(INSTALL_DIR) $(RELSYSDIR)/temporary
release_docs_spec: docs
$(INSTALL_DIR) "$(RELEASE_PATH)"
@@ -253,13 +288,13 @@ release_docs_spec: docs
$(INSTALL_DATA) \
$(TOP_PDF_FILE) $(RELSYSDIR)/pdf
$(INSTALL_DIR) $(RELSYSDIR)/js
- $(INSTALL_DATA) \
- $(JAVASCRIPT) $(RELSYSDIR)/js
+ $(INSTALL_DATA) $(JAVASCRIPT) $(RELSYSDIR)/js
$(INSTALL_DATA) $(INDEX_FILES) $(MAN_INDEX) $(RELSYSDIR)
$(INSTALL_DIR) $(RELSYSDIR)/docbuild
$(INSTALL_DATA) $(INDEX_SCRIPT) $(MAN_INDEX_SCRIPT) $(JAVASCRIPT_BUILD_SCRIPT) \
$(INDEX_SRC) $(MAN_INDEX_SRC) $(JAVASCRIPT_BUILD_SCRIPT_SRC) \
$(TEMPLATES) $(RELSYSDIR)/docbuild
+ $(RM) -r $(RELSYSDIR)/temporary
release_spec:
diff --git a/system/doc/top/book.xml b/system/doc/top/book.xml
index c94b0f24d6..64c1e4caf6 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,17 @@
<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/general_info/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..dee1871342 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 &copy; 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>&nbsp;</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>&nbsp;&nbsp;<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",
+ " <br/>\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"
@@ -549,7 +522,7 @@ subst_vsn([{VSN,_Path,Link,_Text} | VSNs]) ->
[
" <font size=\"2\"><a class=anum href=\"",Link,"\" target=\"_top\">",
VSN,
- "</a></font><br>\n",
+ "</a></font><br/>\n",
subst_vsn(VSNs)
];
subst_vsn([]) ->
diff --git a/system/doc/top/src/erlresolvelinks.erl b/system/doc/top/src/erlresolvelinks.erl
index cfe8d0fa0b..c1285fa1c3 100644
--- a/system/doc/top/src/erlresolvelinks.erl
+++ b/system/doc/top/src/erlresolvelinks.erl
@@ -27,40 +27,28 @@
%%-----------------------------------------------------------------
-module(erlresolvelinks).
--export([make/0, make/1]).
+-export([make/1]).
-include_lib("kernel/include/file.hrl").
-define(JAVASCRIPT_NAME, "erlresolvelinks.js").
-make() ->
- case os:getenv("ERL_TOP") of
- false ->
- io:format("Variable ERL_TOP is required\n",[]);
- Value ->
- make_from_src(Value, ".")
- end.
-
-make([RootDir, DestDir]) ->
- do_make(RootDir, DestDir);
-make(RootDir) when is_atom(RootDir) ->
- DestDir = filename:join(RootDir, "doc"),
- do_make(RootDir, DestDir).
-
-do_make(_RootDir, _DestDir) ->
- ok.
+make([ErlTop, RootDir, DestDir]) ->
+ make(ErlTop, RootDir, DestDir).
-make_from_src(RootDir, DestDir) ->
+make(ErlTop, RootDir, DestDir) ->
%% doc/Dir
%% erts-Vsn
%% lib/App-Vsn
Name = ?JAVASCRIPT_NAME,
- DocDirs0 = get_dirs(filename:join([RootDir, "system/doc"])),
+ DocDirs0 = get_dirs(filename:join([ErlTop, "system/doc"])),
DocDirs = lists:map(fun({Dir, _DirPath}) ->
D = filename:join(["doc", Dir]),
{D, D} end, DocDirs0),
- ErtsDirs = latest_app_dirs(RootDir, ""),
- AppDirs = latest_app_dirs(RootDir, "lib"),
+ Released = ErlTop /= RootDir,
+
+ ErtsDirs = latest_app_dirs(Released, RootDir, ""),
+ AppDirs = latest_app_dirs(Released, RootDir, "lib"),
AllAppDirs =
lists:map(
@@ -106,30 +94,46 @@ is_dir({File, AFile}) ->
false
end.
-latest_app_dirs(RootDir, Dir) ->
+released_app_vsns([]) ->
+ [];
+released_app_vsns([{AppVsn, Dir} | AVDirs]) ->
+ try
+ {ok, _} = file:read_file_info(filename:join([Dir, "doc", "html"])),
+ [App, Vsn] = string:tokens(AppVsn, "-"),
+ VsnNumList = vsnstr_to_numlist(Vsn),
+ [_Maj, _Min | _] = VsnNumList,
+ [{{App, VsnNumList}, AppVsn} | released_app_vsns(AVDirs)]
+ catch
+ _:_ -> released_app_vsns(AVDirs)
+ end.
+
+latest_app_dirs(Release, RootDir, Dir) ->
ADir = filename:join(RootDir, Dir),
RDirs0 = get_dirs(ADir),
- RDirs1 = lists:filter(fun is_app_dir/1, RDirs0),
-
- SDirs0 =
- lists:map(fun({App, Dir1}) ->
- File = filename:join(Dir1, "vsn.mk"),
- case file:read_file(File) of
- {ok, Bin} ->
- case re:run(Bin, ".*VSN\s*=\s*([0-9\.]+).*",[{capture,[1],list}]) of
- {match, [VsnStr]} ->
- VsnNumList = vsnstr_to_numlist(VsnStr),
- {{App, VsnNumList}, App++"-"++VsnStr};
- nomatch ->
- io:format("No VSN variable found in ~s\n", [File]),
- error
- end;
- {error, Reason} ->
- io:format("~p : ~s\n", [Reason, File]),
- error
- end
- end,
- RDirs1),
+ SDirs0 = case Release of
+ true ->
+ released_app_vsns(RDirs0);
+ false ->
+ lists:map(fun({App, Dir1}) ->
+ File = filename:join(Dir1, "vsn.mk"),
+ case file:read_file(File) of
+ {ok, Bin} ->
+ case re:run(Bin, ".*VSN\s*=\s*([0-9\.]+).*",[{capture,[1],list}]) of
+ {match, [VsnStr]} ->
+ VsnNumList = vsnstr_to_numlist(VsnStr),
+ {{App, VsnNumList}, App++"-"++VsnStr};
+ nomatch ->
+ io:format("No VSN variable found in ~s\n", [File]),
+ error
+ end;
+ {error, Reason} ->
+ io:format("~p : ~s\n", [Reason, File]),
+ error
+ end
+ end,
+ lists:filter(fun is_app_dir/1, RDirs0))
+ end,
+
SDirs1 = lists:keysort(1, SDirs0),
App2Dirs = lists:foldr(fun({{App, _VsnNumList}, AppVsn}, Acc) ->
case lists:keymember(App, 1, Acc) of
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 &copy; 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..3b61052b99 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,26 @@ 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>
+ <li><a href="general_info/deprecations.html" class="modules">Deprecations</a></li>
+ <li><a href="general_info/scheduled_for_removal.html" class="modules">Scheduled for Removal</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 +66,8 @@ limitations under the License.
</ul>
</li>
</ul>
-<b>Application Groups</b>
-<br/>
+
+<h3>Application Groups</h3>
<ul class="flipMenu">
#applinks#
@@ -73,24 +79,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 +143,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..4c62deeffd 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 = \
@@ -89,10 +93,9 @@ DVIPS_FLAGS +=
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
-_create_dirs := $(shell mkdir -p $(HTMLDIR))
$(HTMLDIR)/%.gif: %.gif
- $(INSTALL_DATA) $< $@
+ $(CP) $< $@
docs: html
@@ -105,7 +108,8 @@ gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
debug opt:
clean clean_docs:
- rm -rf $(HTMLDIR)
+ rm -f $(XMLDIR)/*.xml
+ rm -f $(HTMLDIR)/*.gif $(HTMLDIR)/*.html
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
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>
&lt;0.34.0>
3> <input>complex2:foo(3).</input>
4
diff --git a/system/doc/tutorial/overview.xml b/system/doc/tutorial/overview.xml
index 255c22f2c1..bd652b1e4b 100644
--- a/system/doc/tutorial/overview.xml
+++ b/system/doc/tutorial/overview.xml
@@ -245,13 +245,13 @@ Term = binary_to_term(Binary)</pre>
</section>
<section>
- <title>IC</title>
+ <title>IC and CORBA</title>
<p>IC (Erlang IDL Compiler) is an interface generator that, given
an IDL interface specification, automatically generates stub
code in Erlang, C, or Java. See the IC User's Guide and IC
Reference Manual.</p>
- <p>For details, see the <seealso marker="ic:ic">ic</seealso>
- manual page in IC.</p>
+ <p>For details, see the
+ <url href="https://github.com/erlang/corba">corba repository</url>.</p>
</section>
<section>
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)
diff --git a/system/doc/xml/design_principles/.gitignore b/system/doc/xml/design_principles/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/xml/design_principles/.gitignore
diff --git a/system/doc/xml/efficiency_guide/.gitignore b/system/doc/xml/efficiency_guide/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/xml/efficiency_guide/.gitignore
diff --git a/system/doc/xml/embedded/.gitignore b/system/doc/xml/embedded/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/xml/embedded/.gitignore
diff --git a/system/doc/xml/general_info/.gitignore b/system/doc/xml/general_info/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/xml/general_info/.gitignore
diff --git a/system/doc/xml/getting_started/.gitignore b/system/doc/xml/getting_started/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/xml/getting_started/.gitignore
diff --git a/system/doc/xml/installation_guide/.gitignore b/system/doc/xml/installation_guide/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/xml/installation_guide/.gitignore
diff --git a/system/doc/xml/oam/.gitignore b/system/doc/xml/oam/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/xml/oam/.gitignore
diff --git a/system/doc/xml/programming_examples/.gitignore b/system/doc/xml/programming_examples/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/xml/programming_examples/.gitignore
diff --git a/system/doc/xml/reference_manual/.gitignore b/system/doc/xml/reference_manual/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/xml/reference_manual/.gitignore
diff --git a/system/doc/xml/system_architecture_intro/.gitignore b/system/doc/xml/system_architecture_intro/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/xml/system_architecture_intro/.gitignore
diff --git a/system/doc/xml/system_principles/.gitignore b/system/doc/xml/system_principles/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/xml/system_principles/.gitignore
diff --git a/system/doc/xml/tutorial/.gitignore b/system/doc/xml/tutorial/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/system/doc/xml/tutorial/.gitignore