0
Fork 0
mirror of https://github.com/ninenines/cowboy.git synced 2025-07-14 12:20:24 +00:00

Handle supervisor calls properly everywhere

This commit is contained in:
Loïc Hoguin 2018-03-13 10:40:14 +01:00
parent a89732e8e0
commit b9c8d86502
No known key found for this signature in database
GPG key ID: 8A9DF795F6FED764
5 changed files with 35 additions and 47 deletions

View file

@ -20,8 +20,7 @@
-export([shutdown/2]). -export([shutdown/2]).
-export([shutdown_timeout/3]). -export([shutdown_timeout/3]).
-export([terminate/1]). -export([terminate/1]).
-export([which_children/2]). -export([handle_supervisor_call/4]).
-export([count_children/1]).
-record(child, { -record(child, {
pid :: pid(), pid :: pid(),
@ -160,6 +159,24 @@ longest_shutdown_time([#child{shutdown=ChildTime}|Tail], Time) when ChildTime >
longest_shutdown_time([_|Tail], Time) -> longest_shutdown_time([_|Tail], Time) ->
longest_shutdown_time(Tail, Time). longest_shutdown_time(Tail, Time).
-spec handle_supervisor_call(any(), {pid(), any()}, children(), module()) -> ok.
handle_supervisor_call(which_children, {From, Tag}, Children, Module) ->
From ! {Tag, which_children(Children, Module)},
ok;
handle_supervisor_call(count_children, {From, Tag}, Children, _) ->
From ! {Tag, count_children(Children)},
ok;
%% We disable start_child since only incoming requests
%% end up creating a new process.
handle_supervisor_call({start_child, _}, {From, Tag}, _, _) ->
From ! {Tag, {error, start_child_disabled}},
ok;
%% All other calls refer to children. We act in a similar way
%% to a simple_one_for_one so we never find those.
handle_supervisor_call(_, {From, Tag}, _, _) ->
From ! {Tag, {error, not_found}},
ok.
-spec which_children(children(), module()) -> [{module(), pid(), worker, [module()]}]. -spec which_children(children(), module()) -> [{module(), pid(), worker, [module()]}].
which_children(Children, Module) -> which_children(Children, Module) ->
[{Module, Pid, worker, [Module]} || #child{pid=Pid} <- Children]. [{Module, Pid, worker, [Module]} || #child{pid=Pid} <- Children].

View file

@ -202,14 +202,8 @@ loop(State=#state{parent=Parent, socket=Socket, transport=Transport, opts=Opts,
Msg = {'EXIT', Pid, _} -> Msg = {'EXIT', Pid, _} ->
loop(down(State, Pid, Msg), Buffer); loop(down(State, Pid, Msg), Buffer);
%% Calls from supervisor module. %% Calls from supervisor module.
{'$gen_call', {From, Tag}, which_children} -> {'$gen_call', From, Call} ->
From ! {Tag, cowboy_children:which_children(Children, ?MODULE)}, cowboy_children:handle_supervisor_call(Call, From, Children, ?MODULE),
loop(State, Buffer);
{'$gen_call', {From, Tag}, count_children} ->
From ! {Tag, cowboy_children:count_children(Children)},
loop(State, Buffer);
{'$gen_call', {From, Tag}, _} ->
From ! {Tag, {error, ?MODULE}},
loop(State, Buffer); loop(State, Buffer);
%% Unknown messages. %% Unknown messages.
Msg -> Msg ->

View file

@ -247,14 +247,8 @@ loop(State=#state{parent=Parent, socket=Socket, transport=Transport,
Msg = {'EXIT', Pid, _} -> Msg = {'EXIT', Pid, _} ->
loop(down(State, Pid, Msg), Buffer); loop(down(State, Pid, Msg), Buffer);
%% Calls from supervisor module. %% Calls from supervisor module.
{'$gen_call', {From, Tag}, which_children} -> {'$gen_call', From, Call} ->
From ! {Tag, cowboy_children:which_children(Children, ?MODULE)}, cowboy_children:handle_supervisor_call(Call, From, Children, ?MODULE),
loop(State, Buffer);
{'$gen_call', {From, Tag}, count_children} ->
From ! {Tag, cowboy_children:count_children(Children)},
loop(State, Buffer);
{'$gen_call', {From, Tag}, _} ->
From ! {Tag, {error, ?MODULE}},
loop(State, Buffer); loop(State, Buffer);
Msg -> Msg ->
error_logger:error_msg("Received stray message ~p.", [Msg]), error_logger:error_msg("Received stray message ~p.", [Msg]),

View file

@ -250,6 +250,10 @@ handler_loop(State=#state{socket=Socket, messages={OK, Closed, Error},
websocket_close(State, HandlerState, timeout); websocket_close(State, HandlerState, timeout);
{timeout, OlderTRef, ?MODULE} when is_reference(OlderTRef) -> {timeout, OlderTRef, ?MODULE} when is_reference(OlderTRef) ->
handler_loop(State, HandlerState, SoFar); handler_loop(State, HandlerState, SoFar);
%% Calls from supervisor module.
{'$gen_call', From, Call} ->
cowboy_children:handle_supervisor_call(Call, From, [], ?MODULE),
handler_loop(State, HandlerState, SoFar);
Message -> Message ->
handler_call(State, HandlerState, handler_call(State, HandlerState,
SoFar, websocket_info, Message, fun handler_before_loop/3) SoFar, websocket_info, Message, fun handler_before_loop/3)

View file

@ -670,10 +670,7 @@ supervisor_count_children_ws(Config) ->
{ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []), {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
timer:sleep(100), timer:sleep(100),
Pid = do_get_remote_pid_tcp(Socket), Pid = do_get_remote_pid_tcp(Socket),
%% We use gen_server:call directly because the supervisor:count_children Counts = supervisor:count_children(Pid),
%% function has a timeout of infinity.
%% @todo This can be changed to supervisor:count_children/1 once it is fixed.
Counts = gen_server:call(Pid, count_children, 1000),
1 = proplists:get_value(specs, Counts), 1 = proplists:get_value(specs, Counts),
0 = proplists:get_value(active, Counts), 0 = proplists:get_value(active, Counts),
0 = proplists:get_value(supervisors, Counts), 0 = proplists:get_value(supervisors, Counts),
@ -741,10 +738,7 @@ supervisor_delete_child_not_found_ws(Config) ->
{ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []), {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
timer:sleep(100), timer:sleep(100),
Pid = do_get_remote_pid_tcp(Socket), Pid = do_get_remote_pid_tcp(Socket),
%% We use gen_server:call directly because the supervisor:delete_child {error, not_found} = supervisor:delete_child(Pid, cowboy_websocket),
%% function has a timeout of infinity.
%% @todo This can be changed to supervisor:delete_child/2 once it is fixed.
{error, not_found} = gen_server:call(Pid, {delete_child, cowboy_websocket}, 1000),
ok. ok.
%% supervisor:get_childspec/2. %% supervisor:get_childspec/2.
@ -808,10 +802,7 @@ supervisor_get_childspec_not_found_ws(Config) ->
{ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []), {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
timer:sleep(100), timer:sleep(100),
Pid = do_get_remote_pid_tcp(Socket), Pid = do_get_remote_pid_tcp(Socket),
%% We use gen_server:call directly because the supervisor:get_childspec {error, not_found} = supervisor:get_childspec(Pid, cowboy_websocket),
%% function has a timeout of infinity.
%% @todo This can be changed to supervisor:get_childspec/2 once it is fixed.
{error, not_found} = gen_server:call(Pid, {get_childspec, cowboy_websocket}, 1000),
ok. ok.
%% supervisor:restart_child/2. %% supervisor:restart_child/2.
@ -875,10 +866,7 @@ supervisor_restart_child_not_found_ws(Config) ->
{ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []), {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
timer:sleep(100), timer:sleep(100),
Pid = do_get_remote_pid_tcp(Socket), Pid = do_get_remote_pid_tcp(Socket),
%% We use gen_server:call directly because the supervisor:restart_child {error, not_found} = supervisor:restart_child(Pid, cowboy_websocket),
%% function has a timeout of infinity.
%% @todo This can be changed to supervisor:restart_child/2 once it is fixed.
{error, not_found} = gen_server:call(Pid, {restart_child, cowboy_websocket}, 1000),
ok. ok.
%% supervisor:start_child/2 must return {error, start_child_disabled} %% supervisor:start_child/2 must return {error, start_child_disabled}
@ -929,13 +917,10 @@ supervisor_start_child_not_found_ws(Config) ->
{ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []), {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
timer:sleep(100), timer:sleep(100),
Pid = do_get_remote_pid_tcp(Socket), Pid = do_get_remote_pid_tcp(Socket),
%% We use gen_server:call directly because the supervisor:start_child {error, start_child_disabled} = supervisor:start_child(Pid, #{
%% function has a timeout of infinity.
%% @todo This can be changed to supervisor:start_child/2 once it is fixed.
{error, start_child_disabled} = gen_server:call(Pid, {start_child, #{
id => error, id => error,
start => {error, error, []} start => {error, error, []}
}}, 1000), }),
ok. ok.
%% supervisor:terminate_child/2. %% supervisor:terminate_child/2.
@ -999,10 +984,7 @@ supervisor_terminate_child_not_found_ws(Config) ->
{ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []), {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
timer:sleep(100), timer:sleep(100),
Pid = do_get_remote_pid_tcp(Socket), Pid = do_get_remote_pid_tcp(Socket),
%% We use gen_server:call directly because the supervisor:terminate_child {error, not_found} = supervisor:terminate_child(Pid, cowboy_websocket),
%% function has a timeout of infinity.
%% @todo This can be changed to supervisor:terminate_child/2 once it is fixed.
{error, not_found} = gen_server:call(Pid, {terminate_child, cowboy_websocket}, 1000),
ok. ok.
%% supervisor:which_children/1. %% supervisor:which_children/1.
@ -1072,8 +1054,5 @@ supervisor_which_children_ws(Config) ->
{ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []), {ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
timer:sleep(100), timer:sleep(100),
Pid = do_get_remote_pid_tcp(Socket), Pid = do_get_remote_pid_tcp(Socket),
%% We use gen_server:call directly because the supervisor:which_children [] = supervisor:which_children(Pid),
%% function has a timeout of infinity.
%% @todo This can be changed to supervisor:which_children/1 once it is fixed.
[] = gen_server:call(Pid, which_children, 1000),
ok. ok.