summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Watson <tim@rabbitmq.com>2012-09-03 15:31:48 +0100
committerTim Watson <tim@rabbitmq.com>2012-09-03 15:31:48 +0100
commitd43da6ef1a8de96ae08f48bde55b0e38fa6d2e2d (patch)
tree767ded57b62247ff338b4afc64bf35b0e474d1ae
parent6934fac0e30d9d9c4f179c38598aaa78a76dced4 (diff)
downloadrabbitmq-server-d43da6ef1a8de96ae08f48bde55b0e38fa6d2e2d.tar.gz
Test that simple_one_for_one_terminate shutdowns no longer deadlock and
handle reporting errors not caught during shutdown (e.g., 'killed' pids)
-rw-r--r--src/rabbit_tests.erl1
-rw-r--r--src/supervisor2.erl3
-rw-r--r--src/supervisor2_tests.erl100
3 files changed, 103 insertions, 1 deletions
diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl
index ccac12c6..e1914ac2 100644
--- a/src/rabbit_tests.erl
+++ b/src/rabbit_tests.erl
@@ -32,6 +32,7 @@
-define(TIMEOUT, 5000).
all_tests() ->
+ ok = supervisor2_tests:test_all(),
passed = gm_tests:all_tests(),
passed = mirrored_supervisor_tests:all_tests(),
application:set_env(rabbit, file_handles_high_watermark, 10, infinity),
diff --git a/src/supervisor2.erl b/src/supervisor2.erl
index 89a8fd92..632b7808 100644
--- a/src/supervisor2.erl
+++ b/src/supervisor2.erl
@@ -834,7 +834,8 @@ terminate_simple_children(Child, Dynamics, SupName) ->
timeout_stop(Child, TRef, TimeoutMsg, Timedout),
ReportError = shutdown_error_reporter(SupName),
Report = fun(_, ok) -> ok;
- (Pid, {error, R}) -> ReportError(R, Child#child{pid = Pid})
+ (Pid, {error, R}) -> ReportError(R, Child#child{pid = Pid});
+ (Pid, Other) -> ReportError(Other, Child#child{pid = Pid})
end,
[receive
{'EXIT', Pid, Reason} -> Report(Pid, Reason)
diff --git a/src/supervisor2_tests.erl b/src/supervisor2_tests.erl
new file mode 100644
index 00000000..c4916791
--- /dev/null
+++ b/src/supervisor2_tests.erl
@@ -0,0 +1,100 @@
+%% The contents of this file are subject to the Mozilla Public License
+%% Version 1.1 (the "License"); you may not use this file except in
+%% compliance with the License. You may obtain a copy of the License at
+%% http://www.mozilla.org/MPL/
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+%% License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Original Code is RabbitMQ.
+%%
+%% The Initial Developer of the Original Code is VMware, Inc.
+%% Copyright (c) 2011-2012 VMware, Inc. All rights reserved.
+%%
+
+-module(supervisor2_tests).
+-behaviour(supervisor2).
+
+-export([test_all/0, start_sup/0, start_link/0, start_child/0]).
+-export([init/1]).
+
+-compile(export_all).
+
+-include_lib("eunit/include/eunit.hrl").
+
+test_all() ->
+ eunit:test(supervisor2, [verbose]).
+
+terminate_simple_children_without_deadlock_test_() ->
+ [{setup, fun init_supervisor/0,
+ {with, [fun ensure_children_are_alive/1,
+ fun shutdown_and_verify_all_children_died/1]}},
+ {setup, fun init_supervisor/0,
+ {with, [fun shutdown_whilst_interleaving_exits_occur/1]}}].
+
+%%
+%% Public (test facing) API
+%%
+
+start_sup() ->
+ supervisor2:start_link({local, ?MODULE}, ?MODULE, []).
+
+start_link() ->
+ Pid = spawn_link(fun loop_infinity/0),
+ {ok, Pid}.
+
+start_child() ->
+ {ok, Pid} = supervisor2:start_child(?MODULE, []),
+ Pid.
+
+%%
+%% supervisor2 callbacks
+%%
+
+init([parent]) ->
+ {ok, {{one_for_one, 0, 1},
+ [{test_sup, {?MODULE, start_sup, []},
+ transient, 5000, supervisor, [?MODULE]}]}};
+init([]) ->
+ {ok, {{simple_one_for_one_terminate, 0, 1},
+ [{test_worker, {?MODULE, start_link, []},
+ temporary, 1000, worker, []}]}}.
+
+%%
+%% Private API
+%%
+
+ensure_children_are_alive({_, ChildPids}) ->
+ ?assertEqual(true,
+ lists:all(fun erlang:is_process_alive/1, ChildPids)).
+
+shutdown_and_verify_all_children_died({Parent, ChildPids}=State) ->
+ ensure_children_are_alive(State),
+ TestSup = erlang:whereis(?MODULE),
+ ?assertEqual(true, erlang:is_process_alive(TestSup)),
+ ?assertMatch(ok, supervisor2:terminate_child(Parent, test_sup)),
+ ?assertMatch([], [P || P <- ChildPids,
+ erlang:is_process_alive(P)]),
+ ?assertEqual(false, erlang:is_process_alive(TestSup)).
+
+shutdown_whilst_interleaving_exits_occur({Parent, ChildPids}=State) ->
+ ensure_children_are_alive(State),
+ TestPid = self(),
+ Ref = erlang:make_ref(),
+ spawn(fun() ->
+ TestPid ! {Ref, supervisor2:terminate_child(Parent, test_sup)}
+ end),
+ [exit(P, kill) || P <- ChildPids],
+ receive {Ref, Res} ->
+ ?assertEqual(ok, Res)
+ end.
+
+init_supervisor() ->
+ {ok, Pid} = supervisor2:start_link(?MODULE, [parent]),
+ {Pid, [start_child() || _ <- lists:seq(1, 100)]}.
+
+loop_infinity() ->
+ loop_infinity().
+