ebb_route_fsm.erl 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. -module(ebb_route_fsm).
  2. -behaviour(gen_fsm).
  3. -include("../include/ebb_prim.hrl").
  4. %% API
  5. -export([start_link/2]).
  6. %% gen_fsm callbacks
  7. -export([init/1, handle_event/3, handle_sync_event/4, handle_info/3,
  8. terminate/3, code_change/4]).
  9. %% state callbakcs
  10. -export([forwarding/2]).
  11. -define(DICT, orddict).
  12. -record(state, {arity, map, vals, receiver}).
  13. %%====================================================================
  14. %% API
  15. %%====================================================================
  16. %%--------------------------------------------------------------------
  17. %% Function: start_link() -> ok,Pid} | ignore | {error,Error}
  18. %% Description:Creates a gen_fsm process which calls Module:init/1 to
  19. %% initialize. To ensure a synchronized start-up procedure, this function
  20. %% does not return until Module:init/1 has returned.
  21. %%--------------------------------------------------------------------
  22. start_link(Route = #route{}, Receiver) when is_pid(Receiver) ->
  23. gen_fsm:start_link(?MODULE, {Route, Receiver}, []).
  24. %%====================================================================
  25. %% gen_fsm callbacks
  26. %%====================================================================
  27. %%--------------------------------------------------------------------
  28. %% Function: init(Args) -> {ok, StateName, State} |
  29. %% {ok, StateName, State, Timeout} |
  30. %% ignore |
  31. %% {stop, StopReason}
  32. %% Description:Whenever a gen_fsm is started using gen_fsm:start/[3,4] or
  33. %% gen_fsm:start_link/3,4, this function is called by the new process to
  34. %% initialize.
  35. %%--------------------------------------------------------------------
  36. init({Route=#route{in=In}, Receiver}) ->
  37. State = #state{arity=In, map=build_map(Route),
  38. vals=?DICT:new(), receiver=Receiver},
  39. {ok, forwarding, State}.
  40. %%--------------------------------------------------------------------
  41. %% Function:
  42. %% state_name(Event, State) -> {next_state, NextStateName, NextState}|
  43. %% {next_state, NextStateName,
  44. %% NextState, Timeout} |
  45. %% {stop, Reason, NewState}
  46. %% Description:There should be one instance of this function for each possible
  47. %% state name. Whenever a gen_fsm receives an event sent using
  48. %% gen_fsm:send_event/2, the instance of this function with the same name as
  49. %% the current state name StateName is called to handle the event. It is also
  50. %% called if a timeout occurs.
  51. %%--------------------------------------------------------------------
  52. forwarding({in, N, Val},
  53. State = #state{arity=Arity, map=Map,
  54. vals=Vals, receiver=Receiver})
  55. when N > 0, N =< Arity ->
  56. Vals2 = ?DICT:store(N, Val, Vals),
  57. State2 = State#state{vals=Vals2},
  58. lists:foreach(fun(M) -> ebb_event:out(Receiver, M, Val) end,
  59. ?DICT:fetch(N, Map)),
  60. case is_done(State2) of
  61. true -> {stop, normal, State2};
  62. false -> {next_state, forwarding, State2}
  63. end.
  64. %%--------------------------------------------------------------------
  65. %% Function:
  66. %% state_name(Event, From, State) -> {next_state, NextStateName, NextState} |
  67. %% {next_state, NextStateName,
  68. %% NextState, Timeout} |
  69. %% {reply, Reply, NextStateName, NextState}|
  70. %% {reply, Reply, NextStateName,
  71. %% NextState, Timeout} |
  72. %% {stop, Reason, NewState}|
  73. %% {stop, Reason, Reply, NewState}
  74. %% Description: There should be one instance of this function for each
  75. %% possible state name. Whenever a gen_fsm receives an event sent using
  76. %% gen_fsm:sync_send_event/2,3, the instance of this function with the same
  77. %% name as the current state name StateName is called to handle the event.
  78. %%--------------------------------------------------------------------
  79. %%--------------------------------------------------------------------
  80. %% Function:
  81. %% handle_event(Event, StateName, State) -> {next_state, NextStateName,
  82. %% NextState} |
  83. %% {next_state, NextStateName,
  84. %% NextState, Timeout} |
  85. %% {stop, Reason, NewState}
  86. %% Description: Whenever a gen_fsm receives an event sent using
  87. %% gen_fsm:send_all_state_event/2, this function is called to handle
  88. %% the event.
  89. %%--------------------------------------------------------------------
  90. handle_event(_Event, StateName, State) ->
  91. {next_state, StateName, State}.
  92. %%--------------------------------------------------------------------
  93. %% Function:
  94. %% handle_sync_event(Event, From, StateName,
  95. %% State) -> {next_state, NextStateName, NextState} |
  96. %% {next_state, NextStateName, NextState,
  97. %% Timeout} |
  98. %% {reply, Reply, NextStateName, NextState}|
  99. %% {reply, Reply, NextStateName, NextState,
  100. %% Timeout} |
  101. %% {stop, Reason, NewState} |
  102. %% {stop, Reason, Reply, NewState}
  103. %% Description: Whenever a gen_fsm receives an event sent using
  104. %% gen_fsm:sync_send_all_state_event/2,3, this function is called to handle
  105. %% the event.
  106. %%--------------------------------------------------------------------
  107. handle_sync_event(_Event, _From, StateName, State) ->
  108. Reply = ok,
  109. {reply, Reply, StateName, State}.
  110. %%--------------------------------------------------------------------
  111. %% Function:
  112. %% handle_info(Info,StateName,State)-> {next_state, NextStateName, NextState}|
  113. %% {next_state, NextStateName, NextState,
  114. %% Timeout} |
  115. %% {stop, Reason, NewState}
  116. %% Description: This function is called by a gen_fsm when it receives any
  117. %% other message than a synchronous or asynchronous event
  118. %% (or a system message).
  119. %%--------------------------------------------------------------------
  120. handle_info(_Info, StateName, State) ->
  121. {next_state, StateName, State}.
  122. %%--------------------------------------------------------------------
  123. %% Function: terminate(Reason, StateName, State) -> void()
  124. %% Description:This function is called by a gen_fsm when it is about
  125. %% to terminate. It should be the opposite of Module:init/1 and do any
  126. %% necessary cleaning up. When it returns, the gen_fsm terminates with
  127. %% Reason. The return value is ignored.
  128. %%--------------------------------------------------------------------
  129. terminate(_Reason, _StateName, _State) ->
  130. ok.
  131. %%--------------------------------------------------------------------
  132. %% Function:
  133. %% code_change(OldVsn, StateName, State, Extra) -> {ok, StateName, NewState}
  134. %% Description: Convert process state when code is changed
  135. %%--------------------------------------------------------------------
  136. code_change(_OldVsn, StateName, State, _Extra) ->
  137. {ok, StateName, State}.
  138. %%--------------------------------------------------------------------
  139. %%% Internal functions
  140. %%--------------------------------------------------------------------
  141. build_map(#route{map=RawMap, out=Out}) ->
  142. lists:foldl(fun({N, M}, Map) -> ?DICT:append(N, M, Map) end,
  143. ?DICT:new(),
  144. lists:zip(RawMap, lists:seq(1, Out))).
  145. is_done(#state{arity=Arity, vals=Vals}) ->
  146. ?DICT:size(Vals) == Arity.