aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorSiri Hansen <[email protected]>2014-11-06 14:48:36 +0100
committerSiri Hansen <[email protected]>2014-11-06 14:48:36 +0100
commiteeba41c3f9a56948d62de66065851f509ae02b43 (patch)
treec73dd9ceb2fbd71e5c0a6713715b7a4df56838db /lib
parent5349afa77f6daca3525ed1b5e1081c5379a4ca65 (diff)
parentd69c45710f1beb570151dd6e2d35d6a3925d9029 (diff)
downloadotp-eeba41c3f9a56948d62de66065851f509ae02b43.tar.gz
otp-eeba41c3f9a56948d62de66065851f509ae02b43.tar.bz2
otp-eeba41c3f9a56948d62de66065851f509ae02b43.zip
Merge branch 'siri/sup-spec-maps/OTP-11043'
* siri/sup-spec-maps/OTP-11043: Update emacs skeleton for supervisor to use maps Update dialyzer test with maps in supervisor properties Add documentation of maps in supervisor flags and child specs Add test of maps in supervisor flags and child specs New function supervisor:get_childspec/2 Allow maps for supervisor flags and child specs Rebase supervisor Add more tests of supervisor
Diffstat (limited to 'lib')
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/results/supervisor_incorrect_return2
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/custom_sup.erl2
-rw-r--r--lib/stdlib/doc/src/supervisor.xml255
-rw-r--r--lib/stdlib/src/supervisor.erl177
-rw-r--r--lib/stdlib/test/supervisor_SUITE.erl305
-rw-r--r--lib/tools/emacs/erlang-skels.el29
6 files changed, 598 insertions, 172 deletions
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/results/supervisor_incorrect_return b/lib/dialyzer/test/behaviour_SUITE_data/results/supervisor_incorrect_return
index e89caf3cf7..4103a2d8b4 100644
--- a/lib/dialyzer/test/behaviour_SUITE_data/results/supervisor_incorrect_return
+++ b/lib/dialyzer/test/behaviour_SUITE_data/results/supervisor_incorrect_return
@@ -1,2 +1,2 @@
-supervisor_incorrect_return.erl:14: The inferred return type of init/1 ({'ok',{{'one_against_one',0,1},[{_,_,_,_,_,_},...]}}) has nothing in common with 'ignore' | {'ok',{{'one_for_all',non_neg_integer(),non_neg_integer()} | {'one_for_one',non_neg_integer(),non_neg_integer()} | {'rest_for_one',non_neg_integer(),non_neg_integer()} | {'simple_one_for_one',non_neg_integer(),non_neg_integer()},[{_,{atom() | tuple(),atom(),'undefined' | [any()]},'permanent' | 'temporary' | 'transient','brutal_kill' | 'infinity' | non_neg_integer(),'supervisor' | 'worker','dynamic' | [atom() | tuple()]}]}}, which is the expected return type for the callback of supervisor behaviour
+supervisor_incorrect_return.erl:14: The inferred return type of init/1 ({'ok',{{'one_against_one',0,1},[{_,_,_,_,_,_},...]}}) has nothing in common with 'ignore' | {'ok',{{'one_for_all',non_neg_integer(),pos_integer()} | {'one_for_one',non_neg_integer(),pos_integer()} | {'rest_for_one',non_neg_integer(),pos_integer()} | {'simple_one_for_one',non_neg_integer(),pos_integer()} | #{},[{_,{atom() | tuple(),atom(),'undefined' | [any()]},'permanent' | 'temporary' | 'transient','brutal_kill' | 'infinity' | non_neg_integer(),'supervisor' | 'worker','dynamic' | [atom() | tuple()]} | #{}]}}, which is the expected return type for the callback of supervisor behaviour
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/custom_sup.erl b/lib/dialyzer/test/behaviour_SUITE_data/src/custom_sup.erl
index 76da1fda70..401ee88eab 100644
--- a/lib/dialyzer/test/behaviour_SUITE_data/src/custom_sup.erl
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/custom_sup.erl
@@ -13,7 +13,7 @@
-export([init/1]).
-spec init(atom()) ->
- {ok, {{supervisor:strategy(), non_neg_integer(), non_neg_integer()},
+ {ok, {{supervisor:strategy(), non_neg_integer(), pos_integer()},
[supervisor:child_spec()]}} | ignore.
init(StorageName) ->
diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml
index 3a5027d595..ffac1c0bd7 100644
--- a/lib/stdlib/doc/src/supervisor.xml
+++ b/lib/stdlib/doc/src/supervisor.xml
@@ -37,12 +37,12 @@
the <c>gen_event</c>, <c>gen_fsm</c>, or <c>gen_server</c>
behaviours. A supervisor implemented using this module will have
a standard set of interface functions and include functionality
- for tracing and error reporting. Supervisors are used to build an
+ for tracing and error reporting. Supervisors are used to build a
hierarchical process structure called a supervision tree, a
nice way to structure a fault tolerant application. Refer to
<em>OTP Design Principles</em> for more information.</p>
- <p>A supervisor assumes the definition of which child processes to
- supervise to be located in a callback module exporting a
+ <p>A supervisor expects the definition of which child processes to
+ supervise to be specified in a callback module exporting a
pre-defined set of functions.</p>
<p>Unless otherwise stated, all functions in this module will fail
if the specified supervisor does not exist or if bad arguments
@@ -53,18 +53,30 @@
<title>Supervision Principles</title>
<p>The supervisor is responsible for starting, stopping and
monitoring its child processes. The basic idea of a supervisor is
- that it should keep its child processes alive by restarting them
+ that it shall keep its child processes alive by restarting them
when necessary.</p>
- <p>The children of a supervisor is defined as a list of
+ <p>The children of a supervisor are defined as a list of
<em>child specifications</em>. When the supervisor is started, the child
processes are started in order from left to right according to
this list. When the supervisor terminates, it first terminates
its child processes in reversed start order, from right to left.</p>
- <p>A supervisor can have one of the following <em>restart strategies</em>:</p>
+ <marker id="sup_flags"/>
+ <p>The properties of a supervisor are defined by the supervisor
+ flags. This is the type definition for the supervisor flags:
+ </p>
+ <pre>sup_flags() = #{strategy => strategy(), % optional
+ intensity => non_neg_integer(), % optional
+ period => pos_integer()} % optional
+ </pre>
+ <p>A supervisor can have one of the following <em>restart
+ strategies</em>, specified with the <c>strategy</c> key in the
+ above map:
+ </p>
<list type="bulleted">
<item>
<p><c>one_for_one</c> - if one child process terminates and
- should be restarted, only that child process is affected.</p>
+ should be restarted, only that child process is
+ affected. This is the default restart strategy.</p>
</item>
<item>
<p><c>one_for_all</c> - if one child process terminates and
@@ -94,43 +106,53 @@
instead the child specification identifier is used,
<c>terminate_child/2</c> will return
<c>{error,simple_one_for_one}</c>.</p>
- <p>Because a <c>simple_one_for_one</c> supervisor could have many
- children, it shuts them all down at same time. So, order in which they
- are stopped is not defined. For the same reason, it could have an
- overhead with regards to the <c>Shutdown</c> strategy.</p>
+ <p>Because a <c>simple_one_for_one</c> supervisor could have
+ many children, it shuts them all down asynchronously. This
+ means that the children will do their cleanup in parallel,
+ and therefore the order in which they are stopped is not
+ defined.</p>
</item>
</list>
<p>To prevent a supervisor from getting into an infinite loop of
- child process terminations and restarts, a <em>maximum restart frequency</em>
- is defined using two integer values <c>MaxR</c>
- and <c>MaxT</c>. If more than <c>MaxR</c> restarts occur within
- <c>MaxT</c> seconds, the supervisor terminates all child
- processes and then itself.
+ child process terminations and restarts, a <em>maximum restart
+ intensity</em> is defined using two integer values specified
+ with the <c>intensity</c> and <c>period</c> keys in the above
+ map. Assuming the values <c>MaxR</c> for <c>intensity</c>
+ and <c>MaxT</c> for <c>period</c>, then if more than <c>MaxR</c>
+ restarts occur within <c>MaxT</c> seconds, the supervisor will
+ terminate all child processes and then itself. The default value
+ for <c>intensity</c> is <c>1</c>, and the default value
+ for <c>period</c> is <c>5</c>.
</p>
<marker id="child_spec"/>
<p>This is the type definition of a child specification:</p>
- <pre>
-child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
- Id = term()
- StartFunc = {M,F,A}
- M = F = atom()
- A = [term()]
- Restart = permanent | transient | temporary
- Shutdown = brutal_kill | int()>0 | infinity
- Type = worker | supervisor
- Modules = [Module] | dynamic
- Module = atom()</pre>
+ <pre>child_spec() = #{id => child_id(), % mandatory
+ start => mfargs(), % mandatory
+ restart => restart(), % optional
+ shutdown => shutdown(), % optional
+ type => worker(), % optional
+ modules => modules()} % optional</pre>
+ <p>The old tuple format is kept for backwards compatibility,
+ see <seealso marker="#type-child_spec">child_spec()</seealso>,
+ but the map is preferred.
+ </p>
<list type="bulleted">
<item>
- <p><c>Id</c> is a name that is used to identify the child
+ <p><c>id</c> is used to identify the child
specification internally by the supervisor.</p>
+ <p>The <c>id</c> key is mandatory.</p>
+ <p>Note that this identifier on occations has been called
+ "name". As far as possible, the terms "identifier" or "id"
+ are now used but in order to keep backwards compatibility,
+ some occurences of "name" can still be found, for example
+ in error messages.</p>
</item>
<item>
- <p><c>StartFunc</c> defines the function call used to start
- the child process. It should be a module-function-arguments
+ <p><c>start</c> defines the function call used to start the
+ child process. It must be a module-function-arguments
tuple <c>{M,F,A}</c> used as <c>apply(M,F,A)</c>.</p>
<p>The start function <em>must create and link to</em> the child
- process, and should return <c>{ok,Child}</c> or
+ process, and must return <c>{ok,Child}</c> or
<c>{ok,Child,Info}</c> where <c>Child</c> is the pid of
the child process and <c>Info</c> an arbitrary term which is
ignored by the supervisor.</p>
@@ -143,20 +165,23 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
error tuple <c>{error,Error}</c>.</p>
<p>Note that the <c>start_link</c> functions of the different
behaviour modules fulfill the above requirements.</p>
+ <p>The <c>start</c> key is mandatory.</p>
</item>
<item>
- <p><c>Restart</c> defines when a terminated child process
- should be restarted. A <c>permanent</c> child process should
- always be restarted, a <c>temporary</c> child process should
+ <p><c>restart</c> defines when a terminated child process
+ shall be restarted. A <c>permanent</c> child process will
+ always be restarted, a <c>temporary</c> child process will
never be restarted (even when the supervisor's restart strategy
is <c>rest_for_one</c> or <c>one_for_all</c> and a sibling's
death causes the temporary process to be terminated) and a
- <c>transient</c> child process should be restarted only if
+ <c>transient</c> child process will be restarted only if
it terminates abnormally, i.e. with another exit reason
than <c>normal</c>, <c>shutdown</c> or <c>{shutdown,Term}</c>.</p>
+ <p>The <c>restart</c> key is optional. If it is not given, the
+ default value <c>permanent</c> will be used.</p>
</item>
<item>
- <p><c>Shutdown</c> defines how a child process should be
+ <p><c>shutdown</c> defines how a child process shall be
terminated. <c>brutal_kill</c> means the child process will
be unconditionally terminated using <c>exit(Child,kill)</c>.
An integer timeout value means that the supervisor will tell
@@ -166,35 +191,45 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
no exit signal is received within the specified number of milliseconds,
the child process is unconditionally terminated using
<c>exit(Child,kill)</c>.</p>
- <p>If the child process is another supervisor, <c>Shutdown</c>
+ <p>If the child process is another supervisor, the shutdown time
should be set to <c>infinity</c> to give the subtree ample
- time to shutdown. It is also allowed to set it to <c>infinity</c>,
+ time to shut down. It is also allowed to set it to <c>infinity</c>,
if the child process is a worker.</p>
<warning>
- <p>Be careful by setting the <c>Shutdown</c> strategy to
+ <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
child process, it must be implemented in a safe way and its cleanup
procedure must always return.</p>
</warning>
<p>Note that all child processes implemented using the standard
- OTP behavior modules automatically adhere to the shutdown
+ OTP behaviour modules automatically adhere to the shutdown
protocol.</p>
+ <p>The <c>shutdown</c> key is optional. If it is not given,
+ the default value <c>5000</c> will be used if the child is
+ of type <c>worker</c>; and <c>infinity</c> will be used if
+ the child is of type <c>supervisor</c>.</p>
</item>
<item>
- <p><c>Type</c> specifies if the child process is a supervisor or
+ <p><c>type</c> specifies if the child process is a supervisor or
a worker.</p>
+ <p>The <c>type</c> key is optional. If it is not given, the
+ default value <c>worker</c> will be used.</p>
</item>
<item>
- <p><c>Modules</c> is used by the release handler during code
+ <p><c>modules</c> is used by the release handler during code
replacement to determine which processes are using a certain
- module. As a rule of thumb <c>Modules</c> should be a list
- with one element <c>[Module]</c>, where <c>Module</c> is
- the callback module, if the child process is a supervisor,
- gen_server or gen_fsm. If the child process is an event
- manager (gen_event) with a dynamic set of callback modules,
- <c>Modules</c> should be <c>dynamic</c>. See <em>OTP Design Principles</em>
- for more information about release handling.</p>
+ module. As a rule of thumb, if the child process is a
+ <c>supervisor</c>, <c>gen_server</c>, or <c>gen_fsm</c>,
+ this should be a list with one element <c>[Module]</c>,
+ where <c>Module</c> is the callback module. If the child
+ process is an event manager (<c>gen_event</c>) with a
+ dynamic set of callback modules, the value <c>dynamic</c>
+ shall be used. See <em>OTP Design Principles</em> for more
+ information about release handling.</p>
+ <p>The <c>modules</c> key is optional. If it is not given, it
+ defaults to <c>[M]</c>, where <c>M</c> comes from the
+ child's start <c>{M,F,A}</c></p>
</item>
<item>
<p>Internally, the supervisor also keeps track of the pid
@@ -213,11 +248,20 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
</datatype>
<datatype>
<name name="child_spec"/>
+ <desc><p>The tuple format is kept for backwards compatibility
+ only. A map is preferred; see more details
+ <seealso marker="#child_spec">above</seealso>.</p></desc>
</datatype>
<datatype>
<name name="mfargs"/>
- <desc><p><c>A</c> (the argument list) has the value
- <c>undefined</c> if <c>Restart</c> is <c>temporary</c>.</p>
+ <desc>
+ <p>The value <c>undefined</c> for <c><anno>A</anno></c> (the
+ argument list) is only to be used internally
+ in <c>supervisor</c>. If the restart type of the child
+ is <c>temporary</c>, then the process is never to be
+ restarted and therefore there is no need to store the real
+ argument list. The value <c>undefined</c> will then be
+ stored instead.</p>
</desc>
</datatype>
<datatype>
@@ -233,6 +277,12 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
<name name="strategy"/>
</datatype>
<datatype>
+ <name name="sup_flags"/>
+ <desc><p>The tuple format is kept for backwards compatibility
+ only. A map is preferred; see more details
+ <seealso marker="#sup_flags">above</seealso>.</p></desc>
+ </datatype>
+ <datatype>
<name name="sup_ref"/>
</datatype>
<datatype>
@@ -253,20 +303,20 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
the supervisor is linked to the calling process (its
supervisor).</p>
<p>The created supervisor process calls <c><anno>Module</anno>:init/1</c> to
- find out about restart strategy, maximum restart frequency
+ find out about restart strategy, maximum restart intensity
and child processes. To ensure a synchronized start-up
procedure, <c>start_link/2,3</c> does not return until
<c><anno>Module</anno>:init/1</c> has returned and all child processes
have been started.</p>
- <p>If <c><anno>SupName</anno>={local,Name}</c> the supervisor is registered
+ <p>If <c><anno>SupName</anno>={local,Name}</c>, the supervisor is registered
locally as <c>Name</c> using <c>register/2</c>. If
<c><anno>SupName</anno>={global,Name}</c> the supervisor is registered
globally as <c>Name</c> using <c>global:register_name/2</c>. If
<c><anno>SupName</anno>={via,<anno>Module</anno>,<anno>Name</anno>}</c> the supervisor
is registered as <c>Name</c> using the registry represented by
- <c>Module</c>. The <c>Module</c> callback should export the functions
+ <c>Module</c>. The <c>Module</c> callback must export the functions
<c>register_name/2</c>, <c>unregister_name/1</c> and <c>send/2</c>,
- which should behave like the corresponding functions in <c>global</c>.
+ which shall behave like the corresponding functions in <c>global</c>.
Thus, <c>{via,global,<anno>Name</anno>}</c> is a valid reference.</p>
<p>If no name is provided, the supervisor is not registered.</p>
<p><c><anno>Module</anno></c> is the name of the callback module.</p>
@@ -274,14 +324,14 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
the argument to <c><anno>Module</anno>:init/1</c>.</p>
<p>If the supervisor and its child processes are successfully
created (i.e. if all child process start functions return
- <c>{ok,Child}</c>, <c>{ok,Child,Info}</c>, or <c>ignore</c>)
+ <c>{ok,Child}</c>, <c>{ok,Child,Info}</c>, or <c>ignore</c>),
the function returns <c>{ok,Pid}</c>, where <c>Pid</c> is
the pid of the supervisor. If there already exists a process
- with the specified <c><anno>SupName</anno></c> the function returns
+ with the specified <c><anno>SupName</anno></c>, the function returns
<c>{error,{already_started,Pid}}</c>, where <c>Pid</c> is
the pid of that process.</p>
<p>If <c><anno>Module</anno>:init/1</c> returns <c>ignore</c>, this function
- returns <c>ignore</c> as well and the supervisor terminates
+ returns <c>ignore</c> as well, and the supervisor terminates
with reason <c>normal</c>.
If <c><anno>Module</anno>:init/1</c> fails or returns an incorrect value,
this function returns <c>{error,Term}</c> where <c>Term</c>
@@ -297,7 +347,6 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
<func>
<name name="start_child" arity="2"/>
<fsummary>Dynamically add a child process to a supervisor.</fsummary>
- <type name="child_spec"/>
<type name="startchild_ret"/>
<type name="startchild_err"/>
<desc>
@@ -314,35 +363,35 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
<item><c>{via,Module,Name}</c>, if the supervisor is registered
through an alternative process registry.</item>
</list>
- <p><c><anno>ChildSpec</anno></c> should be a valid child specification
+ <p><c><anno>ChildSpec</anno></c> must be a valid child specification
(unless the supervisor is a <c>simple_one_for_one</c>
- supervisor, see below). The child process will be started by
+ supervisor; see below). The child process will be started by
using the start function as defined in the child
specification.</p>
- <p>If the case of a <c>simple_one_for_one</c> supervisor,
+ <p>In the case of a <c>simple_one_for_one</c> supervisor,
the child specification defined in <c>Module:init/1</c> will
- be used and <c><anno>ChildSpec</anno></c> should instead be an arbitrary
+ be used, and <c><anno>ChildSpec</anno></c> shall instead be an arbitrary
list of terms <c><anno>List</anno></c>. The child process will then be
started by appending <c><anno>List</anno></c> to the existing start
function arguments, i.e. by calling
<c>apply(M, F, A++<anno>List</anno>)</c> where <c>{M,F,A}</c> is the start
function defined in the child specification.</p>
<p>If there already exists a child specification with
- the specified <c><anno>Id</anno></c>, <c><anno>ChildSpec</anno></c> is discarded and
+ the specified identifier, <c><anno>ChildSpec</anno></c> is discarded, and
the function returns <c>{error,already_present}</c> or
<c>{error,{already_started,<anno>Child</anno>}}</c>, depending on if
the corresponding child process is running or not.</p>
<p>If the child process start function returns <c>{ok,<anno>Child</anno>}</c>
- or <c>{ok,<anno>Child</anno>,<anno>Info</anno>}</c>, the child specification and pid is
+ or <c>{ok,<anno>Child</anno>,<anno>Info</anno>}</c>, the child specification and pid are
added to the supervisor and the function returns the same
value.</p>
<p>If the child process start function returns <c>ignore</c>,
the child specification is added to the supervisor, the pid
- is set to <c>undefined</c> and the function returns
+ is set to <c>undefined</c>, and the function returns
<c>{ok,undefined}</c>.</p>
<p>If the child process start function returns an error tuple or
an erroneous value, or if it fails, the child specification is
- discarded and the function returns <c>{error,Error}</c> where
+ discarded, and the function returns <c>{error,Error}</c> where
<c>Error</c> is a term containing information about the error
and child specification.</p>
</desc>
@@ -366,7 +415,7 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
<p>If the child is temporary, the child specification is deleted as
soon as the process terminates. This means
- that <c>delete_child/2</c> has no meaning
+ that <c>delete_child/2</c> has no meaning,
and <c>restart_child/2</c> can not be used for these
children.</p>
@@ -375,13 +424,13 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
process is alive, but is not a child of the given
supervisor, the function will return
<c>{error,not_found}</c>. If the child specification
- identifier is given instead instead of a <c>pid()</c>, the
+ identifier is given instead of a <c>pid()</c>, the
function will return <c>{error,simple_one_for_one}</c>.</p>
<p>If successful, the function returns <c>ok</c>. If there is
no child specification with the specified <c><anno>Id</anno></c>, the
function returns <c>{error,not_found}</c>.</p>
- <p>See <c>start_child/2</c> for a description of
- <c><anno>SupRef</anno></c>.</p>
+ <p>See <seealso marker="#SupRef"><c>start_child/2</c></seealso>
+ for a description of <c><anno>SupRef</anno></c>.</p>
</desc>
</func>
<func>
@@ -390,15 +439,15 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
<desc>
<p>Tells the supervisor <c><anno>SupRef</anno></c> to delete the child
specification identified by <c><anno>Id</anno></c>. The corresponding child
- process must not be running, use <c>terminate_child/2</c> to
+ process must not be running. Use <c>terminate_child/2</c> to
terminate it.</p>
- <p>See <seealso marker="#SupRef"><c>start_child/2</c></seealso> for a description of
- <c>SupRef</c>.</p>
+ <p>See <seealso marker="#SupRef"><c>start_child/2</c></seealso>
+ for a description of <c><anno>SupRef</anno></c>.</p>
<p>If successful, the function returns <c>ok</c>. If the child
specification identified by <c><anno>Id</anno></c> exists but
the corresponding child process is running or about to be restarted,
the function returns <c>{error,running}</c> or
- <c>{error,restarting}</c> respectively. If the child specification
+ <c>{error,restarting}</c>, respectively. If the child specification
identified by <c><anno>Id</anno></c> does not exist, the function
returns <c>{error,not_found}</c>.</p>
</desc>
@@ -410,10 +459,10 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
<p>Tells the supervisor <c><anno>SupRef</anno></c> to restart
a child process corresponding to the child specification
identified by <c><anno>Id</anno></c>. The child
- specification must exist and the corresponding child process
+ specification must exist, and the corresponding child process
must not be running.</p>
<p>Note that for temporary children, the child specification
- is automatically deleted when the child terminates, and thus
+ is automatically deleted when the child terminates; thus
it is not possible to restart such children.</p>
<p>See <seealso marker="#SupRef"><c>start_child/2</c></seealso>
for a description of <c>SupRef</c>.</p>
@@ -429,7 +478,7 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
is added to the supervisor and the function returns the same
value.</p>
<p>If the child process start function returns <c>ignore</c>,
- the pid remains set to <c>undefined</c> and the function
+ the pid remains set to <c>undefined</c>, and the function
returns <c>{ok,undefined}</c>.</p>
<p>If the child process start function returns an error tuple
or an erroneous value, or if it fails, the function returns
@@ -462,7 +511,7 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
<item>
<p><c><anno>Child</anno></c> - the pid of the corresponding child
process, the atom <c>restarting</c> if the process is about to be
- restarted or <c>undefined</c> if there is no such process.</p>
+ restarted, or <c>undefined</c> if there is no such process.</p>
</item>
<item>
<p><c><anno>Type</anno></c> - as defined in the child specification.</p>
@@ -475,8 +524,8 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
</func>
<func>
<name name="count_children" arity="1"/>
- <fsummary>Return counts for the number of childspecs, active children,
- supervisors and workers.</fsummary>
+ <fsummary>Return counts for the number of child specifications,
+ active children, supervisors, and workers.</fsummary>
<desc>
<p>Returns a property list (see <c>proplists</c>) containing the
counts for each of the following elements of the supervisor's
@@ -500,6 +549,8 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
process is still alive.</p>
</item>
</list>
+ <p>See <seealso marker="#SupRef"><c>start_child/2</c></seealso>
+ for a description of <c><anno>SupRef</anno></c>.</p>
</desc>
</func>
<func>
@@ -511,11 +562,23 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
correct, or <c>{error,<anno>Error</anno>}</c> otherwise.</p>
</desc>
</func>
+ <func>
+ <name name="get_childspec" arity="2"/>
+ <fsummary>Return the child specification map for the given
+ child.</fsummary>
+ <desc>
+ <p>Returns the child specification map for the child identified
+ by <c>Id</c> under supervisor <c>SupRef</c>. The returned
+ map contains all keys, both mandatory and optional.</p>
+ <p>See <seealso marker="#SupRef"><c>start_child/2</c></seealso>
+ for a description of <c><anno>SupRef</anno></c>.</p>
+ </desc>
+ </func>
</funcs>
<section>
<title>CALLBACK FUNCTIONS</title>
- <p>The following functions should be exported from a
+ <p>The following functions must be exported from a
<c>supervisor</c> callback module.</p>
</section>
<funcs>
@@ -524,33 +587,37 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
<fsummary>Return a supervisor specification.</fsummary>
<type>
<v>Args = term()</v>
- <v>Result = {ok,{{RestartStrategy,MaxR,MaxT},[ChildSpec]}} | ignore</v>
- <v>&nbsp;RestartStrategy = <seealso marker="#type-strategy">strategy()</seealso></v>
- <v>&nbsp;MaxR = integer()>=0</v>
- <v>&nbsp;MaxT = integer()>0</v>
+ <v>Result = {ok,{SupFlags,[ChildSpec]}} | ignore</v>
+ <v>&nbsp;SupFlags = <seealso marker="#type-sup_flags">sup_flags()</seealso></v>
<v>&nbsp;ChildSpec = <seealso marker="#type-child_spec">child_spec()</seealso></v>
</type>
<desc>
<p>Whenever a supervisor is started using
<c>supervisor:start_link/2,3</c>, this function is called by
the new process to find out about restart strategy, maximum
- restart frequency and child specifications.</p>
+ restart intensity, and child specifications.</p>
<p><c>Args</c> is the <c>Args</c> argument provided to the start
function.</p>
- <p><c>RestartStrategy</c> is the restart strategy and
- <c>MaxR</c> and <c>MaxT</c> defines the maximum restart
- frequency of the supervisor. <c>[ChildSpec]</c> is a list of
- valid child specifications defining which child processes
- the supervisor should start and monitor. See the discussion
- about Supervision Principles above.</p>
+ <p><c>SupFlags</c> is the supervisor flags defining the
+ restart strategy and max restart intensity for the
+ supervisor. <c>[ChildSpec]</c> is a list of valid child
+ specifications defining which child processes the supervisor
+ shall start and monitor. See the discussion about
+ Supervision Principles above.</p>
<p>Note that when the restart strategy is
<c>simple_one_for_one</c>, the list of child specifications
must be a list with one child specification only.
- (The <c>Id</c> is ignored). No child process is then started
+ (The child specification identifier is ignored.) No child process is then started
during the initialization phase, but all children are assumed
to be started dynamically using
<c>supervisor:start_child/2</c>.</p>
<p>The function may also return <c>ignore</c>.</p>
+ <p>Note that this function might also be called as a part of a
+ code upgrade procedure. For this reason, the function should
+ not have any side effects. See
+ <seealso marker="doc/design_principles:appup_cookbook#sup">Design
+ Principles</seealso> for more information about code upgrade
+ of supervisors.</p>
</desc>
</func>
</funcs>
diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl
index ede2742875..658c00dc77 100644
--- a/lib/stdlib/src/supervisor.erl
+++ b/lib/stdlib/src/supervisor.erl
@@ -25,7 +25,7 @@
start_child/2, restart_child/2,
delete_child/2, terminate_child/2,
which_children/1, count_children/1,
- check_childspecs/1]).
+ check_childspecs/1, get_childspec/2]).
%% Internal exports
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
@@ -34,7 +34,7 @@
%%--------------------------------------------------------------------------
--export_type([child_spec/0, startchild_ret/0, strategy/0]).
+-export_type([sup_flags/0, child_spec/0, startchild_ret/0, strategy/0]).
%%--------------------------------------------------------------------------
@@ -53,7 +53,13 @@
| {'global', Name :: atom()}
| {'via', Module :: module(), Name :: any()}
| pid().
--type child_spec() :: {Id :: child_id(),
+-type child_spec() :: #{id => child_id(), % mandatory
+ start => mfargs(), % mandatory
+ restart => restart(), % optional
+ shutdown => shutdown(), % optional
+ type => worker(), % optional
+ modules => modules()} % optional
+ | {Id :: child_id(),
StartFunc :: mfargs(),
Restart :: restart(),
Shutdown :: shutdown(),
@@ -63,6 +69,23 @@
-type strategy() :: 'one_for_all' | 'one_for_one'
| 'rest_for_one' | 'simple_one_for_one'.
+-type sup_flags() :: #{strategy => strategy(), % optional
+ intensity => non_neg_integer(), % optional
+ period => pos_integer()} % optional
+ | {RestartStrategy :: strategy(),
+ Intensity :: non_neg_integer(),
+ Period :: pos_integer()}.
+
+%%--------------------------------------------------------------------------
+%% Defaults
+-define(default_flags, #{strategy => one_for_one,
+ intensity => 1,
+ period => 5}).
+-define(default_child_spec, #{restart => permanent,
+ type => worker}).
+%% Default 'shutdown' is 5000 for workers and infinity for supervisors.
+%% Default 'modules' is [M], where M comes from the child's start {M,F,A}.
+
%%--------------------------------------------------------------------------
-record(child, {% pid is undefined when child is not running
@@ -96,10 +119,7 @@
-define(is_simple(State), State#state.strategy =:= simple_one_for_one).
-callback init(Args :: term()) ->
- {ok, {{RestartStrategy :: strategy(),
- MaxR :: non_neg_integer(),
- MaxT :: non_neg_integer()},
- [ChildSpec :: child_spec()]}}
+ {ok, {SupFlags :: sup_flags(), [ChildSpec :: child_spec()]}}
| ignore.
-define(restarting(_Pid_), {restarting,_Pid_}).
@@ -178,6 +198,14 @@ delete_child(Supervisor, Name) ->
terminate_child(Supervisor, Name) ->
call(Supervisor, {terminate_child, Name}).
+-spec get_childspec(SupRef, Id) -> Result when
+ SupRef :: sup_ref(),
+ Id :: pid() | child_id(),
+ Result :: {'ok', child_spec()} | {'error', Error},
+ Error :: 'not_found'.
+get_childspec(Supervisor, Name) ->
+ call(Supervisor, {get_childspec, Name}).
+
-spec which_children(SupRef) -> [{Id,Child,Type,Modules}] when
SupRef :: sup_ref(),
Id :: child_id() | undefined,
@@ -431,6 +459,14 @@ handle_call({delete_child, Name}, _From, State) ->
{reply, {error, not_found}, State}
end;
+handle_call({get_childspec, Name}, _From, State) ->
+ case get_child(Name, State, ?is_simple(State)) of
+ {value, Child} ->
+ {reply, {ok, child_to_spec(Child)}, State};
+ false ->
+ {reply, {error, not_found}, State}
+ end;
+
handle_call(which_children, _From, #state{children = [#child{restart_type = temporary,
child_type = CT,
modules = Mods}]} =
@@ -610,13 +646,11 @@ terminate(_Reason, State) ->
code_change(_, State, _) ->
case (State#state.module):init(State#state.args) of
{ok, {SupFlags, StartSpec}} ->
- case catch check_flags(SupFlags) of
- ok ->
- {Strategy, MaxIntensity, Period} = SupFlags,
- update_childspec(State#state{strategy = Strategy,
- intensity = MaxIntensity,
- period = Period},
- StartSpec);
+ case set_flags(SupFlags, State) of
+ {ok, State1} ->
+ update_childspec(State1, StartSpec);
+ {invalid_type, SupFlags} ->
+ {error, {bad_flags, SupFlags}}; % backwards compatibility
Error ->
{error, Error}
end;
@@ -626,14 +660,6 @@ code_change(_, State, _) ->
Error
end.
-check_flags({Strategy, MaxIntensity, Period}) ->
- validStrategy(Strategy),
- validIntensity(MaxIntensity),
- validPeriod(Period),
- ok;
-check_flags(What) ->
- {bad_flags, What}.
-
update_childspec(State, StartSpec) when ?is_simple(State) ->
case check_startspec(StartSpec) of
{ok, [Child]} ->
@@ -1188,25 +1214,36 @@ remove_child(Child, State) ->
%% Returns: {ok, state()} | Error
%%-----------------------------------------------------------------
init_state(SupName, Type, Mod, Args) ->
- case catch init_state1(SupName, Type, Mod, Args) of
- {ok, State} ->
- {ok, State};
- Error ->
- Error
+ set_flags(Type, #state{name = supname(SupName,Mod),
+ module = Mod,
+ args = Args}).
+
+set_flags(Flags, State) ->
+ try check_flags(Flags) of
+ #{strategy := Strategy, intensity := MaxIntensity, period := Period} ->
+ {ok, State#state{strategy = Strategy,
+ intensity = MaxIntensity,
+ period = Period}}
+ catch
+ Thrown -> Thrown
end.
-init_state1(SupName, {Strategy, MaxIntensity, Period}, Mod, Args) ->
+check_flags(SupFlags) when is_map(SupFlags) ->
+ do_check_flags(maps:merge(?default_flags,SupFlags));
+check_flags({Strategy, MaxIntensity, Period}) ->
+ check_flags(#{strategy => Strategy,
+ intensity => MaxIntensity,
+ period => Period});
+check_flags(What) ->
+ throw({invalid_type, What}).
+
+do_check_flags(#{strategy := Strategy,
+ intensity := MaxIntensity,
+ period := Period} = Flags) ->
validStrategy(Strategy),
validIntensity(MaxIntensity),
validPeriod(Period),
- {ok, #state{name = supname(SupName,Mod),
- strategy = Strategy,
- intensity = MaxIntensity,
- period = Period,
- module = Mod,
- args = Args}};
-init_state1(_SupName, Type, _, _) ->
- {invalid_type, Type}.
+ Flags.
validStrategy(simple_one_for_one) -> true;
validStrategy(one_for_one) -> true;
@@ -1227,14 +1264,7 @@ supname(N, _) -> N.
%%% ------------------------------------------------------
%%% Check that the children start specification is valid.
-%%% Shall be a six (6) tuple
-%%% {Name, Func, RestartType, Shutdown, ChildType, Modules}
-%%% where Name is an atom
-%%% Func is {Mod, Fun, Args} == {atom(), atom(), list()}
-%%% RestartType is permanent | temporary | transient
-%%% Shutdown = integer() > 0 | infinity | brutal_kill
-%%% ChildType = supervisor | worker
-%%% Modules = [atom()] | dynamic
+%%% Input: [child_spec()]
%%% Returns: {ok, [child_rec()]} | Error
%%% ------------------------------------------------------
@@ -1244,6 +1274,9 @@ check_startspec([ChildSpec|T], Res) ->
case check_childspec(ChildSpec) of
{ok, Child} ->
case lists:keymember(Child#child.name, #child.name, Res) of
+ %% The error message duplicate_child_name is kept for
+ %% backwards compatibility, although
+ %% duplicate_child_id would be more correct.
true -> {duplicate_child_name, Child#child.name};
false -> check_startspec(T, [Child | Res])
end;
@@ -1252,16 +1285,41 @@ check_startspec([ChildSpec|T], Res) ->
check_startspec([], Res) ->
{ok, lists:reverse(Res)}.
+check_childspec(ChildSpec) when is_map(ChildSpec) ->
+ catch do_check_childspec(maps:merge(?default_child_spec,ChildSpec));
check_childspec({Name, Func, RestartType, Shutdown, ChildType, Mods}) ->
- catch check_childspec(Name, Func, RestartType, Shutdown, ChildType, Mods);
+ check_childspec(#{id => Name,
+ start => Func,
+ restart => RestartType,
+ shutdown => Shutdown,
+ type => ChildType,
+ modules => Mods});
check_childspec(X) -> {invalid_child_spec, X}.
-check_childspec(Name, Func, RestartType, Shutdown, ChildType, Mods) ->
+do_check_childspec(#{restart := RestartType,
+ type := ChildType} = ChildSpec)->
+ Name = case ChildSpec of
+ #{id := N} -> N;
+ _ -> throw(missing_id)
+ end,
+ Func = case ChildSpec of
+ #{start := F} -> F;
+ _ -> throw(missing_start)
+ end,
validName(Name),
validFunc(Func),
validRestartType(RestartType),
validChildType(ChildType),
- validShutdown(Shutdown, ChildType),
+ Shutdown = case ChildSpec of
+ #{shutdown := S} -> S;
+ #{type := worker} -> 5000;
+ #{type := supervisor} -> infinity
+ end,
+ validShutdown(Shutdown),
+ Mods = case ChildSpec of
+ #{modules := Ms} -> Ms;
+ _ -> {M,_,_} = Func, [M]
+ end,
validMods(Mods),
{ok, #child{name = Name, mfargs = Func, restart_type = RestartType,
shutdown = Shutdown, child_type = ChildType, modules = Mods}}.
@@ -1282,11 +1340,11 @@ validRestartType(temporary) -> true;
validRestartType(transient) -> true;
validRestartType(RestartType) -> throw({invalid_restart_type, RestartType}).
-validShutdown(Shutdown, _)
+validShutdown(Shutdown)
when is_integer(Shutdown), Shutdown > 0 -> true;
-validShutdown(infinity, _) -> true;
-validShutdown(brutal_kill, _) -> true;
-validShutdown(Shutdown, _) -> throw({invalid_shutdown, Shutdown}).
+validShutdown(infinity) -> true;
+validShutdown(brutal_kill) -> true;
+validShutdown(Shutdown) -> throw({invalid_shutdown, Shutdown}).
validMods(dynamic) -> true;
validMods(Mods) when is_list(Mods) ->
@@ -1299,6 +1357,19 @@ validMods(Mods) when is_list(Mods) ->
Mods);
validMods(Mods) -> throw({invalid_modules, Mods}).
+child_to_spec(#child{name = Name,
+ mfargs = Func,
+ restart_type = RestartType,
+ shutdown = Shutdown,
+ child_type = ChildType,
+ modules = Mods}) ->
+ #{id => Name,
+ start => Func,
+ restart => RestartType,
+ shutdown => Shutdown,
+ type => ChildType,
+ modules => Mods}.
+
%%% ------------------------------------------------------
%%% Add a new restart and calculate if the max restart
%%% intensity has been reached (in that case the supervisor
@@ -1367,14 +1438,14 @@ report_error(Error, Reason, Child, SupName) ->
extract_child(Child) when is_list(Child#child.pid) ->
[{nb_children, length(Child#child.pid)},
- {name, Child#child.name},
+ {id, Child#child.name},
{mfargs, Child#child.mfargs},
{restart_type, Child#child.restart_type},
{shutdown, Child#child.shutdown},
{child_type, Child#child.child_type}];
extract_child(Child) ->
[{pid, Child#child.pid},
- {name, Child#child.name},
+ {id, Child#child.name},
{mfargs, Child#child.mfargs},
{restart_type, Child#child.restart_type},
{shutdown, Child#child.shutdown},
diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl
index 836ea7c030..c98654aef7 100644
--- a/lib/stdlib/test/supervisor_SUITE.erl
+++ b/lib/stdlib/test/supervisor_SUITE.erl
@@ -37,9 +37,11 @@
sup_start_ignore_child/1, sup_start_ignore_temporary_child/1,
sup_start_ignore_temporary_child_start_child/1,
sup_start_ignore_temporary_child_start_child_simple/1,
- sup_start_error_return/1, sup_start_fail/1, sup_stop_infinity/1,
- sup_stop_timeout/1, sup_stop_brutal_kill/1, child_adm/1,
- child_adm_simple/1, child_specs/1, extra_return/1]).
+ sup_start_error_return/1, sup_start_fail/1,
+ sup_start_map/1, sup_start_map_faulty_specs/1,
+ sup_stop_infinity/1, sup_stop_timeout/1, sup_stop_brutal_kill/1,
+ child_adm/1, child_adm_simple/1, child_specs/1, extra_return/1,
+ sup_flags/1]).
%% Tests concept permanent, transient and temporary
-export([ permanent_normal/1, transient_normal/1,
@@ -65,7 +67,8 @@
do_not_save_child_specs_for_temporary_children/1,
simple_one_for_one_scale_many_temporary_children/1,
simple_global_supervisor/1, hanging_restart_loop/1,
- hanging_restart_loop_simple/1]).
+ hanging_restart_loop_simple/1, code_change/1, code_change_map/1,
+ code_change_simple/1, code_change_simple_map/1]).
%%-------------------------------------------------------------------------
@@ -73,8 +76,8 @@ suite() ->
[{ct_hooks,[ts_install_cth]}].
all() ->
- [{group, sup_start}, {group, sup_stop}, child_adm,
- child_adm_simple, extra_return, child_specs,
+ [{group, sup_start}, {group, sup_start_map}, {group, sup_stop}, child_adm,
+ child_adm_simple, extra_return, child_specs, sup_flags,
{group, restart_one_for_one},
{group, restart_one_for_all},
{group, restart_simple_one_for_one},
@@ -85,7 +88,8 @@ all() ->
count_children, do_not_save_start_parameters_for_temporary_children,
do_not_save_child_specs_for_temporary_children,
simple_one_for_one_scale_many_temporary_children, temporary_bystander,
- simple_global_supervisor, hanging_restart_loop, hanging_restart_loop_simple].
+ simple_global_supervisor, hanging_restart_loop, hanging_restart_loop_simple,
+ code_change, code_change_map, code_change_simple, code_change_simple_map].
groups() ->
[{sup_start, [],
@@ -94,6 +98,8 @@ groups() ->
sup_start_ignore_temporary_child_start_child,
sup_start_ignore_temporary_child_start_child_simple,
sup_start_error_return, sup_start_fail]},
+ {sup_start_map, [],
+ [sup_start_map, sup_start_map_faulty_specs]},
{sup_stop, [],
[sup_stop_infinity, sup_stop_timeout,
sup_stop_brutal_kill]},
@@ -256,6 +262,60 @@ sup_start_fail(Config) when is_list(Config) ->
check_exit_reason(Term).
%%-------------------------------------------------------------------------
+%% Tests that the supervisor process starts correctly with map
+%% startspec, and that the full childspec can be read.
+sup_start_map(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ Child1 = #{id=>child1, start=>{supervisor_1, start_child, []}},
+ Child2 = #{id=>child2,
+ start=>{supervisor_1, start_child, []},
+ shutdown=>brutal_kill},
+ Child3 = #{id=>child3,
+ start=>{supervisor_1, start_child, []},
+ type=>supervisor},
+ {ok, Pid} = start_link({ok, {#{}, [Child1,Child2,Child3]}}),
+
+ %% Check default values
+ {ok,#{id:=child1,
+ start:={supervisor_1,start_child,[]},
+ restart:=permanent,
+ shutdown:=5000,
+ type:=worker,
+ modules:=[supervisor_1]}} = supervisor:get_childspec(Pid, child1),
+ {ok,#{id:=child2,
+ start:={supervisor_1,start_child,[]},
+ restart:=permanent,
+ shutdown:=brutal_kill,
+ type:=worker,
+ modules:=[supervisor_1]}} = supervisor:get_childspec(Pid, child2),
+ {ok,#{id:=child3,
+ start:={supervisor_1,start_child,[]},
+ restart:=permanent,
+ shutdown:=infinity,
+ type:=supervisor,
+ modules:=[supervisor_1]}} = supervisor:get_childspec(Pid, child3),
+ {error,not_found} = supervisor:get_childspec(Pid, child4),
+ terminate(Pid, shutdown).
+
+%%-------------------------------------------------------------------------
+%% Tests that the supervisor produces good error messages when start-
+%% and child specs are faulty.
+sup_start_map_faulty_specs(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ Child1 = #{start=>{supervisor_1, start_child, []}},
+ Child2 = #{id=>child2},
+ Child3 = #{id=>child3,
+ start=>{supervisor_1, start_child, []},
+ silly_flag=>true},
+ Child4 = child4,
+ {error,{start_spec,missing_id}} = start_link({ok, {#{}, [Child1]}}),
+ {error,{start_spec,missing_start}} = start_link({ok, {#{}, [Child2]}}),
+ {ok,Pid} = start_link({ok, {#{}, [Child3]}}),
+ terminate(Pid,shutdown),
+ {error,{start_spec,{invalid_child_spec,child4}}} =
+ start_link({ok, {#{}, [Child4]}}).
+
+%%-------------------------------------------------------------------------
%% See sup_stop/1 when Shutdown = infinity, this walue is allowed for
%% children of type supervisor _AND_ worker.
sup_stop_infinity(Config) when is_list(Config) ->
@@ -479,7 +539,7 @@ child_adm_simple(Config) when is_list(Config) ->
%% Tests child specs, invalid formats should be rejected.
child_specs(Config) when is_list(Config) ->
process_flag(trap_exit, true),
- {ok, _Pid} = start_link({ok, {{one_for_one, 2, 3600}, []}}),
+ {ok, Pid} = start_link({ok, {{one_for_one, 2, 3600}, []}}),
{error, _} = supervisor:start_child(sup_test, hej),
%% Bad child specs
@@ -509,6 +569,7 @@ child_specs(Config) when is_list(Config) ->
{error, {invalid_modules,dy}}
= supervisor:start_child(sup_test, B5),
+ {error, {badarg, _}} = supervisor:check_childspecs(B1), % should be list
{error, {invalid_mfa,mfa}} = supervisor:check_childspecs([B1]),
{error, {invalid_restart_type,prmanent}} =
supervisor:check_childspecs([B2]),
@@ -524,6 +585,54 @@ child_specs(Config) when is_list(Config) ->
ok = supervisor:check_childspecs([C3]),
ok = supervisor:check_childspecs([C4]),
ok = supervisor:check_childspecs([C5]),
+
+ {error,{duplicate_child_name,child}} = supervisor:check_childspecs([C1,C2]),
+
+ terminate(Pid, shutdown),
+
+ %% Faulty child specs in supervisor start
+ {error, {start_spec, {invalid_mfa, mfa}}} =
+ start_link({ok, {{one_for_one, 2, 3600}, [B1]}}),
+ {error, {start_spec, {invalid_restart_type, prmanent}}} =
+ start_link({ok, {{simple_one_for_one, 2, 3600}, [B2]}}),
+
+ %% simple_one_for_one needs exactly one child
+ {error,{bad_start_spec,[]}} =
+ start_link({ok, {{simple_one_for_one, 2, 3600}, []}}),
+ {error,{bad_start_spec,[C1,C2]}} =
+ start_link({ok, {{simple_one_for_one, 2, 3600}, [C1,C2]}}),
+
+ ok.
+
+%%-------------------------------------------------------------------------
+%% Test error handling of supervisor flags
+sup_flags(_Config) ->
+ process_flag(trap_exit,true),
+ {error,{supervisor_data,{invalid_strategy,_}}} =
+ start_link({ok, {{none_for_one, 2, 3600}, []}}),
+ {error,{supervisor_data,{invalid_strategy,_}}} =
+ start_link({ok, {#{strategy=>none_for_one}, []}}),
+ {error,{supervisor_data,{invalid_intensity,_}}} =
+ start_link({ok, {{one_for_one, infinity, 3600}, []}}),
+ {error,{supervisor_data,{invalid_intensity,_}}} =
+ start_link({ok, {#{intensity=>infinity}, []}}),
+ {error,{supervisor_data,{invalid_period,_}}} =
+ start_link({ok, {{one_for_one, 2, 0}, []}}),
+ {error,{supervisor_data,{invalid_period,_}}} =
+ start_link({ok, {#{period=>0}, []}}),
+ {error,{supervisor_data,{invalid_period,_}}} =
+ start_link({ok, {{one_for_one, 2, infinity}, []}}),
+ {error,{supervisor_data,{invalid_period,_}}} =
+ start_link({ok, {#{period=>infinity}, []}}),
+
+ %% SupFlags other than a map or a 3-tuple
+ {error,{supervisor_data,{invalid_type,_}}} =
+ start_link({ok, {{one_for_one, 2}, []}}),
+
+ %% Unexpected flags are ignored
+ {ok,Pid} = start_link({ok,{#{silly_flag=>true},[]}}),
+ terminate(Pid,shutdown),
+
ok.
%%-------------------------------------------------------------------------
@@ -1647,6 +1756,186 @@ hanging_restart_loop_simple(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
+%% Test the code_change function
+code_change(_Config) ->
+ process_flag(trap_exit, true),
+
+ SupFlags = {one_for_one, 0, 1},
+ {ok, Pid} = start_link({ok, {SupFlags, []}}),
+ [] = supervisor:which_children(Pid),
+
+ %% Change supervisor flags
+ S1 = sys:get_state(Pid),
+ ok = fake_upgrade(Pid,{ok, {{one_for_one, 1, 3}, []}}),
+ S2 = sys:get_state(Pid),
+ true = (S1 /= S2),
+
+ %% Faulty childspec
+ FaultyChild = {child1, permanent, brutal_kill, worker, []}, % missing start
+ {error,{error,{invalid_child_spec,FaultyChild}}} =
+ fake_upgrade(Pid,{ok,{SupFlags,[FaultyChild]}}),
+
+ %% Add child1 and child2
+ Child1 = {child1, {supervisor_1, start_child, []},
+ permanent, 2000, worker, []},
+ Child2 = {child2, {supervisor_1, start_child, []},
+ permanent, brutal_kill, worker, []},
+ ok = fake_upgrade(Pid,{ok,{SupFlags,[Child1,Child2]}}),
+ %% Children are not automatically started
+ {ok,_} = supervisor:restart_child(Pid,child1),
+ {ok,_} = supervisor:restart_child(Pid,child2),
+ [{child2,_,_,_},{child1,_,_,_}] = supervisor:which_children(Pid),
+
+ %% Change child1, remove child2 and add child3
+ Child11 = {child1, {supervisor_1, start_child, []},
+ permanent, 1000, worker, []},
+ Child3 = {child3, {supervisor_1, start_child, []},
+ permanent, brutal_kill, worker, []},
+ ok = fake_upgrade(Pid,{ok, {SupFlags, [Child11,Child3]}}),
+ %% Children are not deleted on upgrade, so it is ok that child2 is
+ %% still here
+ [{child2,_,_,_},{child3,_,_,_},{child1,_,_,_}] =
+ supervisor:which_children(Pid),
+
+ %% Ignore during upgrade
+ ok = fake_upgrade(Pid,ignore),
+
+ %% Error during upgrade
+ {error, faulty_return} = fake_upgrade(Pid,faulty_return),
+
+ %% Faulty flags
+ {error,{error, {invalid_intensity,faulty_intensity}}} =
+ fake_upgrade(Pid,{ok, {{one_for_one,faulty_intensity,1}, []}}),
+ {error,{error,{bad_flags, faulty_flags}}} =
+ fake_upgrade(Pid,{ok, {faulty_flags, []}}),
+
+ terminate(Pid,shutdown).
+
+code_change_map(_Config) ->
+ process_flag(trap_exit, true),
+
+ {ok, Pid} = start_link({ok, {#{}, []}}),
+ [] = supervisor:which_children(Pid),
+
+ %% Change supervisor flags
+ S1 = sys:get_state(Pid),
+ ok = fake_upgrade(Pid,{ok, {#{intensity=>1, period=>3}, []}}),
+ S2 = sys:get_state(Pid),
+ true = (S1 /= S2),
+
+ %% Faulty childspec
+ FaultyChild = #{id=>faulty_child},
+ {error,{error,missing_start}} =
+ fake_upgrade(Pid,{ok,{#{},[FaultyChild]}}),
+
+ %% Add child1 and child2
+ Child1 = #{id=>child1,
+ start=>{supervisor_1, start_child, []},
+ shutdown=>2000},
+ Child2 = #{id=>child2,
+ start=>{supervisor_1, start_child, []}},
+ ok = fake_upgrade(Pid,{ok,{#{},[Child1,Child2]}}),
+ %% Children are not automatically started
+ {ok,_} = supervisor:restart_child(Pid,child1),
+ {ok,_} = supervisor:restart_child(Pid,child2),
+ [{child2,_,_,_},{child1,_,_,_}] = supervisor:which_children(Pid),
+ {ok,#{shutdown:=2000}} = supervisor:get_childspec(Pid,child1),
+
+ %% Change child1, remove child2 and add child3
+ Child11 = #{id=>child1,
+ start=>{supervisor_1, start_child, []},
+ shutdown=>1000},
+ Child3 = #{id=>child3,
+ start=>{supervisor_1, start_child, []}},
+ ok = fake_upgrade(Pid,{ok, {#{}, [Child11,Child3]}}),
+ %% Children are not deleted on upgrade, so it is ok that child2 is
+ %% still here
+ [{child2,_,_,_},{child3,_,_,_},{child1,_,_,_}] =
+ supervisor:which_children(Pid),
+ {ok,#{shutdown:=1000}} = supervisor:get_childspec(Pid,child1),
+
+ %% Ignore during upgrade
+ ok = fake_upgrade(Pid,ignore),
+
+ %% Error during upgrade
+ {error, faulty_return} = fake_upgrade(Pid,faulty_return),
+
+ %% Faulty flags
+ {error,{error, {invalid_intensity,faulty_intensity}}} =
+ fake_upgrade(Pid,{ok, {#{intensity=>faulty_intensity}, []}}),
+
+ terminate(Pid,shutdown).
+
+code_change_simple(_Config) ->
+ process_flag(trap_exit, true),
+
+ SimpleChild1 = {child1,{supervisor_1, start_child, []}, permanent,
+ brutal_kill, worker, []},
+ SimpleFlags = {simple_one_for_one, 0, 1},
+ {ok, SimplePid} = start_link({ok, {SimpleFlags,[SimpleChild1]}}),
+ %% Change childspec
+ SimpleChild11 = {child1,{supervisor_1, start_child, []}, permanent,
+ 1000, worker, []},
+ ok = fake_upgrade(SimplePid,{ok,{SimpleFlags,[SimpleChild11]}}),
+
+ %% Attempt to add child
+ SimpleChild2 = {child2,{supervisor_1, start_child, []}, permanent,
+ brutal_kill, worker, []},
+
+ {error, {error, {ok,[_,_]}}} =
+ fake_upgrade(SimplePid,{ok,{SimpleFlags,[SimpleChild1,SimpleChild2]}}),
+
+ %% Attempt to remove child
+ {error, {error, {ok,[]}}} = fake_upgrade(SimplePid,{ok,{SimpleFlags,[]}}),
+
+ terminate(SimplePid,shutdown),
+ ok.
+
+code_change_simple_map(_Config) ->
+ process_flag(trap_exit, true),
+
+ SimpleChild1 = #{id=>child1,
+ start=>{supervisor_1, start_child, []}},
+ SimpleFlags = #{strategy=>simple_one_for_one},
+ {ok, SimplePid} = start_link({ok, {SimpleFlags,[SimpleChild1]}}),
+ %% Change childspec
+ SimpleChild11 = #{id=>child1,
+ start=>{supervisor_1, start_child, []},
+ shutdown=>1000},
+ ok = fake_upgrade(SimplePid,{ok,{SimpleFlags,[SimpleChild11]}}),
+
+ %% Attempt to add child
+ SimpleChild2 = #{id=>child2,
+ start=>{supervisor_1, start_child, []}},
+ {error, {error, {ok, [_,_]}}} =
+ fake_upgrade(SimplePid,{ok,{SimpleFlags,[SimpleChild1,SimpleChild2]}}),
+
+ %% Attempt to remove child
+ {error, {error, {ok, []}}} =
+ fake_upgrade(SimplePid,{ok,{SimpleFlags,[]}}),
+
+ terminate(SimplePid,shutdown),
+ ok.
+
+fake_upgrade(Pid,NewInitReturn) ->
+ ok = sys:suspend(Pid),
+
+ %% Update state to fake code change
+ %% The #state record in supervisor.erl holds the arguments given
+ %% to the callback init function. By replacing these arguments the
+ %% init function will return something new and by that fake a code
+ %% change (see init function above in this module).
+ Fun = fun(State) ->
+ Size = size(State), % 'args' is the last field in #state.
+ setelement(Size,State,NewInitReturn)
+ end,
+ sys:replace_state(Pid,Fun),
+
+ R = sys:change_code(Pid,gen_server,dummy_vsn,[]),
+ ok = sys:resume(Pid),
+ R.
+
+%%-------------------------------------------------------------------------
terminate(Pid, Reason) when Reason =/= supervisor ->
terminate(dummy, Pid, dummy, Reason).
diff --git a/lib/tools/emacs/erlang-skels.el b/lib/tools/emacs/erlang-skels.el
index 78929ac510..8d2c02e455 100644
--- a/lib/tools/emacs/erlang-skels.el
+++ b/lib/tools/emacs/erlang-skels.el
@@ -1,7 +1,7 @@
;;
;; %CopyrightBegin%
;;
-;; Copyright Ericsson AB 2010. All Rights Reserved.
+;; Copyright Ericsson AB 2010-2014. All Rights Reserved.
;;
;; The contents of this file are subject to the Erlang Public License,
;; Version 1.1, (the "License"); you may not use this file except in
@@ -352,26 +352,25 @@ Please see the function `tempo-define-template'.")
"%% @doc" n
"%% Whenever a supervisor is started using supervisor:start_link/[2,3]," n
"%% this function is called by the new process to find out about" n
- "%% restart strategy, maximum restart frequency and child" n
+ "%% restart strategy, maximum restart intensity, and child" n
"%% specifications." n
"%%" n
"%% @spec init(Args) -> {ok, {SupFlags, [ChildSpec]}} |" n
"%% ignore |" n
"%% {error, Reason}" n
(erlang-skel-separator-end 2)
- "init([]) ->" n>
- "RestartStrategy = one_for_one," n>
- "MaxRestarts = 1000," n>
- "MaxSecondsBetweenRestarts = 3600," n
- "" n>
- "SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts}," n
+ "init([]) ->" n
"" n>
- "Restart = permanent," n>
- "Shutdown = 2000," n>
- "Type = worker," n
+ "SupFlags = #{strategy => one_for_one," n>
+ "intensity => 1," n>
+ "period => 5}," n
"" n>
- "AChild = {'AName', {'AModule', start_link, []}," n>
- "Restart, Shutdown, Type, ['AModule']}," n
+ "AChild = #{id => 'AName'," n>
+ "start => {'AModule', start_link, []}," n>
+ "restart => permanent," n>
+ "shutdown => 5000," n>
+ "type => worker," n>
+ "modules => ['AModule']}," n
"" n>
"{ok, {SupFlags, [AChild]}}." n
n
@@ -379,7 +378,7 @@ Please see the function `tempo-define-template'.")
"%%% Internal functions" n
(erlang-skel-double-separator-end 3)
)
- "*The template of an supervisor behaviour.
+ "*The template of a supervisor behaviour.
Please see the function `tempo-define-template'.")
(defvar erlang-skel-supervisor-bridge
@@ -449,7 +448,7 @@ Please see the function `tempo-define-template'.")
"%%% Internal functions" n
(erlang-skel-double-separator-end 3)
)
- "*The template of an supervisor_bridge behaviour.
+ "*The template of a supervisor_bridge behaviour.
Please see the function `tempo-define-template'.")
(defvar erlang-skel-generic-server