|  | @@ -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.
 |