** (EXIT) process attempted to call itself when I have a child iterate through

Viewed 121

I have a function So when the update function is called in a genserver call I have

update()

which then, given a condition might call

a function to update all of the similar type of GenServer

But I run into

 ** (EXIT) process attempted to call itself 

I can't think of any ways around it. How do I get around this? Do I have to call out to an entirely new process which handles it?

There's an intermediate function that needs to have values that are returned when I call into all of the similar GenServers.

1 Answers

But I run into

** (EXIT) process attempted to call itself

You mean like this:

defmodule GenServer1 do
  use GenServer
  
  @impl true
  def init(_) do
    server_pid1 = self() 
    server_pid2 = GenServer2.start()
    {:ok, [server_pid1, server_pid2] }
  end

  @impl true
  def handle_call(:update, _from, all_servers) do
    update(all_servers)
    {:reply, :hello, all_servers}
  end
  def handle_call(:do_stuff, _from, all_servers) do
    {:reply, :goodbye, all_servers}
  end

  def update(all_servers) do
    Enum.each(all_servers, fn server -> GenServer.call(server, :do_stuff) end)
  end

  ## Public api:

  def start do
    GenServer.start_link(GenServer1, 0)
  end

  def initiate_update(pid) do
    GenServer.call(pid, :update)
  end

end

defmodule GenServer2 do
  use GenServer

  @impl true
  def init(state) do
    {:ok, state}
  end

  @impl true
  def handle_call(:do_stuff, _from, state) do
    {:reply, state}
  end

  #Public api:
  
  def start() do
    GenServer.start_link(GenServer2, 0)
  end
end

In iex:

iex(1)> {:ok, gen_server1} = GenServer1.start   
{:ok, #PID<0.152.0>}
iex(2)> GenServer1.initiate_update(gen_server1) 

01:06:24.113 [error] GenServer #PID<0.152.0> terminating
** (stop) exited in: GenServer.call(#PID<0.152.0>, :do_stuff, 5000)
    ** (EXIT) process attempted to call itself  <------
          ^   ^    ^     ^    ^
          |   |    |     |    |

    (elixir 1.13.4) lib/gen_server.ex:1023: GenServer.call/3 
    (elixir 1.13.4) lib/enum.ex:937: Enum."-each/2-lists^foreach/1-0-"/2
    (gen_server1 0.1.0) lib/gen_server1.ex:13: GenServer1.handle_call/3
    (stdlib 3.17.2) gen_server.erl:721: :gen_server.try_handle_call/4
    (stdlib 3.17.2) gen_server.erl:750: :gen_server.handle_msg/6
    (stdlib 3.17.2) proc_lib.erl:226: :proc_lib.init_p_do_apply/3
Last message (from #PID<0.150.0>): :update
State: [#PID<0.152.0>, {:ok, #PID<0.153.0>}]
Client #PID<0.150.0> is alive

    (stdlib 3.17.2) gen.erl:233: :gen.do_call/4
    (elixir 1.13.4) lib/gen_server.ex:1027: GenServer.call/3
    (stdlib 3.17.2) erl_eval.erl:685: :erl_eval.do_apply/6
    (elixir 1.13.4) src/elixir.erl:296: :elixir.recur_eval/3
    (elixir 1.13.4) src/elixir.erl:274: :elixir.eval_forms/3
    (iex 1.13.4) lib/iex/evaluator.ex:310: IEx.Evaluator.handle_eval/3
    (iex 1.13.4) lib/iex/evaluator.ex:285: IEx.Evaluator.do_eval/3
    (iex 1.13.4) lib/iex/evaluator.ex:274: IEx.Evaluator.eval/3
** (EXIT from #PID<0.150.0>) shell process exited with reason: exited in: GenServer.call(#PID<0.152.0>, :do_stuff, 5000)
    ** (EXIT) process attempted to call itself

Interactive Elixir (1.13.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> 

Don't call handle_call from within a handle_call--that creates a deadlock that will never return. Elixir kindly detects that deadlock and errors out. I think the deadlock happens because the first handle call can't return until the second handle_call returns, but the second handle_call can't return because a process can only deal with one message at a time, and while it's dealing with the first handle_call's message, it does not allow the second handle_call's message to be processed. So, the second handle_call has to wait for the first handle_call's message to be processed, but processing of a message ends when the handler returns.

Do I have to call out to an entirely new process which handles it?

You can probably run 400 million erlang processes at the same time on your laptop, what do you care if you have to start another process?

Related