Nona
Nona

Reputation: 5462

How to pass in extra arguments when starting a child worker of a Supervisor in OTP?

Suppose I have the following setup:

defmodule NestedSupervisorTree do
  # this will be the top line supervisor
  use Supervisor

  def start_link, do: Supervisor.start_link(__MODULE__, :ok, name: __MODULE__)

  def init(:ok) do
    children = [
      supervisor(BranchSupervisor, [], restart: :temporary)
      #worker(TreeWorker, [], restart: :temporary)
    ]

    supervise(children, strategy: :simple_one_for_one)

  end

  def start_branch(args) do
    {_, branch_id} = Supervisor.start_child(__MODULE__, [args])
  end
end

defmodule BranchSupervisor do
  # this will be the top line supervisor
  use Supervisor

  def start_link(args), do: Supervisor.start_link(__MODULE__, [args], name: __MODULE__)

  def init(args) do
    IO.puts "branch init args:"
    IO.inspect args
    children = [
      worker(TreeWorker, [args], restart: :temporary)
    ]

    supervise(children, strategy: :simple_one_for_one)
  end

  def start_worker do
    {_, wid} = Supervisor.start_child(__MODULE__, [3])
  end
end

defmodule TreeWorker do
  def start_link(args) do
    IO.puts "worker args:"
    IO.inspect args
    #IO.puts String.codepoints raw
    {:ok, spawn(fn -> loop end)}
  end

  def loop do
    receive do
      :stop -> :ok

      msg ->
        IO.inspect msg
        loop
    end
  end
end

Suppose I issue the following commands in the iex terminal in the following order:

 iex> {_, pid} = NestedSupervisorTree.start_link
 iex> {_, cid} = NestedSupervisorTree.start_branch(2)
 iex> BranchSupervisor.start_worker
 # returns:
{:error,
 {:EXIT,
  {:undef,
   [{TreeWorker, :start_link, [[2], 3], []},
    {:supervisor, :do_start_child_i, 3, [file: 'supervisor.erl', line: 359]},
    {:supervisor, :handle_call, 3, [file: 'supervisor.erl', line: 384]},
    {:gen_server, :try_handle_call, 4, [file: 'gen_server.erl', line: 629]},
    {:gen_server, :handle_msg, 5, [file: 'gen_server.erl', line: 661]},
    {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 240]}]}}}

How do I inject "extra arguments" in the call to BranchSupervisor.start_worker? If I remove the "3" in {_, wid} = Supervisor.start_child(__MODULE__, [3]) everyting works. Is this even the right thing to do?

This is mostly a learning exercise.

Upvotes: 3

Views: 3192

Answers (1)

Dogbert
Dogbert

Reputation: 222118

The [3] here is being appended to the args passed to worker in BranchSupervisor.init/1, which is [[2]], making the final arguments [[2]] ++ [3] => [[2], 3]. Since the list is two element long, the Supervisor is then calling TreeWorker.start_link/2 like TreeWorker.start_link([2], 3), so if you want to accept two arguments like this, you just need to change start_link to accept two arguments:

defmodule TreeWorker do
  def start_link(arg1, arg2) do
    # `arg1 == [2]` and `arg2 == 3` here
    ...
  end
end

This behavior is documented in Supervisor.start_child/2:

In the case of :simple_one_for_one, the child specification defined in the supervisor is used and instead of a child_spec, an arbitrary list of terms is expected. The child process will then be started by appending the given list to the existing function arguments in the child specification.

Upvotes: 3

Related Questions