ebb_par_fsm.erl 8.0 KB

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