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?