Created
October 8, 2015 17:25
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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