[erlang-questions] uncatched cowboy websocket handler termination
pablo platt
pablo.platt@REDACTED
Mon May 6 20:28:51 CEST 2013
Hi,
I'm using a cowboy websocket handler as a simple chat transport.
The websocket handler pass messages to a session gen_server.
The session gen_server traps exits and links to the websocket pid
so if the websocket pid dies, the session gen_server should terminate as
well.
I sometimes see sessions that thinks that the websocket pid is still alive
although checking with is_process_alive/1 shows it is not. That leads to
zombie sessions that accumulate over time.
Is there a race condition in my code or something else I'm missing in
catching the termination of the websocket pid?
Please see a simplified version of the websocket handler and the session
gen_server below.
Thanks
-module(my_session).
-behaviour(gen_server).
-export([handle_message/2]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
code_change/3]).
-record(state, {ws}).
handle_message(undefined, Msg) ->
case supervisor:start_child(my_session_sup, [Msg, self()]) of
{error, Reason} -> {close, 1000, <<"">>};
{ok, Pid} ->
gen_server:call(Pid, {websocket, self()}),
self() ! {session, Pid},
{text, <<"ok">>}
end;
handle_message(Pid, Msg) ->
gen_server:call(Pid, {msg, Msg}).
init([Msg, WS]) ->
case auth(Msg) of
false -> {stop, normal};
true ->
process_flag(trap_exit,true),
{ok, #state{})
end.
handle_call({websocket, Pid}, _, State) ->
link(Pid),
{reply, ok, State#state{ws=Pid}};
handle_call({msg, Msg}, _, State) ->
Resp = msg(Msg, State),
{reply, Resp, State}.
handle_info({'EXIT', _Pid, _Reason}, State) ->
{stop, normal, State}.
...
-module(ws_handler).
-behaviour(cowboy_websocket_handler).
-export([init/3]).
-export([websocket_init/3]).
-export([websocket_handle/3]).
-export([websocket_info/3]).
-export([websocket_terminate/3]).
-record(state, {tref, session}).
init({tcp, http}, _Req, _Opts) ->
{upgrade, protocol, cowboy_websocket}.
websocket_init(_TransportName, Req, _Opts) ->
Tref = erlang:send_after(10000, self(), timeout),
{ok, Req, #state{tref=Tref}}.
websocket_handle({text, ""}, Req, State) ->
erlang:cancel_timer(State#state.tref),
Tref = erlang:send_after(10000, self(), timeout),
{ok, Req, State#state{tref=Tref}};
websocket_handle({text, Msg}, Req, State) ->
Tref = erlang:send_after(10000, self(), timeout),
Resp = my_session:handle_message(State#state.session, Msg),
{reply, Resp, Req, State#state{tref=Tref}};
websocket_handle(_Data, Req, State) ->
{ok, Req, State}.
websocket_info({session, Pid}, Req, State) ->
{ok, Req, State#state{session=Pid}};
websocket_info({terminate}, Req, State) ->
{reply, {close, 1000, <<"">>}, Req, State};
websocket_info({timeout, _Ref, Msg}, Req, State) ->
{shutdown, Req, State};
websocket_info(_Info, Req, State) ->
{ok, Req, State}.
websocket_terminate(_Reason, _Req, _State) ->
ok.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20130506/37088e54/attachment.htm>
More information about the erlang-questions
mailing list