defmodule Pi.Eval.Supervisor do
@moduledoc "Dynamic supervisor for stateful eval session evaluators."
use DynamicSupervisor
@spec start_link(keyword()) :: GenServer.on_start()
def start_link(opts \\ []) do
DynamicSupervisor.start_link(__MODULE__, opts, name: __MODULE__)
end
@spec install() :: :ok | {:ok, pid()} | {:error, term()}
def install do
install_registry()
case Process.whereis(__MODULE__) do
nil ->
case start_link([]) do
{:ok, pid} ->
Process.unlink(pid)
{:ok, pid}
other ->
other
end
_pid ->
:ok
end
end
@spec evaluator(String.t(), keyword()) :: {:ok, pid()} | {:error, term()}
def evaluator(session_id, opts \\ []) when is_binary(session_id) do
install()
case Registry.lookup(Pi.Eval.Registry, session_id) do
[{pid, _value}] when is_pid(pid) ->
if Process.alive?(pid), do: {:ok, pid}, else: start_evaluator(session_id, opts)
[] ->
start_evaluator(session_id, opts)
end
end
defp start_evaluator(session_id, opts) do
child_spec = {Pi.Eval.Evaluator, Keyword.put(opts, :session_id, session_id)}
DynamicSupervisor.start_child(__MODULE__, child_spec)
end
@impl true
def init(_opts), do: DynamicSupervisor.init(strategy: :one_for_one)
defp install_registry do
case Process.whereis(Pi.Eval.Registry) do
nil ->
case Registry.start_link(keys: :unique, name: Pi.Eval.Registry) do
{:ok, pid} -> Process.unlink(pid)
{:error, {:already_started, _pid}} -> :ok
{:error, _reason} -> :ok
end
_pid ->
:ok
end
end
end