-module(wpool_process_callbacks).
-behaviour(gen_event).
%% The callbacks are called in an extremely dynamic from call/3.
-hank([unused_callbacks]).
-export([init/1, handle_event/2, handle_call/2]).
-export([notify/3, add_callback_module/2, remove_callback_module/2]).
-type state() :: module().
-export_type([state/0]).
-type event() :: handle_init_start | handle_worker_creation | handle_worker_death.
-export_type([event/0]).
-callback handle_init_start(wpool:name()) -> any().
-callback handle_worker_creation(wpool:name()) -> any().
-callback handle_worker_death(wpool:name(), term()) -> any().
-optional_callbacks([handle_init_start/1, handle_worker_creation/1,
handle_worker_death/2]).
%% @private
-spec init(module()) -> {ok, state()}.
init(Module) ->
{ok, Module}.
%% @private
-spec handle_event({event(), [any()]}, state()) -> {ok, state()}.
handle_event({Event, Args}, Module) ->
call(Module, Event, Args),
{ok, Module}.
%% @private
-spec handle_call(Msg, state()) -> {ok, {error, {unexpected_call, Msg}}, state()}.
handle_call(Msg, State) ->
{ok, {error, {unexpected_call, Msg}}, State}.
%% @doc Sends a notification to all registered callback modules.
-spec notify(event(), #{event_manager := any(), _ => _}, [any()]) -> ok.
notify(Event, #{event_manager := EventMgr}, Args) ->
gen_event:notify(EventMgr, {Event, Args});
notify(_, _, _) ->
ok.
%% @doc Adds a callback module.
-spec add_callback_module(wpool:name(), module()) -> ok | {error, any()}.
add_callback_module(EventManager, Module) ->
case ensure_loaded(Module) of
ok ->
gen_event:add_handler(EventManager, {wpool_process_callbacks, Module}, Module);
Other ->
Other
end.
%% @doc Removes a callback module.
-spec remove_callback_module(wpool:name(), module()) -> ok | {error, any()}.
remove_callback_module(EventManager, Module) ->
gen_event:delete_handler(EventManager, {wpool_process_callbacks, Module}, Module).
call(Module, Event, Args) ->
try
case erlang:function_exported(Module, Event, length(Args)) of
true ->
erlang:apply(Module, Event, Args);
_ ->
ok
end
catch
E:R ->
error_logger:warning_msg("Could not call callback module, error:~p, reason:~p", [E, R])
end.
ensure_loaded(Module) ->
case code:ensure_loaded(Module) of
{module, Module} ->
ok;
{error, embedded} -> %% We are in embedded mode so the module was loaded if exists
ok;
Other ->
Other
end.