Skip to content

Instantly share code, notes, and snippets.

@marcelog
Created May 11, 2013 15:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marcelog/5560239 to your computer and use it in GitHub Desktop.
Save marcelog/5560239 to your computer and use it in GitHub Desktop.
gen_server that supervise and restarts event handlers (that implement the gen_event behavior) added with gen_event:add_sup_handler/3, suitable to be used in a standard otp supervision tree
-module(event_handler_guard).
-author('marcelog@gmail.com').
-behavior(gen_server).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Types.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-record(state, {}).
-type state():: #state{}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Exports.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Public API.
-export([start_link/1]).
%%% gen_server callbacks.
-export([
init/1, terminate/2, code_change/3,
handle_call/3, handle_cast/2, handle_info/2
]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Public API.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% @doc Starts the gen_server. Accepts a list of module names, which are the
%% event handlers needed to start and monitor.
-spec start_link([module()]) -> {ok, pid()} | ignore | {error, term()}.
start_link(EventHandlers) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, EventHandlers, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% gen_server API.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec init([module()]) -> {ok, state()}.
init(EventHandlers) ->
[start(E) || E <- EventHandlers],
{ok, #state{}, hibernate}.
-spec handle_cast(any(), state()) -> {noreply, state()}.
handle_cast(Msg, State) ->
lager:error("Invalid cast: ~p", [Msg]),
{noreply, State, hibernate}.
%% @doc Expects a gen_event_EXIT message and re-starts the event handler.
-spec handle_info(any(), state()) -> {noreply, state()}.
handle_info({gen_event_EXIT, Handler, Reason}, State) ->
lager:error("Event Handler ~p died with: ~p", [Handler,Reason]),
start(Handler),
{noreply, State, hibernate};
handle_info(Msg, State) ->
lager:error("Invalid msg: ~p", [Msg]),
{noreply, State, hibernate}.
-spec handle_call(
term(), {pid(), reference()}, state()
) -> {reply, term() | {invalid_request, term()}, state()}.
handle_call(Req, _From, State) ->
lager:error("Invalid request: ~p", [Req]),
{reply, invalid_request, State, hibernate}.
-spec terminate(atom(), state()) -> ok.
terminate(_Reason, _State) ->
ok.
-spec code_change(string(), state(), any()) -> {ok, state()}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Private API.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% @doc Assuming that all the event handlers have a register/0 function, that
%% actually does the gen_event:add_sup_handler/3 call, just for convenience.
-spec start(module()) -> ok | {'EXIT', term()} | term().
start(EventHandler) ->
EventHandler:register().
@tonywallace64
Copy link

Lovely work. I am surprised I can't do this out of the box, but I can't see another way to do this given that supervision tree starts processes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment