chut / src / chut_user_monitor.erl

Fred T-H 9dbb59e 





































Fred T-H b151044 


Fred T-H 9dbb59e 


































Fred T-H b151044 

Fred T-H 9dbb59e 










Fred T-H b151044 
Fred T-H b256108 
Fred T-H 9dbb59e 


Fred T-H b151044 
Fred T-H 9dbb59e 










Fred T-H b151044 


Fred T-H 9dbb59e 
Fred T-H b151044 
Fred T-H 9dbb59e 







































Fred T-H b151044 


Fred T-H 9dbb59e 

































%%%===================================================================
%%% chut_user_monitor is an fsm responsible for keeping the user alive when
%%% no clients are connected. It's also responsible for a user timing
%%% out when clients haven't connected for a given wait time.
%%% Eventually, this might be used to monitor other users the current
%%% user is having a conversation with so when they log off, the current
%%% user knows.
%%%===================================================================
-module(chut_user_monitor).
-behaviour(gen_fsm).

%% API
-export([start_link/3, add_handler/2, delete_handler/2]).

%% gen_fsm callbacks
-export([init/1, waiting/2, listening/2, handle_event/3,
         handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).

%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link(UserId, Dispatcher, TimeOut) ->
%%               {ok,Pid} | ignore | {error,Error}
%% Description:Creates a gen_fsm process which calls Module:init/1 to
%% initialize. To ensure a synchronized start-up procedure, this function
%% does not return until Module:init/1 has returned.
%%
%% In this one, TimeOut is the time to live of the user when there are
%% no listen_handlers connected to the event manager.
%% The Dispatcher is yet unused, but will be when monitoring of users
%% is added in (no YAGNI rethinking needed, I hope!)
%% The UserId is needed for the fsm to terminate its supervisor when
%% the time is out.
%%--------------------------------------------------------------------
start_link(UserId, Dispatcher, TimeOut) ->
  gen_fsm:start_link({global, {monitor,UserId}},
                     ?MODULE,
                     orddict:from_list([{id, UserId},
                                        {dispatcher, Dispatcher},
                                        {timeout, TimeOut}]),
                     []).

%%====================================================================
%%--------------------------------------------------------------------
%% Function: add_handler(UserId, HandlerId) -> ok
%% Description: Sends a message to the FSM to start tracking a new
%% client listener.
%%--------------------------------------------------------------------
add_handler(UserId, HandlerId) ->
    gen_fsm:sync_send_all_state_event({global, {monitor,UserId}}, {add_handler, HandlerId}).

%%--------------------------------------------------------------------
%% Function: delete_handler(UserId, HandlerId) -> ok
%% Description: Orders the fsm to stop tracking the client listener.
%% If there are no listeners left, the FSM will switch into waiting
%% mode and timeout if there are no new handlers.
%%--------------------------------------------------------------------
delete_handler(UserId, HandlerId) ->
    gen_fsm:send_event({global, {monitor,UserId}}, {delete_handler, HandlerId}).

%%====================================================================
%% gen_fsm callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, StateName, State} |
%%                         {ok, StateName, State, Timeout} |
%%                         ignore                              |
%%                         {stop, StopReason}
%% Description:Whenever a gen_fsm is started using gen_fsm:start/[3,4] or
%% gen_fsm:start_link/3,4, this function is called by the new process to
%% initialize.
%% Added to the state declared in start_link is an empty list of
%% handlers.
%%--------------------------------------------------------------------
init(Opts) ->
    T = orddict:fetch(timeout,Opts),
    {ok, waiting, orddict:store(handlers, [], Opts), T}.


%%--------------------------------------------------------------------
%% Function: waiting(timeout, State) -> {stop, normal, State}
%% Description: the fsm waits until it times out, in which case it
%% will kill its supervisor. Adding a handler (global event) will stop
%% the waiting state
%%--------------------------------------------------------------------
waiting(timeout, State) ->
    %% maybe store messages?
    io:format("timing out~n"),
    UserId = orddict:fetch(id, State),
    chut_user:terminate(UserId), % take care, circular dependency!
    {stop, normal, State};
