DynamicSupervisor.start_child not working when passing child spec inside it

Supervisor code :

defmodule My.Supervisor do
  use Supervisor

  def start_link(opts) do
      Supervisor.start_link(__MODULE__, :ok, opts)
  end

  def init(:ok) do
    children = [
      {DynamicSupervisor, name: GamesSupervisor, strategy: :one_for_one}
    ]

    Supervisor.init(children, strategy: :one_for_one)
  end
end

DySup Module :

defmodule DySup do
  def start_child(game_id) do
    DynamicSupervisor.start_child(GamesSupervisor, %{
      id: game_id,
      start: {__MODULE__, :start_link, [:ok]},
      type: :worker,
      restart: :transient,
      shutdown: 500
    })
  end

  def start_link(args) do
    {:ok, args}
  end
end

inside terminal (iex -S mix)
when i do :
DySup.start_child(1)
why i am getting error :
{:error, {:ok, :ok}}

Please help me know, why this error is occurring?

Isn’t this supposed to return an {:ok, child_pid} though (or {:ok, child_pid, more_stuff}?

Since it’s not getting a pid, it’s returning an error, the error message being {:ok, :ok} because the {:ok, args} when args is also :ok.

So, can you help me to know how it should be getting a pid ?

I know why it’s returning {:ok, :ok}

I tried changing start_link to :

 def start_link(_args) do
    {:ok, Process.spawn(fn -> 1 + 2 end, [:link])}
  end

so now when i do (in terminal) :
DySup.start_child(1)

i get :
{:ok, #PID<0.245.0>}

But then when i retrive dynamic superivor childrens by :
DynamicSupervisor.which_children(GamesSupervisor)

I am getting no children :
[]

thanks

Because

Process.spawn(fn -> 1 + 2 end

is already finished - after the function returns it’s value the process dies.

https://elixir-lang.org/getting-started/processes.html#spawn

$ iex
Erlang/OTP 22 [erts-10.4.4] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]

Interactive Elixir (1.9.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> loop = fn
...(1)>   (_, left) when left < 1 ->
...(1)>     :ok
...(1)> 
...(1)>   (next, left) ->
...(1)>     receive do
...(1)>       :ping ->
...(1)>         IO.puts("Got ping")
...(1)>         next.(next, left - 1)
...(1)>       _ ->
...(1)>         :error
...(1)>     end
...(1)> end
#Function<13.91303403/2 in :erl_eval.expr/5>
iex(2)> pid = spawn(fn -> loop.(loop, 3) end)
#PID<0.119.0>
iex(3)> Process.alive?(pid)
true
iex(4)> send(pid, :ping)
Got ping
:ping
iex(5)> Process.alive?(pid)
true
iex(6)> send(pid, :ping)
Got ping
:ping
iex(7)> Process.alive?(pid)
true
iex(8)> send(pid, :ping)
Got ping
:ping
iex(9)> Process.alive?(pid)
false
iex(10)> 
1 Like

Thanks for helping me.

Now i change my code to :

DySup Module :

defmodule DySup do

  def start_child() do
    DynamicSupervisor.start_child(GamesSupervisor,Gen)
  end

  def show_childs do
    DynamicSupervisor.which_children(GamesSupervisor)
  end

  def end_child(pid) do
    Process.exit(pid, :kill)
  end
end

Gen Module :

defmodule Gen do
  use GenServer

  def child_spec(opts) do
    %{
      id: Gen,
      start: {__MODULE__, :start_link, [opts]},
      shutdown: 5_000,
      restart: :transient,
      type: :worker
    }
  end

  def start_link(state \\ []) do
      GenServer.start_link(__MODULE__, state)
  end

  def init(state), do: {:ok, state}

  def handle_call(:hi,_from, state) do
    IO.puts("hi received")
    {:reply,state,state}
  end
end

But, when i call :
DySup.end_child(pid("0.240.0"))

a new process start , i tried overriding child spec of Gen module, i even set restart: :temporary, but still a new process start.

I would use a unique id in the spec…

This is how I start a new worker, from inside a dynamic supervisor.

  def start_worker(id, args \\ %{}) do
    spec = %{
      id: id,
      start: {Worker, :start_link, [id, args]},
      restart: :transient,
      type: :worker
    }
    DynamicSupervisor.start_child(__MODULE__, spec)
  end

Also I would not stop workers like this.

but like this (in the worker)

  def stop(world), do: GenServer.cast(world, :stop)

  @impl GenServer
  def handle_cast(:stop, state), do: {:stop, :normal, state}
1 Like

Thanks,

Now i know how to stop a GenServer process by :

And now this code is working fine :

defmodule DySup do
  def start_child() do
    DynamicSupervisor.start_child(GamesSupervisor, Gen)
  end

  def show_childs do
    DynamicSupervisor.which_children(GamesSupervisor)
  end

  def end_child(pid) do
    Process.exit(pid, :kill)
  end

  def stop(pid), do: GenServer.cast(pid, :stop)
end

defmodule Gen do
  use GenServer

  def child_spec(opts) do
    %{
      id: Gen,
      start: {__MODULE__, :start_link, [opts]},
      shutdown: 5_000,
      restart: :transient,
      type: :worker
    }
  end

  def start_link(state \\ []) do
    GenServer.start_link(__MODULE__, state)
  end

  def init(state), do: {:ok, state}

  def handle_call(:hi, _from, state) do
    IO.puts("hi received")
    {:reply, state, state}
  end

  def handle_cast(:stop, state), do: {:stop, :normal, state}
end

But when i add :

I am getting :


iex(1)> DySup.start_worker(1)
** (exit) exited in: GenServer.call(DySup, {:start_child, {{Worker, :start_link, [1, %{}]}, :transient, 5000, :worker, [Worker]}}, :infinity)
    ** (EXIT) no process: the process is not alive or there's no process currently associated with the given name, possibly because its application isn't started
    (elixir) lib/gen_server.ex:999: GenServer.call/3

Also i want to know, is it is good thing to have one child specification with same id to create multiple supervisor’s childrens because as per definition :

id - Required key. Used by the supervisor to identify the child specification.

As all childs will run same code in parallel differentiated using their pids.

It was just an example of mine… In my case, the GenServer module is called Worker. It will not work until You put your own name :slight_smile:

Also, I use Registry with Dynamic Supervisor to be able to get the pid by name.