I’ve been reading through the code for Elixir supervisors for my own curiosity; when start_child is called in the code it then passes through to the function ‘call’ and that in turn does a GenServer.call with whatever arguments I have passed; my questions now being; the module passed as ‘supervisor’ doesn’t implement a handle call and the macro invoked when you ‘use Supervisor’ doesn’t inject anything for this request, so what handles this request for start_child or any of the calls in the module; the only occurrence of start_child are those three lines around the one I just linked to so I’m a little bit dumbfounded by this.
Recall that Elixir’s code builds on top of OTP’s supervisor. So I suspect that the handle_call
s that you are looking for are here.
6 Likes
That clears everything up and things make sense; thank you.
As a small addendum, you could also use tracing to discover the module of which :handle_call
is invoked. Here’s a small script which proves that :supervisor
is indeed that module:
# start a supervisor
{:ok, supervisor} = Supervisor.start_link([], strategy: :one_for_one)
# trace function calls in supervisor
:erlang.trace(supervisor, true, [:call])
:erlang.trace_pattern({:_, :_, :_}, [])
# start a child
Supervisor.start_child(supervisor, %{
id: :child,
start: {Agent, :start_link, [fn -> nil end]}
})
# Pick up all traces, and print the first `:handle_call`
Stream.repeatedly(fn ->
receive do
x -> x
end
end)
|> Stream.filter(&match?({:trace, _pid, :call, {_mod, :handle_call, _args}}, &1))
|> Enum.take(1)
|> hd()
|> IO.inspect()
# Output:
#
# {:trace, #PID<0.76.0>, :call,
# {:supervisor, :handle_call,
# [
# {:start_child,
# %{
# id: :child,
# start: {Agent, :start_link, [#Function<0.1373404 in file:test.exs>]}
# }},
# {#PID<0.73.0>, #Reference<0.4263660754.4251713537.141908>},
# {:state, {#PID<0.76.0>, Supervisor.Default}, :one_for_one, [], :undefined,
# 3, 5, [], 0, Supervisor.Default,
# {:ok, {%{intensity: 3, period: 5, strategy: :one_for_one}, []}}}
# ]}}
6 Likes