aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stdlib
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stdlib')
-rw-r--r--lib/stdlib/doc/src/digraph_utils.xml4
-rw-r--r--lib/stdlib/doc/src/ets.xml2
-rw-r--r--lib/stdlib/doc/src/gen_event.xml61
-rw-r--r--lib/stdlib/doc/src/gen_fsm.xml55
-rw-r--r--lib/stdlib/doc/src/gen_server.xml39
-rw-r--r--lib/stdlib/doc/src/lists.xml2
-rw-r--r--lib/stdlib/doc/src/supervisor.xml24
-rw-r--r--lib/stdlib/doc/src/supervisor_bridge.xml7
-rw-r--r--lib/stdlib/src/digraph_utils.erl4
-rw-r--r--lib/stdlib/src/gen.erl22
-rw-r--r--lib/stdlib/src/gen_event.erl10
-rw-r--r--lib/stdlib/src/gen_fsm.erl25
-rw-r--r--lib/stdlib/src/gen_server.erl23
-rw-r--r--lib/stdlib/src/supervisor.erl170
-rw-r--r--lib/stdlib/test/Makefile2
-rw-r--r--lib/stdlib/test/digraph_utils_SUITE.erl5
-rw-r--r--lib/stdlib/test/dummy_via.erl94
-rw-r--r--lib/stdlib/test/gen_event_SUITE.erl26
-rw-r--r--lib/stdlib/test/gen_fsm_SUITE.erl71
-rw-r--r--lib/stdlib/test/gen_server_SUITE.erl62
-rw-r--r--lib/stdlib/test/supervisor_SUITE.erl122
-rw-r--r--lib/stdlib/test/supervisor_deadlock.erl45
22 files changed, 738 insertions, 137 deletions
diff --git a/lib/stdlib/doc/src/digraph_utils.xml b/lib/stdlib/doc/src/digraph_utils.xml
index e44632bfd2..ef6e1cb46f 100644
--- a/lib/stdlib/doc/src/digraph_utils.xml
+++ b/lib/stdlib/doc/src/digraph_utils.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2000</year><year>2011</year>
+ <year>2000</year><year>2012</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -156,7 +156,7 @@
<p>Creates a digraph where the vertices are
the <seealso marker="#strong_components">strongly connected
components</seealso> of <c><anno>Digraph</anno></c> as returned by
- <c>strong_components/1</c>. If X and Y are strongly
+ <c>strong_components/1</c>. If X and Y are two different strongly
connected components, and there exist vertices x and y in X
and Y respectively such that there is an
edge <seealso marker="#emanate">emanating</seealso> from x
diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml
index efd9514db6..0486090e92 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -671,7 +671,7 @@ ets:is_compiled_ms(Broken).</code>
<c>duplicate_bag</c>, the function returns a list of
arbitrary length.</p>
<p>Note that the time order of object insertions is preserved;
- The first object inserted with the given key will be first
+ the first object inserted with the given key will be first
in the resulting list, and so on.</p>
<p>Insert and look-up times in tables of type <c>set</c>,
<c>bag</c> and <c>duplicate_bag</c> are constant, regardless
diff --git a/lib/stdlib/doc/src/gen_event.xml b/lib/stdlib/doc/src/gen_event.xml
index 79a0c8ad89..ef81b06500 100644
--- a/lib/stdlib/doc/src/gen_event.xml
+++ b/lib/stdlib/doc/src/gen_event.xml
@@ -120,8 +120,10 @@ gen_event:stop -----> Module:terminate/2
<name>start_link(EventMgrName) -> Result</name>
<fsummary>Create a generic event manager process in a supervision tree.</fsummary>
<type>
- <v>EventMgrName = {local,Name} | {global,Name}</v>
+ <v>EventMgrName = {local,Name} | {global,GlobalName}
+ | {via,Module,ViaName}</v>
<v>&nbsp;Name = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Result = {ok,Pid} | {error,{already_started,Pid}}</v>
<v>&nbsp;Pid = pid()</v>
</type>
@@ -132,10 +134,17 @@ gen_event:stop -----> Module:terminate/2
the event manager is linked to the supervisor.</p>
<p>If <c>EventMgrName={local,Name}</c>, the event manager is
registered locally as <c>Name</c> using <c>register/2</c>.
- If <c>EventMgrName={global,Name}</c>, the event manager is
- registered globally as <c>Name</c> using
+ If <c>EventMgrName={global,GlobalName}</c>, the event manager is
+ registered globally as <c>GlobalName</c> using
<c>global:register_name/2</c>. If no name is provided,
- the event manager is not registered.</p>
+ the event manager is not registered.
+ If <c>EventMgrName={via,Module,ViaName}</c>, the event manager will
+ register with the registry represented by <c>Module</c>.
+ The <c>Module</c> callback should export the functions
+ <c>register_name/2</c>, <c>unregister_name/1</c>,
+ <c>whereis_name/1</c> and <c>send/2</c>, which should behave like the
+ corresponding functions in <c>global</c>. Thus,
+ <c>{via,global,GlobalName}</c> is a valid reference.</p>
<p>If the event manager is successfully created the function
returns <c>{ok,Pid}</c>, where <c>Pid</c> is the pid of
the event manager. If there already exists a process with
@@ -149,8 +158,10 @@ gen_event:stop -----> Module:terminate/2
<name>start(EventMgrName) -> Result</name>
<fsummary>Create a stand-alone event manager process.</fsummary>
<type>
- <v>EventMgrName = {local,Name} | {global,Name}</v>
+ <v>EventMgrName = {local,Name} | {global,GlobalName}
+ | {via,Module,ViaName}</v>
<v>&nbsp;Name = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Result = {ok,Pid} | {error,{already_started,Pid}}</v>
<v>&nbsp;Pid = pid()</v>
</type>
@@ -166,8 +177,10 @@ 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,Name} | pid()</v>
+ <v>EventMgr = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Handler = Module | {Module,Id}</v>
<v>&nbsp;Module = atom()</v>
<v>&nbsp;Id = term()</v>
@@ -185,8 +198,10 @@ gen_event:stop -----> Module:terminate/2
<item><c>Name</c>, if the event manager is locally registered,</item>
<item><c>{Name,Node}</c>, if the event manager is locally
registered at another node, or</item>
- <item><c>{global,Name}</c>, if the event manager is globally
+ <item><c>{global,GlobalName}</c>, if the event manager is globally
registered.</item>
+ <item><c>{via,Module,ViaName}</c>, if the event manager is registered
+ through an alternative process registry.</item>
</list>
<p><c>Handler</c> is the name of the callback module <c>Module</c> or
a tuple <c>{Module,Id}</c>, where <c>Id</c> is any term.
@@ -208,8 +223,10 @@ 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,Name} | pid()</v>
+ <v>EventMgr = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Handler = Module | {Module,Id}</v>
<v>&nbsp;Module = atom()</v>
<v>&nbsp;Id = term()</v>
@@ -253,8 +270,10 @@ gen_event:stop -----> Module:terminate/2
<name>sync_notify(EventMgrRef, Event) -> ok</name>
<fsummary>Notify an event manager about an event.</fsummary>
<type>
- <v>EventMgrRef = Name | {Name,Node} | {global,Name} | pid()</v>
+ <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Event = term()</v>
</type>
<desc>
@@ -278,8 +297,10 @@ gen_event:stop -----> Module:terminate/2
<name>call(EventMgrRef, Handler, Request, Timeout) -> Result</name>
<fsummary>Make a synchronous call to a generic event manager.</fsummary>
<type>
- <v>EventMgrRef = Name | {Name,Node} | {global,Name} | pid()</v>
+ <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Handler = Module | {Module,Id}</v>
<v>&nbsp;Module = atom()</v>
<v>&nbsp;Id = term()</v>
@@ -318,8 +339,10 @@ gen_event:stop -----> Module:terminate/2
<name>delete_handler(EventMgrRef, Handler, Args) -> Result</name>
<fsummary>Delete an event handler from a generic event manager.</fsummary>
<type>
- <v>EventMgrRef = Name | {Name,Node} | {global,Name} | pid()</v>
+ <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Handler = Module | {Module,Id}</v>
<v>&nbsp;Module = atom()</v>
<v>&nbsp;Id = term()</v>
@@ -346,8 +369,10 @@ gen_event:stop -----> Module:terminate/2
<name>swap_handler(EventMgrRef, {Handler1,Args1}, {Handler2,Args2}) -> Result</name>
<fsummary>Replace an event handler in a generic event manager.</fsummary>
<type>
- <v>EventMgrRef = Name | {Name,Node} | {global,Name} | pid()</v>
+ <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Handler1 = Handler2 = Module | {Module,Id}</v>
<v>&nbsp;Module = atom()</v>
<v>&nbsp;Id = term()</v>
@@ -390,8 +415,10 @@ gen_event:stop -----> Module:terminate/2
<name>swap_sup_handler(EventMgrRef, {Handler1,Args1}, {Handler2,Args2}) -> Result</name>
<fsummary>Replace an event handler in a generic event manager.</fsummary>
<type>
- <v>EventMgrRef = Name | {Name,Node} | {global,Name} | pid()</v>
+ <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Handler1 = Handler 2 = Module | {Module,Id}</v>
<v>&nbsp;Module = atom()</v>
<v>&nbsp;Id = term()</v>
@@ -412,8 +439,10 @@ gen_event:stop -----> Module:terminate/2
<name>which_handlers(EventMgrRef) -> [Handler]</name>
<fsummary>Return all event handlers installed in a generic event manager.</fsummary>
<type>
- <v>EventMgrRef = Name | {Name,Node} | {global,Name} | pid()</v>
+ <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Handler = Module | {Module,Id}</v>
<v>&nbsp;Module = atom()</v>
<v>&nbsp;Id = term()</v>
@@ -429,8 +458,10 @@ gen_event:stop -----> Module:terminate/2
<name>stop(EventMgrRef) -> ok</name>
<fsummary>Terminate a generic event manager.</fsummary>
<type>
- <v>EventMgrRef = Name | {Name,Node} | {global,Name} | pid()</v>
+ <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>Name = Node = atom()</v>
+ <v>GlobalName = ViaName = term()</v>
</type>
<desc>
<p>Terminates the event manager <c>EventMgrRef</c>. Before
diff --git a/lib/stdlib/doc/src/gen_fsm.xml b/lib/stdlib/doc/src/gen_fsm.xml
index 421eeb4fd3..73c1911f1e 100644
--- a/lib/stdlib/doc/src/gen_fsm.xml
+++ b/lib/stdlib/doc/src/gen_fsm.xml
@@ -84,9 +84,10 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
<name>start_link(FsmName, Module, Args, Options) -> Result</name>
<fsummary>Create a gen_fsm process in a supervision tree.</fsummary>
<type>
- <v>FsmName = {local,Name} | {global,GlobalName}</v>
+ <v>FsmName = {local,Name} | {global,GlobalName}
+ | {via,Module,ViaName}</v>
<v>&nbsp;Name = atom()</v>
- <v>&nbsp;GlobalName = term()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Module = atom()</v>
<v>Args = term()</v>
<v>Options = [Option]</v>
@@ -113,8 +114,16 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
locally as <c>Name</c> using <c>register/2</c>.
If <c>FsmName={global,GlobalName}</c>, the gen_fsm is
registered globally as <c>GlobalName</c> using
- <c>global:register_name/2</c>. If no name is provided,
- the gen_fsm is not registered.</p>
+ <c>global:register_name/2</c>.
+ If <c>EventMgrName={via,Module,ViaName}</c>, the event manager will
+ register with the registry represented by <c>Module</c>.
+ The <c>Module</c> callback should export the functions
+ <c>register_name/2</c>, <c>unregister_name/1</c>,
+ <c>whereis_name/1</c> and <c>send/2</c>, which should behave like the
+ corresponding functions in <c>global</c>. Thus,
+ <c>{via,global,GlobalName}</c> is a valid reference.</p>
+ <p>If no name is provided,
+ the gen_fsm is not registered.</p>
<p><c>Module</c> is the name of the callback module.</p>
<p><c>Args</c> is an arbitrary term which is passed as
the argument to <c>Module:init/1</c>.</p>
@@ -154,9 +163,10 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
<name>start(FsmName, Module, Args, Options) -> Result</name>
<fsummary>Create a stand-alone gen_fsm process.</fsummary>
<type>
- <v>FsmName = {local,Name} | {global,GlobalName}</v>
+ <v>FsmName = {local,Name} | {global,GlobalName}
+ | {via,Module,ViaName}</v>
<v>&nbsp;Name = atom()</v>
- <v>&nbsp;GlobalName = term()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Module = atom()</v>
<v>Args = term()</v>
<v>Options = [Option]</v>
@@ -180,9 +190,10 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
<name>send_event(FsmRef, Event) -> ok</name>
<fsummary>Send an event asynchronously to a generic FSM.</fsummary>
<type>
- <v>FsmRef = Name | {Name,Node} | {global,GlobalName} | pid()</v>
+ <v>FsmRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
- <v>&nbsp;GlobalName = term()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Event = term()</v>
</type>
<desc>
@@ -196,9 +207,11 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
<item>the pid,</item>
<item><c>Name</c>, if the gen_fsm is locally registered,</item>
<item><c>{Name,Node}</c>, if the gen_fsm is locally
- registered at another node, or</item>
- <item><c>{global,GlobalName}</c>, if the gen_fsm is globally
- registered.</item>
+ registered at another node, or</item>
+ <item><c>{global,GlobalName}</c>, if the gen_fsm is globally
+ registered.</item>
+ <item><c>{via,Module,ViaName}</c>, if the event manager is registered
+ through an alternative process registry.</item>
</list>
<p><c>Event</c> is an arbitrary term which is passed as one of
the arguments to <c>Module:StateName/2</c>.</p>
@@ -208,9 +221,10 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
<name>send_all_state_event(FsmRef, Event) -> ok</name>
<fsummary>Send an event asynchronously to a generic FSM.</fsummary>
<type>
- <v>FsmRef = Name | {Name,Node} | {global,GlobalName} | pid()</v>
+ <v>FsmRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
- <v>&nbsp;GlobalName = term()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Event = term()</v>
</type>
<desc>
@@ -232,9 +246,10 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
<name>sync_send_event(FsmRef, Event, Timeout) -> Reply</name>
<fsummary>Send an event synchronously to a generic FSM.</fsummary>
<type>
- <v>FsmRef = Name | {Name,Node} | {global,GlobalName} | pid()</v>
+ <v>FsmRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
- <v>&nbsp;GlobalName = term()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Event = term()</v>
<v>Timeout = int()>0 | infinity</v>
<v>Reply = term()</v>
@@ -264,9 +279,10 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
<name>sync_send_all_state_event(FsmRef, Event, Timeout) -> Reply</name>
<fsummary>Send an event synchronously to a generic FSM.</fsummary>
<type>
- <v>FsmRef = Name | {Name,Node} | {global,GlobalName} | pid()</v>
+ <v>FsmRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
- <v>&nbsp;GlobalName = term()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Event = term()</v>
<v>Timeout = int()>0 | infinity</v>
<v>Reply = term()</v>
@@ -388,9 +404,10 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
<v>&nbsp;&nbsp;&nbsp;&nbsp;| {log_to_file,FileName} | {install,{Func,FuncState}}</v>
<v>StateName = atom()</v>
<v>StateData = term()</v>
- <v>FsmName = {local,Name} | {global,GlobalName}</v>
+ <v>FsmName = {local,Name} | {global,GlobalName}
+ | {via,Module,ViaName}</v>
<v>&nbsp;Name = atom()</v>
- <v>&nbsp;GlobalName = term()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Timeout = int() | infinity</v>
</type>
<desc>
diff --git a/lib/stdlib/doc/src/gen_server.xml b/lib/stdlib/doc/src/gen_server.xml
index edeb7dff91..9edff1b9cf 100644
--- a/lib/stdlib/doc/src/gen_server.xml
+++ b/lib/stdlib/doc/src/gen_server.xml
@@ -83,9 +83,10 @@ gen_server:abcast -----> Module:handle_cast/2
<name>start_link(ServerName, Module, Args, Options) -> Result</name>
<fsummary>Create a gen_server process in a supervision tree.</fsummary>
<type>
- <v>ServerName = {local,Name} | {global,GlobalName}</v>
+ <v>ServerName = {local,Name} | {global,GlobalName}
+ | {via,Module,ViaName}</v>
<v>&nbsp;Name = atom()</v>
- <v>&nbsp;GlobalName = term()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Module = atom()</v>
<v>Args = term()</v>
<v>Options = [Option]</v>
@@ -111,14 +112,22 @@ gen_server:abcast -----> Module:handle_cast/2
If <c>ServerName={global,GlobalName}</c> the gen_server is
registered globally as <c>GlobalName</c> using
<c>global:register_name/2</c>. If no name is provided,
- the gen_server is not registered.</p>
+ the gen_server is not registered.
+ If <c>EventMgrName={via,Module,ViaName}</c>, the event manager will
+ register with the registry represented by <c>Module</c>.
+ The <c>Module</c> callback should export the functions
+ <c>register_name/2</c>, <c>unregister_name/1</c>,
+ <c>whereis_name/1</c> and <c>send/2</c>, which should behave like the
+ corresponding functions in <c>global</c>. Thus,
+ <c>{via,global,GlobalName}</c> is a valid reference.</p>
<p><c>Module</c> is the name of the callback module.</p>
<p><c>Args</c> is an arbitrary term which is passed as
the argument to <c>Module:init/1</c>.</p>
<p>If the option <c>{timeout,Time}</c> is present,
the gen_server is allowed to spend <c>Time</c> milliseconds
initializing or it will be terminated and the start function
- will return <c>{error,timeout}</c>.</p>
+ will return <c>{error,timeout}</c>.
+ </p>
<p>If the option <c>{debug,Dbgs}</c> is present,
the corresponding <c>sys</c> function will be called for each
item in <c>Dbgs</c>. See
@@ -151,9 +160,10 @@ gen_server:abcast -----> Module:handle_cast/2
<name>start(ServerName, Module, Args, Options) -> Result</name>
<fsummary>Create a stand-alone gen_server process.</fsummary>
<type>
- <v>ServerName = {local,Name} | {global,GlobalName}</v>
+ <v>ServerName = {local,Name} | {global,GlobalName}
+ | {via,Module,ViaName}</v>
<v>&nbsp;Name = atom()</v>
- <v>&nbsp;GlobalName = term()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Module = atom()</v>
<v>Args = term()</v>
<v>Options = [Option]</v>
@@ -178,9 +188,10 @@ gen_server:abcast -----> Module:handle_cast/2
<name>call(ServerRef, Request, Timeout) -> Reply</name>
<fsummary>Make a synchronous call to a generic server.</fsummary>
<type>
- <v>ServerRef = Name | {Name,Node} | {global,GlobalName} | pid()</v>
+ <v>ServerRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Node = atom()</v>
- <v>&nbsp;GlobalName = term()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Request = term()</v>
<v>Timeout = int()>0 | infinity</v>
<v>Reply = term()</v>
@@ -198,6 +209,8 @@ gen_server:abcast -----> Module:handle_cast/2
registered at another node, or</item>
<item><c>{global,GlobalName}</c>, if the gen_server is
globally registered.</item>
+ <item><c>{via,Module,ViaName}</c>, if the gen_server is
+ registered through an alternative process registry.</item>
</list>
<p><c>Request</c> is an arbitrary term which is passed as one of
the arguments to <c>Module:handle_call/3</c>.</p>
@@ -281,9 +294,10 @@ gen_server:abcast -----> Module:handle_cast/2
<name>cast(ServerRef, Request) -> ok</name>
<fsummary>Send an asynchronous request to a generic server.</fsummary>
<type>
- <v>ServerRef = Name | {Name,Node} | {global,GlobalName} | pid()</v>
+ <v>ServerRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Node = atom()</v>
- <v>&nbsp;GlobalName = term()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Request = term()</v>
</type>
<desc>
@@ -355,9 +369,10 @@ gen_server:abcast -----> Module:handle_cast/2
<v>&nbsp;&nbsp;&nbsp;Dbg = trace | log | statistics</v>
<v>&nbsp;&nbsp;&nbsp;&nbsp;| {log_to_file,FileName} | {install,{Func,FuncState}}</v>
<v>State = term()</v>
- <v>ServerName = {local,Name} | {global,GlobalName}</v>
+ <v>ServerName = {local,Name} | {global,GlobalName}
+ | {via,Module,ViaName}</v>
<v>&nbsp;Name = atom()</v>
- <v>&nbsp;GlobalName = term()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Timeout = int() | infinity</v>
</type>
<desc>
diff --git a/lib/stdlib/doc/src/lists.xml b/lib/stdlib/doc/src/lists.xml
index 7042c84437..96a0942710 100644
--- a/lib/stdlib/doc/src/lists.xml
+++ b/lib/stdlib/doc/src/lists.xml
@@ -663,7 +663,7 @@ splitwith(Pred, List) ->
<desc>
<p>Returns the sub-list of <c><anno>List1</anno></c> starting at position 1
and with (max) <c><anno>Len</anno></c> elements. It is not an error for
- <c><anno>Len</anno></c> to exceed the length of the list -- in that case
+ <c><anno>Len</anno></c> to exceed the length of the list, in that case
the whole list is returned.</p>
</desc>
</func>
diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml
index ff06b04d07..9b41672bf1 100644
--- a/lib/stdlib/doc/src/supervisor.xml
+++ b/lib/stdlib/doc/src/supervisor.xml
@@ -267,8 +267,14 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
<p>If <c><anno>SupName</anno>={local,Name}</c> the supervisor is registered
locally as <c>Name</c> using <c>register/2</c>. If
<c><anno>SupName</anno>={global,Name}</c> the supervisor is registered
- globally as <c>Name</c> using <c>global:register_name/2</c>.
- If no name is provided, the supervisor is not registered.</p>
+ globally as <c>Name</c> using <c>global:register_name/2</c>. If
+ <c><anno>SupName</anno>={via,Module,Name}</c> the supervisor
+ is registered as <c>Name</c> using the registry represented by
+ <c>Module</c>. The <c>Module</c> callback should export the functions
+ <c>register_name/2</c>, <c>unregister_name/1</c> and <c>send/2</c>,
+ which should behave like the corresponding functions in <c>global</c>.
+ Thus, <c>{via,global,Name}</c> is a valid reference.</p>
+ <p>If no name is provided, the supervisor is not registered.</p>
<p><c><anno>Module</anno></c> is the name of the callback module.</p>
<p><c><anno>Args</anno></c> is an arbitrary term which is passed as
the argument to <c><anno>Module</anno>:init/1</c>.</p>
@@ -311,6 +317,8 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
registered at another node, or</item>
<item><c>{global,Name}</c>, if the supervisor is globally
registered.</item>
+ <item><c>{via,Module,Name}</c>, if the supervisor is registered
+ through an alternative process registry.</item>
</list>
<p><c><anno>ChildSpec</anno></c> should be a valid child specification
(unless the supervisor is a <c>simple_one_for_one</c>
@@ -394,10 +402,11 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
<c>SupRef</c>.</p>
<p>If successful, the function returns <c>ok</c>. If the child
specification identified by <c><anno>Id</anno></c> exists but
- the corresponding child process is running, the function
- returns <c>{error,running}</c>. If the child specification
- identified by <c><anno>Id</anno></c> does not exist, the function returns
- <c>{error,not_found}</c>.</p>
+ the corresponding child process is running or about to be restarted,
+ the function returns <c>{error,running}</c> or
+ <c>{error,restarting}</c> respectively. If the child specification
+ identified by <c><anno>Id</anno></c> does not exist, the function
+ returns <c>{error,not_found}</c>.</p>
</desc>
</func>
<func>
@@ -458,7 +467,8 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
</item>
<item>
<p><c><anno>Child</anno></c> - the pid of the corresponding child
- process, or <c>undefined</c> if there is no such process.</p>
+ process, the atom <c>restarting</c> if the process is about to be
+ restarted or <c>undefined</c> if there is no such process.</p>
</item>
<item>
<p><c><anno>Type</anno></c> - as defined in the child specification.</p>
diff --git a/lib/stdlib/doc/src/supervisor_bridge.xml b/lib/stdlib/doc/src/supervisor_bridge.xml
index c1a5e7947f..f6712d6c1d 100644
--- a/lib/stdlib/doc/src/supervisor_bridge.xml
+++ b/lib/stdlib/doc/src/supervisor_bridge.xml
@@ -63,6 +63,13 @@
If <c><anno>SupBridgeName</anno>={global,<anno>Name</anno>}</c> the supervisor_bridge is
registered globally as <c><anno>Name</anno></c> using
<c>global:register_name/2</c>.
+ If <c><anno>SupBridgeName</anno>={via,<anno>Module</anno>,<anno>Name</anno>}</c> the supervisor_bridge is
+ registered as <c><anno>Name</anno></c> using a registry represented
+ by <anno>Module</anno>. The <c>Module</c> callback should export
+ the functions <c>register_name/2</c>, <c>unregister_name/1</c>
+ and <c>send/2</c>, which should behave like the
+ corresponding functions in <c>global</c>. Thus,
+ <c>{via,global,GlobalName}</c> is a valid reference.
If no name is provided, the supervisor_bridge is not registered.
If there already exists a process with the specified
<c><anno>SupBridgeName</anno></c> the function returns
diff --git a/lib/stdlib/src/digraph_utils.erl b/lib/stdlib/src/digraph_utils.erl
index e221be15a1..807b5c12a1 100644
--- a/lib/stdlib/src/digraph_utils.erl
+++ b/lib/stdlib/src/digraph_utils.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2012. 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
@@ -370,5 +370,5 @@ condense('$end_of_table', _T, _SC, _G, _SCG, _I2C) ->
condense(I, T, SC, G, SCG, I2C) ->
[{_,C}] = ets:lookup(I2C, I),
digraph:add_vertex(SCG, C),
- digraph:add_edge(SCG, SC, C),
+ [digraph:add_edge(SCG, SC, C) || C =/= SC],
condense(ets:next(T, I), T, SC, G, SCG, I2C).
diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl
index 5d803091b6..42555aedd7 100644
--- a/lib/stdlib/src/gen.erl
+++ b/lib/stdlib/src/gen.erl
@@ -36,7 +36,7 @@
%%-----------------------------------------------------------------
-type linkage() :: 'link' | 'nolink'.
--type emgr_name() :: {'local', atom()} | {'global', term()}.
+-type emgr_name() :: {'local', atom()} | {'global', term()} | {via, atom(), term()}.
-type start_ret() :: {'ok', pid()} | 'ignore' | {'error', term()}.
@@ -53,7 +53,7 @@
%% start(GenMod, LinkP, Name, Mod, Args, Options)
%% GenMod = atom(), callback module implementing the 'real' fsm
%% LinkP = link | nolink
-%% Name = {local, atom()} | {global, term()}
+%% Name = {local, atom()} | {global, term()} | {via, atom(), term()}
%% Args = term(), init arguments (to Mod:init/1)
%% Options = [{timeout, Timeout} | {debug, [Flag]} | {spawn_opt, OptionList}]
%% Flag = trace | log | {logfile, File} | statistics | debug
@@ -158,9 +158,12 @@ call(Name, Label, Request, Timeout)
exit(noproc)
end;
%% Global by name
-call({global, _Name}=Process, Label, Request, Timeout)
- when Timeout =:= infinity;
- is_integer(Timeout), Timeout >= 0 ->
+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),
@@ -274,6 +277,7 @@ reply({To, Tag}, Reply) ->
%%% Misc. functions.
%%%-----------------------------------------------------------------
where({global, Name}) -> global:whereis_name(Name);
+where({via, Module, Name}) -> Module:whereis_name(Name);
where({local, Name}) -> whereis(Name).
name_register({local, Name} = LN) ->
@@ -287,8 +291,16 @@ name_register({global, Name} = GN) ->
case global:register_name(Name, self()) of
yes -> true;
no -> {false, where(GN)}
+ end;
+name_register({via, Module, Name} = GN) ->
+ case Module:register_name(Name, self()) of
+ yes ->
+ true;
+ no ->
+ {false, where(GN)}
end.
+
timeout(Options) ->
case opt(timeout, Options) of
{ok, Time} ->
diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl
index 3317b30e5c..ca05df20c0 100644
--- a/lib/stdlib/src/gen_event.erl
+++ b/lib/stdlib/src/gen_event.erl
@@ -107,8 +107,10 @@
-type add_handler_ret() :: ok | term() | {'EXIT',term()}.
-type del_handler_ret() :: ok | term() | {'EXIT',term()}.
--type emgr_name() :: {'local', atom()} | {'global', atom()}.
--type emgr_ref() :: atom() | {atom(), atom()} | {'global', atom()} | pid().
+-type emgr_name() :: {'local', atom()} | {'global', atom()}
+ | {'via', atom(), term()}.
+-type emgr_ref() :: atom() | {atom(), atom()} | {'global', atom()}
+ | {'via', atom(), term()} | pid().
-type start_ret() :: {'ok', pid()} | {'error', term()}.
%%---------------------------------------------------------------------------
@@ -143,6 +145,7 @@ init_it(Starter, Parent, Name0, _, _, Options) ->
name({local,Name}) -> Name;
name({global,Name}) -> Name;
+name({via,_, Name}) -> Name;
name(Pid) when is_pid(Pid) -> Pid.
-spec add_handler(emgr_ref(), handler(), term()) -> term().
@@ -209,6 +212,9 @@ call1(M, Handler, Query, Timeout) ->
send({global, Name}, Cmd) ->
catch global:send(Name, Cmd),
ok;
+send({via, Mod, Name}, Cmd) ->
+ catch Mod:send(Name, Cmd),
+ ok;
send(M, Cmd) ->
M ! Cmd,
ok.
diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl
index 80866c0806..51dc0bcee2 100644
--- a/lib/stdlib/src/gen_fsm.erl
+++ b/lib/stdlib/src/gen_fsm.erl
@@ -165,7 +165,7 @@
%%% start(Name, Mod, Args, Options)
%%% start_link(Mod, Args, Options)
%%% start_link(Name, Mod, Args, Options) where:
-%%% Name ::= {local, atom()} | {global, atom()}
+%%% Name ::= {local, atom()} | {global, atom()} | {via, atom(), term()}
%%% Mod ::= atom(), callback module implementing the 'real' fsm
%%% Args ::= term(), init arguments (to Mod:init/1)
%%% Options ::= [{debug, [Flag]}]
@@ -191,6 +191,9 @@ start_link(Name, Mod, Args, Options) ->
send_event({global, Name}, Event) ->
catch global:send(Name, {'$gen_event', Event}),
ok;
+send_event({via, Mod, Name}, Event) ->
+ catch Mod:send(Name, {'$gen_event', Event}),
+ ok;
send_event(Name, Event) ->
Name ! {'$gen_event', Event},
ok.
@@ -214,6 +217,9 @@ sync_send_event(Name, Event, Timeout) ->
send_all_state_event({global, Name}, Event) ->
catch global:send(Name, {'$gen_all_state_event', Event}),
ok;
+send_all_state_event({via, Mod, Name}, Event) ->
+ catch Mod:send(Name, {'$gen_all_state_event', Event}),
+ ok;
send_all_state_event(Name, Event) ->
Name ! {'$gen_all_state_event', Event},
ok.
@@ -273,7 +279,10 @@ cancel_timer(Ref) ->
enter_loop(Mod, Options, StateName, StateData) ->
enter_loop(Mod, Options, StateName, StateData, self(), infinity).
-enter_loop(Mod, Options, StateName, StateData, ServerName = {_,_}) ->
+enter_loop(Mod, Options, StateName, StateData, {Scope,_} = ServerName)
+ when Scope == local; Scope == global ->
+ enter_loop(Mod, Options, StateName, StateData, ServerName,infinity);
+enter_loop(Mod, Options, StateName, StateData, {via,_,_} = ServerName) ->
enter_loop(Mod, Options, StateName, StateData, ServerName,infinity);
enter_loop(Mod, Options, StateName, StateData, Timeout) ->
enter_loop(Mod, Options, StateName, StateData, self(), Timeout).
@@ -303,6 +312,15 @@ get_proc_name({global, Name}) ->
Name;
_Pid ->
exit(process_not_registered_globally)
+ end;
+get_proc_name({via, Mod, Name}) ->
+ case Mod:whereis_name(Name) of
+ undefined ->
+ exit({process_not_registered_via, Mod});
+ Pid when Pid =:= self() ->
+ Name;
+ _Pid ->
+ exit({process_not_registered_via, Mod})
end.
get_parent() ->
@@ -367,12 +385,15 @@ init_it(Starter, Parent, Name0, Mod, Args, Options) ->
name({local,Name}) -> Name;
name({global,Name}) -> Name;
+name({via,_, Name}) -> Name;
name(Pid) when is_pid(Pid) -> Pid.
unregister_name({local,Name}) ->
_ = (catch unregister(Name));
unregister_name({global,Name}) ->
_ = global:unregister_name(Name);
+unregister_name({via, Mod, Name}) ->
+ _ = Mod:unregister_name(Name);
unregister_name(Pid) when is_pid(Pid) ->
Pid.
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index f720ec15f5..b384e8baf7 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -142,7 +142,7 @@
%%% start(Name, Mod, Args, Options)
%%% start_link(Mod, Args, Options)
%%% start_link(Name, Mod, Args, Options) where:
-%%% Name ::= {local, atom()} | {global, atom()}
+%%% Name ::= {local, atom()} | {global, atom()} | {via, atom(), term()}
%%% Mod ::= atom(), callback module implementing the 'real' server
%%% Args ::= term(), init arguments (to Mod:init/1)
%%% Options ::= [{timeout, Timeout} | {debug, [Flag]}]
@@ -194,6 +194,9 @@ call(Name, Request, Timeout) ->
cast({global,Name}, Request) ->
catch global:send(Name, cast_msg(Request)),
ok;
+cast({via, Mod, Name}, Request) ->
+ catch Mod:send(Name, cast_msg(Request)),
+ ok;
cast({Name,Node}=Dest, Request) when is_atom(Name), is_atom(Node) ->
do_cast(Dest, Request);
cast(Dest, Request) when is_atom(Dest) ->
@@ -266,7 +269,11 @@ multi_call(Nodes, Name, Req, Timeout)
enter_loop(Mod, Options, State) ->
enter_loop(Mod, Options, State, self(), infinity).
-enter_loop(Mod, Options, State, ServerName = {_, _}) ->
+enter_loop(Mod, Options, State, ServerName = {Scope, _})
+ when Scope == local; Scope == local ->
+ enter_loop(Mod, Options, State, ServerName, infinity);
+
+enter_loop(Mod, Options, State, ServerName = {via, _, _}) ->
enter_loop(Mod, Options, State, ServerName, infinity);
enter_loop(Mod, Options, State, Timeout) ->
@@ -327,12 +334,15 @@ init_it(Starter, Parent, Name0, Mod, Args, Options) ->
name({local,Name}) -> Name;
name({global,Name}) -> Name;
+name({via,_, Name}) -> Name;
name(Pid) when is_pid(Pid) -> Pid.
unregister_name({local,Name}) ->
_ = (catch unregister(Name));
unregister_name({global,Name}) ->
_ = global:unregister_name(Name);
+unregister_name({via, Mod, Name}) ->
+ _ = Mod:unregister_name(Name);
unregister_name(Pid) when is_pid(Pid) ->
Pid.
@@ -827,6 +837,15 @@ get_proc_name({global, Name}) ->
Name;
_Pid ->
exit(process_not_registered_globally)
+ end;
+get_proc_name({via, Mod, Name}) ->
+ case Mod:whereis_name(Name) of
+ undefined ->
+ exit({process_not_registered_via, Mod});
+ Pid when Pid =:= self() ->
+ Name;
+ _Pid ->
+ exit({process_not_registered_via, Mod})
end.
get_parent() ->
diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl
index b49550ff6d..c10da1989c 100644
--- a/lib/stdlib/src/supervisor.erl
+++ b/lib/stdlib/src/supervisor.erl
@@ -28,8 +28,9 @@
check_childspecs/1]).
%% Internal exports
--export([init/1, handle_call/3, handle_info/2, terminate/2, code_change/3]).
--export([handle_cast/2]).
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+-export([try_again_restart/2]).
%%--------------------------------------------------------------------------
@@ -37,7 +38,7 @@
%%--------------------------------------------------------------------------
--type child() :: 'undefined' | pid() | [pid()].
+-type child() :: 'undefined' | pid().
-type child_id() :: term().
-type mfargs() :: {M :: module(), F :: atom(), A :: [term()] | undefined}.
-type modules() :: [module()] | 'dynamic'.
@@ -62,8 +63,8 @@
%%--------------------------------------------------------------------------
-record(child, {% pid is undefined when child is not running
- pid = undefined :: child(),
- name,
+ pid = undefined :: child() | {restarting,pid()} | [pid()],
+ name :: child_id(),
mfargs :: mfargs(),
restart_type :: restart(),
shutdown :: shutdown(),
@@ -95,6 +96,8 @@
[ChildSpec :: child_spec()]}}
| ignore.
+-define(restarting(_Pid_), {restarting,_Pid_}).
+
%%% ---------------------------------------------------
%%% This is a general process supervisor built upon gen_server.erl.
%%% Servers/processes should/could also be built using gen_server.erl.
@@ -139,7 +142,8 @@ start_child(Supervisor, ChildSpec) ->
Result :: {'ok', Child :: child()}
| {'ok', Child :: child(), Info :: term()}
| {'error', Error},
- Error :: 'running' | 'not_found' | 'simple_one_for_one' | term().
+ Error :: 'running' | 'restarting' | 'not_found' | 'simple_one_for_one' |
+ term().
restart_child(Supervisor, Name) ->
call(Supervisor, {restart_child, Name}).
@@ -147,7 +151,7 @@ restart_child(Supervisor, Name) ->
SupRef :: sup_ref(),
Id :: child_id(),
Result :: 'ok' | {'error', Error},
- Error :: 'running' | 'not_found' | 'simple_one_for_one'.
+ Error :: 'running' | 'restarting' | 'not_found' | 'simple_one_for_one'.
delete_child(Supervisor, Name) ->
call(Supervisor, {delete_child, Name}).
@@ -169,7 +173,7 @@ terminate_child(Supervisor, Name) ->
-spec which_children(SupRef) -> [{Id,Child,Type,Modules}] when
SupRef :: sup_ref(),
Id :: child_id() | undefined,
- Child :: child(),
+ Child :: child() | 'restarting',
Type :: worker(),
Modules :: modules().
which_children(Supervisor) ->
@@ -198,6 +202,17 @@ check_childspecs(ChildSpecs) when is_list(ChildSpecs) ->
end;
check_childspecs(X) -> {error, {badarg, X}}.
+%%%-----------------------------------------------------------------
+%%% Called by timer:apply_after from restart/2
+-spec try_again_restart(SupRef, Child) -> ok when
+ SupRef :: sup_ref(),
+ Child :: child_id() | pid().
+try_again_restart(Supervisor, Child) ->
+ cast(Supervisor, {try_again_restart, Child}).
+
+cast(Supervisor, Req) ->
+ gen_server:cast(Supervisor, Req).
+
%%% ---------------------------------------------------
%%%
%%% Initialize the supervisor.
@@ -384,6 +399,8 @@ handle_call({restart_child, Name}, _From, State) ->
Error ->
{reply, Error, State}
end;
+ {value, #child{pid=?restarting(_)}} ->
+ {reply, {error, restarting}, State};
{value, _} ->
{reply, {error, running}, State};
_ ->
@@ -395,6 +412,8 @@ handle_call({delete_child, Name}, _From, State) ->
{value, Child} when Child#child.pid =:= undefined ->
NState = remove_child(Child, State),
{reply, ok, NState};
+ {value, #child{pid=?restarting(_)}} ->
+ {reply, {error, restarting}, State};
{value, _} ->
{reply, {error, running}, State};
_ ->
@@ -413,13 +432,17 @@ handle_call(which_children, _From, #state{children = [#child{restart_type = RTyp
child_type = CT,
modules = Mods}]} =
State) when ?is_simple(State) ->
- Reply = lists:map(fun({Pid, _}) -> {undefined, Pid, CT, Mods} end,
+ Reply = lists:map(fun({?restarting(_),_}) -> {undefined,restarting,CT,Mods};
+ ({Pid, _}) -> {undefined, Pid, CT, Mods} end,
?DICT:to_list(dynamics_db(RType, State#state.dynamics))),
{reply, Reply, State};
handle_call(which_children, _From, State) ->
Resp =
- lists:map(fun(#child{pid = Pid, name = Name,
+ lists:map(fun(#child{pid = ?restarting(_), name = Name,
+ child_type = ChildType, modules = Mods}) ->
+ {Name, restarting, ChildType, Mods};
+ (#child{pid = Pid, name = Name,
child_type = ChildType, modules = Mods}) ->
{Name, Pid, ChildType, Mods}
end,
@@ -432,8 +455,11 @@ handle_call(count_children, _From, #state{children = [#child{restart_type = temp
when ?is_simple(State) ->
{Active, Count} =
?SETS:fold(fun(Pid, {Alive, Tot}) ->
- if is_pid(Pid) -> {Alive+1, Tot +1};
- true -> {Alive, Tot + 1} end
+ case is_pid(Pid) andalso is_process_alive(Pid) of
+ true ->{Alive+1, Tot +1};
+ false ->
+ {Alive, Tot + 1}
+ end
end, {0, 0}, dynamics_db(temporary, State#state.dynamics)),
Reply = case CT of
supervisor -> [{specs, 1}, {active, Active},
@@ -448,8 +474,12 @@ handle_call(count_children, _From, #state{children = [#child{restart_type = RTy
when ?is_simple(State) ->
{Active, Count} =
?DICT:fold(fun(Pid, _Val, {Alive, Tot}) ->
- if is_pid(Pid) -> {Alive+1, Tot +1};
- true -> {Alive, Tot + 1} end
+ case is_pid(Pid) andalso is_process_alive(Pid) of
+ true ->
+ {Alive+1, Tot +1};
+ false ->
+ {Alive, Tot + 1}
+ end
end, {0, 0}, dynamics_db(RType, State#state.dynamics)),
Reply = case CT of
supervisor -> [{specs, 1}, {active, Active},
@@ -486,14 +516,42 @@ count_child(#child{pid = Pid, child_type = supervisor},
end.
-%%% Hopefully cause a function-clause as there is no API function
-%%% that utilizes cast.
--spec handle_cast('null', state()) -> {'noreply', state()}.
+%%% If a restart attempt failed, this message is sent via
+%%% timer:apply_after(0,...) in order to give gen_server the chance to
+%%% check it's inbox before trying again.
+-spec handle_cast({try_again_restart, child_id() | pid()}, state()) ->
+ {'noreply', state()} | {stop, shutdown, state()}.
-handle_cast(null, State) ->
- error_logger:error_msg("ERROR: Supervisor received cast-message 'null'~n",
- []),
- {noreply, State}.
+handle_cast({try_again_restart,Pid}, #state{children=[Child]}=State)
+ when ?is_simple(State) ->
+ RT = Child#child.restart_type,
+ RPid = restarting(Pid),
+ case dynamic_child_args(RPid, dynamics_db(RT, State#state.dynamics)) of
+ {ok, Args} ->
+ {M, F, _} = Child#child.mfargs,
+ NChild = Child#child{pid = RPid, mfargs = {M, F, Args}},
+ case restart(NChild,State) of
+ {ok, State1} ->
+ {noreply, State1};
+ {shutdown, State1} ->
+ {stop, shutdown, State1}
+ end;
+ error ->
+ {noreply, State}
+ end;
+
+handle_cast({try_again_restart,Name}, State) ->
+ case lists:keyfind(Name,#child.name,State#state.children) of
+ Child = #child{pid=?restarting(_)} ->
+ case restart(Child,State) of
+ {ok, State1} ->
+ {noreply, State1};
+ {shutdown, State1} ->
+ {stop, shutdown, State1}
+ end;
+ _ ->
+ {noreply,State}
+ end.
%%
%% Take care of terminated children.
@@ -623,7 +681,7 @@ handle_start_child(Child, State) ->
{error, What} ->
{{error, {What, Child}}, State}
end;
- {value, OldChild} when OldChild#child.pid =/= undefined ->
+ {value, OldChild} when is_pid(OldChild#child.pid) ->
{{error, {already_started, OldChild#child.pid}}, State};
{value, _OldChild} ->
{{error, already_present}, State}
@@ -677,7 +735,21 @@ do_restart(temporary, Reason, Child, State) ->
restart(Child, State) ->
case add_restart(State) of
{ok, NState} ->
- restart(NState#state.strategy, Child, NState);
+ case restart(NState#state.strategy, Child, NState) of
+ {try_again,NState2} ->
+ %% Leaving control back to gen_server before
+ %% trying again. This way other incoming requsts
+ %% for the supervisor can be handled - e.g. a
+ %% shutdown request for the supervisor or the
+ %% child.
+ Id = if ?is_simple(State) -> Child#child.pid;
+ true -> Child#child.name
+ end,
+ timer:apply_after(0,?MODULE,try_again_restart,[self(),Id]),
+ {ok,NState2};
+ Other ->
+ Other
+ end;
{terminate, NState} ->
report_error(shutdown, reached_max_restart_intensity,
Child, State#state.name),
@@ -685,9 +757,9 @@ restart(Child, State) ->
end.
restart(simple_one_for_one, Child, State) ->
- #child{mfargs = {M, F, A}} = Child,
- Dynamics = ?DICT:erase(Child#child.pid, dynamics_db(Child#child.restart_type,
- State#state.dynamics)),
+ #child{pid = OldPid, mfargs = {M, F, A}} = Child,
+ Dynamics = ?DICT:erase(OldPid, dynamics_db(Child#child.restart_type,
+ State#state.dynamics)),
case do_start_child_i(M, F, A) of
{ok, Pid} ->
NState = State#state{dynamics = ?DICT:store(Pid, A, Dynamics)},
@@ -696,10 +768,13 @@ restart(simple_one_for_one, Child, State) ->
NState = State#state{dynamics = ?DICT:store(Pid, A, Dynamics)},
{ok, NState};
{error, Error} ->
+ NState = State#state{dynamics = ?DICT:store(restarting(OldPid), A,
+ Dynamics)},
report_error(start_error, Error, Child, State#state.name),
- restart(Child, State)
+ {try_again, NState}
end;
restart(one_for_one, Child, State) ->
+ OldPid = Child#child.pid,
case do_start_child(State#state.name, Child) of
{ok, Pid} ->
NState = replace_child(Child#child{pid = Pid}, State),
@@ -708,8 +783,9 @@ restart(one_for_one, Child, State) ->
NState = replace_child(Child#child{pid = Pid}, State),
{ok, NState};
{error, Reason} ->
+ NState = replace_child(Child#child{pid = restarting(OldPid)}, State),
report_error(start_error, Reason, Child, State#state.name),
- restart(Child, State)
+ {try_again, NState}
end;
restart(rest_for_one, Child, State) ->
{ChAfter, ChBefore} = split_child(Child#child.pid, State#state.children),
@@ -718,7 +794,9 @@ restart(rest_for_one, Child, State) ->
{ok, ChAfter3} ->
{ok, State#state{children = ChAfter3 ++ ChBefore}};
{error, ChAfter3} ->
- restart(Child, State#state{children = ChAfter3 ++ ChBefore})
+ NChild = Child#child{pid=restarting(Child#child.pid)},
+ NState = State#state{children = ChAfter3 ++ ChBefore},
+ {try_again, replace_child(NChild,NState)}
end;
restart(one_for_all, Child, State) ->
Children1 = del_child(Child#child.pid, State#state.children),
@@ -727,9 +805,14 @@ restart(one_for_all, Child, State) ->
{ok, NChs} ->
{ok, State#state{children = NChs}};
{error, NChs} ->
- restart(Child, State#state{children = NChs})
+ NChild = Child#child{pid=restarting(Child#child.pid)},
+ NState = State#state{children = NChs},
+ {try_again, replace_child(NChild,NState)}
end.
+restarting(Pid) when is_pid(Pid) -> ?restarting(Pid);
+restarting(RPid) -> RPid.
+
%%-----------------------------------------------------------------
%% Func: terminate_children/2
%% Args: Children = [child_rec()] in termination order
@@ -753,7 +836,7 @@ terminate_children([Child | Children], SupName, Res) ->
terminate_children([], _SupName, Res) ->
Res.
-do_terminate(Child, SupName) when Child#child.pid =/= undefined ->
+do_terminate(Child, SupName) when is_pid(Child#child.pid) ->
case shutdown(Child#child.pid, Child#child.shutdown) of
ok ->
ok;
@@ -764,7 +847,7 @@ do_terminate(Child, SupName) when Child#child.pid =/= undefined ->
end,
Child#child{pid = undefined};
do_terminate(Child, _SupName) ->
- Child.
+ Child#child{pid = undefined}.
%%-----------------------------------------------------------------
%% Shutdowns a child. We must check the EXIT value
@@ -865,7 +948,7 @@ terminate_dynamic_children(Child, Dynamics, SupName) ->
TRef = erlang:start_timer(Time, self(), kill),
wait_dynamic_children(Child, Pids, Sz, TRef, EStack0)
end,
- %% Unrool stacked errors and report them
+ %% Unroll stacked errors and report them
?DICT:fold(fun(Reason, Ls, _) ->
report_error(shutdown_error, Reason,
Child#child{pid=Ls}, SupName)
@@ -884,7 +967,7 @@ monitor_dynamic_children(#child{restart_type=temporary}, Dynamics) ->
end
end, {?SETS:new(), ?DICT:new()}, Dynamics);
monitor_dynamic_children(#child{restart_type=RType}, Dynamics) ->
- ?DICT:fold(fun(P, _, {Pids, EStack}) ->
+ ?DICT:fold(fun(P, _, {Pids, EStack}) when is_pid(P) ->
case monitor_child(P) of
ok ->
{?SETS:add_element(P, Pids), EStack};
@@ -892,7 +975,9 @@ monitor_dynamic_children(#child{restart_type=RType}, Dynamics) ->
{Pids, EStack};
{error, Reason} ->
{Pids, ?DICT:append(Reason, P, EStack)}
- end
+ end;
+ (?restarting(_), _, {Pids, EStack}) ->
+ {Pids, EStack}
end, {?SETS:new(), ?DICT:new()}, Dynamics).
@@ -1019,13 +1104,20 @@ get_child(Name, State, _) ->
lists:keysearch(Name, #child.name, State#state.children).
get_dynamic_child(Pid, #state{children=[Child], dynamics=Dynamics}) ->
- case is_dynamic_pid(Pid, dynamics_db(Child#child.restart_type, Dynamics)) of
+ DynamicsDb = dynamics_db(Child#child.restart_type, Dynamics),
+ case is_dynamic_pid(Pid, DynamicsDb) of
true ->
{value, Child#child{pid=Pid}};
false ->
- case erlang:is_process_alive(Pid) of
- true -> false;
- false -> {value, Child}
+ RPid = restarting(Pid),
+ case is_dynamic_pid(RPid, DynamicsDb) of
+ true ->
+ {value, Child#child{pid=RPid}};
+ false ->
+ case erlang:is_process_alive(Pid) of
+ true -> false;
+ false -> {value, Child}
+ end
end
end.
diff --git a/lib/stdlib/test/Makefile b/lib/stdlib/test/Makefile
index aa6a660c34..4de6ea3ee7 100644
--- a/lib/stdlib/test/Makefile
+++ b/lib/stdlib/test/Makefile
@@ -20,6 +20,7 @@ MODULES= \
digraph_utils_SUITE \
dummy1_h \
dummy_h \
+ dummy_via \
edlin_expand_SUITE \
epp_SUITE \
erl_eval_helper \
@@ -66,6 +67,7 @@ MODULES= \
string_SUITE \
supervisor_1 \
supervisor_2 \
+ supervisor_deadlock \
naughty_child \
shell_SUITE \
supervisor_SUITE \
diff --git a/lib/stdlib/test/digraph_utils_SUITE.erl b/lib/stdlib/test/digraph_utils_SUITE.erl
index 12c486c25f..6b554c2fb7 100644
--- a/lib/stdlib/test/digraph_utils_SUITE.erl
+++ b/lib/stdlib/test/digraph_utils_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2012. 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
@@ -211,8 +211,7 @@ condensation(Config) when is_list(Config) ->
{lists:sort(V1), lists:sort(V2)}
end,
?line Es = lists:map(Fun, digraph:edges(CG)),
- ?line [{[b],[c]},{[b],[d]},{[e,f,g],[e,f,g]},{[h],[h]},{[i,j],[i,j]}] =
- lists:sort(Es),
+ ?line [{[b],[c]},{[b],[d]}] = lists:sort(Es),
?line true = digraph:delete(CG),
?line true = digraph:delete(G),
ok.
diff --git a/lib/stdlib/test/dummy_via.erl b/lib/stdlib/test/dummy_via.erl
new file mode 100644
index 0000000000..e405811cbe
--- /dev/null
+++ b/lib/stdlib/test/dummy_via.erl
@@ -0,0 +1,94 @@
+-module(dummy_via).
+-export([reset/0,
+ register_name/2,
+ whereis_name/1,
+ unregister_name/1,
+ send/2]).
+
+
+reset() ->
+ P = whereis(?MODULE),
+ catch unlink(P),
+ Ref = erlang:monitor(process, P),
+ catch exit(P, kill),
+ receive {'DOWN',Ref,_,_,_} -> ok end,
+ Me = self(),
+ Pid = spawn_link(fun() ->
+ register(?MODULE, self()),
+ Me ! {self(), started},
+ loop([])
+ end),
+ receive
+ {Pid, started} ->
+ Pid
+ after 10000 ->
+ exit(timeout)
+ end.
+
+register_name(Name, Pid) when is_pid(Pid) ->
+ call({register_name, Name, Pid}).
+
+unregister_name(Name) ->
+ call({unregister_name, Name}).
+
+whereis_name(Name) ->
+ call({whereis_name, Name}).
+
+send(Name, Msg) ->
+ case whereis_name(Name) of
+ undefined ->
+ exit({badarg, {Name, Msg}});
+ Pid when is_pid(Pid) ->
+ Pid ! Msg,
+ Pid
+ end.
+
+call(Req) ->
+ MRef = erlang:monitor(process, ?MODULE),
+ ?MODULE ! {self(), MRef, Req},
+ receive
+ {'DOWN', MRef, _, _, _} ->
+ erlang:error(badarg);
+ {MRef, badarg} ->
+ erlang:error(badarg);
+ {MRef, Reply} ->
+ Reply
+ after 5000 ->
+ erlang:error(timeout)
+ end.
+
+loop(Reg) ->
+ receive
+ {'DOWN', _, _, P, _} when is_pid(P) ->
+ loop([X || {_,Pid,_} = X <- Reg, Pid =/= P]);
+ {From, Ref, Request} when is_pid(From), is_reference(Ref) ->
+ {Reply, NewReg} = handle_request(Request, Reg),
+ From ! {Ref, Reply},
+ loop(NewReg)
+ end.
+
+handle_request({register_name, Name, Pid}, Reg) when is_pid(Pid) ->
+ case lists:keyfind(Name, 1, Reg) of
+ false ->
+ Ref = erlang:monitor(process, Pid),
+ {yes, [{Name, Pid, Ref}|Reg]};
+ _ ->
+ {no, Reg}
+ end;
+handle_request({whereis_name, Name}, Reg) ->
+ case lists:keyfind(Name, 1, Reg) of
+ {_, Pid, _} ->
+ {Pid, Reg};
+ false ->
+ {undefined, Reg}
+ end;
+handle_request({unregister_name, Name}, Reg) ->
+ case lists:keyfind(Name, 1, Reg) of
+ {_, _, Ref} ->
+ catch erlang:demonitor(Ref);
+ _ ->
+ ok
+ end,
+ {ok, lists:keydelete(Name, 1, Reg)};
+handle_request(_, Reg) ->
+ {badarg, Reg}.
diff --git a/lib/stdlib/test/gen_event_SUITE.erl b/lib/stdlib/test/gen_event_SUITE.erl
index b3a7edc140..5c51e12e35 100644
--- a/lib/stdlib/test/gen_event_SUITE.erl
+++ b/lib/stdlib/test/gen_event_SUITE.erl
@@ -62,6 +62,8 @@ start(suite) -> [];
start(Config) when is_list(Config) ->
OldFl = process_flag(trap_exit, true),
+ ?line dummy_via:reset(),
+
?line {ok, Pid0} = gen_event:start(), %anonymous
?line [] = gen_event:which_handlers(Pid0),
?line ok = gen_event:stop(Pid0),
@@ -85,6 +87,11 @@ start(Config) when is_list(Config) ->
?line [] = gen_event:which_handlers(Pid4),
?line ok = gen_event:stop({global, my_dummy_name}),
+ ?line {ok, Pid5} = gen_event:start_link({via, dummy_via, my_dummy_name}),
+ ?line [] = gen_event:which_handlers({via, dummy_via, my_dummy_name}),
+ ?line [] = gen_event:which_handlers(Pid5),
+ ?line ok = gen_event:stop({via, dummy_via, my_dummy_name}),
+
?line {ok, _} = gen_event:start_link({local, my_dummy_name}),
?line {error, {already_started, _}} =
gen_event:start_link({local, my_dummy_name}),
@@ -92,15 +99,28 @@ start(Config) when is_list(Config) ->
gen_event:start({local, my_dummy_name}),
?line ok = gen_event:stop(my_dummy_name),
- ?line {ok, Pid5} = gen_event:start_link({global, my_dummy_name}),
+ ?line {ok, Pid6} = gen_event:start_link({global, my_dummy_name}),
?line {error, {already_started, _}} =
gen_event:start_link({global, my_dummy_name}),
?line {error, {already_started, _}} =
gen_event:start({global, my_dummy_name}),
- exit(Pid5, shutdown),
+ exit(Pid6, shutdown),
+ receive
+ {'EXIT', Pid6, shutdown} -> ok
+ after 10000 ->
+ ?t:fail(exit_gen_event)
+ end,
+
+ ?line {ok, Pid7} = gen_event:start_link({via, dummy_via, my_dummy_name}),
+ ?line {error, {already_started, _}} =
+ gen_event:start_link({via, dummy_via, my_dummy_name}),
+ ?line {error, {already_started, _}} =
+ gen_event:start({via, dummy_via, my_dummy_name}),
+
+ exit(Pid7, shutdown),
receive
- {'EXIT', Pid5, shutdown} -> ok
+ {'EXIT', Pid7, shutdown} -> ok
after 10000 ->
?t:fail(exit_gen_event)
end,
diff --git a/lib/stdlib/test/gen_fsm_SUITE.erl b/lib/stdlib/test/gen_fsm_SUITE.erl
index d60629d841..bdb4ea65b5 100644
--- a/lib/stdlib/test/gen_fsm_SUITE.erl
+++ b/lib/stdlib/test/gen_fsm_SUITE.erl
@@ -21,11 +21,11 @@
-include_lib("test_server/include/test_server.hrl").
%% Test cases
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2]).
--export([ start1/1, start2/1, start3/1, start4/1 , start5/1, start6/1,
- start7/1, start8/1, start9/1, start10/1, start11/1]).
+-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([ abnormal1/1, abnormal2/1]).
@@ -56,14 +56,14 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
-all() ->
+all() ->
[{group, start}, {group, abnormal}, shutdown,
{group, sys}, hibernate, enter_loop].
-groups() ->
+groups() ->
[{start, [],
[start1, start2, start3, start4, start5, start6, start7,
- start8, start9, start10, start11]},
+ start8, start9, start10, start11, start12]},
{abnormal, [], [abnormal1, abnormal2]},
{sys, [],
[sys1, call_format_status, error_format_status]}].
@@ -258,6 +258,25 @@ start11(Config) when is_list(Config) ->
test_server:messages_get(),
ok.
+%% Via register linked
+start12(Config) when is_list(Config) ->
+ ?line dummy_via:reset(),
+ ?line {ok, Pid} =
+ gen_fsm:start_link({via, dummy_via, my_fsm}, gen_fsm_SUITE, [], []),
+ ?line {error, {already_started, Pid}} =
+ gen_fsm:start_link({via, dummy_via, my_fsm}, gen_fsm_SUITE, [], []),
+ ?line {error, {already_started, Pid}} =
+ gen_fsm:start({via, dummy_via, my_fsm}, gen_fsm_SUITE, [], []),
+
+ ?line ok = do_func_test(Pid),
+ ?line ok = do_sync_func_test(Pid),
+ ?line ok = do_func_test({via, dummy_via, my_fsm}),
+ ?line ok = do_sync_func_test({via, dummy_via, my_fsm}),
+ ?line stop_it({via, dummy_via, my_fsm}),
+
+ test_server:messages_get(),
+ ok.
+
%% Check that time outs in calls work
abnormal1(suite) -> [];
@@ -362,7 +381,25 @@ call_format_status(Config) when is_list(Config) ->
?line Status4 = sys:get_status(GlobalName2),
?line {status, Pid4, _Mod, [_PDict4, running, _, _, Data4]} = Status4,
?line [format_status_called | _] = lists:reverse(Data4),
- ?line stop_it(Pid4).
+ ?line stop_it(Pid4),
+
+ %% check that format_status can handle a name being a term other than a
+ %% pid or atom
+ ?line dummy_via:reset(),
+ ViaName1 = {via, dummy_via, "CallFormatStatus"},
+ ?line {ok, Pid5} = gen_fsm:start(ViaName1, gen_fsm_SUITE, [], []),
+ ?line Status5 = sys:get_status(ViaName1),
+ ?line {status, Pid5, _Mod, [_PDict5, running, _, _, Data5]} = Status5,
+ ?line [format_status_called | _] = lists:reverse(Data5),
+ ?line stop_it(Pid5),
+ ViaName2 = {via, dummy_via, {name, "term"}},
+ ?line {ok, Pid6} = gen_fsm:start(ViaName2, gen_fsm_SUITE, [], []),
+ ?line Status6 = sys:get_status(ViaName2),
+ ?line {status, Pid6, _Mod, [_PDict6, running, _, _, Data6]} = Status6,
+ ?line [format_status_called | _] = lists:reverse(Data6),
+ ?line stop_it(Pid6).
+
+
error_format_status(Config) when is_list(Config) ->
?line error_logger_forwarder:register(),
@@ -520,6 +557,8 @@ enter_loop(doc) ->
enter_loop(Config) when is_list(Config) ->
OldFlag = process_flag(trap_exit, true),
+ ?line dummy_via:reset(),
+
%% Locally registered process + {local, Name}
?line {ok, Pid1a} =
proc_lib:start_link(?MODULE, enter_loop, [local, local]),
@@ -623,10 +662,22 @@ enter_loop(Config) when is_list(Config) ->
{'EXIT', Pid6b, process_not_registered_globally} ->
ok
after 1000 ->
- ?line test_server:fail(gen_server_started)
+ ?line test_server:fail(gen_fsm_started)
end,
global:unregister_name(armitage),
+ dummy_via:register_name(armitage, self()),
+ ?line {ok, Pid6c} =
+ proc_lib:start_link(?MODULE, enter_loop, [anon, via]),
+ receive
+ {'EXIT', Pid6c, {process_not_registered_via, dummy_via}} ->
+ ok
+ after 1000 ->
+ ?line test_server:fail({gen_fsm_started, process_info(self(),
+ messages)})
+ end,
+ dummy_via:unregister_name(armitage),
+
process_flag(trap_exit, OldFlag),
ok.
@@ -635,6 +686,7 @@ enter_loop(Reg1, Reg2) ->
case Reg1 of
local -> register(armitage, self());
global -> global:register_name(armitage, self());
+ via -> dummy_via:register_name(armitage, self());
anon -> ignore
end,
proc_lib:init_ack({ok, self()}),
@@ -643,6 +695,9 @@ enter_loop(Reg1, Reg2) ->
gen_fsm:enter_loop(?MODULE, [], state0, [], {local,armitage});
global ->
gen_fsm:enter_loop(?MODULE, [], state0, [], {global,armitage});
+ via ->
+ gen_fsm:enter_loop(?MODULE, [], state0, [],
+ {via, dummy_via, armitage});
anon ->
gen_fsm:enter_loop(?MODULE, [], state0, [])
end.
diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl
index 7fb8d54f2d..cdf15ba017 100644
--- a/lib/stdlib/test/gen_server_SUITE.erl
+++ b/lib/stdlib/test/gen_server_SUITE.erl
@@ -36,7 +36,7 @@
]).
% spawn export
--export([spec_init_local/2, spec_init_global/2,
+-export([spec_init_local/2, spec_init_global/2, spec_init_via/2,
spec_init_default_timeout/2, spec_init_anonymous/1,
spec_init_anonymous_default_timeout/1,
spec_init_not_proc_lib/1, cast_fast_messup/0]).
@@ -199,6 +199,35 @@ start(Config) when is_list(Config) ->
test_server:fail(not_stopped)
end,
+ %% via register
+ ?line dummy_via:reset(),
+ ?line {ok, Pid6} =
+ gen_server:start({via, dummy_via, my_test_name},
+ gen_server_SUITE, [], []),
+ ?line ok = gen_server:call({via, dummy_via, my_test_name}, started_p),
+ ?line {error, {already_started, Pid6}} =
+ gen_server:start({via, dummy_via, my_test_name},
+ gen_server_SUITE, [], []),
+ ?line ok = gen_server:call({via, dummy_via, my_test_name}, stop),
+ test_server:sleep(1),
+ ?line {'EXIT', {noproc,_}} = (catch gen_server:call(Pid6, started_p, 10)),
+
+ %% via register linked
+ ?line dummy_via:reset(),
+ ?line {ok, Pid7} =
+ gen_server:start_link({via, dummy_via, my_test_name},
+ gen_server_SUITE, [], []),
+ ?line ok = gen_server:call({via, dummy_via, my_test_name}, started_p),
+ ?line {error, {already_started, Pid7}} =
+ gen_server:start({via, dummy_via, my_test_name},
+ gen_server_SUITE, [], []),
+ ?line ok = gen_server:call({via, dummy_via, my_test_name}, stop),
+ ?line receive
+ {'EXIT', Pid7, stopped} ->
+ ok
+ after 5000 ->
+ test_server:fail(not_stopped)
+ end,
test_server:messages_get(),
%% Must wait for all error messages before going to next test.
@@ -853,6 +882,8 @@ otp_5854(doc) ->
otp_5854(Config) when is_list(Config) ->
OldFlag = process_flag(trap_exit, true),
+ ?line dummy_via:reset(),
+
%% Make sure gen_server:enter_loop does not accept {local,Name}
%% when it's another process than the calling one which is
%% registered under that name
@@ -881,6 +912,18 @@ otp_5854(Config) when is_list(Config) ->
end,
global:unregister_name(armitage),
+ %% (same for {via, Mod, Name})
+ dummy_via:register_name(armitage, self()),
+ ?line {ok, Pid3} =
+ start_link(spec_init_via, [{not_ok, armitage}, []]),
+ receive
+ {'EXIT', Pid3, {process_not_registered_via, dummy_via}} ->
+ ok
+ after 1000 ->
+ ?line test_server:fail(gen_server_started)
+ end,
+ dummy_via:unregister_name(armitage),
+
process_flag(trap_exit, OldFlag),
ok.
@@ -1060,7 +1103,22 @@ spec_init_global({not_ok, Name}, Options) ->
%% Supervised init can occur here ...
gen_server:enter_loop(?MODULE, Options, {}, {global, Name}, infinity).
-spec_init_default_timeout({ok, Name}, Options) ->
+spec_init_via({ok, Name}, Options) ->
+ process_flag(trap_exit, true),
+ dummy_via:register_name(Name, self()),
+ proc_lib:init_ack({ok, self()}),
+ %% Supervised init can occur here ...
+ gen_server:enter_loop(?MODULE, Options, {},
+ {via, dummy_via, Name}, infinity);
+
+spec_init_via({not_ok, Name}, Options) ->
+ process_flag(trap_exit, true),
+ proc_lib:init_ack({ok, self()}),
+ %% Supervised init can occur here ...
+ gen_server:enter_loop(?MODULE, Options, {},
+ {via, dummy_via, Name}, infinity).
+
+spec_init_default_timeout({ok, Name}, Options) ->
process_flag(trap_exit, true),
register(Name, self()),
proc_lib:init_ack({ok, self()}),
diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl
index 71b76c093f..767ae3d62c 100644
--- a/lib/stdlib/test/supervisor_SUITE.erl
+++ b/lib/stdlib/test/supervisor_SUITE.erl
@@ -21,7 +21,7 @@
-module(supervisor_SUITE).
-include_lib("common_test/include/ct.hrl").
--define(TIMEOUT, 1000).
+-define(TIMEOUT, ?t:minutes(1)).
%% Testserver specific export
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
@@ -62,7 +62,8 @@
do_not_save_start_parameters_for_temporary_children/1,
do_not_save_child_specs_for_temporary_children/1,
simple_one_for_one_scale_many_temporary_children/1,
- simple_global_supervisor/1]).
+ simple_global_supervisor/1, hanging_restart_loop/1,
+ hanging_restart_loop_simple/1]).
%%-------------------------------------------------------------------------
@@ -82,7 +83,7 @@ all() ->
count_children_memory, do_not_save_start_parameters_for_temporary_children,
do_not_save_child_specs_for_temporary_children,
simple_one_for_one_scale_many_temporary_children, temporary_bystander,
- simple_global_supervisor].
+ simple_global_supervisor, hanging_restart_loop, hanging_restart_loop_simple].
groups() ->
[{sup_start, [],
@@ -111,10 +112,8 @@ groups() ->
{restart_rest_for_one, [],
[rest_for_one, rest_for_one_escalation]}].
-init_per_suite(Config0) ->
- Config = lists:keydelete(watchdog, 1, Config0),
- Dog = test_server:timetrap(?TIMEOUT),
- [{watchdog, Dog} | Config].
+init_per_suite(Config) ->
+ Config.
end_per_suite(_Config) ->
ok.
@@ -129,18 +128,21 @@ init_per_testcase(count_children_memory, Config) ->
try erlang:memory() of
_ ->
erts_debug:set_internal_state(available_internal_state, true),
- Config
+ Dog = ?t:timetrap(?TIMEOUT),
+ [{watchdog,Dog}|Config]
catch error:notsup ->
{skip, "+Meamin used during test; erlang:memory/1 not available"}
end;
init_per_testcase(_Case, Config) ->
- erlang:display(_Case),
- Config.
+ Dog = ?t:timetrap(?TIMEOUT),
+ [{watchdog,Dog}|Config].
-end_per_testcase(count_children_memory, _Config) ->
+end_per_testcase(count_children_memory, Config) ->
catch erts_debug:set_internal_state(available_internal_state, false),
+ ?t:timetrap_cancel(?config(watchdog,Config)),
ok;
-end_per_testcase(_Case, _Config) ->
+end_per_testcase(_Case, Config) ->
+ ?t:timetrap_cancel(?config(watchdog,Config)),
ok.
start_link(InitResult) ->
@@ -1455,6 +1457,102 @@ gen_server9212() ->
%%-------------------------------------------------------------------------
+%% Test that child and supervisor can be shutdown while hanging in restart loop.
+%% See OTP-9549.
+hanging_restart_loop(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ {ok, Pid} = start_link({ok, {{one_for_one, 8, 10}, []}}),
+ Child1 = {child1, {supervisor_deadlock, start_child, []},
+ permanent, brutal_kill, worker, []},
+
+ %% Ets table with state read by supervisor_deadlock.erl
+ ets:new(supervisor_deadlock,[set,named_table,public]),
+ ets:insert(supervisor_deadlock,{fail_start,false}),
+
+ {ok, CPid1} = supervisor:start_child(sup_test, Child1),
+ link(CPid1),
+
+ ets:insert(supervisor_deadlock,{fail_start,true}),
+ supervisor_deadlock:restart_child(),
+ timer:sleep(2000), % allow restart to happen before proceeding
+
+ {error, already_present} = supervisor:start_child(sup_test, Child1),
+ {error, restarting} = supervisor:restart_child(sup_test, child1),
+ {error, restarting} = supervisor:delete_child(sup_test, child1),
+ [{child1,restarting,worker,[]}] = supervisor:which_children(sup_test),
+ [1,0,0,1] = get_child_counts(sup_test),
+
+ ok = supervisor:terminate_child(sup_test, child1),
+ check_exit_reason(CPid1, error),
+ [{child1,undefined,worker,[]}] = supervisor:which_children(sup_test),
+
+ ets:insert(supervisor_deadlock,{fail_start,false}),
+ {ok, CPid2} = supervisor:restart_child(sup_test, child1),
+ link(CPid2),
+
+ ets:insert(supervisor_deadlock,{fail_start,true}),
+ supervisor_deadlock:restart_child(),
+ timer:sleep(2000), % allow restart to happen before proceeding
+
+ %% Terminating supervisor.
+ %% OTP-9549 fixes so this does not give a timetrap timeout -
+ %% i.e. that supervisor does not hang in restart loop.
+ terminate(Pid,shutdown),
+
+ %% Check that child died with reason from 'restart' request above
+ check_exit_reason(CPid2, error),
+ undefined = whereis(sup_test),
+ ok.
+
+%%-------------------------------------------------------------------------
+%% Test that child and supervisor can be shutdown while hanging in
+%% restart loop, simple_one_for_one.
+%% See OTP-9549.
+hanging_restart_loop_simple(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ Child1 = {child1, {supervisor_deadlock, start_child, []},
+ permanent, brutal_kill, worker, []},
+ {ok, Pid} = start_link({ok, {{simple_one_for_one, 8, 10}, [Child1]}}),
+
+ %% Ets table with state read by supervisor_deadlock.erl
+ ets:new(supervisor_deadlock,[set,named_table,public]),
+ ets:insert(supervisor_deadlock,{fail_start,false}),
+
+ {ok, CPid1} = supervisor:start_child(sup_test, []),
+ link(CPid1),
+
+ ets:insert(supervisor_deadlock,{fail_start,true}),
+ supervisor_deadlock:restart_child(),
+ timer:sleep(2000), % allow restart to happen before proceeding
+
+ {error, simple_one_for_one} = supervisor:restart_child(sup_test, child1),
+ {error, simple_one_for_one} = supervisor:delete_child(sup_test, child1),
+ [{undefined,restarting,worker,[]}] = supervisor:which_children(sup_test),
+ [1,0,0,1] = get_child_counts(sup_test),
+
+ ok = supervisor:terminate_child(sup_test, CPid1),
+ check_exit_reason(CPid1, error),
+ [] = supervisor:which_children(sup_test),
+
+ ets:insert(supervisor_deadlock,{fail_start,false}),
+ {ok, CPid2} = supervisor:start_child(sup_test, []),
+ link(CPid2),
+
+ ets:insert(supervisor_deadlock,{fail_start,true}),
+ supervisor_deadlock:restart_child(),
+ timer:sleep(2000), % allow restart to happen before proceeding
+
+ %% Terminating supervisor.
+ %% OTP-9549 fixes so this does not give a timetrap timeout -
+ %% i.e. that supervisor does not hang in restart loop.
+ terminate(Pid,shutdown),
+
+ %% Check that child died with reason from 'restart' request above
+ check_exit_reason(CPid2, error),
+ undefined = whereis(sup_test),
+ ok.
+
+%%-------------------------------------------------------------------------
terminate(Pid, Reason) when Reason =/= supervisor ->
terminate(dummy, Pid, dummy, Reason).
diff --git a/lib/stdlib/test/supervisor_deadlock.erl b/lib/stdlib/test/supervisor_deadlock.erl
new file mode 100644
index 0000000000..288547a972
--- /dev/null
+++ b/lib/stdlib/test/supervisor_deadlock.erl
@@ -0,0 +1,45 @@
+-module(supervisor_deadlock).
+-compile(export_all).
+
+
+%%%-----------------------------------------------------------------
+%%% gen_server callbacks
+init([child]) ->
+ case ets:lookup(supervisor_deadlock,fail_start) of
+ [{fail_start, false}] ->
+ %% we must not fail on the first init, otherwise supervisor
+ %% terminates immediately
+ {ok, []};
+ [{fail_start, true}] ->
+ %% Restart frequency is MaxR=8, MaxT=10, so this will
+ %% ensure that restart intensity is not reached -> restart
+ %% loop
+ timer:sleep(2000), % NOTE: this could be a gen_server call timeout
+
+ {stop, error}
+ end.
+
+handle_call(_Req, _From, State) ->
+ {reply, ok, State}.
+
+%% Force a restart
+handle_cast(restart, State) ->
+ {stop, error, State}.
+
+handle_info(_Msg, State) ->
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+
+%%%-----------------------------------------------------------------
+%%% Start child
+start_child() ->
+ gen_server:start_link({local, ?MODULE}, ?MODULE, [child], []).
+
+restart_child() ->
+ gen_server:cast(supervisor_deadlock, restart).