Helllo,
I have created a process registry based on Genserver with each process mapped to a name is itself a genserver which holds a function to run as part of it’s state.
In the registry I am using trap_exit to allow the registry to create a new process with the same function to run if a process dies.
The basic problem being soved is I need to be able to look up a dynamically started process by a string. That process needs to be restarted when it dies with the same args as it was given when first created and still be able to be looked up with the same string as it’s predecessor.
My question is - is this the best way to do this?
Here is the code using the current strategy -
defmodule Comet.HandlerRegistry do
use GenServer, restart: :permanent
def start_link(_) do
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
end
def lookup(server, topic) do
GenServer.call(server, {:lookup, topic})
end
def add_handler(server, topic, func) do
GenServer.cast(server, {:create, topic, func})
end
def create_handler(topic, func, funs, handlers, refs) do
if Map.has_key?(handlers, topic) do
{funs, handlers, refs}
else
{:ok, handler} = Comet.Handler.start_link(func)
ref = Process.monitor(handler)
refs = Map.put(refs, ref, topic)
handlers = Map.put(handlers, topic, handler)
funs = Map.put(funs, topic, func)
{funs, handlers, refs}
end
end
@impl true
def init(:ok) do
Process.flag(:trap_exit, true)
funs = %{}
handlers = %{}
refs = %{}
{:ok, {funs, handlers, refs}}
end
@impl true
def handle_call({:lookup, topic}, _from, state) do
{_funs, handlers, _refs} = state
{:reply, Map.fetch(handlers, topic), state}
end
@impl true
def handle_cast({:create, topic, func}, {funs, handlers, refs}) do
{funs, handlers, refs} = create_handler(topic, func, funs, handlers, refs)
{:noreply, {funs, handlers, refs}}
end
@impl true
def handle_info({:DOWN, ref, :process, _pid, _reason}, {funs, handlers, refs}) do
{topic, refs} = Map.pop(refs, ref)
handlers = Map.delete(handlers, topic)
{:noreply, create_handler(topic, Map.get(funs, topic), funs, handlers, refs)}
end
@impl true
def handle_info(_msg, state) do
{:noreply, state}
end
end
Thanks, Sean