<div dir="ltr"><div><div><div>I don't know enough in websocket_init/3 to create the session. I need the userid and auth token which are sent with the first websocket packet.<br></div><br></div>If the session is linked to the websocket pid, Is it possible that websocket_terminate/3 will be called but the session won't get an exit message?<br>
<br></div><div><div>From my code, I thought that the only options are:<br></div><div>- The websocket pid is dead when trying to link to it so the session will crash.<br></div><div>- A link is created. If the websocket pid dies, the session will get an exit message.<br>
<br><br></div><div>Is there a race condition in my implementation?<br></div></div></div><div class="gmail_extra"><br><br><div class="gmail_quote">On Sun, May 12, 2013 at 6:01 PM, fxmy wang <span dir="ltr"><<a href="mailto:fxmywc@gmail.com" target="_blank">fxmywc@gmail.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div>hi pablo,<br></div><div>    When a websocket connection shuts down, the function <strong>ws_handler:websocket_terminate(_Reason, _Req, _State) </strong>will be called.</div>
<div>    So you probably should handle you session-ending through this function call</div><div><br></div><div>    And more generally, it's a good way to initialize your session through <strong>websocket_init(_TransportName, Req, _Opts) </strong>and do the clean up work in 
<strong>ws_handler:websocket_terminate(_Reason, _Req, _State) </strong>
</div><div class="HOEnZb"><div class="h5"><br>On Tuesday, May 7, 2013 2:28:51 AM UTC+8, pablo platt wrote:<blockquote class="gmail_quote" style="margin:0;margin-left:0.8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">
<div><div><div><div><div><div><div><div>Hi,<br><br></div>I'm using a cowboy websocket handler as a simple chat transport.<br></div>The websocket handler pass messages to a session gen_server.<br></div><div>
The session gen_server traps exits and links to the websocket pid <br></div>so if the websocket pid dies, the session gen_server should terminate as well.<br><br></div></div>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.<br>

<br></div>Is there a race condition in my code or something else I'm missing in catching the termination of the websocket pid?<br><br></div>Please see a simplified version of the websocket handler and the session gen_server below.<br>

<br></div><div>Thanks<br></div><div><br>-module(my_session).<br>-behaviour(gen_server).<br><br>-export([handle_message/2]).<br>-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).<br>

<br>-record(state, {ws}).<br><br>handle_message(undefined, Msg) -><br>    case supervisor:start_child(my_session_sup, [Msg, self()]) of<br>        {error, Reason} -> {close, 1000, <<"">>};<br>
        {ok, Pid} -><br>
</div><div>            gen_server:call(Pid, {websocket, self()}),<br></div><div>            self() ! {session, Pid},<br>            {text, <<"ok">>}<br>    end;<br>handle_message(Pid, Msg) -><br>
    gen_server:call(Pid, {msg, Msg}).<br>
<br>init([Msg, WS]) -><br>    case auth(Msg) of<br>        false -> {stop, normal};<br>        true -><br>            process_flag(trap_exit,true),<br>            {ok, #state{})<br>    end.<br><br>handle_call({websocket, Pid}, _, State) -><br>

</div><div>  link(Pid),<br></div>  {reply, ok, State#state{ws=Pid}};<br><div>handle_call({msg, Msg}, _, State) -><br>    Resp = msg(Msg, State),<br>   {reply, Resp, State}.<br><br>handle_info({'EXIT', _Pid, _Reason}, State) -><br>

    {stop, normal, State}.<br><br>...<br><br><br>-module(ws_handler).<br>-behaviour(cowboy_websocket_handler).<br><br>-export([init/3]).<br>-export([websocket_init/3]).<br>-export([websocket_handle/3]).<br>-export([websocket_info/3]).<br>

-export([websocket_terminate/3]).<br><br>-record(state, {tref, session}).<br><br>init({tcp, http}, _Req, _Opts) -><br>    {upgrade, protocol, cowboy_websocket}.<br><br>websocket_init(_TransportName, Req, _Opts) -><br>

    Tref = erlang:send_after(10000, self(), timeout),<br>    {ok, Req, #state{tref=Tref}}.<br><br>websocket_handle({text, ""}, Req, State) -><br>    erlang:cancel_timer(State#state.tref),<br>    Tref = erlang:send_after(10000, self(), timeout),<br>

    {ok, Req, State#state{tref=Tref}};    <br>websocket_handle({text, Msg}, Req, State) -><br>    Tref = erlang:send_after(10000, self(), timeout),<br>    Resp = my_session:handle_message(State#state.session, Msg),<br>

    {reply, Resp, Req, State#state{tref=Tref}};<br>websocket_handle(_Data, Req, State) -><br>    {ok, Req, State}.<br><br>websocket_info({session, Pid}, Req, State) -><br>    {ok, Req, State#state{session=Pid}};<br>

websocket_info({terminate}, Req, State) -><br>    {reply, {close, 1000, <<"">>}, Req, State};    <br>websocket_info({timeout, _Ref, Msg}, Req, State) -><br>    {shutdown, Req, State};<br>websocket_info(_Info, Req, State) -><br>

    {ok, Req, State}.<br><br>websocket_terminate(_Reason, _Req, _State) -><br>    ok.<br></div></div>
</blockquote></div></div></blockquote></div><br></div>