aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorSiri Hansen <[email protected]>2014-06-11 14:29:19 +0200
committerSiri Hansen <[email protected]>2014-06-11 14:29:19 +0200
commitda42410cb15c0749ea6cf9b6196b81320d8391b6 (patch)
tree829f5cdb59cf92cdaf381564fe51885644494650 /lib
parent5f2b70f205bc64be545b75db5419111aac11291d (diff)
parent154a057dcbf087deb38b13e97f0a0373e6a72f1d (diff)
downloadotp-da42410cb15c0749ea6cf9b6196b81320d8391b6.tar.gz
otp-da42410cb15c0749ea6cf9b6196b81320d8391b6.tar.bz2
otp-da42410cb15c0749ea6cf9b6196b81320d8391b6.zip
Merge branch 'siri/sync-stop-gen/OTP-11173'
* siri/sync-stop-gen/OTP-11173: Add synchronous stop function to wx_object Fix minor bugs in gen_server and gen_fsm documentation Update gen_event:stop to be synchronous Add synchronous stop functions to gen_server and gen_fsm Add synchronous stop function to proc_lib Add system message 'terminate' Remove old code from stdlib/test/sys_sp2.erl
Diffstat (limited to 'lib')
-rw-r--r--lib/stdlib/doc/src/gen_event.xml35
-rw-r--r--lib/stdlib/doc/src/gen_fsm.xml44
-rw-r--r--lib/stdlib/doc/src/gen_server.xml39
-rw-r--r--lib/stdlib/doc/src/proc_lib.xml35
-rw-r--r--lib/stdlib/doc/src/sys.xml13
-rw-r--r--lib/stdlib/src/gen.erl113
-rw-r--r--lib/stdlib/src/gen_event.erl13
-rw-r--r--lib/stdlib/src/gen_fsm.erl6
-rw-r--r--lib/stdlib/src/gen_server.erl12
-rw-r--r--lib/stdlib/src/proc_lib.erl52
-rw-r--r--lib/stdlib/src/sys.erl27
-rw-r--r--lib/stdlib/test/gen_event_SUITE.erl4
-rw-r--r--lib/stdlib/test/gen_fsm_SUITE.erl106
-rw-r--r--lib/stdlib/test/gen_server_SUITE.erl109
-rw-r--r--lib/stdlib/test/proc_lib_SUITE.erl93
-rw-r--r--lib/stdlib/test/sys_SUITE.erl16
-rw-r--r--lib/stdlib/test/sys_sp1.erl14
-rw-r--r--lib/stdlib/test/sys_sp2.erl33
-rw-r--r--lib/wx/src/wx_object.erl39
-rw-r--r--lib/wx/test/wx_event_SUITE.erl7
-rw-r--r--lib/wx/test/wx_obj_test.erl18
21 files changed, 686 insertions, 142 deletions
diff --git a/lib/stdlib/doc/src/gen_event.xml b/lib/stdlib/doc/src/gen_event.xml
index b9dfff833e..5c96d6e576 100644
--- a/lib/stdlib/doc/src/gen_event.xml
+++ b/lib/stdlib/doc/src/gen_event.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -44,6 +44,7 @@
<pre>
gen_event module Callback module
---------------- ---------------
+gen_event:start
gen_event:start_link -----> -
gen_event:add_handler
@@ -177,7 +178,7 @@ gen_event:stop -----> Module:terminate/2
<name>add_handler(EventMgrRef, Handler, Args) -> Result</name>
<fsummary>Add an event handler to a generic event manager.</fsummary>
<type>
- <v>EventMgr = Name | {Name,Node} | {global,GlobalName}
+ <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName}
| {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
<v>&nbsp;GlobalName = ViaName = term()</v>
@@ -223,7 +224,7 @@ gen_event:stop -----> Module:terminate/2
<name>add_sup_handler(EventMgrRef, Handler, Args) -> Result</name>
<fsummary>Add a supervised event handler to a generic event manager.</fsummary>
<type>
- <v>EventMgr = Name | {Name,Node} | {global,GlobalName}
+ <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName}
| {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
<v>&nbsp;GlobalName = ViaName = term()</v>
@@ -456,19 +457,37 @@ gen_event:stop -----> Module:terminate/2
</func>
<func>
<name>stop(EventMgrRef) -> ok</name>
+ <name>stop(EventMgrRef, Reason, Timeout) -> ok</name>
<fsummary>Terminate a generic event manager.</fsummary>
<type>
<v>EventMgrRef = Name | {Name,Node} | {global,GlobalName}
| {via,Module,ViaName} | pid()</v>
<v>Name = Node = atom()</v>
<v>GlobalName = ViaName = term()</v>
+ <v>Reason = term()</v>
+ <v>Timeout = int()>0 | infinity</v>
</type>
<desc>
- <p>Terminates the event manager <c>EventMgrRef</c>. Before
- terminating, the event manager will call
- <c>Module:terminate(stop,...)</c> for each installed event
- handler.</p>
- <p>See <c>add_handler/3</c> for a description of the argument.</p>
+ <p>Orders the event manager <c>EventMgrRef</c> to exit with
+ the given <c>Reason</c> and waits for it to
+ terminate. Before terminating, the gen_event will call
+ <seealso marker="#Module:terminate/2">Module:terminate(stop,...)</seealso>
+ for each installed event handler.</p>
+ <p>The function returns <c>ok</c> if the event manager terminates
+ with the expected reason. Any other reason than <c>normal</c>,
+ <c>shutdown</c>, or <c>{shutdown,Term}</c> will cause an
+ error report to be issued using
+ <seealso marker="kernel:error_logger#format/2">error_logger:format/2</seealso>.
+ The default <c>Reason</c> is <c>normal</c>.</p>
+ <p><c>Timeout</c> is an integer greater than zero which
+ specifies how many milliseconds to wait for the event manager to
+ terminate, or the atom <c>infinity</c> to wait
+ indefinitely. The default value is <c>infinity</c>. If the
+ event manager has not terminated within the specified time, a
+ <c>timeout</c> exception is raised.</p>
+ <p>If the process does not exist, a <c>noproc</c> exception
+ is raised.</p>
+ <p>See <c>add_handler/3</c> for a description of <c>EventMgrRef</c>.</p>
</desc>
</func>
</funcs>
diff --git a/lib/stdlib/doc/src/gen_fsm.xml b/lib/stdlib/doc/src/gen_fsm.xml
index 848d57f3e6..b1bba3eff0 100644
--- a/lib/stdlib/doc/src/gen_fsm.xml
+++ b/lib/stdlib/doc/src/gen_fsm.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -43,8 +43,11 @@
<pre>
gen_fsm module Callback module
-------------- ---------------
+gen_fsm:start
gen_fsm:start_link -----> Module:init/1
+gen_fsm:stop -----> Module:terminate/3
+
gen_fsm:send_event -----> Module:StateName/2
gen_fsm:send_all_state_event -----> Module:handle_event/3
@@ -187,6 +190,39 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
</desc>
</func>
<func>
+ <name>stop(FsmRef) -> ok</name>
+ <name>stop(FsmRef, Reason, Timeout) -> ok</name>
+ <fsummary>Synchronously stop a generic FSM.</fsummary>
+ <type>
+ <v>FsmRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
+ <v>&nbsp;Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
+ <v>Reason = term()</v>
+ <v>Timeout = int()>0 | infinity</v>
+ </type>
+ <desc>
+ <p>Orders a generic FSM to exit with the given <c>Reason</c>
+ and waits for it to terminate. The gen_fsm will call
+ <seealso marker="#Module:terminate/3">Module:terminate/3</seealso>
+ before exiting.</p>
+ <p>The function returns <c>ok</c> if the generic FSM terminates
+ with the expected reason. Any other reason than <c>normal</c>,
+ <c>shutdown</c>, or <c>{shutdown,Term}</c> will cause an
+ error report to be issued using
+ <seealso marker="kernel:error_logger#format/2">error_logger:format/2</seealso>.
+ The default <c>Reason</c> is <c>normal</c>.</p>
+ <p><c>Timeout</c> is an integer greater than zero which
+ specifies how many milliseconds to wait for the generic FSM
+ to terminate, or the atom <c>infinity</c> to wait
+ indefinitely. The default value is <c>infinity</c>. If the
+ generic FSM has not terminated within the specified time, a
+ <c>timeout</c> exception is raised.</p>
+ <p>If the process does not exist, a <c>noproc</c> exception
+ is raised.</p>
+ </desc>
+ </func>
+ <func>
<name>send_event(FsmRef, Event) -> ok</name>
<fsummary>Send an event asynchronously to a generic FSM.</fsummary>
<type>
@@ -528,7 +564,8 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
<c>Module:init/1</c> for a description of <c>Timeout</c> and <c>hibernate</c>.</p>
<p>If the function returns <c>{stop,Reason,NewStateData}</c>,
the gen_fsm will call
- <c>Module:terminate(Reason,NewStateData)</c> and terminate.</p>
+ <c>Module:terminate(Reason,StateName,NewStateData)</c> and
+ terminate.</p>
</desc>
</func>
<func>
@@ -614,7 +651,8 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
<c>{stop,Reason,NewStateData}</c>, any reply to <c>From</c>
must be given explicitly using <c>gen_fsm:reply/2</c>.
The gen_fsm will then call
- <c>Module:terminate(Reason,NewStateData)</c> and terminate.</p>
+ <c>Module:terminate(Reason,StateName,NewStateData)</c> and
+ terminate.</p>
</desc>
</func>
<func>
diff --git a/lib/stdlib/doc/src/gen_server.xml b/lib/stdlib/doc/src/gen_server.xml
index 62c0394479..a915e567a5 100644
--- a/lib/stdlib/doc/src/gen_server.xml
+++ b/lib/stdlib/doc/src/gen_server.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -43,8 +43,11 @@
<pre>
gen_server module Callback module
----------------- ---------------
+gen_server:start
gen_server:start_link -----> Module:init/1
+gen_server:stop -----> Module:terminate/2
+
gen_server:call
gen_server:multi_call -----> Module:handle_call/3
@@ -184,6 +187,40 @@ gen_server:abcast -----> Module:handle_cast/2
</desc>
</func>
<func>
+ <name>stop(ServerRef) -> ok</name>
+ <name>stop(ServerRef, Reason, Timeout) -> ok</name>
+ <fsummary>Synchronously stop a generic server.</fsummary>
+ <type>
+ <v>ServerRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
+ <v>&nbsp;Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
+ <v>Reason = term()</v>
+ <v>Timeout = int()>0 | infinity</v>
+ </type>
+ <desc>
+ <p>Orders a generic server to exit with the
+ given <c>Reason</c> and waits for it to terminate. The
+ gen_server will call
+ <seealso marker="#Module:terminate/2">Module:terminate/2</seealso>
+ before exiting.</p>
+ <p>The function returns <c>ok</c> if the server terminates
+ with the expected reason. Any other reason than <c>normal</c>,
+ <c>shutdown</c>, or <c>{shutdown,Term}</c> will cause an
+ error report to be issued using
+ <seealso marker="kernel:error_logger#format/2">error_logger:format/2</seealso>.
+ The default <c>Reason</c> is <c>normal</c>.</p>
+ <p><c>Timeout</c> is an integer greater than zero which
+ specifies how many milliseconds to wait for the server to
+ terminate, or the atom <c>infinity</c> to wait
+ indefinitely. The default value is <c>infinity</c>. If the
+ server has not terminated within the specified time, a
+ <c>timeout</c> exception is raised.</p>
+ <p>If the process does not exist, a <c>noproc</c> exception
+ is raised.</p>
+ </desc>
+ </func>
+ <func>
<name>call(ServerRef, Request) -> Reply</name>
<name>call(ServerRef, Request, Timeout) -> Reply</name>
<fsummary>Make a synchronous call to a generic server.</fsummary>
diff --git a/lib/stdlib/doc/src/proc_lib.xml b/lib/stdlib/doc/src/proc_lib.xml
index 5bf5744622..88630b1fdb 100644
--- a/lib/stdlib/doc/src/proc_lib.xml
+++ b/lib/stdlib/doc/src/proc_lib.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -298,6 +298,39 @@ init(Parent) ->
<c>proc_lib</c> functions.</p>
</desc>
</func>
+ <func>
+ <name name="stop" arity="1"/>
+ <fsummary>Terminate a process synchronously.</fsummary>
+ <type variable="Process"/>
+ <desc>Equivalent
+ to <seealso marker="#stop/3">stop(Process, normal, infinity)</seealso>.
+ </desc>
+ </func>
+ <func>
+ <name name="stop" arity="3"/>
+ <fsummary>Terminate a process synchronously.</fsummary>
+ <type variable="Process"/>
+ <type variable="Reason"/>
+ <type variable="Timeout"/>
+ <desc>
+ <p>Orders the process to exit with the given <c>Reason</c> and
+ waits for it to terminate.</p>
+ <p>The function returns <c>ok</c> if the process exits with
+ the given <c>Reason</c> within <c>Timeout</c>
+ milliseconds.</p>
+ <p>If the call times out, a <c>timeout</c> exception is
+ raised.</p>
+ <p>If the process does not exist, a <c>noproc</c>
+ exception is raised.</p>
+ <p>The implementation of this function is based on the
+ <c>terminate</c> system message, and requires that the
+ process handles system messages correctly.
+ See <seealso marker="sys">sys(3)</seealso>
+ and <seealso marker="doc/design_principles:spec_proc">OTP
+ Design Principles</seealso> for information about system
+ messages.</p>
+ </desc>
+ </func>
</funcs>
<section>
diff --git a/lib/stdlib/doc/src/sys.xml b/lib/stdlib/doc/src/sys.xml
index a46fa1289f..7fb82e0e7e 100644
--- a/lib/stdlib/doc/src/sys.xml
+++ b/lib/stdlib/doc/src/sys.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -356,6 +356,17 @@
installed.</p>
</desc>
</func>
+ <func>
+ <name name="terminate" arity="2"/>
+ <name name="terminate" arity="3"/>
+ <fsummary>Terminate the process</fsummary>
+ <desc>
+ <p>This function orders the process to terminate with the
+ given <c><anno>Reason</anno></c>. The termination is done
+ asynchronously, so there is no guarantee that the process is
+ actually terminated when the function returns.</p>
+ </desc>
+ </func>
</funcs>
<section>
diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl
index 63116fa16e..6d7ca3d75c 100644
--- a/lib/stdlib/src/gen.erl
+++ b/lib/stdlib/src/gen.erl
@@ -26,7 +26,7 @@
%%% The standard behaviour should export init_it/6.
%%%-----------------------------------------------------------------
-export([start/5, start/6, debug_options/1,
- call/3, call/4, reply/2]).
+ call/3, call/4, reply/2, stop/1, stop/3]).
-export([init_it/6, init_it/7]).
@@ -145,56 +145,10 @@ init_it2(GenMod, Starter, Parent, Name, Mod, Args, Options) ->
call(Process, Label, Request) ->
call(Process, Label, Request, ?default_timeout).
-%% Local or remote by pid
-call(Pid, Label, Request, Timeout)
- when is_pid(Pid), Timeout =:= infinity;
- is_pid(Pid), is_integer(Timeout), Timeout >= 0 ->
- do_call(Pid, Label, Request, Timeout);
-%% Local by name
-call(Name, Label, Request, Timeout)
- when is_atom(Name), Timeout =:= infinity;
- is_atom(Name), is_integer(Timeout), Timeout >= 0 ->
- case whereis(Name) of
- Pid when is_pid(Pid) ->
- do_call(Pid, Label, Request, Timeout);
- undefined ->
- exit(noproc)
- end;
-%% Global by name
call(Process, Label, Request, Timeout)
- when ((tuple_size(Process) == 2 andalso element(1, Process) == global)
- orelse
- (tuple_size(Process) == 3 andalso element(1, Process) == via))
- andalso
- (Timeout =:= infinity orelse (is_integer(Timeout) andalso Timeout >= 0)) ->
- case where(Process) of
- Pid when is_pid(Pid) ->
- Node = node(Pid),
- try do_call(Pid, Label, Request, Timeout)
- catch
- exit:{nodedown, Node} ->
- %% A nodedown not yet detected by global,
- %% pretend that it was.
- exit(noproc)
- end;
- undefined ->
- exit(noproc)
- end;
-%% Local by name in disguise
-call({Name, Node}, Label, Request, Timeout)
- when Node =:= node(), Timeout =:= infinity;
- Node =:= node(), is_integer(Timeout), Timeout >= 0 ->
- call(Name, Label, Request, Timeout);
-%% Remote by name
-call({_Name, Node}=Process, Label, Request, Timeout)
- when is_atom(Node), Timeout =:= infinity;
- is_atom(Node), is_integer(Timeout), Timeout >= 0 ->
- if
- node() =:= nonode@nohost ->
- exit({nodedown, Node});
- true ->
- do_call(Process, Label, Request, Timeout)
- end.
+ when Timeout =:= infinity; is_integer(Timeout), Timeout >= 0 ->
+ Fun = fun(Pid) -> do_call(Pid, Label, Request, Timeout) end,
+ do_for_proc(Process, Fun).
do_call(Process, Label, Request, Timeout) ->
try erlang:monitor(process, Process) of
@@ -276,6 +230,65 @@ reply({To, Tag}, Reply) ->
Msg = {Tag, Reply},
try To ! Msg catch _:_ -> Msg end.
+%%-----------------------------------------------------------------
+%% Syncronously stop a generic process
+%%-----------------------------------------------------------------
+stop(Process) ->
+ stop(Process, normal, infinity).
+
+stop(Process, Reason, Timeout)
+ when Timeout =:= infinity; is_integer(Timeout), Timeout >= 0 ->
+ Fun = fun(Pid) -> proc_lib:stop(Pid, Reason, Timeout) end,
+ do_for_proc(Process, Fun).
+
+%%-----------------------------------------------------------------
+%% Map different specifications of a process to either Pid or
+%% {Name,Node}. Execute the given Fun with the process as only
+%% argument.
+%% -----------------------------------------------------------------
+
+%% Local or remote by pid
+do_for_proc(Pid, Fun) when is_pid(Pid) ->
+ Fun(Pid);
+%% Local by name
+do_for_proc(Name, Fun) when is_atom(Name) ->
+ case whereis(Name) of
+ Pid when is_pid(Pid) ->
+ Fun(Pid);
+ undefined ->
+ exit(noproc)
+ end;
+%% Global by name
+do_for_proc(Process, Fun)
+ when ((tuple_size(Process) == 2 andalso element(1, Process) == global)
+ orelse
+ (tuple_size(Process) == 3 andalso element(1, Process) == via)) ->
+ case where(Process) of
+ Pid when is_pid(Pid) ->
+ Node = node(Pid),
+ try Fun(Pid)
+ catch
+ exit:{nodedown, Node} ->
+ %% A nodedown not yet detected by global,
+ %% pretend that it was.
+ exit(noproc)
+ end;
+ undefined ->
+ exit(noproc)
+ end;
+%% Local by name in disguise
+do_for_proc({Name, Node}, Fun) when Node =:= node() ->
+ do_for_proc(Name, Fun);
+%% Remote by name
+do_for_proc({_Name, Node} = Process, Fun) when is_atom(Node) ->
+ if
+ node() =:= nonode@nohost ->
+ exit({nodedown, Node});
+ true ->
+ Fun(Process)
+ end.
+
+
%%%-----------------------------------------------------------------
%%% Misc. functions.
%%%-----------------------------------------------------------------
diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl
index bb0ff46268..934b112f6f 100644
--- a/lib/stdlib/src/gen_event.erl
+++ b/lib/stdlib/src/gen_event.erl
@@ -31,8 +31,8 @@
%%% Modified by Martin - uses proc_lib, sys and gen!
--export([start/0, start/1, start_link/0, start_link/1, stop/1, notify/2,
- sync_notify/2,
+-export([start/0, start/1, start_link/0, start_link/1, stop/1, stop/3,
+ notify/2, sync_notify/2,
add_handler/3, add_sup_handler/3, delete_handler/3, swap_handler/3,
swap_sup_handler/3, which_handlers/1, call/3, call/4, wake_hib/4]).
@@ -193,7 +193,11 @@ swap_sup_handler(M, {H1, A1}, {H2, A2}) ->
which_handlers(M) -> rpc(M, which_handlers).
-spec stop(emgr_ref()) -> 'ok'.
-stop(M) -> rpc(M, stop).
+stop(M) ->
+ gen:stop(M).
+
+stop(M, Reason, Timeout) ->
+ gen:stop(M, Reason, Timeout).
rpc(M, Cmd) ->
{ok, Reply} = gen:call(M, self(), Cmd, infinity),
@@ -292,9 +296,6 @@ handle_msg(Msg, Parent, ServerName, MSL, Debug) ->
Args2, MSL, Sup, ServerName),
?reply(Reply),
loop(Parent, ServerName, MSL1, Debug, Hib);
- {From, Tag, stop} ->
- catch terminate_server(normal, Parent, MSL, ServerName),
- ?reply(ok);
{From, Tag, which_handlers} ->
?reply(the_handlers(MSL)),
loop(Parent, ServerName, MSL, Debug, false);
diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl
index 56137cde13..29b1d80088 100644
--- a/lib/stdlib/src/gen_fsm.erl
+++ b/lib/stdlib/src/gen_fsm.erl
@@ -106,6 +106,7 @@
-export([start/3, start/4,
start_link/3, start_link/4,
+ stop/1, stop/3,
send_event/2, sync_send_event/2, sync_send_event/3,
send_all_state_event/2,
sync_send_all_state_event/2, sync_send_all_state_event/3,
@@ -197,6 +198,11 @@ start_link(Mod, Args, Options) ->
start_link(Name, Mod, Args, Options) ->
gen:start(?MODULE, link, Name, Mod, Args, Options).
+stop(Name) ->
+ gen:stop(Name).
+
+stop(Name, Reason, Timeout) ->
+ gen:stop(Name, Reason, Timeout).
send_event({global, Name}, Event) ->
catch global:send(Name, {'$gen_event', Event}),
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index 22acdf10b7..9794c73cc2 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -88,6 +88,7 @@
%% API
-export([start/3, start/4,
start_link/3, start_link/4,
+ stop/1, stop/3,
call/2, call/3,
cast/2, reply/2,
abcast/2, abcast/3,
@@ -177,6 +178,17 @@ start_link(Name, Mod, Args, Options) ->
%% -----------------------------------------------------------------
+%% Stop a generic server and wait for it to terminate.
+%% If the server is located at another node, that node will
+%% be monitored.
+%% -----------------------------------------------------------------
+stop(Name) ->
+ gen:stop(Name).
+
+stop(Name, Reason, Timeout) ->
+ gen:stop(Name, Reason, Timeout).
+
+%% -----------------------------------------------------------------
%% Make a call to a generic server.
%% If the server is located at another node, that node will
%% be monitored.
diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl
index 1eb6fc2e86..c925e70613 100644
--- a/lib/stdlib/src/proc_lib.erl
+++ b/lib/stdlib/src/proc_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -30,7 +30,8 @@
hibernate/3,
init_ack/1, init_ack/2,
init_p/3,init_p/5,format/1,format/2,initial_call/1,
- translate_initial_call/1]).
+ translate_initial_call/1,
+ stop/1, stop/3]).
%% Internal exports.
-export([wake_up/3]).
@@ -750,3 +751,50 @@ format_tag(Tag, Data) ->
modifier(latin1) -> "";
modifier(_) -> "t".
+
+
+%%% -----------------------------------------------------------
+%%% Stop a process and wait for it to terminate
+%%% -----------------------------------------------------------
+-spec stop(Process) -> 'ok' when
+ Process :: pid() | RegName | {RegName,node()},
+ RegName :: atom().
+stop(Process) ->
+ stop(Process, normal, infinity).
+
+-spec stop(Process, Reason, Timeout) -> 'ok' when
+ Process :: pid() | RegName | {RegName,node()},
+ RegName :: atom(),
+ Reason :: term(),
+ Timeout :: timeout().
+stop(Process, Reason, Timeout) ->
+ {Pid, Mref} = erlang:spawn_monitor(do_stop(Process, Reason)),
+ receive
+ {'DOWN', Mref, _, _, Reason} ->
+ ok;
+ {'DOWN', Mref, _, _, {noproc,{sys,terminate,_}}} ->
+ exit(noproc);
+ {'DOWN', Mref, _, _, CrashReason} ->
+ exit(CrashReason)
+ after Timeout ->
+ exit(Pid, kill),
+ receive
+ {'DOWN', Mref, _, _, _} ->
+ exit(timeout)
+ end
+ end.
+
+-spec do_stop(Process, Reason) -> Fun when
+ Process :: pid() | RegName | {RegName,node()},
+ RegName :: atom(),
+ Reason :: term(),
+ Fun :: fun(() -> no_return()).
+do_stop(Process, Reason) ->
+ fun() ->
+ Mref = erlang:monitor(process, Process),
+ ok = sys:terminate(Process, Reason, infinity),
+ receive
+ {'DOWN', Mref, _, _, ExitReason} ->
+ exit(ExitReason)
+ end
+ end.
diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl
index e25cc25f57..ec6813a28a 100644
--- a/lib/stdlib/src/sys.erl
+++ b/lib/stdlib/src/sys.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,6 +24,7 @@
get_state/1, get_state/2,
replace_state/2, replace_state/3,
change_code/4, change_code/5,
+ terminate/2, terminate/3,
log/2, log/3, trace/2, trace/3, statistics/2, statistics/3,
log_to_file/2, log_to_file/3, no_debug/1, no_debug/2,
install/2, install/3, remove/2, remove/3]).
@@ -159,6 +160,19 @@ change_code(Name, Mod, Vsn, Extra) ->
change_code(Name, Mod, Vsn, Extra, Timeout) ->
send_system_msg(Name, {change_code, Mod, Vsn, Extra}, Timeout).
+-spec terminate(Name, Reason) -> 'ok' when
+ Name :: name(),
+ Reason :: term().
+terminate(Name, Reason) ->
+ send_system_msg(Name, {terminate, Reason}).
+
+-spec terminate(Name, Reason, Timeout) -> 'ok' when
+ Name :: name(),
+ Reason :: term(),
+ Timeout :: timeout().
+terminate(Name, Reason, Timeout) ->
+ send_system_msg(Name, {terminate, Reason}, Timeout).
+
%%-----------------------------------------------------------------
%% Debug commands
%%-----------------------------------------------------------------
@@ -294,6 +308,8 @@ mfa(Name, {debug, {Func, Arg2}}) ->
{sys, Func, [Name, Arg2]};
mfa(Name, {change_code, Mod, Vsn, Extra}) ->
{sys, change_code, [Name, Mod, Vsn, Extra]};
+mfa(Name, {terminate, Reason}) ->
+ {sys, terminate, [Name, Reason]};
mfa(Name, Atom) ->
{sys, Atom, [Name]}.
@@ -309,7 +325,7 @@ mfa(Name, Req, Timeout) ->
%% Returns: This function *never* returns! It calls the function
%% Module:system_continue(Parent, NDebug, Misc)
%% there the process continues the execution or
-%% Module:system_terminate(Raeson, Parent, Debug, Misc) if
+%% Module:system_terminate(Reason, Parent, Debug, Misc) if
%% the process should terminate.
%% The Module must export system_continue/3, system_terminate/4
%% and format_status/2 for status information.
@@ -335,7 +351,10 @@ handle_system_msg(SysState, Msg, From, Parent, Mod, Debug, Misc, Hib) ->
suspend_loop(suspended, Parent, Mod, NDebug, NMisc, Hib);
{running, Reply, NDebug, NMisc} ->
_ = gen:reply(From, Reply),
- Mod:system_continue(Parent, NDebug, NMisc)
+ Mod:system_continue(Parent, NDebug, NMisc);
+ {{terminating, Reason}, Reply, NDebug, NMisc} ->
+ _ = gen:reply(From, Reply),
+ Mod:system_terminate(Reason, Parent, NDebug, NMisc)
end.
%%-----------------------------------------------------------------
@@ -415,6 +434,8 @@ do_cmd(SysState, get_status, Parent, Mod, Debug, Misc) ->
do_cmd(SysState, {debug, What}, _Parent, _Mod, Debug, Misc) ->
{Res, NDebug} = debug_cmd(What, Debug),
{SysState, Res, NDebug, Misc};
+do_cmd(_, {terminate, Reason}, _Parent, _Mod, Debug, Misc) ->
+ {{terminating, Reason}, ok, Debug, Misc};
do_cmd(suspended, {change_code, Module, Vsn, Extra}, _Parent,
Mod, Debug, Misc) ->
{Res, NMisc} = do_change_code(Mod, Module, Vsn, Extra, Misc),
diff --git a/lib/stdlib/test/gen_event_SUITE.erl b/lib/stdlib/test/gen_event_SUITE.erl
index 60a1ba8c60..576a5adfce 100644
--- a/lib/stdlib/test/gen_event_SUITE.erl
+++ b/lib/stdlib/test/gen_event_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -106,7 +106,7 @@ start(Config) when is_list(Config) ->
?line {error, {already_started, _}} =
gen_event:start({global, my_dummy_name}),
- exit(Pid6, shutdown),
+ ok = gen_event:stop({global, my_dummy_name}, shutdown, 10000),
receive
{'EXIT', Pid6, shutdown} -> ok
after 10000 ->
diff --git a/lib/stdlib/test/gen_fsm_SUITE.erl b/lib/stdlib/test/gen_fsm_SUITE.erl
index 8aeec07ae8..2e266198e9 100644
--- a/lib/stdlib/test/gen_fsm_SUITE.erl
+++ b/lib/stdlib/test/gen_fsm_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -27,6 +27,9 @@
-export([start1/1, start2/1, start3/1, start4/1, start5/1, start6/1,
start7/1, start8/1, start9/1, start10/1, start11/1, start12/1]).
+-export([stop1/1, stop2/1, stop3/1, stop4/1, stop5/1, stop6/1, stop7/1,
+ stop8/1, stop9/1, stop10/1]).
+
-export([ abnormal1/1, abnormal2/1]).
-export([shutdown/1]).
@@ -64,6 +67,8 @@ groups() ->
[{start, [],
[start1, start2, start3, start4, start5, start6, start7,
start8, start9, start10, start11, start12]},
+ {stop, [],
+ [stop1, stop2, stop3, stop4, stop5, stop6, stop7, stop8, stop9, stop10]},
{abnormal, [], [abnormal1, abnormal2]},
{sys, [],
[sys1, call_format_status, error_format_status, get_state, replace_state]}].
@@ -278,6 +283,105 @@ start12(Config) when is_list(Config) ->
ok.
+%% Anonymous, reason 'normal'
+stop1(_Config) ->
+ {ok, Pid} = gen_fsm:start(?MODULE, [], []),
+ ok = gen_fsm:stop(Pid),
+ false = erlang:is_process_alive(Pid),
+ {'EXIT',noproc} = (catch gen_fsm:stop(Pid)),
+ ok.
+
+%% Anonymous, other reason
+stop2(_Config) ->
+ {ok,Pid} = gen_fsm:start(?MODULE, [], []),
+ ok = gen_fsm:stop(Pid, other_reason, infinity),
+ false = erlang:is_process_alive(Pid),
+ ok.
+
+%% Anonymous, invalid timeout
+stop3(_Config) ->
+ {ok,Pid} = gen_fsm:start(?MODULE, [], []),
+ {'EXIT',_} = (catch gen_fsm:stop(Pid, other_reason, invalid_timeout)),
+ true = erlang:is_process_alive(Pid),
+ ok = gen_fsm:stop(Pid),
+ false = erlang:is_process_alive(Pid),
+ ok.
+
+%% Registered name
+stop4(_Config) ->
+ {ok,Pid} = gen_fsm:start({local,to_stop},?MODULE, [], []),
+ ok = gen_fsm:stop(to_stop),
+ false = erlang:is_process_alive(Pid),
+ {'EXIT',noproc} = (catch gen_fsm:stop(to_stop)),
+ ok.
+
+%% Registered name and local node
+stop5(_Config) ->
+ {ok,Pid} = gen_fsm:start({local,to_stop},?MODULE, [], []),
+ ok = gen_fsm:stop({to_stop,node()}),
+ false = erlang:is_process_alive(Pid),
+ {'EXIT',noproc} = (catch gen_fsm:stop({to_stop,node()})),
+ ok.
+
+%% Globally registered name
+stop6(_Config) ->
+ {ok, Pid} = gen_fsm:start({global, to_stop}, ?MODULE, [], []),
+ ok = gen_fsm:stop({global,to_stop}),
+ false = erlang:is_process_alive(Pid),
+ {'EXIT',noproc} = (catch gen_fsm:stop({global,to_stop})),
+ ok.
+
+%% 'via' registered name
+stop7(_Config) ->
+ dummy_via:reset(),
+ {ok, Pid} = gen_fsm:start({via, dummy_via, to_stop},
+ ?MODULE, [], []),
+ ok = gen_fsm:stop({via, dummy_via, to_stop}),
+ false = erlang:is_process_alive(Pid),
+ {'EXIT',noproc} = (catch gen_fsm:stop({via, dummy_via, to_stop})),
+ ok.
+
+%% Anonymous on remote node
+stop8(_Config) ->
+ {ok,Node} = test_server:start_node(gen_fsm_SUITE_stop8,slave,[]),
+ Dir = filename:dirname(code:which(?MODULE)),
+ rpc:call(Node,code,add_path,[Dir]),
+ {ok, Pid} = rpc:call(Node,gen_fsm,start,[?MODULE,[],[]]),
+ ok = gen_fsm:stop(Pid),
+ false = rpc:call(Node,erlang,is_process_alive,[Pid]),
+ {'EXIT',noproc} = (catch gen_fsm:stop(Pid)),
+ true = test_server:stop_node(Node),
+ {'EXIT',{{nodedown,Node},_}} = (catch gen_fsm:stop(Pid)),
+ ok.
+
+%% Registered name on remote node
+stop9(_Config) ->
+ {ok,Node} = test_server:start_node(gen_fsm_SUITE_stop9,slave,[]),
+ Dir = filename:dirname(code:which(?MODULE)),
+ rpc:call(Node,code,add_path,[Dir]),
+ {ok, Pid} = rpc:call(Node,gen_fsm,start,[{local,to_stop},?MODULE,[],[]]),
+ ok = gen_fsm:stop({to_stop,Node}),
+ undefined = rpc:call(Node,erlang,whereis,[to_stop]),
+ false = rpc:call(Node,erlang,is_process_alive,[Pid]),
+ {'EXIT',noproc} = (catch gen_fsm:stop({to_stop,Node})),
+ true = test_server:stop_node(Node),
+ {'EXIT',{{nodedown,Node},_}} = (catch gen_fsm:stop({to_stop,Node})),
+ ok.
+
+%% Globally registered name on remote node
+stop10(_Config) ->
+ {ok,Node} = test_server:start_node(gen_fsm_SUITE_stop10,slave,[]),
+ Dir = filename:dirname(code:which(?MODULE)),
+ rpc:call(Node,code,add_path,[Dir]),
+ {ok, Pid} = rpc:call(Node,gen_fsm,start,[{global,to_stop},?MODULE,[],[]]),
+ global:sync(),
+ ok = gen_fsm:stop({global,to_stop}),
+ false = rpc:call(Node,erlang,is_process_alive,[Pid]),
+ {'EXIT',noproc} = (catch gen_fsm:stop({global,to_stop})),
+ true = test_server:stop_node(Node),
+ {'EXIT',noproc} = (catch gen_fsm:stop({global,to_stop})),
+ ok.
+
%% Check that time outs in calls work
abnormal1(suite) -> [];
abnormal1(Config) when is_list(Config) ->
diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl
index 960e7f60e7..c3ec4932b3 100644
--- a/lib/stdlib/test/gen_server_SUITE.erl
+++ b/lib/stdlib/test/gen_server_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -35,6 +35,9 @@
error_format_status/1, get_state/1, replace_state/1, call_with_huge_message_queue/1
]).
+-export([stop1/1, stop2/1, stop3/1, stop4/1, stop5/1, stop6/1, stop7/1,
+ stop8/1, stop9/1, stop10/1]).
+
% spawn export
-export([spec_init_local/2, spec_init_global/2, spec_init_via/2,
spec_init_default_timeout/2, spec_init_global_default_timeout/2,
@@ -50,7 +53,7 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [start, crash, call, cast, cast_fast, info, abcast,
+ [start, {group,stop}, crash, call, cast, cast_fast, info, abcast,
multicall, multicall_down, call_remote1, call_remote2,
call_remote3, call_remote_n1, call_remote_n2,
call_remote_n3, spec_init,
@@ -61,7 +64,8 @@ all() ->
call_with_huge_message_queue].
groups() ->
- [].
+ [{stop, [],
+ [stop1, stop2, stop3, stop4, stop5, stop6, stop7, stop8, stop9, stop10]}].
init_per_suite(Config) ->
Config.
@@ -235,6 +239,105 @@ start(Config) when is_list(Config) ->
process_flag(trap_exit, OldFl),
ok.
+%% Anonymous, reason 'normal'
+stop1(_Config) ->
+ {ok, Pid} = gen_server:start(?MODULE, [], []),
+ ok = gen_server:stop(Pid),
+ false = erlang:is_process_alive(Pid),
+ {'EXIT',noproc} = (catch gen_server:stop(Pid)),
+ ok.
+
+%% Anonymous, other reason
+stop2(_Config) ->
+ {ok,Pid} = gen_server:start(?MODULE, [], []),
+ ok = gen_server:stop(Pid, other_reason, infinity),
+ false = erlang:is_process_alive(Pid),
+ ok.
+
+%% Anonymous, invalid timeout
+stop3(_Config) ->
+ {ok,Pid} = gen_server:start(?MODULE, [], []),
+ {'EXIT',_} = (catch gen_server:stop(Pid, other_reason, invalid_timeout)),
+ true = erlang:is_process_alive(Pid),
+ ok = gen_server:stop(Pid),
+ false = erlang:is_process_alive(Pid),
+ ok.
+
+%% Registered name
+stop4(_Config) ->
+ {ok,Pid} = gen_server:start({local,to_stop},?MODULE, [], []),
+ ok = gen_server:stop(to_stop),
+ false = erlang:is_process_alive(Pid),
+ {'EXIT',noproc} = (catch gen_server:stop(to_stop)),
+ ok.
+
+%% Registered name and local node
+stop5(_Config) ->
+ {ok,Pid} = gen_server:start({local,to_stop},?MODULE, [], []),
+ ok = gen_server:stop({to_stop,node()}),
+ false = erlang:is_process_alive(Pid),
+ {'EXIT',noproc} = (catch gen_server:stop({to_stop,node()})),
+ ok.
+
+%% Globally registered name
+stop6(_Config) ->
+ {ok, Pid} = gen_server:start({global, to_stop}, ?MODULE, [], []),
+ ok = gen_server:stop({global,to_stop}),
+ false = erlang:is_process_alive(Pid),
+ {'EXIT',noproc} = (catch gen_server:stop({global,to_stop})),
+ ok.
+
+%% 'via' registered name
+stop7(_Config) ->
+ dummy_via:reset(),
+ {ok, Pid} = gen_server:start({via, dummy_via, to_stop},
+ ?MODULE, [], []),
+ ok = gen_server:stop({via, dummy_via, to_stop}),
+ false = erlang:is_process_alive(Pid),
+ {'EXIT',noproc} = (catch gen_server:stop({via, dummy_via, to_stop})),
+ ok.
+
+%% Anonymous on remote node
+stop8(_Config) ->
+ {ok,Node} = test_server:start_node(gen_server_SUITE_stop8,slave,[]),
+ Dir = filename:dirname(code:which(?MODULE)),
+ rpc:call(Node,code,add_path,[Dir]),
+ {ok, Pid} = rpc:call(Node,gen_server,start,[?MODULE,[],[]]),
+ ok = gen_server:stop(Pid),
+ false = rpc:call(Node,erlang,is_process_alive,[Pid]),
+ {'EXIT',noproc} = (catch gen_server:stop(Pid)),
+ true = test_server:stop_node(Node),
+ {'EXIT',{{nodedown,Node},_}} = (catch gen_server:stop(Pid)),
+ ok.
+
+%% Registered name on remote node
+stop9(_Config) ->
+ {ok,Node} = test_server:start_node(gen_server_SUITE_stop9,slave,[]),
+ Dir = filename:dirname(code:which(?MODULE)),
+ rpc:call(Node,code,add_path,[Dir]),
+ {ok, Pid} = rpc:call(Node,gen_server,start,[{local,to_stop},?MODULE,[],[]]),
+ ok = gen_server:stop({to_stop,Node}),
+ undefined = rpc:call(Node,erlang,whereis,[to_stop]),
+ false = rpc:call(Node,erlang,is_process_alive,[Pid]),
+ {'EXIT',noproc} = (catch gen_server:stop({to_stop,Node})),
+ true = test_server:stop_node(Node),
+ {'EXIT',{{nodedown,Node},_}} = (catch gen_server:stop({to_stop,Node})),
+ ok.
+
+%% Globally registered name on remote node
+stop10(_Config) ->
+ {ok,Node} = test_server:start_node(gen_server_SUITE_stop10,slave,[]),
+ Dir = filename:dirname(code:which(?MODULE)),
+ rpc:call(Node,code,add_path,[Dir]),
+ {ok, Pid} = rpc:call(Node,gen_server,start,[{global,to_stop},?MODULE,[],[]]),
+ global:sync(),
+ ok = gen_server:stop({global,to_stop}),
+ false = rpc:call(Node,erlang,is_process_alive,[Pid]),
+ {'EXIT',noproc} = (catch gen_server:stop({global,to_stop})),
+ true = test_server:stop_node(Node),
+ {'EXIT',noproc} = (catch gen_server:stop({global,to_stop})),
+ ok.
+
crash(Config) when is_list(Config) ->
?line error_logger_forwarder:register(),
diff --git a/lib/stdlib/test/proc_lib_SUITE.erl b/lib/stdlib/test/proc_lib_SUITE.erl
index 8dca69bac4..b6f1973a05 100644
--- a/lib/stdlib/test/proc_lib_SUITE.erl
+++ b/lib/stdlib/test/proc_lib_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -27,7 +27,7 @@
init_per_group/2,end_per_group/2,
crash/1, sync_start_nolink/1, sync_start_link/1,
spawn_opt/1, sp1/0, sp2/0, sp3/1, sp4/2, sp5/1,
- hibernate/1]).
+ hibernate/1, stop/1]).
-export([ otp_6345/1, init_dont_hang/1]).
-export([hib_loop/1, awaken/1]).
@@ -38,6 +38,7 @@
-export([otp_6345_init/1, init_dont_hang_init/1]).
+-export([system_terminate/4]).
-ifdef(STANDALONE).
-define(line, noop, ).
@@ -49,7 +50,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[crash, {group, sync_start}, spawn_opt, hibernate,
- {group, tickets}].
+ {group, tickets}, stop].
groups() ->
[{tickets, [], [otp_6345, init_dont_hang]},
@@ -361,10 +362,94 @@ init_dont_hang(Config) when is_list(Config) ->
exit(Error)
end.
-init_dont_hang_init(Parent) ->
+init_dont_hang_init(_Parent) ->
1 = 2.
+%% Test proc_lib:stop/1,3
+stop(_Config) ->
+ Parent = self(),
+ SysMsgProc =
+ fun() ->
+ receive
+ {system,From,Request} ->
+ sys:handle_system_msg(Request,From,Parent,?MODULE,[],[])
+ end
+ end,
+
+ %% Normal case:
+ %% Process handles system message and terminated with given reason
+ Pid1 = proc_lib:spawn(SysMsgProc),
+ ok = proc_lib:stop(Pid1),
+ false = erlang:is_process_alive(Pid1),
+
+ %% Process does not exit
+ {'EXIT',noproc} = (catch proc_lib:stop(Pid1)),
+
+ %% Badly handled system message
+ DieProc =
+ fun() ->
+ receive
+ {system,_From,_Request} ->
+ exit(die)
+ end
+ end,
+ Pid2 = proc_lib:spawn(DieProc),
+ {'EXIT',{die,_}} = (catch proc_lib:stop(Pid2)),
+
+ %% Hanging process => timeout
+ HangProc =
+ fun() ->
+ receive
+ {system,_From,_Request} ->
+ timer:sleep(5000)
+ end
+ end,
+ Pid3 = proc_lib:spawn(HangProc),
+ {'EXIT',timeout} = (catch proc_lib:stop(Pid3,normal,1000)),
+
+ %% Success case with other reason than 'normal'
+ Pid4 = proc_lib:spawn(SysMsgProc),
+ ok = proc_lib:stop(Pid4,other_reason,infinity),
+ false = erlang:is_process_alive(Pid4),
+
+ %% System message is handled, but process dies with other reason
+ %% than the given (in system_terminate/4 below)
+ Pid5 = proc_lib:spawn(SysMsgProc),
+ {'EXIT',{badmatch,2}} = (catch proc_lib:stop(Pid5,crash,infinity)),
+ false = erlang:is_process_alive(Pid5),
+
+ %% Local registered name
+ Pid6 = proc_lib:spawn(SysMsgProc),
+ register(to_stop,Pid6),
+ ok = proc_lib:stop(to_stop),
+ undefined = whereis(to_stop),
+ false = erlang:is_process_alive(Pid6),
+
+ %% Remote registered name
+ {ok,Node} = test_server:start_node(proc_lib_SUITE_stop,slave,[]),
+ Dir = filename:dirname(code:which(?MODULE)),
+ rpc:call(Node,code,add_path,[Dir]),
+ Pid7 = spawn(Node,SysMsgProc),
+ true = rpc:call(Node,erlang,register,[to_stop,Pid7]),
+ Pid7 = rpc:call(Node,erlang,whereis,[to_stop]),
+ ok = proc_lib:stop({to_stop,Node}),
+ undefined = rpc:call(Node,erlang,whereis,[to_stop]),
+ false = rpc:call(Node,erlang,is_process_alive,[Pid7]),
+
+ %% Local and remote registered name, but non-existing
+ {'EXIT',noproc} = (catch proc_lib:stop(to_stop)),
+ {'EXIT',noproc} = (catch proc_lib:stop({to_stop,Node})),
+
+ true = test_server:stop_node(Node),
+
+ %% Remote registered name, but non-existing node
+ {'EXIT',{{nodedown,Node},_}} = (catch proc_lib:stop({to_stop,Node})),
+ ok.
+system_terminate(crash,_Parent,_Deb,_State) ->
+ 1 = 2;
+system_terminate(Reason,_Parent,_Deb,_State) ->
+ exit(Reason).
%%-----------------------------------------------------------------
%% The error_logger handler used.
diff --git a/lib/stdlib/test/sys_SUITE.erl b/lib/stdlib/test/sys_SUITE.erl
index f38bc87ae5..047ee9f1fa 100644
--- a/lib/stdlib/test/sys_SUITE.erl
+++ b/lib/stdlib/test/sys_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -202,14 +202,7 @@ spec_proc(Mod) ->
{Mod,system_get_state},{throw,fail}},_}} ->
ok
end,
- Mod:stop(),
- WaitForUnregister = fun W() ->
- case whereis(Mod) of
- undefined -> ok;
- _ -> timer:sleep(10), W()
- end
- end,
- WaitForUnregister(),
+ ok = sys:terminate(Mod, normal),
{ok,_} = Mod:start_link(4),
ok = case catch sys:replace_state(Mod, fun(_) -> {} end) of
{} ->
@@ -218,8 +211,7 @@ spec_proc(Mod) ->
{Mod,system_replace_state},{throw,fail}},_}} ->
ok
end,
- Mod:stop(),
- WaitForUnregister(),
+ ok = sys:terminate(Mod, normal),
{ok,_} = Mod:start_link(4),
StateFun = fun(_) -> error(fail) end,
ok = case catch sys:replace_state(Mod, StateFun) of
@@ -231,7 +223,7 @@ spec_proc(Mod) ->
{'EXIT',{{callback_failed,StateFun,{error,fail}},_}} ->
ok
end,
- Mod:stop().
+ ok = sys:terminate(Mod, normal).
%%%%%%%%%%%%%%%%%%%%
%% Dummy server
diff --git a/lib/stdlib/test/sys_sp1.erl b/lib/stdlib/test/sys_sp1.erl
index e84ffcfa12..0fb288991f 100644
--- a/lib/stdlib/test/sys_sp1.erl
+++ b/lib/stdlib/test/sys_sp1.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,7 +17,7 @@
%% %CopyrightEnd%
%%
-module(sys_sp1).
--export([start_link/1, stop/0]).
+-export([start_link/1]).
-export([alloc/0, free/1]).
-export([init/1]).
-export([system_continue/3, system_terminate/4,
@@ -31,10 +31,6 @@
start_link(NumCh) ->
proc_lib:start_link(?MODULE, init, [[self(),NumCh]]).
-stop() ->
- ?MODULE ! stop,
- ok.
-
alloc() ->
?MODULE ! {self(), alloc},
receive
@@ -70,11 +66,7 @@ loop(Chs, Parent, Deb) ->
loop(Chs2, Parent, Deb2);
{system, From, Request} ->
sys:handle_system_msg(Request, From, Parent,
- ?MODULE, Deb, Chs);
- stop ->
- sys:handle_debug(Deb, fun write_debug/3,
- ?MODULE, {in, stop}),
- ok
+ ?MODULE, Deb, Chs)
end.
system_continue(Parent, Deb, Chs) ->
diff --git a/lib/stdlib/test/sys_sp2.erl b/lib/stdlib/test/sys_sp2.erl
index 56a5e4d071..a0847b5838 100644
--- a/lib/stdlib/test/sys_sp2.erl
+++ b/lib/stdlib/test/sys_sp2.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,7 +17,7 @@
%% %CopyrightEnd%
%%
-module(sys_sp2).
--export([start_link/1, stop/0]).
+-export([start_link/1]).
-export([alloc/0, free/1]).
-export([init/1]).
-export([system_continue/3, system_terminate/4,
@@ -30,10 +30,6 @@
start_link(NumCh) ->
proc_lib:start_link(?MODULE, init, [[self(),NumCh]]).
-stop() ->
- ?MODULE ! stop,
- ok.
-
alloc() ->
?MODULE ! {self(), alloc},
receive
@@ -45,11 +41,6 @@ free(Ch) ->
?MODULE ! {free, Ch},
ok.
-%% can't use 2-tuple for state here as we do in sys_sp1, since the 2-tuple
-%% is not compatible with the backward compatibility handling for
-%% sys:get_state in sys.erl
--record(state, {alloc,free}).
-
init([Parent,NumCh]) ->
register(?MODULE, self()),
Chs = channels(NumCh),
@@ -74,11 +65,7 @@ loop(Chs, Parent, Deb) ->
loop(Chs2, Parent, Deb2);
{system, From, Request} ->
sys:handle_system_msg(Request, From, Parent,
- ?MODULE, Deb, Chs);
- stop ->
- sys:handle_debug(Deb, fun write_debug/3,
- ?MODULE, {in, stop}),
- ok
+ ?MODULE, Deb, Chs)
end.
system_continue(Parent, Deb, Chs) ->
@@ -91,17 +78,17 @@ write_debug(Dev, Event, Name) ->
io:format(Dev, "~p event = ~p~n", [Name, Event]).
channels(NumCh) ->
- #state{alloc=[], free=lists:seq(1,NumCh)}.
+ {_Allocated=[], _Free=lists:seq(1,NumCh)}.
-alloc(#state{free=[]}=Channels) ->
- {{error, "no channels available"}, Channels};
-alloc(#state{alloc=Allocated, free=[H|T]}) ->
- {H, #state{alloc=[H|Allocated], free=T}}.
+alloc({_, []}) ->
+ {error, "no channels available"};
+alloc({Allocated, [H|T]}) ->
+ {H, {[H|Allocated], T}}.
-free(Ch, #state{alloc=Alloc, free=Free}=Channels) ->
+free(Ch, {Alloc, Free}=Channels) ->
case lists:member(Ch, Alloc) of
true ->
- #state{alloc=lists:delete(Ch, Alloc), free=[Ch|Free]};
+ {lists:delete(Ch, Alloc), [Ch|Free]};
false ->
Channels
end.
diff --git a/lib/wx/src/wx_object.erl b/lib/wx/src/wx_object.erl
index 80f8937656..249ea1cee3 100644
--- a/lib/wx/src/wx_object.erl
+++ b/lib/wx/src/wx_object.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -102,6 +102,7 @@
%% API
-export([start/3, start/4,
start_link/3, start_link/4,
+ stop/1, stop/3,
call/2, call/3,
cast/2,
reply/2,
@@ -215,6 +216,42 @@ gen_response({ok, Pid}) ->
gen_response(Reply) ->
Reply.
+%% @spec (Ref::wxObject()|atom()|pid()) -> ok
+%% @doc Stops a generic wx_object server with reason 'normal'.
+%% Invokes terminate(Reason,State) in the server. The call waits until
+%% the process is terminated. If the process does not exist, an
+%% exception is raised.
+stop(Ref = #wx_ref{state=Pid}) when is_pid(Pid) ->
+ try
+ gen:stop(Pid)
+ catch _:ExitReason ->
+ erlang:error({ExitReason, {?MODULE, stop, [Ref]}})
+ end;
+stop(Name) when is_atom(Name) orelse is_pid(Name) ->
+ try
+ gen:stop(Name)
+ catch _:ExitReason ->
+ erlang:error({ExitReason, {?MODULE, stop, [Name]}})
+ end.
+
+%% @spec (Ref::wxObject()|atom()|pid(), Reason::term(), Timeout::timeout()) -> ok
+%% @doc Stops a generic wx_object server with the given Reason.
+%% Invokes terminate(Reason,State) in the server. The call waits until
+%% the process is terminated. If the call times out, or if the process
+%% does not exist, an exception is raised.
+stop(Ref = #wx_ref{state=Pid}, Reason, Timeout) when is_pid(Pid) ->
+ try
+ gen:stop(Pid, Reason, Timeout)
+ catch _:ExitReason ->
+ erlang:error({ExitReason, {?MODULE, stop, [Ref, Reason, Timeout]}})
+ end;
+stop(Name, Reason, Timeout) when is_atom(Name) orelse is_pid(Name) ->
+ try
+ gen:stop(Name, Reason, Timeout)
+ catch _:ExitReason ->
+ erlang:error({ExitReason, {?MODULE, stop, [Name, Reason, Timeout]}})
+ end.
+
%% @spec (Ref::wxObject()|atom()|pid(), Request::term()) -> term()
%% @doc Make a call to a wx_object server.
%% The call waits until it gets a result.
diff --git a/lib/wx/test/wx_event_SUITE.erl b/lib/wx/test/wx_event_SUITE.erl
index 076f16ba16..2c6c59bb55 100644
--- a/lib/wx/test/wx_event_SUITE.erl
+++ b/lib/wx/test/wx_event_SUITE.erl
@@ -542,13 +542,14 @@ handler_clean(_Config) ->
?mt(wxFrame, Frame1),
wxWindow:show(Frame1),
?m([_|_], lists:sort(wx_test_lib:flush())),
- ?m({stop,_}, wx_obj_test:stop(Frame1, fun(_) -> normal end)),
+ ?m(ok, wx_obj_test:stop(Frame1)),
?m([{terminate,normal}], lists:sort(wx_test_lib:flush())),
- Frame2 = wx_obj_test:start([{init, Init}]),
+ Terminate = fun({Frame,_}) -> wxWindow:destroy(Frame) end,
+ Frame2 = wx_obj_test:start([{init, Init}, {terminate, Terminate}]),
wxWindow:show(Frame2),
?m([_|_], lists:sort(wx_test_lib:flush())),
- ?m({stop,_}, wx_obj_test:stop(Frame2, fun(_) -> wxWindow:destroy(Frame2), normal end)),
+ ?m(ok, wx_obj_test:stop(Frame2)),
?m([{terminate,normal}], lists:sort(wx_test_lib:flush())),
timer:sleep(104),
?m({[],[],[]}, white_box_check_event_handlers()),
diff --git a/lib/wx/test/wx_obj_test.erl b/lib/wx/test/wx_obj_test.erl
index f47f2fbc46..6c648c65f8 100644
--- a/lib/wx/test/wx_obj_test.erl
+++ b/lib/wx/test/wx_obj_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,7 +18,7 @@
-module(wx_obj_test).
-include_lib("wx/include/wx.hrl").
--export([start/1, stop/2]).
+-export([start/1, stop/1]).
%% wx_object callbacks
-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3,
@@ -29,8 +29,8 @@
start(Opts) ->
wx_object:start_link(?MODULE, [{parent, self()}| Opts], []).
-stop(Object, Fun) ->
- wx_object:call(Object, {stop, Fun}).
+stop(Object) ->
+ wx_object:stop(Object).
init(Opts) ->
Parent = proplists:get_value(parent, Opts),
@@ -61,8 +61,6 @@ handle_event(Event, State = #state{parent=Parent}) ->
handle_call(What, From, State = #state{user_state=US}) when is_function(What) ->
Result = What(US),
{reply, {call, Result, From}, State};
-handle_call({stop, Fun}, From, State = #state{user_state=US}) ->
- {stop, Fun(US), {stop, From}, State};
handle_call(What, From, State) ->
{reply, {call, What, From}, State}.
@@ -79,7 +77,13 @@ handle_info(What, State = #state{parent=Pid}) ->
Pid ! {info, What},
{noreply, State}.
-terminate(What, #state{parent=Pid}) ->
+terminate(What, #state{parent=Pid, opts=Opts, user_state=US}) ->
+ case proplists:get_value(terminate, Opts) of
+ undefined ->
+ ok;
+ Terminate ->
+ Terminate(US)
+ end,
Pid ! {terminate, What},
ok.