summaryrefslogtreecommitdiff
path: root/lib/stdlib
diff options
context:
space:
mode:
authorRickard Green <rickard@erlang.org>2019-10-18 19:37:06 +0200
committerRickard Green <rickard@erlang.org>2019-10-22 16:00:44 +0200
commit22aa1f15bf28b98ab1243b64c5e8508f020e6e4b (patch)
tree383116736efb1d9892e6af23fffa2de9301f7f27 /lib/stdlib
parent7fe7fa3dde556b5b92522f8279d465bb52baf1f6 (diff)
downloaderlang-22aa1f15bf28b98ab1243b64c5e8508f020e6e4b.tar.gz
Support for start_monitor in proc_lib and gen behaviours
Diffstat (limited to 'lib/stdlib')
-rw-r--r--lib/stdlib/doc/src/gen_event.xml35
-rw-r--r--lib/stdlib/doc/src/gen_server.xml45
-rw-r--r--lib/stdlib/doc/src/gen_statem.xml56
-rw-r--r--lib/stdlib/doc/src/proc_lib.xml96
-rw-r--r--lib/stdlib/src/gen.erl36
-rw-r--r--lib/stdlib/src/gen_event.erl22
-rw-r--r--lib/stdlib/src/gen_server.erl12
-rw-r--r--lib/stdlib/src/gen_statem.erl23
-rw-r--r--lib/stdlib/src/proc_lib.erl183
-rw-r--r--lib/stdlib/test/gen_event_SUITE.erl51
-rw-r--r--lib/stdlib/test/gen_server_SUITE.erl44
-rw-r--r--lib/stdlib/test/gen_statem_SUITE.erl58
-rw-r--r--lib/stdlib/test/proc_lib_SUITE.erl184
13 files changed, 721 insertions, 124 deletions
diff --git a/lib/stdlib/doc/src/gen_event.xml b/lib/stdlib/doc/src/gen_event.xml
index 2915c4f507..f13afd1d34 100644
--- a/lib/stdlib/doc/src/gen_event.xml
+++ b/lib/stdlib/doc/src/gen_event.xml
@@ -50,6 +50,7 @@
gen_event module Callback module
---------------- ---------------
gen_event:start
+gen_event:start_monitor
gen_event:start_link -----> -
gen_event:add_handler
@@ -436,6 +437,40 @@ gen_event:stop -----> Module:terminate/2
</func>
<func>
+ <name since="OTP @OTP-16120@">start_monitor() -> Result</name>
+ <name since="OTP @OTP-16120@">start_monitor(EventMgrName | Options) -> Result</name>
+ <name since="OTP @OTP-16120@">start_monitor(EventMgrName, Options) -> Result</name>
+ <fsummary>Create a stand-alone event manager process.</fsummary>
+ <type>
+ <v>EventMgrName = {local,Name} | {global,GlobalName} | {via,Module,ViaName}</v>
+ <v>&nbsp;Name = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
+ <v>Options = [Option]</v>
+ <v>&nbsp;Option = {debug,Dbgs} | {timeout,Time} | {hibernate_after,HibernateAfterTimeout} | {spawn_opt,SOpts}</v>
+ <v>&nbsp;&nbsp;Dbgs = [Dbg]</v>
+ <v>&nbsp;&nbsp;&nbsp;Dbg = trace | log | statistics | {log_to_file,FileName} | {install,{Func,FuncState}}</v>
+ <v>&nbsp;&nbsp;SOpts = [term()]</v>
+ <v>Result = {ok,{Pid,Mon}} | {error,{already_started,Pid}}</v>
+ <v>&nbsp;Pid = pid()</v>
+ </type>
+ <desc>
+ <p>Creates a stand-alone event manager process, that is, an event
+ manager that is not part of a supervision tree (and thus has
+ no supervisor) and atomically sets up a monitor to
+ the newly created process.</p>
+ <p>For a description of the arguments and return values, see
+ <seealso marker="#start_link/0"><c>start_link/0,1</c></seealso>.
+ Note that the return value on successful start differs from
+ <c>start_link/3,4</c>. <c>start_monitor/3,4</c> will return
+ <c>{ok,{Pid,Mon}}</c> where <c>Pid</c> is the process identifier
+ of the process, and <c>Mon</c> is a reference to the monitor
+ set up to monitor the process. If the start is not successful,
+ the caller will be blocked until the <c>DOWN</c> message has
+ been received and removed from the message queue.</p>
+ </desc>
+ </func>
+
+ <func>
<name since="">stop(EventMgrRef) -> ok</name>
<name since="OTP 18.0">stop(EventMgrRef, Reason, Timeout) -> ok</name>
<fsummary>Terminate a generic event manager.</fsummary>
diff --git a/lib/stdlib/doc/src/gen_server.xml b/lib/stdlib/doc/src/gen_server.xml
index a4554d7657..4c948246f7 100644
--- a/lib/stdlib/doc/src/gen_server.xml
+++ b/lib/stdlib/doc/src/gen_server.xml
@@ -48,6 +48,7 @@
gen_server module Callback module
----------------- ---------------
gen_server:start
+gen_server:start_monitor
gen_server:start_link -----> Module:init/1
gen_server:stop -----> Module:terminate/2
@@ -232,7 +233,7 @@ gen_server:abcast -----> Module:handle_cast/2
is needed than the <c>gen_server</c> process behavior provides.</p>
<p><c>Module</c>, <c>Options</c>, and <c>ServerName</c> have
the same meanings as when calling
- <seealso marker="#start_link/3"><c>start[_link]/3,4</c></seealso>.
+ <seealso marker="#start_link/3"><c>start[_link|_monitor]/3,4</c></seealso>.
However, if <c>ServerName</c> is specified, the process must
have been registered accordingly <em>before</em> this function
is called.</p>
@@ -466,6 +467,43 @@ gen_server:abcast -----> Module:handle_cast/2
</func>
<func>
+ <name since="OTP @OTP-16120@">start_monitor(Module, Args, Options) -> Result</name>
+ <name since="OTP @OTP-16120@">start_monitor(ServerName, Module, Args, Options) -> Result</name>
+ <fsummary>Create a standalone <c>gen_server</c> process.</fsummary>
+ <type>
+ <v>ServerName = {local,Name} | {global,GlobalName}</v>
+ <v>&nbsp;&nbsp;| {via,Module,ViaName}</v>
+ <v>&nbsp;Name = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
+ <v>Module = atom()</v>
+ <v>Args = term()</v>
+ <v>Options = [Option]</v>
+ <v>&nbsp;Option = {debug,Dbgs} | {timeout,Time} | {hibernate_after,HibernateAfterTimeout} | {spawn_opt,SOpts}</v>
+ <v>&nbsp;&nbsp;Dbgs = [Dbg]</v>
+ <v>&nbsp;&nbsp;&nbsp;Dbg = trace | log | statistics | {log_to_file,FileName} | {install,{Func,FuncState}}</v>
+ <v>&nbsp;&nbsp;SOpts = [term()]</v>
+ <v>Result = {ok,{Pid,Mon}} | ignore | {error,Error}</v>
+ <v>&nbsp;Pid = pid()</v>
+ <v>&nbsp;Error = {already_started,Pid} | term()</v>
+ </type>
+ <desc>
+ <p>Creates a standalone <c>gen_server</c> process, that is, a
+ <c>gen_server</c> process that is not part of a supervision tree
+ (and thus has no supervisor) and atomically sets up a monitor to
+ the newly created server.</p>
+ <p>For a description of arguments and return values, see
+ <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>.
+ Note that the return value on successful start differs from
+ <c>start_link/3,4</c>. <c>start_monitor/3,4</c> will return
+ <c>{ok,{Pid,Mon}}</c> where <c>Pid</c> is the process identifier
+ of the server, and <c>Mon</c> is a reference to the monitor
+ set up to monitor the server. If the start is not successful,
+ the caller will be blocked until the <c>DOWN</c> message has
+ been received and removed from the message queue.</p>
+ </desc>
+ </func>
+
+ <func>
<name since="OTP 18.0">stop(ServerRef) -> ok</name>
<name since="OTP 18.0">stop(ServerRef, Reason, Timeout) -> ok</name>
<fsummary>Synchronously stop a generic server.</fsummary>
@@ -782,8 +820,9 @@ gen_server:abcast -----> Module:handle_cast/2
</type>
<desc>
<p>Whenever a <c>gen_server</c> process is started using
- <seealso marker="#start/3"><c>start/3,4</c></seealso> or
- <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>,
+ <seealso marker="#start/3"><c>start/3,4</c></seealso>,
+ <seealso marker="#start_monitor/3"><c>start_monitor/3,4</c></seealso>,
+ or <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>,
this function is called by the new process to initialize.</p>
<p><c>Args</c> is the <c>Args</c> argument provided to the start
function.</p>
diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml
index aaa26df18d..2a3ee8c37d 100644
--- a/lib/stdlib/doc/src/gen_statem.xml
+++ b/lib/stdlib/doc/src/gen_statem.xml
@@ -135,6 +135,7 @@
gen_statem module Callback module
----------------- ---------------
gen_statem:start
+gen_statem:start_monitor
gen_statem:start_link -----> Module:init/1
Server start or code change
@@ -293,7 +294,8 @@ erlang:'!' -----> Module:StateName/3
<c>{hibernate_after, Timeout}</c>
</seealso>
for
- <seealso marker="#start/3"><c>start/3,4</c></seealso> or
+ <seealso marker="#start/3"><c>start/3,4</c></seealso>,
+ <seealso marker="#start_monitor/3"><c>start_monitor/3,4</c></seealso>, or
<seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>
that may be used to automatically hibernate the server.
</p>
@@ -526,8 +528,18 @@ handle_event(_, _, State, Data) ->
<name name="start_ret"/>
<desc>
<p>
- Return value from the start functions, for example,
- <seealso marker="#start_link/3"><c>start_link/3</c></seealso>.
+ Return value from the <c>start()</c> and <c>start_link()</c> functions,
+ for example, <seealso marker="#start_link/3"><c>start_link/3</c></seealso>.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="start_mon_ret"/>
+ <desc>
+ <p>
+ Return value from the
+ <seealso marker="#start_monitor/3"><c>start_monitor()</c></seealso>
+ functions.
</p>
</desc>
</datatype>
@@ -1553,16 +1565,16 @@ handle_event(_, _, State, Data) ->
<p>
<c><anno>Module</anno></c>, <c><anno>Opts</anno></c>
have the same meaning as when calling
- <seealso marker="#start_link/3"><c>start[_link]/3,4</c></seealso>.
+ <seealso marker="#start_link/3"><c>start[_link|_monitor]/3,4</c></seealso>.
</p>
<p>
If <c><anno>Server</anno></c> is <c>self()</c> an anonymous
server is created just as when using
- <seealso marker="#start_link/3"><c>start[_link]/3</c></seealso>.
+ <seealso marker="#start_link/3"><c>start[_link|_monitor]/3</c></seealso>.
If <c><anno>Server</anno></c> is a
<seealso marker="#type-server_name"><c>server_name()</c></seealso>
a named server is created just as when using
- <seealso marker="#start_link/4"><c>start[_link]/4</c></seealso>.
+ <seealso marker="#start_link/4"><c>start[_link|_monitor]/4</c></seealso>.
However, the
<seealso marker="#type-server_name"><c>server_name()</c></seealso>
name must have been registered accordingly
@@ -1750,6 +1762,35 @@ handle_event(_, _, State, Data) ->
</func>
<func>
+ <name name="start_monitor" arity="3" since="OTP @OTP-16120@"/>
+ <name name="start_monitor" arity="4" since="OTP @OTP-16120@"/>
+ <fsummary>Create a standalone <c>gen_statem</c> process.</fsummary>
+ <desc>
+ <p>
+ Creates a standalone <c>gen_statem</c> process according to
+ OTP design principles (using
+ <seealso marker="proc_lib"><c>proc_lib</c></seealso>
+ primitives) and atomically sets up a monitor to
+ the newly created process.
+ As it does not get linked to the calling process,
+ this start function cannot be used by a supervisor
+ to start a child.
+ </p>
+ <p>
+ For a description of arguments and return values, see
+ <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>.
+ Note that the return value on successful start differs from
+ <c>start_link/3,4</c>. <c>start_monitor/3,4</c> will return
+ <c>{ok,{Pid,Mon}}</c> where <c>Pid</c> is the process identifier
+ of the process, and <c>Mon</c> is a reference to the monitor
+ set up to monitor the process. If the start is not successful,
+ the caller will be blocked until the <c>DOWN</c> message has
+ been received and removed from the message queue.
+ </p>
+ </desc>
+ </func>
+
+ <func>
<name name="stop" arity="1" since="OTP 19.0"/>
<fsummary>Synchronously stop a generic server.</fsummary>
<desc>
@@ -1962,7 +2003,8 @@ handle_event(_, _, State, Data) ->
<marker id="Module:init-1"/>
<p>
Whenever a <c>gen_statem</c> is started using
- <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>
+ <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>,
+ <seealso marker="#start_monitor/3"><c>start_monitor/3,4</c></seealso>,
or
<seealso marker="#start/3"><c>start/3,4</c></seealso>,
this function is called by the new process to initialize
diff --git a/lib/stdlib/doc/src/proc_lib.xml b/lib/stdlib/doc/src/proc_lib.xml
index aeb9f48735..9debbfb8d0 100644
--- a/lib/stdlib/doc/src/proc_lib.xml
+++ b/lib/stdlib/doc/src/proc_lib.xml
@@ -86,13 +86,11 @@
</desc>
</datatype>
<datatype>
- <name name="priority_level"/>
- </datatype>
- <datatype>
- <name name="max_heap_size"/>
+ <name name="start_spawn_option"/>
<desc>
- <p>See <seealso marker="erts:erlang#process_flag_max_heap_size">
- erlang:process_flag(max_heap_size, MaxHeapSize)</seealso>.</p>
+ <p>A restricted set of <seealso marker="#type-spawn_option">spawn
+ options</seealso>. Most notably <c>monitor</c> is <em>not</em> part
+ of these options.</p>
</desc>
</datatype>
<datatype>
@@ -295,8 +293,31 @@ init(Parent) ->
<desc>
<p>Spawns a new process and initializes it as described in the
beginning of this manual page. The process is spawned using the
- <seealso marker="erts:erlang#spawn_opt/2"><c>spawn_opt</c></seealso>
+ <seealso marker="erts:erlang#spawn_opt/2"><c>erlang:spawn_opt</c></seealso>
BIFs.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="start" arity="3" since=""/>
+ <name name="start" arity="4" since=""/>
+ <name name="start" arity="5" since=""/>
+ <fsummary>Start a new process synchronously.</fsummary>
+ <desc>
+ <p>Starts a new process synchronously. Spawns the process and
+ waits for it to start. When the process has started, it
+ <em>must</em> call
+ <seealso marker="#init_ack/2"><c>init_ack(Parent, Ret)</c></seealso>
+ or <seealso marker="#init_ack/1"><c>init_ack(Ret)</c></seealso>,
+ where <c>Parent</c> is the process that evaluates this
+ function. At this time, <c>Ret</c> is returned.</p>
+ <p>If <c><anno>Time</anno></c> is specified as an integer, this
+ function waits for <c><anno>Time</anno></c> milliseconds for the
+ new process to call <c>init_ack</c>, or <c>Ret = {error, timeout}</c>
+ will be returned, and the process is killed.</p>
+ <p>Argument <c><anno>SpawnOpts</anno></c>, if specified, is passed
+ as the last argument to the <seealso marker="erts:erlang#spawn_opt/2">
+ <c>spawn_opt/2,3,4,5</c></seealso> BIF.</p>
<note>
<p>Using spawn option <c>monitor</c> is not
allowed. It causes the function to fail with reason
@@ -306,29 +327,68 @@ init(Parent) ->
</func>
<func>
- <name name="start" arity="3" since=""/>
- <name name="start" arity="4" since=""/>
- <name name="start" arity="5" since=""/>
<name name="start_link" arity="3" since=""/>
<name name="start_link" arity="4" since=""/>
<name name="start_link" arity="5" since=""/>
<fsummary>Start a new process synchronously.</fsummary>
<desc>
- <p>Starts a new process synchronously. Spawns the process and
- waits for it to start. When the process has started, it
+ <p>
+ Starts a new process synchronously. Spawns the process and
+ waits for it to start. A link is atomically set on the
+ newly spawned process. When the process has started, it
+ <em>must</em> call
+ <seealso marker="#init_ack/2"><c>init_ack(Parent, Ret)</c></seealso>
+ or <seealso marker="#init_ack/1"><c>init_ack(Ret)</c></seealso>,
+ where <c>Parent</c> is the process that evaluates this
+ function. At this time, <c>Ret</c> is returned.</p>
+ <p>If <c><anno>Time</anno></c> is specified as an integer, this
+ function waits for <c><anno>Time</anno></c> milliseconds for the
+ new process to call <c>init_ack</c>, or <c>Ret = {error, timeout}</c>
+ will be returned, and the process is killed.</p>
+ <p>If the process crashes before it has called <c>init_ack/1,2</c>,
+ <c>Ret = {error, <anno>Reason</anno>}</c> will be returned if
+ the calling process traps exits.</p>
+ <p>Argument <c><anno>SpawnOpts</anno></c>, if specified, is passed
+ as the last argument to the <seealso marker="erts:erlang#spawn_opt/2">
+ <c>spawn_opt/2,3,4,5</c></seealso> BIF.</p>
+ <note>
+ <p>Using spawn option <c>monitor</c> is not
+ allowed. It causes the function to fail with reason
+ <c>badarg</c>.</p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
+ <name name="start_monitor" arity="3" since="OTP @OTP-16120@"/>
+ <name name="start_monitor" arity="4" since="OTP @OTP-16120@"/>
+ <name name="start_monitor" arity="5" since="OTP @OTP-16120@"/>
+ <fsummary>Start a new process synchronously.</fsummary>
+ <desc>
+ <p>
+ Starts a new process synchronously. Spawns the process and
+ waits for it to start. A monitor is atomically set on the
+ newly spawned process. When the process has started, it
<em>must</em> call
<seealso marker="#init_ack/2"><c>init_ack(Parent, Ret)</c></seealso>
or <seealso marker="#init_ack/1"><c>init_ack(Ret)</c></seealso>,
where <c>Parent</c> is the process that evaluates this
function. At this time, <c>Ret</c> is returned.</p>
- <p>If function <c>start_link/3,4,5</c> is used and
- the process crashes before it has called <c>init_ack/1,2</c>,
- <c>{error, <anno>Reason</anno>}</c> is returned if the calling
- process traps exits.</p>
<p>If <c><anno>Time</anno></c> is specified as an integer, this
function waits for <c><anno>Time</anno></c> milliseconds for the
- new process to call <c>init_ack</c>, or <c>{error, timeout}</c> is
- returned, and the process is killed.</p>
+ new process to call <c>init_ack</c>, or <c>Ret = {error, timeout}</c>
+ will be returned, and the process is killed.</p>
+ <p>
+ The return value is <c>{Ret, Mon}</c> where <c>Ret</c> corresponds
+ to the <c>Ret</c> argument in the call to <c>init_ack()</c>, and
+ <c>Mon</c> is the monitor reference of the monitor that has been
+ set up.
+ </p>
+ <p>
+ A <c>'DOWN'</c> message will be delivered to the caller if
+ this function returns, and the spawned process terminates. This is
+ true also in the case when the operation times out.
+ </p>
<p>Argument <c><anno>SpawnOpts</anno></c>, if specified, is passed
as the last argument to the <seealso marker="erts:erlang#spawn_opt/2">
<c>spawn_opt/2,3,4,5</c></seealso> BIF.</p>
diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl
index a7f743bd4c..d0619cbb63 100644
--- a/lib/stdlib/src/gen.erl
+++ b/lib/stdlib/src/gen.erl
@@ -38,7 +38,7 @@
%%-----------------------------------------------------------------
--type linkage() :: 'link' | 'nolink'.
+-type linkage() :: 'monitor' | 'link' | 'nolink'.
-type emgr_name() :: {'local', atom()}
| {'global', term()}
| {'via', Module :: module(), Name :: term()}.
@@ -95,6 +95,13 @@ do_spawn(GenMod, link, Mod, Args, Options) ->
[GenMod, self(), self(), Mod, Args, Options],
Time,
spawn_opts(Options));
+do_spawn(GenMod, monitor, Mod, Args, Options) ->
+ Time = timeout(Options),
+ Ret = proc_lib:start_monitor(?MODULE, init_it,
+ [GenMod, self(), self(), Mod, Args, Options],
+ Time,
+ spawn_opts(Options)),
+ monitor_return(Ret);
do_spawn(GenMod, _, Mod, Args, Options) ->
Time = timeout(Options),
proc_lib:start(?MODULE, init_it,
@@ -108,6 +115,13 @@ do_spawn(GenMod, link, Name, Mod, Args, Options) ->
[GenMod, self(), self(), Name, Mod, Args, Options],
Time,
spawn_opts(Options));
+do_spawn(GenMod, monitor, Name, Mod, Args, Options) ->
+ Time = timeout(Options),
+ Ret = proc_lib:start_monitor(?MODULE, init_it,
+ [GenMod, self(), self(), Name, Mod, Args, Options],
+ Time,
+ spawn_opts(Options)),
+ monitor_return(Ret);
do_spawn(GenMod, _, Name, Mod, Args, Options) ->
Time = timeout(Options),
proc_lib:start(?MODULE, init_it,
@@ -115,6 +129,26 @@ do_spawn(GenMod, _, Name, Mod, Args, Options) ->
Time,
spawn_opts(Options)).
+
+%%
+%% Adjust monitor returns for OTP gen behaviours...
+%%
+%% If an OTP behaviour is introduced that 'init_ack's
+%% other results, this has code has to be moved out
+%% into all behaviours as well as adjusted...
+%%
+monitor_return({{ok, Pid}, Mon}) when is_pid(Pid), is_reference(Mon) ->
+ %% Successful start_monitor()...
+ {ok, {Pid, Mon}};
+monitor_return({Error, Mon}) when is_reference(Mon) ->
+ %% Failure; wait for spawned process to terminate
+ %% and release resources, then return the error...
+ receive
+ {'DOWN', Mon, process, _Pid, _Reason} ->
+ ok
+ end,
+ Error.
+
%%-----------------------------------------------------------------
%% Initiate the new process.
%% Register the name using the Rfunc function
diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl
index 8213282867..f95d721fbb 100644
--- a/lib/stdlib/src/gen_event.erl
+++ b/lib/stdlib/src/gen_event.erl
@@ -31,9 +31,14 @@
%%% Re-written by Joe with new functional interface !
%%% Modified by Martin - uses proc_lib, sys and gen!
+%%%
+%%% NOTE: If init_ack() return values are modified, see comment
+%%% above monitor_return() in gen.erl!
+%%%
-export([start/0, start/1, start/2,
start_link/0, start_link/1, start_link/2,
+ start_monitor/0, start_monitor/1, start_monitor/2,
stop/1, stop/3,
notify/2, sync_notify/2,
add_handler/3, add_sup_handler/3, delete_handler/3, swap_handler/3,
@@ -128,11 +133,12 @@
| {'logfile', string()}.
-type option() :: {'timeout', timeout()}
| {'debug', [debug_flag()]}
- | {'spawn_opt', [proc_lib:spawn_option()]}
+ | {'spawn_opt', [proc_lib:start_spawn_option()]}
| {'hibernate_after', timeout()}.
-type emgr_ref() :: atom() | {atom(), atom()} | {'global', term()}
| {'via', atom(), term()} | pid().
-type start_ret() :: {'ok', pid()} | {'error', term()}.
+-type start_mon_ret() :: {'ok', {pid(),reference()}} | {'error', term()}.
%%---------------------------------------------------------------------------
@@ -183,6 +189,20 @@ start_link(Options) when is_list(Options) ->
start_link(Name, Options) ->
gen:start(?MODULE, link, Name, ?NO_CALLBACK, [], Options).
+-spec start_monitor() -> start_mon_ret().
+start_monitor() ->
+ gen:start(?MODULE, monitor, ?NO_CALLBACK, [], []).
+
+-spec start_monitor(emgr_name() | [option()]) -> start_mon_ret().
+start_monitor(Name) when is_tuple(Name) ->
+ gen:start(?MODULE, monitor, Name, ?NO_CALLBACK, [], []);
+start_monitor(Options) when is_list(Options) ->
+ gen:start(?MODULE, monitor, ?NO_CALLBACK, [], Options).
+
+-spec start_monitor(emgr_name(), [option()]) -> start_mon_ret().
+start_monitor(Name, Options) ->
+ gen:start(?MODULE, monitor, Name, ?NO_CALLBACK, [], Options).
+
%% -spec init_it(pid(), 'self' | pid(), emgr_name(), module(), [term()], [_]) ->
init_it(Starter, self, Name, Mod, Args, Options) ->
init_it(Starter, self(), Name, Mod, Args, Options);
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index 44e9231ebe..5dce5e78ad 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -19,6 +19,11 @@
%%
-module(gen_server).
+%%%
+%%% NOTE: If init_ack() return values are modified, see comment
+%%% above monitor_return() in gen.erl!
+%%%
+
%%% ---------------------------------------------------
%%%
%%% The idea behind THIS server is that the user module
@@ -89,6 +94,7 @@
%% API
-export([start/3, start/4,
start_link/3, start_link/4,
+ start_monitor/3, start_monitor/4,
stop/1, stop/3,
call/2, call/3,
cast/2, reply/2,
@@ -188,6 +194,12 @@ start_link(Mod, Args, Options) ->
start_link(Name, Mod, Args, Options) ->
gen:start(?MODULE, link, Name, Mod, Args, Options).
+start_monitor(Mod, Args, Options) ->
+ gen:start(?MODULE, monitor, Mod, Args, Options).
+
+start_monitor(Name, Mod, Args, Options) ->
+ gen:start(?MODULE, monitor, Name, Mod, Args, Options).
+
%% -----------------------------------------------------------------
%% Stop a generic server and wait for it to terminate.
diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl
index 8965af253b..a98c98b802 100644
--- a/lib/stdlib/src/gen_statem.erl
+++ b/lib/stdlib/src/gen_statem.erl
@@ -21,9 +21,15 @@
-include("logger.hrl").
+%%%
+%%% NOTE: If init_ack() return values are modified, see comment
+%%% above monitor_return() in gen.erl!
+%%%
+
%% API
-export(
[start/3,start/4,start_link/3,start_link/4,
+ start_monitor/3,start_monitor/4,
stop/1,stop/3,
cast/2,call/2,call/3,
enter_loop/4,enter_loop/5,enter_loop/6,
@@ -436,8 +442,9 @@ timeout_event_type(Type) ->
debug_opt()
| {'timeout', Time :: timeout()}
| hibernate_after_opt()
- | {'spawn_opt', [proc_lib:spawn_option()]}.
+ | {'spawn_opt', [proc_lib:start_spawn_option()]}.
-type start_ret() :: {'ok', pid()} | 'ignore' | {'error', term()}.
+-type start_mon_ret() :: {'ok', {pid(),reference()}} | 'ignore' | {'error', term()}.
@@ -469,6 +476,20 @@ start_link(Module, Args, Opts) ->
start_link(ServerName, Module, Args, Opts) ->
gen:start(?MODULE, link, ServerName, Module, Args, Opts).
+%% Start and monitor a state machine
+-spec start_monitor(
+ Module :: module(), Args :: term(), Opts :: [start_opt()]) ->
+ start_mon_ret().
+start_monitor(Module, Args, Opts) ->
+ gen:start(?MODULE, monitor, Module, Args, Opts).
+%%
+-spec start_monitor(
+ ServerName :: server_name(),
+ Module :: module(), Args :: term(), Opts :: [start_opt()]) ->
+ start_mon_ret().
+start_monitor(ServerName, Module, Args, Opts) ->
+ gen:start(?MODULE, monitor, ServerName, Module, Args, Opts).
+
%% Stop a state machine
-spec stop(ServerRef :: server_ref()) -> ok.
stop(ServerRef) ->
diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl
index cfbaf8b242..504d663059 100644
--- a/lib/stdlib/src/proc_lib.erl
+++ b/lib/stdlib/src/proc_lib.erl
@@ -28,6 +28,7 @@
spawn/3, spawn_link/3, spawn/4, spawn_link/4,
spawn_opt/2, spawn_opt/3, spawn_opt/4, spawn_opt/5,
start/3, start/4, start/5, start_link/3, start_link/4, start_link/5,
+ start_monitor/3, start_monitor/4, start_monitor/5,
hibernate/3,
init_ack/1, init_ack/2,
init_p/3,init_p/5,format/1,format/2,format/3,report_cb/2,
@@ -39,25 +40,21 @@
-export([wake_up/3]).
-export_type([spawn_option/0]).
+-export_type([start_spawn_option/0]).
-include("logger.hrl").
%%-----------------------------------------------------------------------------
--type priority_level() :: 'high' | 'low' | 'max' | 'normal'.
--type max_heap_size() :: non_neg_integer() |
- #{ size => non_neg_integer(),
- kill => true,
- error_logger => true}.
--type spawn_option() :: 'link'
- | 'monitor'
- | {'priority', priority_level()}
- | {'max_heap_size', max_heap_size()}
- | {'min_heap_size', non_neg_integer()}
- | {'min_bin_vheap_size', non_neg_integer()}
- | {'fullsweep_after', non_neg_integer()}
- | {'message_queue_data',
- 'off_heap' | 'on_heap' | 'mixed' }.
+-type start_spawn_option() :: 'link'
+ | {'priority', erlang:priority_level()}
+ | {'max_heap_size', erlang:max_heap_size()}
+ | {'min_heap_size', non_neg_integer()}
+ | {'min_bin_vheap_size', non_neg_integer()}
+ | {'fullsweep_after', non_neg_integer()}
+ | {'message_queue_data', erlang:message_queue_data() }.
+
+-type spawn_option() :: erlang:spawn_opt_option().
-type dict_or_pid() :: pid()
| (ProcInfo :: [_])
@@ -65,6 +62,14 @@
%%-----------------------------------------------------------------------------
+-define(VERIFY_NO_MONITOR_OPT(M, F, A, T, Opts),
+ case lists:member(monitor, Opts) of
+ true -> erlang:error(badarg, [M,F,A,T,Opts]);
+ false -> ok
+ end).
+
+%%-----------------------------------------------------------------------------
+
-spec spawn(Fun) -> pid() when
Fun :: function().
@@ -141,17 +146,16 @@ spawn_link(Node, M, F, A) when is_atom(M), is_atom(F), is_list(A) ->
Ancestors = get_ancestors(),
erlang:spawn_link(Node, ?MODULE, init_p, [Parent,Ancestors,M,F,A]).
--spec spawn_opt(Fun, SpawnOpts) -> pid() when
+-spec spawn_opt(Fun, SpawnOpts) -> pid() | {pid(), reference()} when
Fun :: function(),
SpawnOpts :: [spawn_option()].
spawn_opt(F, Opts) when is_function(F) ->
Parent = get_my_name(),
Ancestors = get_ancestors(),
- check_for_monitor(Opts),
erlang:spawn_opt(?MODULE, init_p, [Parent,Ancestors,F],Opts).
--spec spawn_opt(Node, Function, SpawnOpts) -> pid() when
+-spec spawn_opt(Node, Function, SpawnOpts) -> pid() | {pid(), reference()} when
Node :: node(),
Function :: function(),
SpawnOpts :: [spawn_option()].
@@ -159,10 +163,9 @@ spawn_opt(F, Opts) when is_function(F) ->
spawn_opt(Node, F, Opts) when is_function(F) ->
Parent = get_my_name(),
Ancestors = get_ancestors(),
- check_for_monitor(Opts),
erlang:spawn_opt(Node, ?MODULE, init_p, [Parent,Ancestors,F], Opts).
--spec spawn_opt(Module, Function, Args, SpawnOpts) -> pid() when
+-spec spawn_opt(Module, Function, Args, SpawnOpts) -> pid() | {pid(), reference()} when
Module :: module(),
Function :: atom(),
Args :: [term()],
@@ -171,10 +174,9 @@ spawn_opt(Node, F, Opts) when is_function(F) ->
spawn_opt(M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) ->
Parent = get_my_name(),
Ancestors = get_ancestors(),
- check_for_monitor(Opts),
erlang:spawn_opt(?MODULE, init_p, [Parent,Ancestors,M,F,A], Opts).
--spec spawn_opt(Node, Module, Function, Args, SpawnOpts) -> pid() when
+-spec spawn_opt(Node, Module, Function, Args, SpawnOpts) -> pid() | {pid(), reference()} when
Node :: node(),
Module :: module(),
Function :: atom(),
@@ -184,30 +186,13 @@ spawn_opt(M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) ->
spawn_opt(Node, M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) ->
Parent = get_my_name(),
Ancestors = get_ancestors(),
- check_for_monitor(Opts),
erlang:spawn_opt(Node, ?MODULE, init_p, [Parent,Ancestors,M,F,A], Opts).
-%% OTP-6345
-%% monitor spawn_opt option is currently not possible to use
-check_for_monitor(SpawnOpts) ->
- case lists:member(monitor, SpawnOpts) of
- true ->
- erlang:error(badarg);
- false ->
- false
- end.
-
spawn_mon(M,F,A) ->
Parent = get_my_name(),
Ancestors = get_ancestors(),
erlang:spawn_monitor(?MODULE, init_p, [Parent,Ancestors,M,F,A]).
-spawn_opt_mon(M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) ->
- Parent = get_my_name(),
- Ancestors = get_ancestors(),
- check_for_monitor(Opts),
- erlang:spawn_opt(?MODULE, init_p, [Parent,Ancestors,M,F,A], [monitor|Opts]).
-
-spec hibernate(Module, Function, Args) -> no_return() when
Module :: module(),
Function :: atom(),
@@ -216,14 +201,6 @@ spawn_opt_mon(M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) ->
hibernate(M, F, A) when is_atom(M), is_atom(F), is_list(A) ->
erlang:hibernate(?MODULE, wake_up, [M, F, A]).
-ensure_link(SpawnOpts) ->
- case lists:member(link, SpawnOpts) of
- true ->
- SpawnOpts;
- false ->
- [link|SpawnOpts]
- end.
-
-spec init_p(pid(), [pid()], function()) -> term().
init_p(Parent, Ancestors, Fun) when is_function(Fun) ->
@@ -299,20 +276,32 @@ start(M, F, A) when is_atom(M), is_atom(F), is_list(A) ->
Ret :: term() | {error, Reason :: term()}.
start(M, F, A, Timeout) when is_atom(M), is_atom(F), is_list(A) ->
- PidRef = spawn_mon(M, F, A),
- sync_wait_mon(PidRef, Timeout).
+ sync_start(spawn_mon(M, F, A), Timeout).
-spec start(Module, Function, Args, Time, SpawnOpts) -> Ret when
Module :: module(),
Function :: atom(),
Args :: [term()],
Time :: timeout(),
- SpawnOpts :: [spawn_option()],
+ SpawnOpts :: [start_spawn_option()],
Ret :: term() | {error, Reason :: term()}.
start(M, F, A, Timeout, SpawnOpts) when is_atom(M), is_atom(F), is_list(A) ->
- PidRef = spawn_opt_mon(M, F, A, SpawnOpts),
- sync_wait_mon(PidRef, Timeout).
+ ?VERIFY_NO_MONITOR_OPT(M, F, A, Timeout, SpawnOpts),
+ sync_start(?MODULE:spawn_opt(M, F, A, [monitor|SpawnOpts]), Timeout).
+
+sync_start({Pid, Ref}, Timeout) ->
+ receive
+ {ack, Pid, Return} ->
+ erlang:demonitor(Ref, [flush]),
+ Return;
+ {'DOWN', Ref, process, Pid, Reason} ->
+ {error, Reason}
+ after Timeout ->
+ erlang:demonitor(Ref, [flush]),
+ kill_flush(Pid),
+ {error, timeout}
+ end.
-spec start_link(Module, Function, Args) -> Ret when
Module :: module(),
@@ -331,60 +320,88 @@ start_link(M, F, A) when is_atom(M), is_atom(F), is_list(A) ->
Ret :: term() | {error, Reason :: term()}.
start_link(M, F, A, Timeout) when is_atom(M), is_atom(F), is_list(A) ->
- Pid = ?MODULE:spawn_link(M, F, A),
- sync_wait(Pid, Timeout).
+ sync_start_link(?MODULE:spawn_link(M, F, A), Timeout).
-spec start_link(Module, Function, Args, Time, SpawnOpts) -> Ret when
Module :: module(),
Function :: atom(),
Args :: [term()],
Time :: timeout(),
- SpawnOpts :: [spawn_option()],
+ SpawnOpts :: [start_spawn_option()],
Ret :: term() | {error, Reason :: term()}.
start_link(M,F,A,Timeout,SpawnOpts) when is_atom(M), is_atom(F), is_list(A) ->
- Pid = ?MODULE:spawn_opt(M, F, A, ensure_link(SpawnOpts)),
- sync_wait(Pid, Timeout).
+ ?VERIFY_NO_MONITOR_OPT(M, F, A, Timeout, SpawnOpts),
+ sync_start_link(?MODULE:spawn_opt(M, F, A, [link|SpawnOpts]), Timeout).
-sync_wait(Pid, Timeout) ->
+sync_start_link(Pid, Timeout) ->
receive
{ack, Pid, Return} ->
- Return;
+ Return;
{'EXIT', Pid, Reason} ->
- {error, Reason}
+ {error, Reason}
after Timeout ->
- unlink(Pid),
- exit(Pid, kill),
- flush(Pid),
- {error, timeout}
+ kill_flush(Pid),
+ {error, timeout}
end.
-sync_wait_mon({Pid, Ref}, Timeout) ->
+-spec start_monitor(Module, Function, Args) -> {Ret, Mon} when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ Mon :: reference(),
+ Ret :: term() | {error, Reason :: term()}.
+
+start_monitor(M, F, A) when is_atom(M), is_atom(F), is_list(A) ->
+ start_monitor(M, F, A, infinity).
+
+-spec start_monitor(Module, Function, Args, Time) -> {Ret, Mon} when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ Time :: timeout(),
+ Mon :: reference(),
+ Ret :: term() | {error, Reason :: term()}.
+
+start_monitor(M, F, A, Timeout) when is_atom(M), is_atom(F), is_list(A) ->
+ sync_start_monitor(spawn_mon(M, F, A), Timeout).
+
+-spec start_monitor(Module, Function, Args, Time, SpawnOpts) -> {Ret, Mon} when
+ Module :: module(),
+ Function :: atom(),
+ Args :: [term()],
+ Time :: timeout(),
+ SpawnOpts :: [start_spawn_option()],
+ Mon :: reference(),
+ Ret :: term() | {error, Reason :: term()}.
+
+start_monitor(M,F,A,Timeout,SpawnOpts) when is_atom(M),
+ is_atom(F),
+ is_list(A) ->
+ ?VERIFY_NO_MONITOR_OPT(M, F, A, Timeout, SpawnOpts),
+ sync_start_monitor(?MODULE:spawn_opt(M, F, A, [monitor|SpawnOpts]),
+ Timeout).
+
+sync_start_monitor({Pid, Ref}, Timeout) ->
receive
{ack, Pid, Return} ->
- erlang:demonitor(Ref, [flush]),
- Return;
- {'DOWN', Ref, _Type, Pid, Reason} ->
- {error, Reason};
- {'EXIT', Pid, Reason} -> %% link as spawn_opt?
- erlang:demonitor(Ref, [flush]),
- {error, Reason}
+ {Return, Ref};
+ {'DOWN', Ref, process, Pid, Reason} = Down ->
+ self() ! Down,
+ {{error, Reason}, Ref}
after Timeout ->
- erlang:demonitor(Ref, [flush]),
- exit(Pid, kill),
- flush(Pid),
- {error, timeout}
+ kill_flush(Pid),
+ {{error, timeout}, Ref}
end.
--spec flush(pid()) -> 'true'.
+-spec kill_flush(Pid) -> 'ok' when
+ Pid :: pid().
-flush(Pid) ->
- receive
- {'EXIT', Pid, _} ->
- true
- after 0 ->
- true
- end.
+kill_flush(Pid) ->
+ unlink(Pid),
+ exit(Pid, kill),
+ receive {'EXIT', Pid, _} -> ok after 0 -> ok end,
+ ok.
-spec init_ack(Parent, Ret) -> 'ok' when
Parent :: pid(),
diff --git a/lib/stdlib/test/gen_event_SUITE.erl b/lib/stdlib/test/gen_event_SUITE.erl
index 880b10117c..f77a45ccfd 100644
--- a/lib/stdlib/test/gen_event_SUITE.erl
+++ b/lib/stdlib/test/gen_event_SUITE.erl
@@ -112,6 +112,11 @@ start(Config) when is_list(Config) ->
[] = gen_event:which_handlers(Pid1),
ok = gen_event:stop(Pid1),
+ {ok, {Pid1b,Mon1b}} = gen_event:start_monitor(), %anonymous
+ [] = gen_event:which_handlers(Pid1b),
+ ok = gen_event:stop(Pid1b),
+ receive {'DOWN',Mon1b,process,Pid1b,_} -> ok end,
+
{ok, Pid2} = gen_event:start(?LMGR),
[] = gen_event:which_handlers(my_dummy_name),
[] = gen_event:which_handlers(Pid2),
@@ -122,21 +127,45 @@ start(Config) when is_list(Config) ->
[] = gen_event:which_handlers(Pid3),
ok = gen_event:stop(my_dummy_name),
+ {ok, {Pid3b,Mon3b}} = gen_event:start_monitor(?LMGR),
+ [] = gen_event:which_handlers(my_dummy_name),
+ [] = gen_event:which_handlers(Pid3b),
+ ok = gen_event:stop(my_dummy_name),
+ receive {'DOWN',Mon3b,process,Pid3b,_} -> ok end,
+
{ok, Pid4} = gen_event:start_link(?GMGR),
[] = gen_event:which_handlers(?GMGR),
[] = gen_event:which_handlers(Pid4),
ok = gen_event:stop(?GMGR),
+ {ok, {Pid4b,Mon4b}} = gen_event:start_monitor(?GMGR),
+ [] = gen_event:which_handlers(?GMGR),
+ [] = gen_event:which_handlers(Pid4b),
+ ok = gen_event:stop(?GMGR),
+ receive {'DOWN',Mon4b,process,Pid4b,_} -> ok end,
+
{ok, Pid5} = gen_event:start_link({via, dummy_via, my_dummy_name}),
[] = gen_event:which_handlers({via, dummy_via, my_dummy_name}),
[] = gen_event:which_handlers(Pid5),
ok = gen_event:stop({via, dummy_via, my_dummy_name}),
+ {ok, {Pid5b,Mon5b}} = gen_event:start_monitor({via, dummy_via, my_dummy_name}),
+ [] = gen_event:which_handlers({via, dummy_via, my_dummy_name}),
+ [] = gen_event:which_handlers(Pid5b),
+ ok = gen_event:stop({via, dummy_via, my_dummy_name}),
+ receive {'DOWN',Mon5b,process,Pid5b,_} -> ok end,
+
{ok, _} = gen_event:start_link(?LMGR),
{error, {already_started, _}} = gen_event:start_link(?LMGR),
{error, {already_started, _}} = gen_event:start(?LMGR),
ok = gen_event:stop(my_dummy_name),
+ {ok, {Pid5c,Mon5c}} = gen_event:start_monitor(?LMGR),
+ {error, {already_started, Pid5c}} = gen_event:start_monitor(?LMGR),
+ {error, {already_started, Pid5c}} = gen_event:start(?LMGR),
+ ok = gen_event:stop(my_dummy_name),
+ receive {'DOWN',Mon5c,process,Pid5c,_} -> ok end,
+
{ok, Pid6} = gen_event:start_link(?GMGR),
{error, {already_started, _}} = gen_event:start_link(?GMGR),
{error, {already_started, _}} = gen_event:start(?GMGR),
@@ -148,6 +177,17 @@ start(Config) when is_list(Config) ->
ct:fail(exit_gen_event)
end,
+ {ok, {Pid6b,Mon6b}} = gen_event:start_monitor(?GMGR),
+ {error, {already_started, _}} = gen_event:start_monitor(?GMGR),
+ {error, {already_started, _}} = gen_event:start(?GMGR),
+
+ ok = gen_event:stop(?GMGR, shutdown, 10000),
+ receive
+ {'DOWN', Mon6b, process, Pid6b, shutdown} -> ok
+ after 10000 ->
+ ct:fail(exit_gen_event)
+ end,
+
{ok, Pid7} = gen_event:start_link({via, dummy_via, my_dummy_name}),
{error, {already_started, _}} = gen_event:start_link({via, dummy_via, my_dummy_name}),
{error, {already_started, _}} = gen_event:start({via, dummy_via, my_dummy_name}),
@@ -159,6 +199,17 @@ start(Config) when is_list(Config) ->
ct:fail(exit_gen_event)
end,
+ {ok, {Pid7b,Mon7b}} = gen_event:start_monitor({via, dummy_via, my_dummy_name}),
+ {error, {already_started, _}} = gen_event:start_monitor({via, dummy_via, my_dummy_name}),
+ {error, {already_started, _}} = gen_event:start({via, dummy_via, my_dummy_name}),
+
+ exit(Pid7b, shutdown),
+ receive
+ {'DOWN', Mon7b, process, Pid7b, shutdown} -> ok
+ after 10000 ->
+ ct:fail(exit_gen_event)
+ end,
+
process_flag(trap_exit, OldFl),
ok.
diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl
index e29195e895..9794b9ee8d 100644
--- a/lib/stdlib/test/gen_server_SUITE.erl
+++ b/lib/stdlib/test/gen_server_SUITE.erl
@@ -161,6 +161,18 @@ start(Config) when is_list(Config) ->
ct:fail(not_stopped)
end,
+ %% anonymous monitored
+ {ok, {Pid1b, Mon1b}} =
+ gen_server:start_monitor(gen_server_SUITE, [], []),
+ ok = gen_server:call(Pid1b, started_p),
+ ok = gen_server:call(Pid1b, stop),
+ receive
+ {'DOWN', Mon1b, process, Pid1b, stopped} ->
+ ok
+ after 5000 ->
+ ct:fail(not_stopped)
+ end,
+
%% local register
{ok, Pid2} =
gen_server:start({local, my_test_name},
@@ -191,6 +203,22 @@ start(Config) when is_list(Config) ->
ct:fail(not_stopped)
end,
+ %% local register monitored
+ {ok, {Pid3b, Mon3b}} =
+ gen_server:start_monitor({local, my_test_name},
+ gen_server_SUITE, [], []),
+ ok = gen_server:call(my_test_name, started_p),
+ {error, {already_started, Pid3b}} =
+ gen_server:start_monitor({local, my_test_name},
+ gen_server_SUITE, [], []),
+ ok = gen_server:call(my_test_name, stop),
+ receive
+ {'DOWN', Mon3b, process, Pid3b, stopped} ->
+ ok
+ after 5000 ->
+ ct:fail(not_stopped)
+ end,
+
%% global register
{ok, Pid4} =
gen_server:start({global, my_test_name},
@@ -219,6 +247,22 @@ start(Config) when is_list(Config) ->
ct:fail(not_stopped)
end,
+ %% global register monitored
+ {ok, {Pid5b, Mon5b}} =
+ gen_server:start_monitor({global, my_test_name},
+ gen_server_SUITE, [], []),
+ ok = gen_server:call({global, my_test_name}, started_p),
+ {error, {already_started, Pid5b}} =
+ gen_server:start_monitor({global, my_test_name},
+ gen_server_SUITE, [], []),
+ ok = gen_server:call({global, my_test_name}, stop),
+ receive
+ {'DOWN', Mon5b, process, Pid5b, stopped} ->
+ ok
+ after 5000 ->
+ ct:fail(not_stopped)
+ end,
+
%% via register
dummy_via:reset(),
{ok, Pid6} =
diff --git a/lib/stdlib/test/gen_statem_SUITE.erl b/lib/stdlib/test/gen_statem_SUITE.erl
index 053233df9b..3ef5c7f7b3 100644
--- a/lib/stdlib/test/gen_statem_SUITE.erl
+++ b/lib/stdlib/test/gen_statem_SUITE.erl
@@ -130,8 +130,18 @@ start1(Config) ->
%% ?EXPECT_FAILURE(gen_statem:call(Pid0, hej), Reason),
%%process_flag(trap_exit, OldFl),
- ok = verify_empty_msgq().
+ ok = verify_empty_msgq(),
+ {ok,{Pid1,Mon1}} = gen_statem:start_monitor(?MODULE, start_arg(Config, []), []),
+ ok = do_func_test(Pid1),
+ ok = do_sync_func_test(Pid1),
+ stop_it(Pid1),
+ receive
+ {'DOWN', Mon1, process, Pid1, _Reason} ->
+ ok
+ end,
+ ok = verify_empty_msgq().
+
%% anonymous w. shutdown
start2(Config) ->
%% Dont link when shutdown
@@ -186,7 +196,7 @@ start6(Config) ->
ok = verify_empty_msgq().
-%% global register linked
+%% global register linked & monitored
start7(Config) ->
STM = {global,my_stm},
@@ -196,6 +206,8 @@ start7(Config) ->
gen_statem:start_link(STM, ?MODULE, start_arg(Config, []), []),
{error,{already_started,Pid}} =
gen_statem:start(STM, ?MODULE, start_arg(Config, []), []),
+ {error,{already_started,Pid}} =
+ gen_statem:start_monitor(STM, ?MODULE, start_arg(Config, []), []),
ok = do_func_test(Pid),
ok = do_sync_func_test(Pid),
@@ -203,6 +215,28 @@ start7(Config) ->
ok = do_sync_func_test(STM),
stop_it(STM),
+ ok = verify_empty_msgq(),
+
+ {ok,{Pid1,Mon1}} =
+ gen_statem:start_monitor(STM, ?MODULE, start_arg(Config, []), []),
+ {error,{already_started,Pid1}} =
+ gen_statem:start_link(STM, ?MODULE, start_arg(Config, []), []),
+ {error,{already_started,Pid1}} =
+ gen_statem:start(STM, ?MODULE, start_arg(Config, []), []),
+ {error,{already_started,Pid1}} =
+ gen_statem:start_monitor(STM, ?MODULE, start_arg(Config, []), []),
+
+ ok = do_func_test(Pid1),
+ ok = do_sync_func_test(Pid1),
+ ok = do_func_test(STM),
+ ok = do_sync_func_test(STM),
+ stop_it(STM),
+
+ receive
+ {'DOWN', Mon1, process, Pid1, _Reason} ->
+ ok
+ end,
+
ok = verify_empty_msgq().
@@ -226,7 +260,7 @@ start8(Config) ->
%%process_flag(trap_exit, OldFl),
ok = verify_empty_msgq().
-%% local register linked
+%% local register linked & monitored
start9(Config) ->
%%OldFl = process_flag(trap_exit, true),
Name = my_stm,
@@ -244,6 +278,24 @@ start9(Config) ->
stop_it(Pid),
%%process_flag(trap_exit, OldFl),
+ ok = verify_empty_msgq(),
+
+ {ok,{Pid1,Mon1}} =
+ gen_statem:start_monitor(STM, ?MODULE, start_arg(Config, []), []),
+ {error,{already_started,Pid1}} =
+ gen_statem:start_monitor(STM, ?MODULE, start_arg(Config, []), []),
+
+ ok = do_func_test(Pid1),
+ ok = do_sync_func_test(Pid1),
+ ok = do_func_test(Name),
+ ok = do_sync_func_test(Name),
+ stop_it(Pid1),
+
+ receive
+ {'DOWN', Mon1, process, Pid1, _Reason} ->
+ ok
+ end,
+
ok = verify_empty_msgq().
%% global register
diff --git a/lib/stdlib/test/proc_lib_SUITE.erl b/lib/stdlib/test/proc_lib_SUITE.erl
index 127b1317e4..545ad63b9a 100644
--- a/lib/stdlib/test/proc_lib_SUITE.erl
+++ b/lib/stdlib/test/proc_lib_SUITE.erl
@@ -27,8 +27,12 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
crash/1, stacktrace/1, sync_start_nolink/1, sync_start_link/1,
- spawn_opt/1, sp1/0, sp2/0, sp3/1, sp4/2, sp5/1, '\x{447}'/0,
- hibernate/1, stop/1, t_format/1, t_format_arbitrary/1]).
+ sync_start_monitor/1, sync_start_monitor_link/1,
+ sync_start_timeout/1, sync_start_link_timeout/1,
+ sync_start_monitor_link_timeout/1,
+ spawn_opt/1, sp1/0, sp2/0, sp3/1, sp4/2, sp5/1, sp6/1, sp7/1,
+ sp8/1, sp9/1, sp10/1,
+ '\x{447}'/0, hibernate/1, stop/1, t_format/1, t_format_arbitrary/1]).
-export([ otp_6345/1, init_dont_hang/1]).
-export([hib_loop/1, awaken/1]).
@@ -55,7 +59,10 @@ all() ->
groups() ->
[{tickets, [], [otp_6345, init_dont_hang]},
- {sync_start, [], [sync_start_nolink, sync_start_link]}].
+ {sync_start, [], [sync_start_nolink, sync_start_link,
+ sync_start_monitor, sync_start_monitor_link,
+ sync_start_timeout, sync_start_link_timeout,
+ sync_start_monitor_link_timeout]}].
init_per_suite(Config) ->
Config.
@@ -275,6 +282,84 @@ sync_start_link(Config) when is_list(Config) ->
end,
ok.
+sync_start_monitor(Config) when is_list(Config) ->
+ _Pid = spawn_link(?MODULE, sp6, [self()]),
+ receive
+ {sync_started, _} -> ct:fail(async_start)
+ after 1000 -> ok
+ end,
+ receive
+ {Pid2, init} ->
+ Pid2 ! go_on
+ end,
+ receive
+ {sync_started, _} -> ok
+ after 1000 -> ct:fail(no_sync_start)
+ end,
+ receive received_down -> ok
+ after 2000 -> ct:fail(no_down)
+ end,
+ ok.
+
+sync_start_monitor_link(Config) when is_list(Config) ->
+ _Pid = spawn_link(?MODULE, sp7, [self()]),
+ receive
+ {sync_started, _} -> ct:fail(async_start)
+ after 1000 -> ok
+ end,
+ receive
+ {Pid2, init} ->
+ Pid2 ! go_on
+ end,
+ receive
+ {sync_started, _} -> ok
+ after 1000 -> ct:fail(no_sync_start)
+ end,
+ receive received_down -> ok
+ after 1000 -> ct:fail(no_down)
+ end,
+ receive received_exit -> ok
+ after 1000 -> ct:fail(no_exit)
+ end,
+ ok.
+
+sync_start_timeout(Config) when is_list(Config) ->
+ _Pid = spawn_link(?MODULE, sp8, [self()]),
+ receive done -> ok end,
+ receive {received_exit, _} = M1 -> ct:fail(M1)
+ after 0 -> ok
+ end,
+ receive {received_down, _} = M2 -> ct:fail(M2)
+ after 0 -> ok
+ end,
+ ok.
+
+sync_start_link_timeout(Config) when is_list(Config) ->
+ _Pid = spawn_link(?MODULE, sp9, [self()]),
+ receive done -> ok end,
+ receive {received_exit, _} = M1 -> ct:fail(M1)
+ after 0 -> ok
+ end,
+ receive {received_down, _} = M2 -> ct:fail(M2)
+ after 0 -> ok
+ end,
+ ok.
+
+sync_start_monitor_link_timeout(Config) when is_list(Config) ->
+ _Pid = spawn_link(?MODULE, sp10, [self()]),
+ receive done -> ok end,
+ receive {received_exit, _} = M1 -> ct:fail(M1)
+ after 0 -> ok
+ end,
+ receive
+ {received_down, R} ->
+ killed = R,
+ ok
+ after 0 -> ct:fail(no_down)
+ end,
+ ok.
+
+
spawn_opt(Config) when is_list(Config) ->
F = fun sp1/0,
{name,Fname} = erlang:fun_info(F, name),
@@ -313,6 +398,89 @@ sp5(Tester) ->
Pid = proc_lib:start(?MODULE, sp4, [self(), Tester]),
Tester ! {sync_started, Pid}.
+sp6(Tester) ->
+ process_flag(trap_exit, true),
+ {Pid, Mon} = proc_lib:start_monitor(?MODULE, sp4, [self(), Tester]),
+ Tester ! {sync_started, Pid},
+ receive
+ {'EXIT', Pid, normal} ->
+ exit(received_exit)
+ after 1000 ->
+ ok
+ end,
+ receive
+ {'DOWN', Mon, process, Pid, normal} ->
+ Tester ! received_down
+ end.
+
+sp7(Tester) ->
+ process_flag(trap_exit, true),
+ {Pid, Mon} = proc_lib:start_monitor(?MODULE, sp4, [self(), Tester], infinity, [link]),
+ Tester ! {sync_started, Pid},
+ receive
+ {'EXIT', Pid, normal} ->
+ Tester ! received_exit
+ end,
+ receive
+ {'DOWN', Mon, process, Pid, normal} ->
+ Tester ! received_down
+ end.
+
+sp8(Tester) ->
+ process_flag(trap_exit, true),
+ {error,timeout} = proc_lib:start(?MODULE, sp4, [self(), Tester], 500, [link]),
+ receive after 500 -> ok end,
+ receive
+ {'EXIT', _Pid1, Reason1} ->
+ Tester ! {received_exit, Reason1}
+ after 0 ->
+ ok
+ end,
+ receive
+ {'DOWN', _Mon2, process, _Pid2, Reason2} ->
+ Tester ! {received_down, Reason2}
+ after 0 ->
+ ok
+ end,
+ Tester ! done.
+
+sp9(Tester) ->
+ process_flag(trap_exit, true),
+ {error,timeout} = proc_lib:start_link(?MODULE, sp4, [self(), Tester], 500),
+ receive after 500 -> ok end,
+ receive
+ {'EXIT', _Pid1, Reason1} ->
+ Tester ! {received_exit, Reason1}
+ after 0 ->
+ ok
+ end,
+ receive
+ {'DOWN', _Mon, process, _Pid2, Reason2} ->
+ Tester ! {received_down, Reason2}
+ after 0 ->
+ ok
+ end,
+ Tester ! done.
+
+
+sp10(Tester) ->
+ process_flag(trap_exit, true),
+ {{error,timeout}, Mon} = proc_lib:start_monitor(?MODULE, sp4, [self(), Tester], 500, [link]),
+ receive after 500 -> ok end,
+ receive
+ {'EXIT', _Pid1, Reason1} ->
+ Tester ! {received_exit, Reason1}
+ after 0 ->
+ ok
+ end,
+ receive
+ {'DOWN', Mon, process, _Pid2, Reason2} ->
+ Tester ! {received_down, Reason2}
+ after 0 ->
+ ok
+ end,
+ Tester ! done.
+
sp4(Parent, Tester) ->
Tester ! {self(), init},
receive
@@ -421,10 +589,12 @@ hib_receive_messages(N) ->
%% 'monitor' spawn_opt option.
otp_6345(Config) when is_list(Config) ->
Opts = [link,monitor],
- {'EXIT', {badarg,[{proc_lib,check_for_monitor,_,_}|_Stack]}} =
- (catch proc_lib:start(?MODULE, otp_6345_init, [self()],
- 1000, Opts)),
- ok.
+ try
+ blupp = proc_lib:start(?MODULE, otp_6345_init, [self()],
+ 1000, Opts)
+ catch
+ error:badarg -> ok
+ end.
otp_6345_init(Parent) ->
proc_lib:init_ack(Parent, {ok, self()}),