aboutsummaryrefslogtreecommitdiffstats
path: root/system/doc
diff options
context:
space:
mode:
Diffstat (limited to 'system/doc')
-rw-r--r--system/doc/design_principles/Makefile16
-rw-r--r--system/doc/design_principles/applications.xml12
-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.xml4
-rw-r--r--system/doc/design_principles/gen_server_concepts.xml4
-rw-r--r--system/doc/design_principles/release_structure.xml8
-rw-r--r--system/doc/design_principles/spec_proc.xml4
-rw-r--r--system/doc/design_principles/statem.xml1519
-rw-r--r--system/doc/design_principles/sup_princ.xml11
-rw-r--r--system/doc/efficiency_guide/Makefile4
-rw-r--r--system/doc/efficiency_guide/advanced.xml20
-rw-r--r--system/doc/efficiency_guide/binaryhandling.xml84
-rw-r--r--system/doc/efficiency_guide/commoncaveats.xml48
-rw-r--r--system/doc/efficiency_guide/efficiency_guide.erl24
-rw-r--r--system/doc/efficiency_guide/profiling.xml4
-rw-r--r--system/doc/efficiency_guide/retired_myths.xml14
-rw-r--r--system/doc/efficiency_guide/xmlfiles.mk2
-rw-r--r--system/doc/embedded/Makefile4
-rw-r--r--system/doc/getting_started/Makefile4
-rw-r--r--system/doc/getting_started/conc_prog.xml4
-rw-r--r--system/doc/getting_started/seq_prog.xml13
-rw-r--r--system/doc/installation_guide/Makefile22
-rw-r--r--system/doc/installation_guide/xmlfiles.mk6
-rw-r--r--system/doc/oam/Makefile4
-rw-r--r--system/doc/programming_examples/Makefile8
-rw-r--r--system/doc/programming_examples/xmlfiles.mk6
-rw-r--r--system/doc/reference_manual/Makefile4
-rw-r--r--system/doc/reference_manual/errors.xml47
-rw-r--r--system/doc/reference_manual/expressions.xml38
-rw-r--r--system/doc/reference_manual/introduction.xml4
-rw-r--r--system/doc/reference_manual/macros.xml37
-rw-r--r--system/doc/reference_manual/modules.xml8
-rw-r--r--system/doc/reference_manual/typespec.xml26
-rw-r--r--system/doc/reference_manual/xmlfiles.mk2
-rw-r--r--system/doc/system_architecture_intro/Makefile4
-rw-r--r--system/doc/system_architecture_intro/sys_arch_intro.xml16
-rw-r--r--system/doc/system_principles/Makefile6
-rw-r--r--system/doc/system_principles/create_target.xmlsrc10
-rw-r--r--system/doc/system_principles/error_logging.xml117
-rw-r--r--system/doc/system_principles/misc.xml198
-rw-r--r--system/doc/system_principles/part.xml3
-rw-r--r--system/doc/system_principles/system_principles.xml4
-rw-r--r--system/doc/system_principles/versions.xml70
-rw-r--r--system/doc/system_principles/xmlfiles.mk9
-rw-r--r--system/doc/top/Makefile64
-rw-r--r--system/doc/top/book.xml22
-rw-r--r--system/doc/tutorial/Makefile7
-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/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
65 files changed, 1925 insertions, 902 deletions
diff --git a/system/doc/design_principles/Makefile b/system/doc/design_principles/Makefile
index 1465f430a5..2fbd7d087f 100644
--- a/system/doc/design_principles/Makefile
+++ b/system/doc/design_principles/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.
@@ -18,6 +18,7 @@
# %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
# ----------------------------------------------------
@@ -57,11 +60,11 @@ 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) \
@@ -89,7 +92,7 @@ DVIPS_FLAGS +=
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
-$(HTMLDIR)/%.png: %.png
+$(HTMLDIR)/%.svg: %.svg
$(INSTALL_DATA) $< $@
docs: html
@@ -103,6 +106,7 @@ images: $(IMAGE_FILES:%=$(HTMLDIR)/%)
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 *~
diff --git a/system/doc/design_principles/applications.xml b/system/doc/design_principles/applications.xml
index c673fde07e..3b7b8fdaee 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>
@@ -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 e21f2a7f4e..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>2017</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
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/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 5f4e7ac685..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>2017</year>
+ <year>1997</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -312,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 1006485e30..16a901b3a5 100644
--- a/system/doc/design_principles/statem.xml
+++ b/system/doc/design_principles/statem.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2016</year><year>2017</year>
+ <year>2016</year><year>2018</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>
<!-- =================================================================== -->
@@ -72,13 +62,14 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
<p>These relations are interpreted as follows:
if we are in state <c>S</c> and event <c>E</c> occurs, we
are to perform actions <c>A</c> and make a transition to
- state <c>S'</c>. Notice that <c>S'</c> can be equal to <c>S</c>.
+ state <c>S'</c>. Notice that <c>S'</c> can be equal to <c>S</c>
+ and that <c>A</c> can be empty.
</p>
<p>
As <c>A</c> and <c>S'</c> depend only on
<c>S</c> and <c>E</c>, the kind of state machine described
- here is a Mealy Machine
- (see, for example, the corresponding Wikipedia article).
+ here is a Mealy machine
+ (see, for example, the Wikipedia article "Mealy machine").
</p>
<p>
Like most <c>gen_</c> behaviors, <c>gen_statem</c> keeps
@@ -88,7 +79,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,
+ regardless of
+ <seealso marker="#Event Types">Event Type</seealso>
+ (such as <em>call</em>, <em>cast</em> and <em>info</em>)
+ </item>
+ <item>
+ <seealso marker="#Postponing Events">
+ Postponing Events
+ </seealso>
+ (a substitute for selective receive)
+ </item>
+ <item>
+ <seealso marker="#Inserted Events">
+ Inserted Events
+ </seealso>
+ that is: events from the state machine to itself
+ (in particular purely internal events)
+ </item>
+ <item>
+ <seealso marker="#State Enter Calls">
+ State Enter Calls
+ </seealso>
+ (callback on state entry co-located with the rest
+ of each state's callback code)
+ </item>
+ <item>
+ Easy-to-use timeouts
+ (<seealso marker="#State Time-Outs">State Time-Outs</seealso>,
+ <seealso marker="#Event Time-Outs">Event Time-Outs</seealso>
+ and
+ <seealso marker="#Generic Time-Outs">Generic Time-outs</seealso>
+ (named time-outs))
+ </item>
+ </list>
+ <p>
+ If so, or if possibly needed in future versions,
+ then you should consider using <c>gen_statem</c> over
+ <seealso marker="stdlib:gen_server"><c>gen_server</c></seealso>.
+ </p>
+ <p>
+ For simple state machines not needing these features
+ <seealso marker="stdlib:gen_server"><c>gen_server</c></seealso>
+ works just fine.
+ It also has got smaller call overhead,
+ but we are talking about something like 2 vs 3.3 microseconds
+ call roundtrip time here, so if the server callback
+ does just a little bit more than just replying,
+ or if the call is not extremely frequent,
+ that difference will be hard to notice.
+ </p>
+ </section>
+
+<!-- =================================================================== -->
+
+ <section>
+ <marker id="Callback Module" />
+ <title>Callback Module</title>
+ <p>
+ The callback module contains functions that implement
+ the state machine.
+ When an event occurs,
+ the <c>gen_statem</c> behaviour engine
+ calls a function in the callback module with the event,
+ current state and server data.
+ This function performs the actions for this event,
+ and returns the new state and server data
+ and also actions to be performed by the behaviour engine.
+ </p>
+ <p>
+ The behaviour engine holds the state machine state,
+ server data, timer references, a queue of posponed messages
+ and other metadata. It receives all process messages,
+ handles the system messages, and calls the callback module
+ with machine specific events.
</p>
</section>
@@ -100,61 +179,72 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
<p>
The <c>gen_statem</c> behavior supports two callback modes:
</p>
- <list type="bulleted">
+ <taglist>
+ <tag>
+ <seealso marker="stdlib:gen_statem#type-callback_mode">
+ <c>state_functions</c>
+ </seealso>
+ </tag>
<item>
<p>
- In mode
- <seealso marker="stdlib:gen_statem#type-callback_mode"><c>state_functions</c></seealso>,
- the state transition rules are written as some Erlang
- functions, which conform to the following convention:
- </p>
- <pre>
-StateName(EventType, EventContent, Data) ->
- ... code for actions here ...
- {next_state, NewStateName, NewData}.
- </pre>
- <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 callback mode is selected at server start
+ and may be changed with a code upgrade/downgrade.
+ </p>
+ <p>
+ See the section
+ <seealso marker="#Event Handler">Event Handler</seealso>
+ that describes the event handling callback function(s).
+ </p>
<p>
- 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 callback mode is selected by implementing
+ a mandatory callback function
+ <seealso marker="stdlib:gen_statem#Module:callback_mode/0">
+ <c>Module:callback_mode()</c>
+ </seealso>
+ that returns one of the callback modes.
+ </p>
+ <p>
+ The
+ <seealso marker="stdlib:gen_statem#Module:callback_mode/0">
+ <c>Module:callback_mode()</c>
+ </seealso>
+ function may also return a list containing the callback mode
+ and the atom <c>state_enter</c> in which case
+ <seealso marker="#State Enter Calls">State Enter Calls</seealso>
+ are activated for the callback mode.
</p>
<section>
<marker id="Choosing the Callback Mode" />
<title>Choosing the Callback Mode</title>
<p>
+ The short version: choose <c>state_functions</c> -
+ it is the one most like <c>gen_fsm</c>.
+ But if you do not want the restriction that the state
+ must be an atom, or if you do not want to write
+ one event handler function per state; please read on...
+ </p>
+ <p>
The two
- <seealso marker="#Callback Modes">callback modes</seealso>
- give different possibilities
- and restrictions, but one goal remains:
- you want to handle all possible combinations of
- events and states.
+ <seealso marker="#Callback Modes">Callback Modes</seealso>
+ give different possibilities and restrictions,
+ with one common goal:
+ to handle all possible combinations of events and states.
</p>
<p>
This can be done, for example, by focusing on one state at the time
@@ -167,7 +257,7 @@ handle_event(EventType, EventContent, State, Data) ->
With <c>state_functions</c>, you are restricted to use
atom-only states, and the <c>gen_statem</c> engine
branches depending on state name for you.
- This encourages the callback module to gather
+ This encourages the callback module to co-locate
the implementation of all event actions particular
to one state in the same place in the code,
hence to focus on one state at the time.
@@ -186,13 +276,17 @@ handle_event(EventType, EventContent, State, Data) ->
This mode works equally well when you want to focus on
one event at the time or on
one state at the time, but function
- <seealso marker="stdlib:gen_statem#Module:handle_event/4"><c>Module:handle_event/4</c></seealso>
+ <seealso marker="stdlib:gen_statem#Module:handle_event/4">
+ <c>Module:handle_event/4</c>
+ </seealso>
quickly grows too large to handle without branching to
helper functions.
</p>
<p>
The mode enables the use of non-atom states, for example,
complex states or even hierarchical states.
+ See section
+ <seealso marker="#Complex State">Complex State</seealso>.
If, for example, a state diagram is largely alike
for the client side and the server side of a protocol,
you can have a state <c>{StateName,server}</c> or
@@ -208,43 +302,180 @@ handle_event(EventType, EventContent, State, Data) ->
<!-- =================================================================== -->
<section>
- <marker id="State Enter Calls" />
- <title>State Enter Calls</title>
+ <marker id="Event Handler" />
+ <title>Event Handler</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:
+ Which callback function that handles an event
+ depends on the callback mode:
</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 Event Handler">One Event Handler</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>
+ State enter calls are also handled by the event handler and have
+ slightly different arguments. See the section
+ <seealso marker="#State Enter Calls">State Enter Calls</seealso>.
+ </p>
+ <p>
+ The event handler return values are defined in the description of
+ <seealso marker="stdlib:gen_statem#Module:StateName/3">
+ <c>Module:StateName/3</c>
</seealso>
- 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 state transition actions.
+ An empty <c>Actions</c> list is equivalent to not returning the field.
+ </p>
+ <p>
+ See section
+ <seealso marker="#State Transition Actions">
+ State Transition Actions
+ </seealso>
+ for a list of possible
+ state transition actions.
+ </p>
+ <p>
+ If <c>NextState =/= State</c> the state machine changes
+ to a new state. A
+ <seealso marker="#State Enter Calls">state enter call</seealso>
+ is performed if enabled and all
+ <seealso marker="#Postponing Events">postponed events</seealso>
+ are retried.
+ </p>
+ </item>
+ <tag>
+ <c>{keep_state, NewData, Actions}</c><br />
+ <c>{keep_state, NewData}</c>
+ </tag>
+ <item>
+ <p>
+ Same as the <c>next_state</c> values with
+ <c>NextState =:= State</c>, that is, no state change.
+ </p>
+ </item>
+ <tag>
+ <c>{keep_state_and_data, Actions}</c><br />
+ <c>keep_state_and_data</c>
+ </tag>
+ <item>
+ <p>
+ Same as the <c>keep_state</c> values with
+ <c>NextData =:= Data</c>, that is, no change in server data.
+ </p>
+ </item>
+ <tag>
+ <c>{repeat_state, NewData, Actions}</c><br />
+ <c>{repeat_state, NewData}</c><br />
+ <c>{repeat_state_and_data, Actions}</c><br />
+ <c>repeat_state_and_data</c>
+ </tag>
+ <item>
+ <p>
+ Same as the <c>keep_state</c> or <c>keep_state_and_data</c> values,
+ and if
+ <seealso marker="#State Enter Calls">
+ State Enter Calls
+ </seealso>
+ are enabled, repeat the state enter call
+ as if this state was entered again.
+ </p>
+ </item>
+ <tag>
+ <c>{stop, Reason, NewData}</c><br />
+ <c>{stop, Reason}</c>
+ </tag>
+ <item>
+ <p>
+ Stop the server with reason <c>Reason</c>.
+ If the <c>NewData</c> field is used, first update the server data.
+ </p>
+ </item>
+ <tag>
+ <c>{stop_and_reply, Reason, NewData, ReplyActions}</c><br />
+ <c>{stop_and_reply, Reason, ReplyActions}</c>
+ </tag>
+ <item>
+ <p>
+ Same as the <c>stop</c> values, but first execute the given
+ state transition actions that may only be reply actions.
+ </p>
+ </item>
+ </taglist>
+
+ <section>
+ <marker id="The First State" />
+ <title>The First State</title>
+ <p>
+ To decide the first state the
+ <seealso marker="stdlib:gen_statem#Module:init/1">
+ <c>Module:init(Args)</c>
+ </seealso>
+ callback function is called before any
+ <seealso marker="#Event Handler">Event Handler</seealso>
+ is called. This function behaves like an event handler
+ function, but gets its only argument <c>Args</c> from
+ the <c>gen_statem</c>
+ <seealso marker="stdlib:gen_statem#start/3">
+ <c>start/3,4</c>
+ </seealso>
+ or
+ <seealso marker="stdlib:gen_statem#start_link/3">
+ <c>start_link/3,4</c>
+ </seealso>
+ function, and returns <c>{ok, State, Data}</c>
+ or <c>{ok, State, Data, Actions}</c>.
+ If you use the
+ <seealso marker="#Postponing Events"><c>postpone</c></seealso>
+ action from this function, that action is ignored,
+ since there is no event to postpone.
+ </p>
+ </section>
+
</section>
<!-- =================================================================== -->
<section>
- <marker id="Actions" />
- <title>Actions</title>
+ <marker id="State Transition Actions" />
+ <title>State Transition Actions</title>
<p>
In the first section
<seealso marker="#Event-Driven State Machines">
@@ -259,77 +490,102 @@ StateName(EventType, EventContent, Data) ->
</p>
<p>
There are more specific state-transition actions
- that a callback function can order the <c>gen_statem</c>
+ 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 state transition actions:
</p>
- <list type="bulleted">
- <item>
+ <taglist>
+ <tag>
<seealso marker="stdlib:gen_statem#type-postpone">
- Postpone
+ <c>postpone</c>
</seealso>
- the current event, see section
+ <br />
+ <c>{postpone, Boolean}</c>
+ </tag>
+ <item>
+ If set postpone the current event, see section
<seealso marker="#Postponing Events">Postponing Events</seealso>
</item>
- <item>
+ <tag>
<seealso marker="stdlib:gen_statem#type-hibernate">
- Hibernate
+ <c>hibernate</c>
</seealso>
- the <c>gen_statem</c>, treated in
+ <br />
+ <c>{hibernate, Boolean}</c>
+ </tag>
+ <item>
+ If set hibernate the <c>gen_statem</c>, treated in section
<seealso marker="#Hibernation">Hibernation</seealso>
</item>
- <item>
- Start a
+ <tag>
<seealso marker="stdlib:gen_statem#type-state_timeout">
- state time-out</seealso>,
- read more in section
+ <c>{state_timeout, Time}</c>
+ </seealso>
+ <br />
+ <c>{state_timeout, Time, Opts}</c>
+ </tag>
+ <item>
+ Start a state time-out, read more in section
<seealso marker="#State Time-Outs">State Time-Outs</seealso>
</item>
- <item>
- Start a
+ <tag>
<seealso marker="stdlib:gen_statem#type-generic_timeout">
- generic time-out</seealso>,
- read more in section
+ <c>{{timeout, Name}, Time}</c>
+ </seealso>
+ <br />
+ <c>{{timeout, Name}, Time, Opts}</c>
+ </tag>
+ <item>
+ Start a generic time-out, read more in section
<seealso marker="#Generic Time-Outs">Generic Time-Outs</seealso>
</item>
+ <tag>
+ <seealso marker="stdlib:gen_statem#type-event_timeout">
+ <c>{timeout, Time}</c>
+ </seealso>
+ <br />
+ <c>{timeout, Time, Opts}</c><br />
+ <c>Time</c>
+ </tag>
<item>
- Start an
- <seealso marker="stdlib:gen_statem#type-event_timeout">event time-out</seealso>,
- see more in section
+ Start an event time-out, see more in section
<seealso marker="#Event Time-Outs">Event Time-Outs</seealso>
</item>
- <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
+ </tag>
+ <item>
+ Reply to a caller, mentioned at the end of section
<seealso marker="#All State Events">All State Events</seealso>
</item>
- <item>
- Generate the
+ <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.
+ 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 time-outs to relative or absolute times.
+ and set a time-out to use absolute instead of relative time
+ (using the <c>Opts</c> field).
</p>
</section>
@@ -341,8 +597,8 @@ StateName(EventType, EventContent, Data) ->
<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
+ 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>
@@ -350,12 +606,20 @@ StateName(EventType, EventContent, Data) ->
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>,
@@ -364,12 +628,20 @@ StateName(EventType, EventContent, Data) ->
<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">
@@ -377,7 +649,11 @@ StateName(EventType, EventContent, Data) ->
</seealso>
state timer timing out.
</item>
- <tag><c>{timeout,Name}</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-generic_timeout">
@@ -385,7 +661,11 @@ StateName(EventType, EventContent, Data) ->
</seealso>
generic timer timing out.
</item>
- <tag><c>timeout</c></tag>
+ <tag>
+ <seealso marker="stdlib:gen_statem#type-timeout_event_type">
+ <c>timeout</c>
+ </seealso>
+ </tag>
<item>
Generated by state transition action
<seealso marker="stdlib:gen_statem#type-event_timeout">
@@ -394,7 +674,11 @@ StateName(EventType, EventContent, Data) ->
(or its short form <c>Time</c>)
event timer timing out.
</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>
@@ -408,19 +692,75 @@ StateName(EventType, EventContent, Data) ->
<!-- =================================================================== -->
<section>
+ <marker id="State Enter Calls" />
+ <title>State Enter Calls</title>
+ <p>
+ The <c>gen_statem</c> behavior can if this is enabled,
+ regardless of callback mode,
+ automatically
+ <seealso marker="stdlib:gen_statem#type-state_enter">
+ call the state callback
+ </seealso>
+ with special arguments whenever the state changes
+ so you can write state enter actions
+ near the rest of the state transition rules.
+ It typically looks like this:
+ </p>
+ <pre>
+StateName(enter, OldState, Data) ->
+ ... code for state enter actions here ...
+ {keep_state, NewData};
+StateName(EventType, EventContent, Data) ->
+ ... code for actions here ...
+ {next_state, NewStateName, NewData}.</pre>
+ <p>
+ Since the state enter call is not an event there are restrictions
+ on the allowed return value and
+ <seealso marker="#State Transition Actions">State Transition Actions</seealso>.
+ You may not change the state,
+ <seealso marker="#Postponing Events">postpone</seealso>
+ this non-event, or
+ <seealso marker="#Inserted Events">insert events</seealso>.
+ </p>
+ <p>
+ The first state that is entered will get a state enter call
+ with <c>OldState</c> equal to the current state.
+ </p>
+ <p>
+ You may repeat the state enter call using the <c>{repeat_state,...}</c>
+ return value from the
+ <seealso marker="#Event Handler">Event Handler</seealso>.
+ In this case <c>OldState</c> will also be equal to the current state.
+ </p>
+ <p>
+ Depending on how your state machine is specified,
+ this can be a very useful feature,
+ but it forces you to handle the state enter calls in all states.
+ See also the
+ <seealso marker="#State Enter Actions">
+ State Enter Actions
+ </seealso>
+ chapter.
+ </p>
+ </section>
+
+<!-- =================================================================== -->
+
+ <section>
<marker id="Example" />
<title>Example</title>
<p>
A door with a code lock can be seen as a state machine.
Initially, the door is locked. When someone presses a button,
an event is generated.
- Depending on what buttons have been pressed before,
- the sequence so far can be correct, incomplete, or wrong.
- If correct, the door is unlocked for 10 seconds (10,000 milliseconds).
- If incomplete, we wait for another button to be pressed. If
- wrong, we start all over, waiting for a new button sequence.
- </p>
- <image file="../design_principles/code_lock.png">
+ The pressed buttons are collected, up to the number of buttons
+ in the correct code.
+ If correct, the door is unlocked for 10 seconds.
+ If not correct, we wait for a new button to be pressed.
+ </p>
+ <!-- The image is edited with dia in a .dia file,
+ then exported to Scalable Vector Graphics. -->
+ <image file="../design_principles/code_lock.svg" width="80%">
<icaption>Code Lock State Diagram</icaption>
</image>
<p>
@@ -434,43 +774,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() ->
@@ -479,8 +827,6 @@ do_unlock() ->
terminate(_Reason, State, _Data) ->
State =/= locked andalso do_lock(),
ok.
-code_change(_Vsn, State, Data, _Extra) ->
- {ok, State, Data}.
]]></code>
<p>The code is explained in the next sections.</p>
</section>
@@ -556,17 +902,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>
@@ -584,10 +930,6 @@ 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>
@@ -595,8 +937,12 @@ callback_mode() ->
<seealso marker="#Callback Modes"><c>CallbackMode</c></seealso>
for the callback module, in this case
<seealso marker="stdlib:gen_statem#type-callback_mode"><c>state_functions</c></seealso>.
- That is, each state has got its own handler function.
+ That is, each state has got its own handler function:
</p>
+ <code type="erl"><![CDATA[
+callback_mode() ->
+ state_functions.
+ ]]></code>
</section>
@@ -610,17 +956,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>,
@@ -629,44 +975,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
@@ -684,9 +1034,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.
@@ -721,10 +1071,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[
...
@@ -737,16 +1086,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>,
@@ -757,6 +1136,14 @@ 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 event handler needs to know the current state
+ a function <c>handle_common/4</c> can be used instead:
+ </p>
+ <code type="erl"><![CDATA[
+-define(HANDLE_COMMON,
+ ?FUNCTION_NAME(T, C, D) -> handle_common(T, C, ?FUNCTION_NAME, D)).
+ ]]></code>
</section>
<!-- =================================================================== -->
@@ -765,7 +1152,11 @@ handle_event({call,From}, code_length, #{code := Code} = Data) ->
<marker id="One Event Handler" />
<title>One Event Handler</title>
<p>
- If mode <c>handle_event_function</c> is used,
+ If
+ <seealso marker="#Callback Modes">
+ Callback Mode
+ </seealso>
+ <c>handle_event_function</c> is used,
all events are handled in
<seealso marker="stdlib:gen_statem#Module:handle_event/4"><c>Module:handle_event/4</c></seealso>
and we can (but do not have to) use an event-centered approach
@@ -780,28 +1171,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>
<!-- =================================================================== -->
@@ -833,7 +1234,7 @@ init(Args) ->
process_flag(trap_exit, true),
do_lock(),
...
- ]]></code>
+ ]]></code>
<p>
When ordered to shut down, the <c>gen_statem</c> then calls
callback function <c>terminate(shutdown, State, Data)</c>.
@@ -847,7 +1248,7 @@ init(Args) ->
terminate(_Reason, State, _Data) ->
State =/= locked andalso do_lock(),
ok.
- ]]></code>
+ ]]></code>
</section>
<section>
@@ -866,7 +1267,7 @@ terminate(_Reason, State, _Data) ->
...
stop() ->
gen_statem:stop(?NAME).
- ]]></code>
+ ]]></code>
<p>
This makes the <c>gen_statem</c> call callback function
<c>terminate/3</c> just like for a supervised server
@@ -889,30 +1290,29 @@ stop() ->
</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
+ <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 time-out
of 30 seconds, and if we get an event type <c>timeout</c>
@@ -925,6 +1325,13 @@ locked(
Whatever event you act on has already cancelled
the event time-out...
</p>
+ <p>
+ Note that an event time-out does not work well with
+ when you have for example a status call as in
+ <seealso marker="#All State Events">All State Events</seealso>,
+ or handle unknown events, since all kinds of events
+ will cancel the event time-out.
+ </p>
</section>
<!-- =================================================================== -->
@@ -952,37 +1359,43 @@ locked(
<p>
Here is how to accomplish the state time-out
in the previous example by instead using a generic time-out
- named <c>open_tm</c>:
+ 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(),
- {next_state, open, Data#{remaining := Code},
- [{{timeout,open_tm},10000,lock}]};
+ {next_state, open, Data#{buttons := []},
+ [{{timeout,open},10000,lock}]}; % Time in milliseconds
...
-open({timeout,open_tm}, lock, Data) ->
+open({timeout,open}, lock, Data) ->
do_lock(),
{next_state,locked,Data};
open(cast, {button,_}, Data) ->
{keep_state,Data};
...
- ]]></code>
+]]></code>
<p>
- Just as
- <seealso marker="#State Time-Outs">state time-outs</seealso>
- you can restart or cancel a specific generic time-out
+ 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>
- Another way to handle a late time-out can be to not cancel it,
- but to ignore it if it arrives in a state
- where it is known to be late.
+ In this particular case we do not need to cancel the timeout
+ since the timeout event is the only possible reason to
+ change the state from <c>open</c> to <c>locked</c>.
+ </p>
+ <p>
+ 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>
@@ -994,7 +1407,7 @@ open(cast, {button,_}, Data) ->
<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_timer3,4</c></seealso>.
+ <seealso marker="erts:erlang#start_timer/4"><c>erlang:start_timer/3,4</c></seealso>.
Most time-out tasks can be performed with the
time-out features in <c>gen_statem</c>,
but an example of one that can not is if you should need
@@ -1008,13 +1421,16 @@ open(cast, {button,_}, Data) ->
<code type="erl"><![CDATA[
...
locked(
- cast, {button,Digit},
- #{code := Code, remaining := Remaining} = Data) ->
- case Remaining of
- [Digit] ->
+ cast, {button,Button},
+ #{code := Code, length := Length, buttons := Buttons} = Data) ->
+...
+ if
+ NewButtons =:= Code -> % Correct
do_unlock(),
- Tref = erlang:start_timer(10000, self(), lock),
- {next_state, open, Data#{remaining := Code, timer => Tref}};
+ Tref =
+ erlang:start_timer(
+ 10000, self(), lock), % Time in milliseconds
+ {next_state, open, Data#{buttons := [], timer => Tref}};
...
open(info, {timeout,Tref,lock}, #{timer := Tref} = Data) ->
@@ -1023,7 +1439,7 @@ 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
@@ -1063,7 +1479,9 @@ open(cast, {button,_}, Data) ->
</p>
<p>
Postponing is ordered by the state transition
- <seealso marker="stdlib:gen_statem#type-action">action</seealso>
+ <seealso marker="#State Transition Actions">
+ State Transition Action
+ </seealso>
<c>postpone</c>.
</p>
<p>
@@ -1076,15 +1494,18 @@ 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,
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">Callback Mode</seealso>
<seealso marker="stdlib:gen_statem#type-callback_mode"><c>handle_event_function</c></seealso>.
If a change in the value changes the set of events that is handled,
then the value should be kept in the State.
@@ -1134,28 +1555,38 @@ start_link(Code) ->
fun () ->
true = register(?NAME, self()),
do_lock(),
- locked(Code, Code)
+ locked(Code, length(Code), [])
end).
-button(Digit) ->
- ?NAME ! {button,Digit}.
-
-locked(Code, [Digit|Remaining]) ->
+button(Button) ->
+ ?NAME ! {button,Button}.
+ ]]></code>
+ <code type="erl"><![CDATA[
+locked(Code, Length, Buttons) ->
receive
- {button,Digit} when Remaining =:= [] ->
- do_unlock(),
- open(Code);
- {button,Digit} ->
- locked(Code, Remaining);
- {button,_} ->
- locked(Code, Code)
+ {button,Button} ->
+ NewButtons =
+ if
+ length(Buttons) < Length ->
+ Buttons;
+ true ->
+ tl(Buttons)
+ end ++ [Button],
+ if
+ NewButtons =:= Code -> % Correct
+ do_unlock(),
+ open(Code, Length);
+ true -> % Incomplete | Incorrect
+ locked(Code, Length, NewButtons)
+ end
end.
-
-open(Code) ->
+ ]]></code>
+ <code type="erl"><![CDATA[
+open(Code, Length) ->
receive
- after 10000 ->
+ after 10000 -> % Time in milliseconds
do_lock(),
- locked(Code, Code)
+ locked(Code, Length, [])
end.
do_lock() ->
@@ -1178,8 +1609,10 @@ do_unlock() ->
passing non-system messages to the callback module.
</p>
<p>
- The state transition
- <seealso marker="stdlib:gen_statem#type-action">action</seealso>
+ The
+ <seealso marker="#State Transition Actions">
+ State Transition Action
+ </seealso>
<c>postpone</c> is designed to model
selective receives. A selective receive implicitly postpones
any not received events, but the <c>postpone</c>
@@ -1196,16 +1629,16 @@ 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">State Enter Calls</seealso>.
</p>
<p>
You return a list containing <c>state_enter</c> from your
@@ -1219,7 +1652,7 @@ do_unlock() ->
...
init(Code) ->
process_flag(trap_exit, true),
- Data = #{code => Code},
+ Data = #{code => Code, length = length(Code)},
{ok, locked, Data}.
callback_mode() ->
@@ -1227,24 +1660,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.
@@ -1259,13 +1694,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="#State Transition Actions">
+ State Transition Action
+ </seealso>
<c>{next_event,EventType,EventContent}</c>.
</p>
<p>
@@ -1279,58 +1716,75 @@ 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 Event Handler">One Event Handler</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>
@@ -1344,14 +1798,16 @@ handle_event({call,From}, enter, #{buf := Buf} = Data) ->
modifications and some more using state enter calls,
which deserves a new state diagram:
</p>
- <image file="../design_principles/code_lock_2.png">
+ <!-- The image is edited with dia in a .dia file,
+ then exported to Scalable Vector Graphics. -->
+ <image file="../design_principles/code_lock_2.svg" width="80%">
<icaption>Code Lock State Diagram Revisited</icaption>
</image>
<p>
Notice that this state diagram does not specify how to handle
a button event in the state <c>open</c>. So, you need to
- read somewhere else that unspecified events
- must be ignored as in not consumed but handled in some other state.
+ read in some side notes, that is, here: that unspecified events
+ shall be postponed (handled in some later state).
Also, the state diagram does not show that the <c>code_length/0</c>
call must be handled in every state.
</p>
@@ -1368,8 +1824,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) ->
@@ -1377,52 +1833,74 @@ start_link(Code) ->
stop() ->
gen_statem:stop(?NAME).
-button(Digit) ->
- gen_statem:cast(?NAME, {button,Digit}).
+down(Button) ->
+ gen_statem:cast(?NAME, {down,Button}).
+up(Button) ->
+ gen_statem:cast(?NAME, {up,Button}).
code_length() ->
gen_statem:call(?NAME, code_length).
-
+ ]]></code>
+ <code type="erl"><![CDATA[
init(Code) ->
process_flag(trap_exit, true),
- Data = #{code => Code},
+ 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", []).
@@ -1432,9 +1910,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>
@@ -1448,54 +1924,71 @@ code_change(_Vsn, State, Data, _Extra) ->
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}};
-handle_event(
- timeout, _, locked,
- #{code := Code, remaining := Remaining} = Data) ->
- {keep_state, Data#{remaining := Code}};
+ {keep_state, Data#{buttons := []}};
+handle_event(state_timeout, button, locked, Data) ->
+ {keep_state, Data#{buttons := []}};
handle_event(
- cast, {button,Digit}, locked,
- #{code := Code, remaining := Remaining} = Data) ->
- case Remaining of
- [Digit] -> % Complete
+ 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>
@@ -1532,7 +2025,7 @@ handle_event({call,From}, code_length, _State, #{code := Code}) ->
</p>
<code type="erl"><![CDATA[
...
--export([init/1,terminate/3,code_change/4,format_status/2]).
+-export([init/1,terminate/3,format_status/2]).
...
format_status(Opt, [_PDict,State,Data]) ->
@@ -1540,7 +2033,6 @@ format_status(Opt, [_PDict,State,Data]) ->
{State,
maps:filter(
fun (code, _) -> false;
- (remaining, _) -> false;
(_, _) -> true
end,
Data)},
@@ -1576,10 +2068,10 @@ format_status(Opt, [_PDict,State,Data]) ->
<p>
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>,
+ <seealso marker="#State Time-Outs">State Time-Out</seealso>,
or one that affects the event handling
in combination with postponing events.
- We will complicate the previous example
+ 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,
@@ -1588,33 +2080,33 @@ format_status(Opt, [_PDict,State,Data]) ->
<p>
Suppose now that we call <c>set_lock_button</c>
while the door is open,
- and have already postponed a button event
- that until now was not the lock button.
- The sensible thing can be to say that
- the button was pressed too early so it is
- not to be recognized as the lock button.
- However, then it can be surprising that a button event
- that now is the lock button event arrives (as retried postponed)
- immediately after the state transits to <c>locked</c>.
- </p>
- <p>
- So we make the <c>button/1</c> function synchronous
- by using
- <seealso marker="stdlib:gen_statem#call/2"><c>gen_statem:call</c></seealso>
- and still postpone its events in the <c>open</c> state.
- Then a call to <c>button/1</c> during the <c>open</c>
- state does not return until the state transits to <c>locked</c>,
- as it is there the event is handled and the reply is sent.
- </p>
- <p>
- If a process now calls <c>set_lock_button/1</c>
- to change the lock button while another process
- hangs in <c>button/1</c> with the new lock button,
- it can be expected that the hanging lock button call
- immediately takes effect and locks the lock.
- Therefore, we make the current lock button a part of the state,
- so that when we change the lock button, the state changes
- and all postponed events are retried.
+ and we have already postponed a button event
+ that was the new lock button:
+ </p>
+ <code type="erl"><![CDATA[
+1> code_lock:start_link([a,b,c], x).
+{ok,<0.666.0>}
+2> code_lock:button(a).
+ok
+3> code_lock:button(b).
+ok
+4> code_lock:button(c).
+ok
+Open
+5> code_lock:button(y).
+ok
+6> code_lock:set_lock_button(y).
+x
+% What should happen here? Immediate lock or nothing?
+]]></code>
+ <p>
+ We could say that the button was pressed too early
+ so it is not to be recognized as the lock button.
+ Or we can make the lock button part of the state so
+ when we then change the lock button in the locked state,
+ the change becomes a state change
+ and all postponed events are retried,
+ therefore the lock is immediately locked!
</p>
<p>
We define the state as <c>{StateName,LockButton}</c>,
@@ -1627,8 +2119,8 @@ format_status(Opt, [_PDict,State,Data]) ->
-define(NAME, code_lock_3).
-export([start_link/2,stop/0]).
--export([button/1,code_length/0,set_lock_button/1]).
--export([init/1,callback_mode/0,terminate/3,code_change/4,format_status/2]).
+-export([button/1,set_lock_button/1]).
+-export([init/1,callback_mode/0,terminate/3]).
-export([handle_event/4]).
start_link(Code, LockButton) ->
@@ -1637,77 +2129,68 @@ start_link(Code, LockButton) ->
stop() ->
gen_statem:stop(?NAME).
-button(Digit) ->
- gen_statem:call(?NAME, {button,Digit}).
-code_length() ->
- gen_statem:call(?NAME, code_length).
+button(Button) ->
+ gen_statem:cast(?NAME, {button,Button}).
set_lock_button(LockButton) ->
gen_statem:call(?NAME, {set_lock_button,LockButton}).
-
+ ]]></code>
+ <code type="erl"><![CDATA[
init({Code,LockButton}) ->
process_flag(trap_exit, true),
- Data = #{code => Code, remaining => undefined},
+ 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},
- [{reply,From,ok}, 30000]};
- [_|_] -> % 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() ->
@@ -1716,29 +2199,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>
<!-- =================================================================== -->
@@ -1770,17 +2231,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>
@@ -1793,20 +2252,34 @@ handle_event(
<p>
To change that we would need to insert
action <c>hibernate</c> in more places.
- For example, for the state-independent <c>set_lock_button</c>
- and <c>code_length</c> operations that then would have to
- be aware of using <c>hibernate</c> while in the
+ For example, the state-independent <c>set_lock_button</c>
+ operation would have to use <c>hibernate</c> but only in the
<c>{open,_}</c> state, which would clutter the code.
</p>
<p>
- 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-hibernate_after_opt">
+ <c>{hibernate_after, Timeout}</c>
+ </seealso>
+ for
+ <seealso marker="stdlib:gen_statem#start/3">
+ <c>start/3,4</c>
+ </seealso>
+ or
+ <seealso marker="stdlib:gen_statem#start_link/3">
+ <c>start_link/3,4</c>
+ </seealso>
+ that may be used to automatically hibernate the server.
</p>
<p>
- This 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 06ca44a9f6..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>2017</year>
+ <year>1997</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -312,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
diff --git a/system/doc/efficiency_guide/Makefile b/system/doc/efficiency_guide/Makefile
index ce93762ced..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
# ----------------------------------------------------
@@ -96,6 +97,7 @@ 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 *~
diff --git a/system/doc/efficiency_guide/advanced.xml b/system/doc/efficiency_guide/advanced.xml
index 7f719849cc..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>
@@ -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,10 +248,11 @@
<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>
diff --git a/system/doc/efficiency_guide/binaryhandling.xml b/system/doc/efficiency_guide/binaryhandling.xml
index 19f40c9abe..b500329ef9 100644
--- a/system/doc/efficiency_guide/binaryhandling.xml
+++ b/system/doc/efficiency_guide/binaryhandling.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2017</year>
+ <year>2018</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -357,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>
@@ -422,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>
diff --git a/system/doc/efficiency_guide/commoncaveats.xml b/system/doc/efficiency_guide/commoncaveats.xml
index b41ffc3902..367da09ba3 100644
--- a/system/doc/efficiency_guide/commoncaveats.xml
+++ b/system/doc/efficiency_guide/commoncaveats.xml
@@ -169,53 +169,5 @@ multiple_setelement(T0) ->
{Bin1,Bin2} = split_binary(Bin, Num)</code>
</section>
- <section>
- <title>Operator "--"</title>
- <p>The "<c>--</c>" operator has a complexity
- proportional to the product of the length of its operands.
- This means that the operator is very slow if both of its operands
- are long lists:</p>
-
- <p><em>DO NOT</em></p>
- <code type="none"><![CDATA[
- HugeList1 -- HugeList2]]></code>
-
- <p>Instead use the <seealso marker="stdlib:ordsets">ordsets</seealso>
- module in STDLIB:</p>
-
- <p><em>DO</em></p>
- <code type="none">
- HugeSet1 = ordsets:from_list(HugeList1),
- HugeSet2 = ordsets:from_list(HugeList2),
- ordsets:subtract(HugeSet1, HugeSet2)</code>
-
- <p>Obviously, that code does not work if the original order
- of the list is important. If the order of the list must be
- preserved, do as follows:</p>
-
- <p><em>DO</em></p>
- <code type="none"><![CDATA[
- Set = gb_sets:from_list(HugeList2),
- [E || E <- HugeList1, not gb_sets:is_element(E, Set)]]]></code>
-
- <note><p>This code behaves differently from "<c>--</c>"
- if the lists contain duplicate elements (one occurrence
- of an element in HugeList2 removes <em>all</em>
- occurrences in HugeList1.)</p>
- <p>Also, this code compares lists elements using the
- "<c>==</c>" operator, while "<c>--</c>" uses the "<c>=:=</c>" operator.
- If that difference is important, <c>sets</c> can be used instead of
- <c>gb_sets</c>, but <c>sets:from_list/1</c> is much
- slower than <c>gb_sets:from_list/1</c> for long lists.</p></note>
-
- <p>Using the "<c>--</c>" operator to delete an element
- from a list is not a performance problem:</p>
-
- <p><em>OK</em></p>
- <code type="none">
- HugeList1 -- [Element]</code>
-
- </section>
-
</chapter>
diff --git a/system/doc/efficiency_guide/efficiency_guide.erl b/system/doc/efficiency_guide/efficiency_guide.erl
index e982bdae65..c57785aaa3 100644
--- a/system/doc/efficiency_guide/efficiency_guide.erl
+++ b/system/doc/efficiency_guide/efficiency_guide.erl
@@ -1,5 +1,5 @@
-module(efficiency_guide).
--compile(export_all).
+-compile([export_all,nowarn_export_all).
%% DO NOT
naive_reverse([H|T]) ->
@@ -71,28 +71,6 @@ all_but_zeroes_to_list(<<0,T/binary>>, Acc, Remaining) ->
all_but_zeroes_to_list(<<Byte,T/binary>>, Acc, Remaining) ->
all_but_zeroes_to_list(T, [Byte|Acc], Remaining-1).
-non_opt_eq([H|T1], <<H,T2/binary>>) ->
- non_opt_eq(T1, T2);
-non_opt_eq([_|_], <<_,_/binary>>) ->
- false;
-non_opt_eq([], <<>>) ->
- true.
-
-opt_eq(<<H,T1/binary>>, [H|T2]) ->
- opt_eq(T1, T2);
-opt_eq(<<_,_/binary>>, [_|_]) ->
- false;
-opt_eq(<<>>, []) ->
- true.
-
-match_head(List, <<_:10,Data/binary>>) ->
- match_body(List, Data).
-
-match_body([0|_], <<H,_/binary>>) ->
- done;
-match_body([H|T1], <<H,T2/binary>>) ->
- {T1,T2}.
-
count1(<<_,T/binary>>, Count) -> count1(T, Count+1);
count1(<<>>, Count) -> Count.
diff --git a/system/doc/efficiency_guide/profiling.xml b/system/doc/efficiency_guide/profiling.xml
index f185456158..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>
@@ -94,7 +94,7 @@
<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 mmeory. Use the
+ 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>
diff --git a/system/doc/efficiency_guide/retired_myths.xml b/system/doc/efficiency_guide/retired_myths.xml
index 9b914a3b6e..144c942c2b 100644
--- a/system/doc/efficiency_guide/retired_myths.xml
+++ b/system/doc/efficiency_guide/retired_myths.xml
@@ -60,4 +60,18 @@
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 23c0d991b4..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.
diff --git a/system/doc/embedded/Makefile b/system/doc/embedded/Makefile
index 598ed280e5..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
# ----------------------------------------------------
@@ -84,6 +85,7 @@ 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 *~
diff --git a/system/doc/getting_started/Makefile b/system/doc/getting_started/Makefile
index 5283d53e1c..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
# ----------------------------------------------------
@@ -83,6 +84,7 @@ 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 *~
diff --git a/system/doc/getting_started/conc_prog.xml b/system/doc/getting_started/conc_prog.xml
index 7936e0d484..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>2017</year>
+ <year>2003</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -627,7 +627,7 @@ ping finished</pre>
%%% Change the function below to return the name of the node where the
%%% messenger server runs
server_node() ->
- messenger@bill.
+ messenger@super.
%%% This is the server process for the "messenger"
%%% the user list has the format [{ClientPid1, Name1},{ClientPid22, Name2},...]
diff --git a/system/doc/getting_started/seq_prog.xml b/system/doc/getting_started/seq_prog.xml
index 6b7e1cd24f..2b0750ff80 100644
--- a/system/doc/getting_started/seq_prog.xml
+++ b/system/doc/getting_started/seq_prog.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2016</year>
+ <year>2003</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -271,11 +271,12 @@ convert(N, centimeter) ->
{'EXIT',{function_clause,[{tut2,convert,
[3,miles],
[{file,"tut2.erl"},{line,4}]},
- {erl_eval,do_apply,5,[{file,"erl_eval.erl"},{line,482}]},
- {shell,exprs,7,[{file,"shell.erl"},{line,666}]},
- {shell,eval_exprs,7,[{file,"shell.erl"},{line,621}]},
- {shell,eval_loop,3,[{file,"shell.erl"},{line,606}]}]}}</pre>
-
+ {erl_eval,do_apply,6,
+ [{file,"erl_eval.erl"},{line,677}]},
+ {shell,exprs,7,[{file,"shell.erl"},{line,687}]},
+ {shell,eval_exprs,7,[{file,"shell.erl"},{line,642}]},
+ {shell,eval_loop,3,
+ [{file,"shell.erl"},{line,627}]}]}}</pre>
</section>
<section>
diff --git a/system/doc/installation_guide/Makefile b/system/doc/installation_guide/Makefile
index 29edd10501..38252757d6 100644
--- a/system/doc/installation_guide/Makefile
+++ b/system/doc/installation_guide/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2016. All Rights Reserved.
+# Copyright Ericsson AB 1996-2018. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -28,6 +28,8 @@ include $(ERL_TOP)/erts/vsn.mk
#VSN=$(SYSTEM_VSN)
APPLICATION=otp-system-documentation
+XMLDIR := $(XMLDIR)/installation_guide
+
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -43,6 +45,8 @@ include xmlfiles.mk
XML_CHAPTER_FILES=$(INST_GUIDE_CHAPTER_FILES)
+# ----------------------------------------------------
+
TOPDOCDIR=..
BOOK_FILES = book.xml
@@ -55,12 +59,7 @@ XML_FILES = \
$(BOOK_FILES) $(XML_CHAPTER_FILES) \
$(XML_PART_FILES)
-# ----------------------------------------------------
-GENERATED_XML_FILES = \
- INSTALL.xml \
- INSTALL-CROSS.xml \
- INSTALL-WIN32.xml \
- OTP-PATCH-APPLY.xml
+XML_GEN_FILES = $(INST_GUIDE_CHAPTER_GEN_FILES:%=$(XMLDIR)/%)
# ----------------------------------------------------
@@ -88,7 +87,7 @@ DVIPS_FLAGS +=
# Targets
# ----------------------------------------------------
-%.xml: $(ERL_TOP)/HOWTO/%.md $(ERL_TOP)/make/emd2exml
+$(XMLDIR)/%.xml: $(ERL_TOP)/HOWTO/%.md $(ERL_TOP)/make/emd2exml
$(ERL_TOP)/make/emd2exml $< $@
$(REDIRECT_HTML_DIR)/%.html: Makefile
@@ -101,17 +100,18 @@ $(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 $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
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 42b979be85..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
# ----------------------------------------------------
@@ -85,6 +86,7 @@ gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
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 *~
diff --git a/system/doc/programming_examples/Makefile b/system/doc/programming_examples/Makefile
index 4d4aebcbc1..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 = \
@@ -80,6 +83,7 @@ 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 *~
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 b09bf5496c..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
# ----------------------------------------------------
@@ -93,6 +94,7 @@ 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 *~
diff --git a/system/doc/reference_manual/errors.xml b/system/doc/reference_manual/errors.xml
index b16c5da6eb..16d3e7590e 100644
--- a/system/doc/reference_manual/errors.xml
+++ b/system/doc/reference_manual/errors.xml
@@ -108,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>
- 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 cf2d5034aa..76b3e92937 100644
--- a/system/doc/reference_manual/expressions.xml
+++ b/system/doc/reference_manual/expressions.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2017</year>
+ <year>2003</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -567,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>
@@ -1340,9 +1344,9 @@ 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
@@ -1362,10 +1366,12 @@ end</code>
the evaluation. In that case the exception is caught and
the patterns <c>ExceptionPattern</c> with the right exception
class <c>Class</c> are sequentially matched against the caught
- exception. An omitted <c>Class</c> is shorthand for <c>throw</c>.
- If a match succeeds and the optional guard sequence
+ exception. If a match succeeds and the optional guard sequence
<c>ExceptionGuardSeq</c> is true, the corresponding
<c>ExceptionBody</c> is evaluated to become the return value.</p>
+ <p><c>Stacktrace</c>, if specified, must be the name of a variable
+ (not a pattern). The stack trace is bound to the variable when
+ the corresponding <c>ExceptionPattern</c> matches.</p>
<p>If an exception occurs during evaluation of <c>Exprs</c> but
there is no matching <c>ExceptionPattern</c> of the right
<c>Class</c> with a true guard sequence, the exception is passed
@@ -1373,6 +1379,18 @@ end</code>
expression.</p>
<p>If an exception occurs during evaluation of <c>ExceptionBody</c>,
it is not caught.</p>
+ <p>It is allowed to omit <c>Class</c> and <c>Stacktrace</c>.
+ An omitted <c>Class</c> is shorthand for <c>throw</c>:</p>
+
+ <code type="none">
+try Exprs
+catch
+ ExceptionPattern1 [when ExceptionGuardSeq1] ->
+ ExceptionBody1;
+ ExceptionPatternN [when ExceptionGuardSeqN] ->
+ ExceptionBodyN
+end</code>
+
<p>The <c>try</c> expression can have an <c>of</c>
section:
</p>
@@ -1384,10 +1402,10 @@ try Exprs of
PatternN [when GuardSeqN] ->
BodyN
catch
- [Class1:]ExceptionPattern1 [when ExceptionGuardSeq1] ->
+ Class1:ExceptionPattern1[:Stacktrace] [when ExceptionGuardSeq1] ->
ExceptionBody1;
...;
- [ClassN:]ExceptionPatternN [when ExceptionGuardSeqN] ->
+ ClassN:ExceptionPatternN[:Stacktrace] [when ExceptionGuardSeqN] ->
ExceptionBodyN
end</code>
<p>If the evaluation of <c>Exprs</c> succeeds without an exception,
@@ -1408,10 +1426,10 @@ try Exprs of
PatternN [when GuardSeqN] ->
BodyN
catch
- [Class1:]ExceptionPattern1 [when ExceptionGuardSeq1] ->
+ Class1:ExceptionPattern1[:Stacktrace] [when ExceptionGuardSeq1] ->
ExceptionBody1;
...;
- [ClassN:]ExceptionPatternN [when ExceptionGuardSeqN] ->
+ ClassN:ExceptionPatternN[:Stacktrace] [when ExceptionGuardSeqN] ->
ExceptionBodyN
after
AfterBody
@@ -1470,7 +1488,7 @@ try Expr
catch
throw:Term -> Term;
exit:Reason -> {'EXIT',Reason}
- error:Reason -> {'EXIT',{Reason,erlang:get_stacktrace()}}
+ error:Reason:Stk -> {'EXIT',{Reason,Stk}}
end</code>
</section>
diff --git a/system/doc/reference_manual/introduction.xml b/system/doc/reference_manual/introduction.xml
index afbdfa7434..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>2017</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>
diff --git a/system/doc/reference_manual/macros.xml b/system/doc/reference_manual/macros.xml
index a341307ab7..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>2017</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>
diff --git a/system/doc/reference_manual/modules.xml b/system/doc/reference_manual/modules.xml
index 4a97bfeb7b..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>2017</year>
+ <year>2003</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -307,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/typespec.xml b/system/doc/reference_manual/typespec.xml
index f6a19397c3..27cd0ba83d 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>2017</year>
+ <year>2003</year><year>2018</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,8 +123,17 @@
| 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
@@ -151,8 +160,13 @@
Union :: Type1 | Type2
]]></pre>
<p>
+ Integer values are either integer or character literals or expressions
+ consisting of possibily nested unary or binary operations that evaluate to
+ an integer. Such expressions can also be used in bit strings and ranges.
+ </p>
+ <p>
The general form of bit strings is <c>&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).
diff --git a/system/doc/reference_manual/xmlfiles.mk b/system/doc/reference_manual/xmlfiles.mk
index fffcbdd911..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.
diff --git a/system/doc/system_architecture_intro/Makefile b/system/doc/system_architecture_intro/Makefile
index 5000df6038..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
# ----------------------------------------------------
@@ -78,6 +79,7 @@ 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 *~
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_principles/Makefile b/system/doc/system_principles/Makefile
index 657e02024f..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
@@ -77,6 +80,7 @@ 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 *~
diff --git a/system/doc/system_principles/create_target.xmlsrc b/system/doc/system_principles/create_target.xmlsrc
index f9b27ffc35..47b84e5760 100644
--- a/system/doc/system_principles/create_target.xmlsrc
+++ b/system/doc/system_principles/create_target.xmlsrc
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2002</year><year>2016</year>
+ <year>2002</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -263,6 +263,14 @@ os> <input>/usr/local/erl-target/bin/erl -boot /usr/local/erl-target/releases/FI
current directory create not only the file <c>mysystem.rel</c>,
but also file <c>sys.config</c>, the latter file is tacitly
put in the appropriate directory.</p>
+ <p>However, it can also be convenient to replace variables in
+ within a <c>sys.config</c> on the target after unpacking but
+ before running the release. If you have a <c>sys.config.src</c>
+ it will be included and is not required to be a valid Erlang term
+ file like <c>sys.config</c>. Before running the release you must
+ have a valid <c>sys.config</c> in the same directory, so using
+ <c>sys.config.src</c> requires having some tool to populate what is
+ needed and write <c>sys.config</c> to disk before booting the release.</p>
</section>
<section>
diff --git a/system/doc/system_principles/error_logging.xml b/system/doc/system_principles/error_logging.xml
index c99d59ddb7..9cbf7a2e94 100644
--- a/system/doc/system_principles/error_logging.xml
+++ b/system/doc/system_principles/error_logging.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2016</year>
+ <year>2003</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -39,80 +39,71 @@
<code type="none"><![CDATA[
=ERROR REPORT==== 9-Dec-2003::13:25:02 ===
Error in process <0.27.0> with exit value: {{badmatch,[1,2,3]},[{m,f,1},{shell,eval_loop,2}]}]]></code>
- <p>The error information is handled by the <em>error logger</em>, a
- system process registered as <c>error_logger</c>. This process
- receives all error messages from the Erlang runtime system as
- well as from the standard behaviours and different Erlang/OTP
- applications.</p>
+ <p>The error information is handled by Logger, which is part of
+ the Kernel application.</p>
<p>The exit reasons (such as <c>badarg</c>) used by
the runtime system are described in
<seealso marker="doc/reference_manual:errors#exit_reasons">
Errors and Error Handling</seealso>.</p>
- <p>For information about the process <c>error_logger</c> and its user
- interface (with the same name), see the
- <seealso marker="kernel:error_logger">error_logger(3)</seealso>
- manual page in Kernel. The system can be configured so that
- error information
- is written to file or to tty, or both. In addition, user-defined
- applications can send and format error information using
- <c>error_logger</c>.</p>
+ <p>For information about Logger and its user
+ interface, see the
+ <seealso marker="kernel:logger">logger(3)</seealso>
+ manual page and
+ the <seealso marker="kernel:logger_chapter">Logging</seealso>
+ section in the Kernel User's Guide. The system can be configured so that
+ log events are
+ written to file or to tty, or both. In addition, user-defined
+ applications can send and format log events using
+ Logger.</p>
</section>
<section>
- <title>SASL Error Logging</title>
+ <title>Log events from OTP behaviours</title>
<p>The standard behaviours (<c>supervisor</c>, <c>gen_server</c>,
- and so on) send progress and error information to <c>error_logger</c>.
- If the SASL application is started, this information is
- written to tty as well. For more information, see
+ and so on) send progress and error information to
+ Logger. Progress reports are by default not logged, but can be
+ enabled by setting the primary log level to <c>info</c>, for
+ example by using the Kernel configuration
+ parameter <c>logger_level</c>. Supervisor reports, crash reports
+ and other error and information reports are by default logged
+ through the log handler which is set up when the Kernel
+ application is started.</p>
+ <p>Prior to Erlang/OTP 21.0, supervisor, crash, and progress
+ reports were only logged when the SASL application was
+ running. This behaviour can, for backwards compatibility, be
+ enabled by setting the Kernel configuration
+ parameter <seealso marker="kernel:kernel_app#logger_sasl_compatible">
+ <c>logger_sasl_compatible</c></seealso>
+ to <c>true</c>. For more information, see
<seealso marker="sasl:error_logging">SASL Error Logging</seealso>
in the SASL User's Guide.</p>
<pre>
-% <input>erl -boot start_sasl</input>
-Erlang (BEAM) emulator version 5.4.13 [hipe] [threads:0] [kernel-poll]
+% <input>erl -kernel logger_level info</input>
+Erlang/OTP 21 [erts-10.0] [source-13c50db] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe]
-
-=PROGRESS REPORT==== 31-Mar-2006::12:45:58 ===
- supervisor: {local,sasl_safe_sup}
- started: [{pid,&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..dd6c2a1336
--- /dev/null
+++ b/system/doc/system_principles/misc.xml
@@ -0,0 +1,198 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2018</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ </legalnotice>
+
+ <title>Support, Compatibility, Deprecations, and Removal</title>
+ <prepared></prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date>2018-05-21</date>
+ <rev></rev>
+ <file>misc.xml</file>
+ </header>
+
+ <section>
+ <marker id="supported_releases"/>
+ <title>Supported Releases</title>
+ <p>
+ In general, bugs are only fixed on the latest
+ <seealso marker="versions#releases_and_patches">release</seealso>,
+ and new features are introduced in the upcoming release that is
+ under development. However, when we, due to internal reasons, fix
+ bugs on older releases, these will be available and announced as well.
+ </p>
+ <p>
+ Due to the above, pull requests are only accepted on the
+ <c>maint</c> and the <c>master</c> branches in our
+ <url href="https://github.com/erlang/otp">git repository</url>.
+ The <c>maint</c> branch contains changes planned for the next
+ <seealso marker="versions#releases_and_patches">maintenance patch package</seealso>
+ on the latest OTP release and the <c>master</c> branch contain
+ changes planned for the upcoming OTP release.
+ </p>
+ </section>
+
+ <section>
+ <marker id="compatibility"/>
+ <title>Compatibility</title>
+ <p>
+ We always strive to remain as compatible as possible
+ even in the cases where we give no compatibility guarantees.
+ </p>
+ <p>
+ Different parts of the system will be handled differently
+ regarding compatibility. The following items describe how
+ different parts of the system are handled.
+ </p>
+ <taglist>
+ <tag>Erlang Distribution</tag>
+ <item>
+ <p>
+ Erlang nodes can communicate across at least
+ two preceding and two subsequent releases.
+ </p>
+ </item>
+ <tag>Compiled BEAM Code, NIF Libraries and Drivers</tag>
+ <item>
+ <p>
+ Compiled code can be loaded on at least two
+ subsequent releases.
+ </p>
+ <p>
+ Loading on previous releases is <em>not</em> supported.
+ </p>
+ </item>
+ <tag>Compiled HiPE Code</tag>
+ <item>
+ <p>
+ Compiled HiPE code can be loaded on the exact same build
+ of ERTS that was used when compiling the code. It might
+ however work on other builds, the emulator verifies
+ checksums in order to determine if it can load the code
+ or not. Note that HiPE has some limitations. For more
+ information see the documentation of the
+ <seealso marker="hipe:HiPE_app">HiPE</seealso> application.
+ </p>
+ </item>
+ <tag>APIs</tag>
+ <item>
+ <p>Compatible between releases.</p>
+ </item>
+ <tag>Compiler Warnings</tag>
+ <item>
+ <p>New warnings may be issued between releases.</p>
+ </item>
+ <tag>Command Line Arguments</tag>
+ <item>
+ <p>Incompatible changes may occur between releases.</p>
+ </item>
+ <tag>OTP Build Procedures</tag>
+ <item><p>Incompatible changes may occur between releases.</p></item>
+ </taglist>
+ <p>
+ Under certain circumstances incompatible changes might be
+ introduced even in parts of the system that should be compatible
+ between releases. Things that might trigger incompatible changes
+ like this are:
+ </p>
+ <taglist>
+ <tag>Security Issues</tag>
+ <item>
+ <p>
+ It might be necessary to introduce incompatible changes
+ in order to solve a security issue. This kind of
+ incompatibility might occur in a patch.
+ </p>
+ </item>
+ <tag>Bug Fixes</tag>
+ <item>
+ <p>
+ We will not be bug-compatible. A bug fix might introduce
+ incompatible changes. This kind of incompatibility
+ might occur in a patch.
+ </p>
+ </item>
+ <tag>Severe Previous Design Issues</tag>
+ <item>
+ <p>
+ Some parts of OTP were designed a very long time ago and
+ did not necessarily take today's computing environments into
+ account. In some cases the consequences of those design
+ decisions are too severe. This may be performance wise,
+ scalability wise, etc. If we deem the consequences too
+ severe, we might introduce incompatible changes. This kind
+ of incompatibility will not be introduced in a patch, but
+ instead in the next release.
+ </p>
+ </item>
+ </taglist>
+ <p>
+ Peripheral, trace, and debug functionality is at greater
+ risk of being changed in an incompatible way than functionality
+ in the language itself and core libraries used during operation.
+ </p>
+ </section>
+
+ <section>
+ <marker id="deprecation"/>
+ <title>Deprecation</title>
+ <p>
+ Functionality is deprecated when new functionality is
+ introduced that is preferred to be used instead of the
+ old functionality that is being deprecated. The deprecation
+ does <em>not</em> imply removal of the functionality unless
+ an upcoming removal is explicitly stated in the deprecation.
+ </p>
+ <p>
+ Deprecated functionality will be documented as deprecated, and
+ compiler warnings will be issued, when appropriate, as
+ early as possible. That is, the new preferred functionality
+ will appear at the same time as the deprecation is issued.
+ A new deprecation will at least be announced in a release
+ note and the documentation.
+ </p>
+ </section>
+
+ <section>
+ <marker id="removal"/>
+ <title>Removal</title>
+ <p>
+ Legacy solutions may eventually need to be removed. In such
+ cases, they will be phased out on a long enough time period
+ to give users the time to adapt. Before removal of
+ functionality it will be deprecated at least during one
+ release with an explicit announcement about
+ the upcoming removal. A new deprecation will at least be
+ announced in a release note and the documentation.
+ </p>
+ <p>
+ Peripheral, trace, and debug functionality is at greater
+ risk of removal than functionality in the language itself
+ and core libraries used during operation.
+ </p>
+ </section>
+
+</chapter>
+
diff --git a/system/doc/system_principles/part.xml b/system/doc/system_principles/part.xml
index 1b87ecd350..6699389eec 100644
--- a/system/doc/system_principles/part.xml
+++ b/system/doc/system_principles/part.xml
@@ -4,7 +4,7 @@
<part xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>1997</year><year>2016</year>
+ <year>1997</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -34,4 +34,5 @@
<xi:include href="create_target.xml"/>
<xi:include href="upgrade.xml"/>
<xi:include href="versions.xml"/>
+ <xi:include href="misc.xml"/>
</part>
diff --git a/system/doc/system_principles/system_principles.xml b/system/doc/system_principles/system_principles.xml
index 3605df3ade..500522d778 100644
--- a/system/doc/system_principles/system_principles.xml
+++ b/system/doc/system_principles/system_principles.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1996</year><year>2015</year>
+ <year>1996</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -93,7 +93,7 @@ init:stop()</pre>
{progress,kernel_load_completed}
{progress,modules_loaded}
{start,heart}
-{start,error_logger}
+{start,logger}
...</pre>
<p>For a detailed description of the syntax and contents of the
boot script, see the <c>script(4)</c> manual page in SASL.</p>
diff --git a/system/doc/system_principles/versions.xml b/system/doc/system_principles/versions.xml
index b9f7fa4bf6..fbdcc6b2b0 100644
--- a/system/doc/system_principles/versions.xml
+++ b/system/doc/system_principles/versions.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2014</year><year>2016</year>
+ <year>2014</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,9 +32,9 @@
<rev></rev>
<file>versions.xml</file>
</header>
- <marker id="versions section"></marker>
<section>
+ <marker id="versions section"></marker>
<title>OTP Version</title>
<p>As of OTP release 17, the OTP release number corresponds to
the major part of the OTP version. The OTP version as a concept was
@@ -136,8 +136,8 @@
</section>
<section>
- <title>Version Scheme</title>
<marker id="version_scheme"/>
+ <title>Version Scheme</title>
<note><p>The version scheme was changed as of OTP 17.0. This implies
that application versions used prior to OTP 17.0 do not adhere to this
version scheme. <seealso marker="#otp_17_0_app_versions">A list of
@@ -181,7 +181,7 @@
goes for application versions.</p>
<p>In general, versions can have more than three parts.
The versions are then only partially ordered. Such
- versions are only used in exceptional cases. When an extra
+ versions are only used when branching off from another branch. When an extra
part (out of the normal three parts) is added to a version number,
a new branch of versions is made. The new branch has a linear
order against the base version. However, versions on different
@@ -207,8 +207,68 @@
</section>
<section>
- <title>OTP 17.0 Application Versions</title>
+ <marker id="releases_and_patches"/>
+ <title>Releases and Patches</title>
+ <p>
+ When a new OTP release is released it will have an OTP
+ version on the form <c>&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/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 ff8b0c3cd6..3267bf96df 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.
@@ -53,30 +53,45 @@ include ../oam/xmlfiles.mk
BOOK_FILES = book.xml
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)
-
-XMLLINT_SRCDIRS= ../installation_guide:../system_principles:../embedded:../getting_started:../reference_manual:../programming_examples:../efficiency_guide:../tutorial:../design_principles:../oam
+XML_GUIDE_FILES = \
+ $(INST_GUIDE_CHAPTER_FILES:%=installation_guide/%) \
+ $(INST_GUIDE_CHAPTER_GEN_FILES:%=installation_guide/%) \
+ $(SYSTEM_PRINCIPLES_CHAPTER_FILES:%=system_principles/%) \
+ $(SYSTEM_PRINCIPLES_CHAPTER_GEN_FILES:%=system_principles/%) \
+ $(EMBEDDED_CHAPTER_FILES:%=embedded/%) \
+ $(EMBEDDED_CHAPTER_GEN_FILES:%=embedded/%) \
+ $(GETTING_STARTED_CHAPTER_FILES:%=getting_started/%) \
+ $(GETTING_STARTED_CHAPTER_GEN_FILES:%=getting_started/%) \
+ $(REF_MAN_CHAPTER_FILES:%=reference_manual/%) \
+ $(REF_MAN_CHAPTER_GEN_FILES:%=reference_manual/%) \
+ $(PROG_EX_CHAPTER_FILES:%=programming_examples/%) \
+ $(PROG_EX_CHAPTER_GEN_FILES:%=programming_examples/%) \
+ $(EFF_GUIDE_CHAPTER_FILES:%=efficiency_guide/%) \
+ $(EFF_GUIDE_CHAPTER_GEN_FILES:%=efficiency_guide/%) \
+ $(TUTORIAL_CHAPTER_FILES:%=tutorial/%) \
+ $(TUTORIAL_CHAPTER_GEN_FILES:%=tutorial/%) \
+ $(DESIGN_PRINCIPLES_CHAPTER_FILES:%=design_principles/%) \
+ $(DESIGN_PRINCIPLES_CHAPTER_GEN_FILES:%=design_principles/%) \
+ $(OAM_CHAPTER_FILES:%=oam/%) \
+ $(OAM_CHAPTER_GEN_FILES:%=oam/%)
+
+XML_GEN_FILES = \
+ $(XML_GUIDE_FILES:%=$(XMLDIR)/%) \
+ $(XMLDIR)/installation_guide/part.xml \
+ $(XMLDIR)/system_principles/part.xml \
+ $(XMLDIR)/embedded/part.xml \
+ $(XMLDIR)/getting_started/part.xml \
+ $(XMLDIR)/reference_manual/part.xml \
+ $(XMLDIR)/programming_examples/part.xml \
+ $(XMLDIR)/efficiency_guide/part.xml \
+ $(XMLDIR)/tutorial/part.xml \
+ $(XMLDIR)/design_principles/part.xml \
+ $(XMLDIR)/oam/part.xml
+
+
+XMLLINT_SRCDIRS= $(XMLDIR)/installation_guide:$(XMLDIR)/system_principles:$(XMLDIR)/embedded:$(XMLDIR)/getting_started:$(XMLDIR)/reference_manual:$(XMLDIR)/programming_examples:$(XMLDIR)/efficiency_guide:$(XMLDIR)/tutorial:$(XMLDIR)/design_principles:$(XMLDIR)/oam
HTMLDIR= ../html
PDFREFDIR= pdf
@@ -247,7 +262,8 @@ debug opt:
clean:
$(RM) ../html/js/*.js
- $(RM) PR.template
+ $(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)
diff --git a/system/doc/top/book.xml b/system/doc/top/book.xml
index c94b0f24d6..61e75591ef 100644
--- a/system/doc/top/book.xml
+++ b/system/doc/top/book.xml
@@ -4,7 +4,7 @@
<book xmlns:xi="http://www.w3.org/2001/XInclude">
<header titlestyle="normal">
<copyright>
- <year>1997</year><year>2016</year>
+ <year>1997</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -36,16 +36,16 @@
<contents level="2"></contents>
</preamble>
<parts lift="no">
- <xi:include href="../installation_guide/part.xml"/>
- <xi:include href="../system_principles/part.xml"/>
- <xi:include href="../embedded/part.xml"/>
- <xi:include href="../getting_started/part.xml"/>
- <xi:include href="../reference_manual/part.xml"/>
- <xi:include href="../programming_examples/part.xml"/>
- <xi:include href="../efficiency_guide/part.xml"/>
- <xi:include href="../tutorial/part.xml"/>
- <xi:include href="../design_principles/part.xml"/>
- <xi:include href="../oam/part.xml"/>
+ <xi:include href="../xml/installation_guide/part.xml"/>
+ <xi:include href="../xml/system_principles/part.xml"/>
+ <xi:include href="../xml/embedded/part.xml"/>
+ <xi:include href="../xml/getting_started/part.xml"/>
+ <xi:include href="../xml/reference_manual/part.xml"/>
+ <xi:include href="../xml/programming_examples/part.xml"/>
+ <xi:include href="../xml/efficiency_guide/part.xml"/>
+ <xi:include href="../xml/tutorial/part.xml"/>
+ <xi:include href="../xml/design_principles/part.xml"/>
+ <xi:include href="../xml/oam/part.xml"/>
</parts>
<listofterms></listofterms>
<index></index>
diff --git a/system/doc/tutorial/Makefile b/system/doc/tutorial/Makefile
index 2eeb096e23..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 = \
@@ -104,6 +108,7 @@ gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
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 *~
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/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