ROhan Gandhi
ROhan Gandhi

Reputation: 11

How do I send data from a child worker process to parent process?

I have 2 modules, one is the supervisor and the other is its worker. I have calculated something in the 2nd (child) module, how do I send that value to the parent (supervisor) module?

defmodule VampSupervisor do
  use Supervisor

  def start_link(init_arg) do
    Supervisor.start_link(__MODULE__, init_arg)
  end

  def init(init_arg) do
    Process.flag(:trap_exit, true)
    [start,last] = init_arg
    nodes = Enum.chunk_every(start..last, 10)
    children = Enum.map(nodes, fn(chunk_list) ->
      worker(FACT, [chunk_list], [id: List.first(chunk_list), restart: :permanent])
    end)
    supervise(children, strategy: :one_for_one)
  end
end

defmodule FACT do
  use GenServer
  def start_link(init_arg) do
         pid = spawn_link(__MODULE__,:init,[init_arg])
    {:ok,pid}
  end

  def init(init_arg) do
    Enum.each(init_arg, fn(x)->
      spawn(__MODULE__,:main,[x])
    end)
  end

 def main(n) do
    list = make_factor(n,round(:math.sqrt(n)),[])
    {:ok,pid}=GenServer.start_link(VampServer, list)
    Enum.each(list, fn(_)->
      {x,[head,tail]} = GenServer.call(pid,{:check_vampire,n})
      if x==n do
        IO.puts("#{x} #{head} #{tail}") **This has be sent back to supervisor**
      end
    end)
end

Upvotes: 0

Views: 880

Answers (2)

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

There are many issues with your approach.

  1. Supervisor.Spec.supervisor/3 and Supervisor.Spec.worker/3 are deprecated and should be avoided; one should use child specs instead.
  2. Supervisor is not meant to receive user callbacks. The only callback it has is Supervisor.init/1. The processes that are supposed to receive user messages are to be made workers.
  3. You abuse GenServer for doing the simple function call in VampServer.

Instead of this

{:ok, pid} = GenServer.start_link(VampServer, list)
Enum.each(list, fn _ ->
  GenServer.call(pid, {:check_vampire, n})
end)

one should implement a plain old good function Vamp.check_vampire/1 and call it explicitly instead, like

Enum.each(list, fn _ ->
  Vamp.check_vampire(n)
end)

GenServer.call/3 is synchronous and does [almost] exactly what the plain function would do.


The summing up. Start a VampSupervisor as Supervisor with two workers: FACT that will do calculations and FACTConsumer that will receive messages from FACT when needed.

Upvotes: 3

Ashton Wiersdorf
Ashton Wiersdorf

Reputation: 2010

It looks like you're using a supervisor and workers to do some transient work and then report back.

Normally, you start Supervisor and DynamicSupervisor within your application tree, and these supervisors get named so you can refer to them across the node just by their name. These supervisors are meant to be long-lived, and are probably not a good fit for your use case.

A better solution might be to try Task.async/1. (See also the guides on Task.) This will let you fire off a bunch of computations in parallel and have them join back together.

Alternatively, you can define a module to spin up tasks that are supervised under a TaskSupervisor, which get the caller's pid with self() when created. However, I think Task.async/1 might be your best option in this case.

Upvotes: 0

Related Questions