Skip to content

Instantly share code, notes, and snippets.

@fishcakez
Created October 8, 2015 17:25
Show Gist options
  • Save fishcakez/e849551702a33d1c5d2d to your computer and use it in GitHub Desktop.
Save fishcakez/e849551702a33d1c5d2d to your computer and use it in GitHub Desktop.
Simple example of the GenEvent watcher pattern to ensure handlers are re-added on crash
defmodule EventWatcher do
use Application
def start(_type, _args) do
import Supervisor.Spec, warn: false
children = [
worker(GenEvent, [[name: EventWatcher.GenEvent]], [id: :event_manager]),
supervisor(EventWatcher.Watcher.Supervisor, [], [id: :watcher_supervisor])
]
# `:rest_for_one` will restart watchers if event manager exits
opts = [strategy: :rest_for_one, name: EventWatcher.Supervisor]
Supervisor.start_link(children, opts)
end
end
defmodule EventWatcher.Watcher.Supervisor do
use Supervisor
def start_link() do
Supervisor.start_link(__MODULE__, nil, [name: __MODULE__])
end
def init(_) do
default_handlers = [{EventWatcher.IO, :stdio}]
handlers = Application.get_env(:event_watcher, :handlers, default_handlers)
children = for {handler, args} <- handlers do
# Watcher has `:id` matching handler so two watchers don't try to add the
# same handler. `:restart` is `:transient` so a handler can remove itself
# gracefully (see `:normal` stop below), other restarts can be valid too.
worker(EventWatcher.Watcher, [handler, args], [id: handler, restart: :transient])
end
supervise(children, [strategy: :one_for_one])
end
end
defmodule EventWatcher.Watcher do
use GenServer
def start_link(handler, args) do
GenServer.start_link(__MODULE__, {handler, args})
end
def init({handler, args}) do
case GenEvent.add_mon_handler(EventWatcher.GenEvent, handler, args) do
:ok -> {:ok, handler}
{:error, :ignore} -> :ignore # special case to ignore handler
{:error, reason} -> exit({:failed_to_add_handler, handler, reason})
end
end
def handle_info({:gen_event_EXIT, handler, reason}, handler) do
{:stop, reason, handler}
end
end
defmodule EventWatcher.IO do
use GenEvent
def handle_event(event, device) do
IO.inspect(event)
{:ok, device}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment