|
@@ -0,0 +1,184 @@
|
|
|
+-module(ebb_switch_fsm).
|
|
|
+
|
|
|
+-behaviour(gen_fsm).
|
|
|
+
|
|
|
+-include("../include/ebb_prim.hrl").
|
|
|
+
|
|
|
+%% API
|
|
|
+-export([start_link/3]).
|
|
|
+
|
|
|
+%% gen_fsm callbacks
|
|
|
+-export([init/1, handle_event/3, handle_sync_event/4, handle_info/3,
|
|
|
+ terminate/3, code_change/4]).
|
|
|
+
|
|
|
+%% state callbacks
|
|
|
+-export([waiting/2, forwarding/2]).
|
|
|
+
|
|
|
+-define(DICT, orddict).
|
|
|
+
|
|
|
+-record(state, {arity, map, vals, run, receiver}).
|
|
|
+
|
|
|
+%%====================================================================
|
|
|
+%% API
|
|
|
+%%====================================================================
|
|
|
+%%--------------------------------------------------------------------
|
|
|
+%% Function: start_link() -> 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.
|
|
|
+%%--------------------------------------------------------------------
|
|
|
+start_link(Switch = #switch{}, Run, Receiver)
|
|
|
+ when is_function(Run), is_pid(Receiver) ->
|
|
|
+ gen_fsm:start_link(?MODULE, {Switch, Run, Receiver}, []).
|
|
|
+
|
|
|
+%%====================================================================
|
|
|
+%% 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.
|
|
|
+%%--------------------------------------------------------------------
|
|
|
+init({#switch{in=Arity, map=Map}, Run, Receiver}) ->
|
|
|
+ State = #state{arity=Arity, map=?DICT:from_list(Map), vals=?DICT:new(),
|
|
|
+ run=Run, receiver=Receiver},
|
|
|
+ {ok, waiting, State}.
|
|
|
+
|
|
|
+%%--------------------------------------------------------------------
|
|
|
+%% Function:
|
|
|
+%% state_name(Event, State) -> {next_state, NextStateName, NextState}|
|
|
|
+%% {next_state, NextStateName,
|
|
|
+%% NextState, Timeout} |
|
|
|
+%% {stop, Reason, NewState}
|
|
|
+%% Description:There should be one instance of this function for each possible
|
|
|
+%% state name. Whenever a gen_fsm receives an event sent using
|
|
|
+%% gen_fsm:send_event/2, the instance of this function with the same name as
|
|
|
+%% the current state name StateName is called to handle the event. It is also
|
|
|
+%% called if a timeout occurs.
|
|
|
+%%--------------------------------------------------------------------
|
|
|
+
|
|
|
+waiting({in, 1, Tag},
|
|
|
+ State = #state{map=Map, vals=Vals, run=Run, receiver=Receiver}) ->
|
|
|
+ Path = ?DICT:fetch(Tag, Map),
|
|
|
+ {ok, Target} = Run(Path, Receiver),
|
|
|
+ State2 = State#state{receiver=Target},
|
|
|
+ forward_all(Vals, Target),
|
|
|
+ case is_done(State2) of
|
|
|
+ true -> {stop, normal, State2};
|
|
|
+ false -> {next_state, forwarding, State2}
|
|
|
+ end;
|
|
|
+
|
|
|
+waiting({in, N, Val}, State = #state{arity=Arity, vals=Vals})
|
|
|
+ when N > 0, N =< Arity ->
|
|
|
+ Vals2 = ?DICT:store(N, Val, Vals),
|
|
|
+ State2 = State#state{vals=Vals2},
|
|
|
+ {next_state, waiting, State2}.
|
|
|
+
|
|
|
+forwarding({in, N, Val},
|
|
|
+ State = #state{arity=Arity, vals=Vals, receiver=Receiver})
|
|
|
+ when N > 1, N =< Arity ->
|
|
|
+ Vals2 = ?DICT:store(N, Val, Vals),
|
|
|
+ State2 = State#state{vals=Vals2},
|
|
|
+ forward(N, Val, Receiver),
|
|
|
+ case is_done(State2) of
|
|
|
+ true -> {stop, normal, State2};
|
|
|
+ false -> {next_state, forwarding, State2}
|
|
|
+ end.
|
|
|
+
|
|
|
+%%--------------------------------------------------------------------
|
|
|
+%% Function:
|
|
|
+%% state_name(Event, From, 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: There should be one instance of this function for each
|
|
|
+%% possible state name. Whenever a gen_fsm receives an event sent using
|
|
|
+%% gen_fsm:sync_send_event/2,3, the instance of this function with the same
|
|
|
+%% name as the current state name StateName is called to handle the event.
|
|
|
+%%--------------------------------------------------------------------
|
|
|
+
|
|
|
+%%--------------------------------------------------------------------
|
|
|
+%% 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.
|
|
|
+%%--------------------------------------------------------------------
|
|
|
+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}.
|
|
|
+
|
|
|
+%%--------------------------------------------------------------------
|
|
|
+%%% Internal functions
|
|
|
+%%--------------------------------------------------------------------
|
|
|
+
|
|
|
+forward_all(Vals, Receiver) ->
|
|
|
+ lists:foreach(fun({N, Val}) -> forward(N, Val, Receiver) end,
|
|
|
+ ?DICT:to_list(Vals)).
|
|
|
+
|
|
|
+forward(N, Val, Receiver) ->
|
|
|
+ ebb_event:in(Receiver, N-1, Val).
|
|
|
+
|
|
|
+is_done(#state{arity=Arity, vals=Vals}) ->
|
|
|
+ ?DICT:size(Vals) == Arity-1.
|