123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- %% @author Seth Falcon <seth@userprimary.net>
- %% @copyright 2012-2013 Seth Falcon
- %% @doc Helper gen_server to start pool members
- %%
- -module(pooler_starter).
- -behaviour(gen_server).
- -include("pooler.hrl").
- -include_lib("eunit/include/eunit.hrl").
- %% ------------------------------------------------------------------
- %% API Function Exports
- %% ------------------------------------------------------------------
- -export([start_link/2,
- start_member/1,
- start_member/2,
- stop_member_async/1,
- stop/1]).
- %% ------------------------------------------------------------------
- %% gen_server Function Exports
- %% ------------------------------------------------------------------
- -export([init/1,
- handle_call/3,
- handle_cast/2,
- handle_info/2,
- terminate/2,
- code_change/3]).
- %% To help with testing internal functions
- -ifdef(TEST).
- -compile([export_all]).
- -endif.
- %% ------------------------------------------------------------------
- %% API Function Definitions
- %% ------------------------------------------------------------------
- start_link(Pool, Parent) ->
- gen_server:start_link(?MODULE, {Pool, Parent}, []).
- stop(Starter) ->
- gen_server:cast(Starter, stop).
- %% @doc Start a member for the specified `Pool'.
- %%
- %% Member creation with this call is async. This function returns
- %% immediately with create process' pid. When the member has been
- %% created it is sent to the specified pool via
- %% {@link pooler:accept_member/2}.
- %%
- %% Each call starts a single use `pooler_starter' instance via
- %% `pooler_starter_sup'. The instance terminates normally after
- %% creating a single member.
- -spec start_member(#pool{}) -> pid().
- start_member(Pool) ->
- {ok, Pid} = pooler_starter_sup:new_starter(Pool, pool),
- Pid.
- %% @doc Same as {@link start_member/1} except that instead of calling
- %% {@link pooler:accept_member/2} a raw message is sent to `Parent' of
- %% the form `{accept_member, {Ref, Member}'. Where `Member' will
- %% either be the member pid or an error term and `Ref' will be the
- %% Pid of the starter.
- %%
- %% This is used by the init function in the `pooler' to start the
- %% initial set of pool members in parallel.
- start_member(Pool, Parent) ->
- {ok, Pid} = pooler_starter_sup:new_starter(Pool, Parent),
- Pid.
- %% @doc Stop a member in the pool
- %% Member creation can take too long. In this case, the starter
- %% needs to be informed that even if creation succeeds, the
- %% started child should be not be sent back and should be
- %% cleaned up
- -spec stop_member_async(pid()) -> ok.
- stop_member_async(Pid) ->
- gen_server:cast(Pid, stop_member).
- %% ------------------------------------------------------------------
- %% gen_server Function Definitions
- %% ------------------------------------------------------------------
- -record(starter, {pool,
- parent,
- msg}).
- -spec init({#pool{}, pid() | atom()}) -> {'ok', #starter{}, 0}.
- init({Pool, Parent}) ->
- %% trigger immediate timeout message, which we'll use to trigger
- %% the member start.
- {ok, #starter{pool = Pool, parent = Parent}, 0}.
- handle_call(_Request, _From, State) ->
- {noreply, State}.
- handle_cast(stop_member, #starter{msg = {_Me, Pid}, pool = #pool{member_sup = MemberSup}} = State) ->
- %% The process we were starting is no longer valid for the pool.
- %% Cleanup the process and stop normally.
- supervisor:terminate_child(MemberSup, Pid),
- {stop, normal, State};
- handle_cast(accept_member, #starter{msg = Msg, parent = Parent, pool = #pool{name = PoolName}} = State) ->
- %% Process creation has succeeded. Send the member to the pooler
- %% gen_server to be accepted. Pooler gen_server will notify
- %% us if the member was accepted or needs to cleaned up.
- send_accept_member(Parent, PoolName, Msg),
- {noreply, State};
- handle_cast(stop, State) ->
- {stop, normal, State};
- handle_cast(_Request, State) ->
- {noreply, State}.
- -spec handle_info(_, _) -> {'noreply', _}.
- handle_info(timeout,
- #starter{pool = Pool} = State) ->
- Msg = do_start_member(Pool),
- accept_member_async(self()),
- {noreply, State#starter{msg = Msg}};
- handle_info(_Info, State) ->
- {noreply, State}.
- -spec terminate(_, _) -> 'ok'.
- terminate(_Reason, _State) ->
- ok.
- -spec code_change(_, _, _) -> {'ok', _}.
- code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
- do_start_member(#pool{member_sup = PoolSup, name = PoolName}) ->
- case supervisor:start_child(PoolSup, []) of
- {ok, Pid} ->
- {self(), Pid};
- Error ->
- error_logger:error_msg("pool '~s' failed to start member: ~p",
- [PoolName, Error]),
- {self(), Error}
- end.
- send_accept_member(pool, PoolName, Msg) ->
- pooler:accept_member(PoolName, Msg);
- send_accept_member(Pid, _PoolName, Msg) ->
- Pid ! {accept_member, Msg},
- ok.
- accept_member_async(Pid) ->
- gen_server:cast(Pid, accept_member).
|