您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

183 行
6.4 KiB

  1. %% Copyright (c) 2013, Pedram Nimreezi <deadzen@deadzen.com>
  2. %%
  3. %% Permission to use, copy, modify, and/or distribute this software for any
  4. %% purpose with or without fee is hereby granted, provided that the above
  5. %% copyright notice and this permission notice appear in all copies.
  6. %%
  7. %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8. %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9. %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  10. %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11. %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  12. %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  13. %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  14. %% @doc Process table manager for goldrush.
  15. %%
  16. %% Manager responsible for the processes, which serve as heir of the
  17. %% {@link gr_counter:start_link/0. <em>Counter</em>} and
  18. %% {@link gr_param:start_link/0. <em>Param</em>} ets table processes.
  19. %% This process creates the table and initial data then assigns itself
  20. %% to inherit the ets table if any process responsible for it is killed.
  21. %% It then waits to give it back while that process is recreated by its
  22. %% supervisor.
  23. -module(gr_manager).
  24. -behaviour(gen_server).
  25. %% API
  26. -export([start_link/3, wait_for_pid/1]).
  27. %% gen_server callbacks
  28. -export([init/1,
  29. handle_call/3,
  30. handle_cast/2,
  31. handle_info/2,
  32. terminate/2,
  33. code_change/3]).
  34. -define(SERVER, ?MODULE).
  35. -record(state, {table_id :: ets:tab(), managee :: atom()}).
  36. %%%===================================================================
  37. %%% API
  38. %%%===================================================================
  39. %% Setup the initial data for the ets table
  40. -spec setup(atom() | pid(), term()) -> ok.
  41. setup(Name, Data) ->
  42. gen_server:cast(Name, {setup, Data}).
  43. %%--------------------------------------------------------------------
  44. %% @doc
  45. %% Starts the server
  46. %%
  47. %% @spec start_link(Name, Managee, Data) -> {ok, Pid} | ignore |
  48. %% {error, Error}
  49. %% @end
  50. %%--------------------------------------------------------------------
  51. start_link(Name, Managee, Data) ->
  52. gen_server:start_link({local, Name}, ?MODULE, [Managee, Data], []).
  53. %%%===================================================================
  54. %%% gen_server callbacks
  55. %%%===================================================================
  56. %%--------------------------------------------------------------------
  57. %% @private
  58. %% @doc
  59. %% Initializes the server
  60. %%
  61. %% @spec init(Args) -> {ok, State} |
  62. %% {ok, State, Timeout} |
  63. %% ignore |
  64. %% {stop, Reason}
  65. %% @end
  66. %%--------------------------------------------------------------------
  67. init([Managee, Data]) ->
  68. process_flag(trap_exit, true),
  69. setup(self(), Data),
  70. {ok, #state{managee=Managee}}.
  71. %%--------------------------------------------------------------------
  72. %% @private
  73. %% @doc
  74. %% Handling call messages
  75. %%
  76. %% @spec handle_call(Request, From, State) ->
  77. %% {reply, Reply, State} |
  78. %% {reply, Reply, State, Timeout} |
  79. %% {noreply, State} |
  80. %% {noreply, State, Timeout} |
  81. %% {stop, Reason, Reply, State} |
  82. %% {stop, Reason, State}
  83. %% @end
  84. %%--------------------------------------------------------------------
  85. handle_call(_Request, _From, State) ->
  86. Reply = {error, unhandled_message},
  87. {reply, Reply, State}.
  88. %%--------------------------------------------------------------------
  89. %% @private
  90. %% @doc
  91. %% Handling cast messages
  92. %%
  93. %% @spec handle_cast(Msg, State) -> {noreply, State} |
  94. %% {noreply, State, Timeout} |
  95. %% {stop, Reason, State}
  96. %% @end
  97. %%--------------------------------------------------------------------
  98. handle_cast({setup, Data}, State = #state{managee=Managee}) ->
  99. ManageePid = whereis(Managee),
  100. link(ManageePid),
  101. TableId = ets:new(?MODULE, [set, private]),
  102. ets:insert(TableId, Data),
  103. ets:setopts(TableId, {heir, self(), Data}),
  104. ets:give_away(TableId, ManageePid, Data),
  105. {noreply, State#state{table_id=TableId}};
  106. handle_cast(_Msg, State) ->
  107. {noreply, State}.
  108. %%--------------------------------------------------------------------
  109. %% @private
  110. %% @doc
  111. %% Handling all non call/cast messages
  112. %%
  113. %% @spec handle_info(Info, State) -> {noreply, State} |
  114. %% {noreply, State, Timeout} |
  115. %% {stop, Reason, State}
  116. %% @end
  117. %%--------------------------------------------------------------------
  118. handle_info({'EXIT', _Pid, _Reason}, State) ->
  119. {noreply, State};
  120. handle_info({'ETS-TRANSFER', TableId, _Pid, Data}, State = #state{managee=Managee}) ->
  121. ManageePid = wait_for_pid(Managee),
  122. link(ManageePid),
  123. ets:give_away(TableId, ManageePid, Data),
  124. {noreply, State#state{table_id=TableId}}.
  125. %% @doc Wait for a registered process to be associated to a process identifier.
  126. %% @spec wait_for_pid(Managee) -> ManageePid
  127. -spec wait_for_pid(atom()) -> pid().
  128. wait_for_pid(Managee) when is_atom(Managee), Managee =/= undefined ->
  129. case whereis(Managee) of
  130. undefined ->
  131. timer:sleep(1),
  132. wait_for_pid(Managee);
  133. ManageePid -> ManageePid
  134. end.
  135. %%--------------------------------------------------------------------
  136. %% @private
  137. %% @doc
  138. %% This function is called by a gen_server when it is about to
  139. %% terminate. It should be the opposite of Module:init/1 and do any
  140. %% necessary cleaning up. When it returns, the gen_server terminates
  141. %% with Reason. The return value is ignored.
  142. %%
  143. %% @spec terminate(Reason, State) -> void()
  144. %% @end
  145. %%--------------------------------------------------------------------
  146. terminate(_Reason, _State) ->
  147. ok.
  148. %%--------------------------------------------------------------------
  149. %% @private
  150. %% @doc
  151. %% Convert process state when code is changed
  152. %%
  153. %% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
  154. %% @end
  155. %%--------------------------------------------------------------------
  156. code_change(_OldVsn, State, _Extra) ->
  157. {ok, State}.
  158. %%%===================================================================
  159. %%% Internal functions
  160. %%%===================================================================