waiting(_Event, State) -> % catch-all to avoid crashing
    io:format("invalid timeout msg:~p~n",[_Event]),
    TimeOut = orddict:fetch(timeout, State),
    {next_state, waiting, State, TimeOut}.

%%--------------------------------------------------------------------
%% function: listening({delete_handler, HandlerId}, State) ->
%%              {next_state, waiting, NewState, TimeOut} |
%%              {next_state, listening, NewState}
%% Description: the fsm just keeps listening for the order to stop
%% tracking event handlers. When no handler is left, switch to the
%% waiting state, otherwise, keep listening.
%%--------------------------------------------------------------------
listening({delete_handler, HandlerId}, State) ->
    Handlers = orddict:fetch(handlers, State),
    TimeOut = orddict:fetch(timeout, State),
    TempState = orddict:erase(handlers, State),
    NewHandlers = lists:delete(HandlerId, Handlers),
    NewState = orddict:store(handlers, NewHandlers, TempState),
    case NewHandlers of
        [] ->    {next_state, waiting, NewState, TimeOut};
        [_|_] -> {next_state, listening, NewState}
    end;
listening(_Event, State) -> % avoid crashes!
    {next_state, listening, State}.

%%--------------------------------------------------------------------
%% Function:
%% handle_event(Event, StateName, State) -> {next_state, NextStateName,
%%                                                NextState} |
%%                                          {next_state, NextStateName,
%%                                                NextState, Timeout} |
%%                                          {stop, Reason, NewState}
%% Description: Whenever a gen_fsm receives an event sent using
%% gen_fsm:send_all_state_event/2, this function is called to handle
%% the event.
%%--------------------------------------------------------------------
handle_event(_Event, StateName, State) ->
  {next_state, StateName, State}.

%%--------------------------------------------------------------------
%% Function:
%% handle_sync_event(Event, From, StateName,
%%                   State) -> {next_state, NextStateName, NextState} |
%%                             {next_state, NextStateName, NextState,
%%                              Timeout} |
%%                             {reply, Reply, NextStateName, NextState}|
%%                             {reply, Reply, NextStateName, NextState,
%%                              Timeout} |
%%                             {stop, Reason, NewState} |
%%                             {stop, Reason, Reply, NewState}
%% Description: Whenever a gen_fsm receives an event sent using
%% gen_fsm:sync_send_all_state_event/2,3, this function is called to handle
%% the event.
%%
%% When receiving {add_handler, HandlerId} as an event, this will
%% make the fsm start tracking it and switch the state to listening.
%%--------------------------------------------------------------------
handle_sync_event({add_handler, HandlerId}, _From, _StateName, State) ->
    Handlers = orddict:fetch(handlers, State),
    TempState = orddict:erase(handlers, State),
    NewState = orddict:store(handlers, [HandlerId | Handlers], TempState),
    {reply, ok, listening, NewState};
handle_sync_event(_Event, _From, StateName, State) ->
    Reply = ok,
    {reply, Reply, StateName, State}.

%%--------------------------------------------------------------------
%% Function:
%% handle_info(Info,StateName,State)-> {next_state, NextStateName, NextState}|
%%                                     {next_state, NextStateName, NextState,
%%                                       Timeout} |
%%                                     {stop, Reason, NewState}
%% Description: This function is called by a gen_fsm when it receives any
%% other message than a synchronous or asynchronous event
%% (or a system message).
%%--------------------------------------------------------------------
handle_info(_Info, StateName, State) ->
    {next_state, StateName, State}.

%%--------------------------------------------------------------------
%% Function: terminate(Reason, StateName, State) -> void()
%% Description:This function is called by a gen_fsm when it is about
%% to terminate. It should be the opposite of Module:init/1 and do any
%% necessary cleaning up. When it returns, the gen_fsm terminates with
%% Reason. The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _StateName, _State) ->
    ok.

%%--------------------------------------------------------------------
%% Function:
%% code_change(OldVsn, StateName, State, Extra) -> {ok, StateName, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, StateName, State, _Extra) ->
    {ok, StateName, State}.
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.