branch=main - disterl gen_server:call from atomvm fails
from esp32
{server, remote_node} ! <<"hello world from atomvm">>
works as expected but
gen_server:call({server, remote_node}, <<"hello world from atomvm">>)
throws an exception
.. AtomVM/libs/estdlib/src/gen_server.erl"},{line,434}]}]
Could you please post the full error log? It probably can easily be reproduced but I'm afk right now.
I guess this is from AtomVM to BEAM? I.e. the gen_server process runs on a BEAM node? Which OTP version?
We might miss a bit of disterl implementation or a gen_server protocol compatibility with BEAM.
- no more info than this:
CRASH
======
pid: <0.1.0>
Stacktrace:
[{gen_server,call,2,[{file,"../AtomVM/libs/estdlib/src/gen_server.erl"},{line,434}]}]
cp: #CP<module: 9, label: 18, offset: 16>
x[0]: error
x[1]: function_clause
x[2]: {1,1,60,1,[{4,1820}],error}
Stack
-----
[]
[]
#CP<module: 1, label: 14, offset: 0>
Mailbox
-------
Monitors
--------
link to <0.2.0>
link to <0.8.0>
link to <0.14.0>
**End Of Crash Report**
Return value: error
I (3856) AtomVM: AtomVM application terminated. Going to sleep forever ...
-
yes it's from atomvm (running on esp32-s3
-
i feel (../AtomVM/libs/estdlib/src/gen_server.erl"},{line,434}])
call(Name, Request, TimeoutMs) when is_atom(Name) ->
case erlang:whereis(Name) of
undefined ->
erlang:exit({noproc, {?MODULE, ?FUNCTION_NAME, [Name, Request]}});
Pid when is_pid(Pid) ->
call(Pid, Request, TimeoutMs)
end;
call(Pid, Request, TimeoutMs) when is_pid(Pid) ->
MonitorRef = monitor(process, Pid),
Pid ! {'$gen_call', {self(), MonitorRef}, Request},
receive
{'DOWN', MonitorRef, process, Pid, {E, []} = _Reason} ->
erlang:exit({E, {?MODULE, ?FUNCTION_NAME, [Pid, Request]}});
{'DOWN', MonitorRef, process, Pid, {_E, _L} = Reason} ->
erlang:exit(Reason);
{'DOWN', MonitorRef, process, Pid, Atom} when is_atom(Atom) ->
erlang:exit({Atom, {?MODULE, ?FUNCTION_NAME, [Pid, Request]}});
{MonitorRef, Reply} ->
demonitor(MonitorRef, [flush]),
Reply
after TimeoutMs ->
demonitor(MonitorRef, [flush]),
erlang:exit({timeout, {?MODULE, ?FUNCTION_NAME, [Pid, Request]}})
end.
cannot handle {server, remote_node}
That seems easy to fix but I cannot test it right now. I can come up with a PR tonight for you to test.
take your time. i'm not in a run.
The roadmap to fix this is a little longer as I first expected and is as follows:
- Fix
monitor/2bif to work with an atom, looking up the registered process. - Factorize
gen_servercode (and probablygen_statem) to skip the whereis bit and take advantage of the fixedmonitor/2. This would have the added benefit of fixing potential race conditions if the process is unregistered and another registered between thewhereis/1call and themonitor/2call. Also we could remove the guards in most calls as badarg can be raised bymonitor/2. - Fix
monitor/2bif to work with{atom(), atom()}tuples by sending the appropriate disterl command. - Change the
server_ref/0type ingen_server.
i do not know if somebody is still working on this issue.
i found that it's still not possible to
gen_server:call({server, remote_node}, <<"hello world from atomvm">>) because monitor/2 does not except {atom(), atom()}. as long as this holds true i will be using:
call(ServerRef, Request, TimeoutMs) ->
GetPortOrPid = fun(Object, Is) ->
P = whereis(Object),
Isa = Is(P),
if Isa == true -> P; true -> undefined end
end,
TypeAndId = fun({Service, Node}) when is_atom(Service) andalso is_atom(Node) ->
{port, GetPortOrPid(network_port, fun is_port/1)}
;
(Ref) when is_atom(Ref) ->
{process, GetPortOrPid(Ref, fun is_pid/1)}
;
(Ref) when is_pid(Ref) ->
{process, Ref}
;
(_) ->
{undefined, undefined}
end,
{Type, Id} = TypeAndId(ServerRef),
MonitorRef = monitor(Type, Id),
...
it seems to me that this issue has been resolved (had a look at the code but did not test (yet)). is this assumption correct
sad to write that this issue is still unresolved - the pgm terminates with Return value: error.
maybe i missed something ..
in gen.erl line 52 the call MonitorRef = monitor(process, ServerRef) fails when ServerRef is not a local pid.
applying the same (i know, it's far from beeing perfect) workaround to gen:call/4 as previously applied to gen_server:call/3 results in successful calls gen_server:call({service, 'remote server'}, Request).