ebb_func_fsm.erl 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. -module(ebb_func_fsm).
  2. -behaviour(gen_fsm).
  3. -include("../include/ebb_prim.hrl").
  4. %% API
  5. -export([start_link/2, 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. %% state callbacks
  10. -export([waiting/2, running/2]).
  11. -define(DICT, orddict).
  12. -record(state, {func, args, call, 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(Func = #func{}, Receiver) ->
  23. start_link(Func, local, Receiver).
  24. start_link(Func = #func{}, Mode, Receiver)
  25. when is_pid(Receiver), Mode == local orelse Mode == distributed ->
  26. gen_fsm:start_link(?MODULE, {Func, Mode, Receiver}, []).
  27. %%====================================================================
  28. %% gen_fsm callbacks
  29. %%====================================================================
  30. %%--------------------------------------------------------------------
  31. %% Function: init(Args) -> {ok, StateName, State} |
  32. %% {ok, StateName, State, Timeout} |
  33. %% ignore |
  34. %% {stop, StopReason}
  35. %% Description:Whenever a gen_fsm is started using gen_fsm:start/[3,4] or
  36. %% gen_fsm:start_link/3,4, this function is called by the new process to
  37. %% initialize.
  38. %%--------------------------------------------------------------------
  39. init({Func, Mode, Receiver}) ->
  40. Call = fun(Code, Args, Return) ->
  41. ebb_work_supv:start_work(Mode, Code, Args, Return)
  42. end,
  43. State = #state{func=Func, args=?DICT:new(), call=Call,
  44. receiver=Receiver},
  45. case Func of
  46. #func{in=0,code=Code} ->
  47. Call(Code, [], self()),
  48. {ok, running, State};
  49. _ -> {ok, waiting, State}
  50. end.
  51. %%--------------------------------------------------------------------
  52. %% Function:
  53. %% state_name(Event, State) -> {next_state, NextStateName, NextState}|
  54. %% {next_state, NextStateName,
  55. %% NextState, Timeout} |
  56. %% {stop, Reason, NewState}
  57. %% Description:There should be one instance of this function for each possible
  58. %% state name. Whenever a gen_fsm receives an event sent using
  59. %% gen_fsm:send_event/2, the instance of this function with the same name as
  60. %% the current state name StateName is called to handle the event. It is also
  61. %% called if a timeout occurs.
  62. %%--------------------------------------------------------------------
  63. waiting({in, N, Arg},
  64. State = #state{func=Func = #func{in=In}, args=Args, call=Call})
  65. when N > 0, N =< In ->
  66. Args2 = ?DICT:store(N, Arg, Args),
  67. State2 = State#state{args=Args2},
  68. case is_ready(State2) of
  69. true -> VArgs =
  70. [ V || {_, V} <- lists:keysort(1, ?DICT:to_list(Args2)) ],
  71. Call(Func#func.code, VArgs, self()),
  72. {next_state, running, State2};
  73. false -> {next_state, waiting, State2}
  74. end.
  75. running({return, Value}, State = #state{receiver=Receiver}) ->
  76. ebb_event:out(Receiver, 1, Value),
  77. {stop, normal, State}.
  78. %%--------------------------------------------------------------------
  79. %% Function:
  80. %% state_name(Event, From, State) -> {next_state, NextStateName, NextState} |
  81. %% {next_state, NextStateName,
  82. %% NextState, Timeout} |
  83. %% {reply, Reply, NextStateName, NextState}|
  84. %% {reply, Reply, NextStateName,
  85. %% NextState, Timeout} |
  86. %% {stop, Reason, NewState}|
  87. %% {stop, Reason, Reply, NewState}
  88. %% Description: There should be one instance of this function for each
  89. %% possible state name. Whenever a gen_fsm receives an event sent using
  90. %% gen_fsm:sync_send_event/2,3, the instance of this function with the same
  91. %% name as the current state name StateName is called to handle the event.
  92. %%--------------------------------------------------------------------
  93. %%--------------------------------------------------------------------
  94. %% Function:
  95. %% handle_event(Event, StateName, State) -> {next_state, NextStateName,
  96. %% NextState} |
  97. %% {next_state, NextStateName,
  98. %% NextState, Timeout} |
  99. %% {stop, Reason, NewState}
  100. %% Description: Whenever a gen_fsm receives an event sent using
  101. %% gen_fsm:send_all_state_event/2, this function is called to handle
  102. %% the event.
  103. %%--------------------------------------------------------------------
  104. handle_event(_Event, StateName, State) ->
  105. {next_state, StateName, State}.
  106. %%--------------------------------------------------------------------
  107. %% Function:
  108. %% handle_sync_event(Event, From, StateName,
  109. %% State) -> {next_state, NextStateName, NextState} |
  110. %% {next_state, NextStateName, NextState,
  111. %% Timeout} |
  112. %% {reply, Reply, NextStateName, NextState}|
  113. %% {reply, Reply, NextStateName, NextState,
  114. %% Timeout} |
  115. %% {stop, Reason, NewState} |
  116. %% {stop, Reason, Reply, NewState}
  117. %% Description: Whenever a gen_fsm receives an event sent using
  118. %% gen_fsm:sync_send_all_state_event/2,3, this function is called to handle
  119. %% the event.
  120. %%--------------------------------------------------------------------
  121. handle_sync_event(_Event, _From, StateName, State) ->
  122. Reply = ok,
  123. {reply, Reply, StateName, State}.
  124. %%--------------------------------------------------------------------
  125. %% Function:
  126. %% handle_info(Info,StateName,State)-> {next_state, NextStateName, NextState}|
  127. %% {next_state, NextStateName, NextState,
  128. %% Timeout} |
  129. %% {stop, Reason, NewState}
  130. %% Description: This function is called by a gen_fsm when it receives any
  131. %% other message than a synchronous or asynchronous event
  132. %% (or a system message).
  133. %%--------------------------------------------------------------------
  134. handle_info(_Info, StateName, State) ->
  135. {next_state, StateName, State}.
  136. %%--------------------------------------------------------------------
  137. %% Function: terminate(Reason, StateName, State) -> void()
  138. %% Description:This function is called by a gen_fsm when it is about
  139. %% to terminate. It should be the opposite of Module:init/1 and do any
  140. %% necessary cleaning up. When it returns, the gen_fsm terminates with
  141. %% Reason. The return value is ignored.
  142. %%--------------------------------------------------------------------
  143. terminate(_Reason, _StateName, _State) ->
  144. ok.
  145. %%--------------------------------------------------------------------
  146. %% Function:
  147. %% code_change(OldVsn, StateName, State, Extra) -> {ok, StateName, NewState}
  148. %% Description: Convert process state when code is changed
  149. %%--------------------------------------------------------------------
  150. code_change(_OldVsn, StateName, State, _Extra) ->
  151. {ok, StateName, State}.
  152. %%--------------------------------------------------------------------
  153. %%% Internal functions
  154. %%--------------------------------------------------------------------
  155. is_ready(#state{func=#func{in=In}, args=Args}) ->
  156. ?DICT:size(Args) == In.