Hey All! I’ve been struggling to implement some fairly basic functionality and was hoping somebody would be kind enough to point me in the right direction. I’ve been reading The Little Elixir and OTP Guidebook and would like to build on the ThySupervisor + ThyWorker example and implement it into a phoenix application. I’m struggling as I don’t know if I need an actual supervisor that starts up with the phoenix app and use ThySupervisor, which implements GenServer, as a server similiar to the Pooly application in the book or if I can somehow initialize the ThySupervisor upon starting the application and then dynamically add and kill process as I wish. Any insight would be greatly appreciated!
I haven’t read the book, but i think you can just add your supervisor to the list of children in lib/<you_app_name>.ex
.
...
def start(_type, _args) do
import Supervisor.Spec
# Define workers and child supervisors to be supervised
children = [
# Start the Ecto repository
supervisor(Test.Repo, []),
# Start the endpoint when the application starts
supervisor(Test.Endpoint, []),
# Start your own worker by calling: Test.Worker.start_link(arg1, arg2, arg3)
# worker(Test.Worker, [arg1, arg2, arg3]),
supervisor(ThySupervisor, [])
]
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: Test.Supervisor]
Supervisor.start_link(children, opts)
end
...
I guess what is throwing me off is that in the book the ThySupervisor uses GenServer and not Supervisor. I’ll post some code when I get home, just thought I’d throw it out there now.
I think it’s fine that it uses a genserver as long as it conforms to the supervisor behaviour.
I guess I don’t fully understand supervisor behavior, in the example we are just passing a child specification list to the start link function similar to the example in the link below.
When I add it to the list of children I get an undefined function error as it is expecting start_link/0.
defmodule ThySupervisor do
use GenServer
def start_link(child_spec_list) do
GenServer.start_link(__MODULE__, [child_spec_list])
end
def start_child(supervisor, child_spec) do
GenServer.call(supervisor, {:start_child, child_spec})
end
def terminate_child(supervisor, pid) when is_pid(pid) do
GenServer.call(supervisor, {:terminate_child, pid})
end
def count_children(supervisor) do
GenServer.call(supervisor, :count_children)
end
#Callbacks
def init([child_spec_list]) do
Process.flag(:trap_exit, true)
state = child_spec_list
|> start_children
|> Enum.into(Map.new)
{:ok, state}
end
def handle_call({:start_child, child_spec}, _from, state) do
case start_child(child_spec) do
{:ok, pid} ->
new_state = state |> Map.put(pid, child_spec)
{:reply, {:ok, pid}, new_state}
:error ->
{:reply, {:error, "error starting child"}, state}
end
end
def handle_call({:terminate_child, pid}, _from, state) do
case terminate_child(pid) do
:ok ->
new_state = state |> Map.delete(pid)
{:reply, :ok, new_state}
:error ->
{:reply, {:error, "error terminating child"}, state}
end
end
def handle_call(:count_children, _from, state) do
{:reply, Map.size(state), state}
end
def handle_info({:EXIT, from, :killed}, state) do
new_state = state |> Map.delete(from)
{:noreply, new_state}
end
#Private Functions
defp start_child({module, function, args}) do
case apply(module, function, args) do
pid when is_pid(pid) ->
Process.link(pid)
{:ok, pid}
_ ->
:error
end
end
defp start_children([child_spec|rest]) do
case start_child(child_spec) do
{:ok, pid} ->
[{pid, child_spec} | start_children(rest)]
:error ->
:error
end
end
defp start_children([]), do: []
defp terminate_child(pid) do
Process.exit(pid, :kill)
:ok
end
end
Maybe try passing supervisor(ThySupervisor, [[]])
to the list of children? Then an empty list would be the child_spec_list
for start_link(child_spec_list)
.