I'm learning Erlang, and am managing to find answers and work out solutions to most issues relatively efficiently, but have spent an unacceptable amount of time working out this one:
What is the correct way to add handlers to a gen_event module that is being being started by a supervisor process? In my exploration so far, I have been able to set up processes, gen_servers, supervisors (that start gen_servers), and gen_event modules, but everything I've attempted to actually add a handler to gen_event is crashing my supervisor process — and sometimes even my shell!!
The supervisor module:
-module(sup).
-behaviour(supervisor).
%% API
-export([start_link/0, init/1]).
-export([stop/0]).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init(_) ->
EventServerSpec = #{
id => osc_event_server,
start => {gen_event, start_link, [{local, osc_server}]},
modules => dynamic
},
ChildSpecList = [EventServerSpec, child(frequency), child(bkpt_server)],
SupFlags = #{strategy => rest_for_one,
intensity => 2, period => 3600},
{ok, {SupFlags, ChildSpecList}}.
child(Module) ->
#{id => Module,
start => {Module, start_link, []},
restart => permanent,
shutdown => 2000,
type => worker,
modules => [Module]}.
… and some bits and pieces from the gen_event module, osc_event_server (hopefully the relevant ones!)
...
init([]) ->
{ok, Socket} = gen_udp:open(8980, [binary, {active, false}]),
{ok, #{socket => Socket, target_port => get_target_port()}}.
...
handle_event({send_osc, Path, Data}, State) ->
TargetPort = maps:get(target_port, State),
Socket = maps:get(socket, State),
sendMessage(Socket, TargetPort, Path, Data),
{ok, State};
...
As poorly as I understand the gen_event behaviour, I'm not much surprised by the result of running the code that includes these snippets:
init
block of 'osc_event_server'The two things that I'd like to incorporate:
Thanks very much. Feel free to be critical of anything you see… e.g., my whole approach :) As much Erlang as I've come to understand at this point, it's nothing, I'm sure, compared to the amount of Erlang I misunderstand.
You should consider the gen_event
process as a kind of service that exists on your node. Usually you'd give it a name, and use that name to talk to it. Until you do, it is a little standalone piece of code.
That means that if you want to add handlers, given that you can't run arbitrary code within the supervisor to do it, the simplest way to do it is to have your supervisor spawn a sibling process after the gen_event
process is ready.
Mark the new sibling as transient
or temporary
, have it be a little gen_server
or supervisor_bridge
worker, and give it the name and handlers of the gen_event
. The little worker should add the handlers. It can then shut down with reason normal
, or if needs be, supervise the handlers in case they crash to re-add them.