diff options
author | Rickard Green <rickard@erlang.org> | 2019-10-18 19:37:06 +0200 |
---|---|---|
committer | Rickard Green <rickard@erlang.org> | 2019-10-22 16:00:44 +0200 |
commit | 22aa1f15bf28b98ab1243b64c5e8508f020e6e4b (patch) | |
tree | 383116736efb1d9892e6af23fffa2de9301f7f27 /lib/stdlib | |
parent | 7fe7fa3dde556b5b92522f8279d465bb52baf1f6 (diff) | |
download | erlang-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.xml | 35 | ||||
-rw-r--r-- | lib/stdlib/doc/src/gen_server.xml | 45 | ||||
-rw-r--r-- | lib/stdlib/doc/src/gen_statem.xml | 56 | ||||
-rw-r--r-- | lib/stdlib/doc/src/proc_lib.xml | 96 | ||||
-rw-r--r-- | lib/stdlib/src/gen.erl | 36 | ||||
-rw-r--r-- | lib/stdlib/src/gen_event.erl | 22 | ||||
-rw-r--r-- | lib/stdlib/src/gen_server.erl | 12 | ||||
-rw-r--r-- | lib/stdlib/src/gen_statem.erl | 23 | ||||
-rw-r--r-- | lib/stdlib/src/proc_lib.erl | 183 | ||||
-rw-r--r-- | lib/stdlib/test/gen_event_SUITE.erl | 51 | ||||
-rw-r--r-- | lib/stdlib/test/gen_server_SUITE.erl | 44 | ||||
-rw-r--r-- | lib/stdlib/test/gen_statem_SUITE.erl | 58 | ||||
-rw-r--r-- | lib/stdlib/test/proc_lib_SUITE.erl | 184 |
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> Name = atom()</v> + <v> GlobalName = ViaName = term()</v> + <v>Options = [Option]</v> + <v> Option = {debug,Dbgs} | {timeout,Time} | {hibernate_after,HibernateAfterTimeout} | {spawn_opt,SOpts}</v> + <v> Dbgs = [Dbg]</v> + <v> Dbg = trace | log | statistics | {log_to_file,FileName} | {install,{Func,FuncState}}</v> + <v> SOpts = [term()]</v> + <v>Result = {ok,{Pid,Mon}} | {error,{already_started,Pid}}</v> + <v> 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> | {via,Module,ViaName}</v> + <v> Name = atom()</v> + <v> GlobalName = ViaName = term()</v> + <v>Module = atom()</v> + <v>Args = term()</v> + <v>Options = [Option]</v> + <v> Option = {debug,Dbgs} | {timeout,Time} | {hibernate_after,HibernateAfterTimeout} | {spawn_opt,SOpts}</v> + <v> Dbgs = [Dbg]</v> + <v> Dbg = trace | log | statistics | {log_to_file,FileName} | {install,{Func,FuncState}}</v> + <v> SOpts = [term()]</v> + <v>Result = {ok,{Pid,Mon}} | ignore | {error,Error}</v> + <v> Pid = pid()</v> + <v> 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()}), |