You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Describe the bug Erlang docs for gen_server:call/2,3 state that a call can exit with {normal, _Location} if handle_call/3 returns {stop, normal, _} without replying:
normal {shutdown,Term}
The server stopped during the call by returning {stop,Reason,_} from its Module:handle_call/3 callback, without replying. See also stop/3.
But I encountered a situation where a gen_server call can exit with the normal reason while handle_call/3 returns a reply and handle_info/2 stops the server process by returning {stop, normal, State}.
To Reproduce
Consider the following code (full code with an Elixir example here).
This will return a map with exit reasons (or success if a call didn't result in an exit) as keys and how many times each corresponding exit reason occurred as values. The numbers can differ from run to run, in this particular invocation all 100 calls resulted in normal exits.
Expected behavior
The map doesn't contain normal as a key; i.e. not a single call results in a normal exit since we stop the server process from inside the handle_info/2 callback, not the handle_call/3 callback.
Affected versions
25.3.2.4.
Additional context
I encountered this "bug" in an Elixir project and this is a stripped down more-or-less-literal translation to Erlang; because I don't usually code in Erlang it can be incorrect. Also, interestingly enough, if we don't print to the standard output inside the handle_info/2 callback (i.e., pass false as the third argument of gen_server_call_normal:run/3) the issue seems to no longer occur:
Yes, the documentation is incorrect/inaccurate. You will get a normal exit reason if the process returns {stop,normal,_} from any callback while the call is in progress. What happens is this:
Caller sets up a monitor and sends message.
Server timeout triggers and stops the server with reason normal.
Caller gets monitor 'DOWN' signal with reason normal and returns that as reason.
When the caller gets the 'DOWN' it has no way of knowing whether it was its call or something else that cause the process to exit, so it just returns the reason to the caller.
The io:format needs to be there because an io:format does a receive which forces a synchronization of the signal queue. Without it the monitor signal in the message queue will not be processed until after the death of the process, which means that it will receive noproc reasons.
Without io:format
Caller sends monitor and message signals.
Server timeout triggers and stops the server with reason normal.
When terminating server sees that it has a monitor signal to handle and sends noproc to caller.
With io:format
Caller sends monitor and message signals.
Server timeout triggers
Server does io:format (i.e. a receive) and detects the monitor signal
Server terminates and sees that it has a monitor to handle and sends normal to caller.
You should get exactly the same behavior by only adding a receive unknown -> ok after 0 -> ok end instead of the io:format.
Describe the bug
Erlang docs for
gen_server:call/2,3
state that a call can exit with{normal, _Location}
ifhandle_call/3
returns{stop, normal, _}
without replying:But I encountered a situation where a
gen_server
call can exit with thenormal
reason whilehandle_call/3
returns a reply andhandle_info/2
stops the server process by returning{stop, normal, State}
.To Reproduce
Consider the following code (full code with an Elixir example here).
gen_server_call_normal.erl
:gen_server_call_normal_server.erl
:Perform 100
gen_server
calls in the shell:This will return a map with exit reasons (or
success
if a call didn't result in an exit) as keys and how many times each corresponding exit reason occurred as values. The numbers can differ from run to run, in this particular invocation all 100 calls resulted innormal
exits.Expected behavior
The map doesn't contain
normal
as a key; i.e. not a single call results in anormal
exit since we stop the server process from inside thehandle_info/2
callback, not thehandle_call/3
callback.Affected versions
25.3.2.4.
Additional context
I encountered this "bug" in an Elixir project and this is a stripped down more-or-less-literal translation to Erlang; because I don't usually code in Erlang it can be incorrect. Also, interestingly enough, if we don't print to the standard output inside the
handle_info/2
callback (i.e., passfalse
as the third argument ofgen_server_call_normal:run/3
) the issue seems to no longer occur:The text was updated successfully, but these errors were encountered: