无监督 gen_server 在收到退出信号时不调用终止
Unsupervised gen_server doesn't call terminate when it receives exit signal
gen_server
关于 Module:terminate
回调的文档说:
Even if the gen_server process is not part of a supervision tree, this
function is called if it receives an 'EXIT' message from its parent.
Reason is the same as in the 'EXIT' message.
这是我的 handle_info
和 terminate
函数:
handle_info(UnknownMessage, State) ->
io:format("Got unknown message: ~p~n", [UnknownMessage]),
{noreply, State}.
terminate(Reason, State) ->
io:format("Terminating with reason: ~p~n", [Reason]).
我使用 gen_server:start
启动此服务器。我假设当我调用 erlang:exit(Pid, fuckoff)
时,它应该调用 terminate
回调函数。但它显示:
Got unknown message: {'EXIT',<0.33.0>,fuckoff}
表示正在调用handle_info
。但是当我调用 gen_server:stop
时,一切都按照文档中提到的那样工作。我正在从 shell 呼叫我的 gen_server
。你能澄清一下吗?
[更新]
Here是gen_server
中decode_msg
函数的源代码。如果它收到任何 'EXIT' 消息,它应该调用 terminate
函数:
decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, Hib) ->
case Msg of
{system, From, Req} ->
sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
[Name, State, Mod, Time], Hib);
{'EXIT', Parent, Reason} ->
terminate(Reason, Name, Msg, Mod, State, Debug);
_Msg when Debug =:= [] ->
handle_msg(Msg, Parent, Name, State, Mod);
_Msg ->
Debug1 = sys:handle_debug(Debug, fun print_event/3,
Name, {in, Msg}),
handle_msg(Msg, Parent, Name, State, Mod, Debug1)
end.
在我的例子中它没有调用 terminate
函数。
[更新]
当我使用 gen_server:start_link()
启动 gen_server
时,使用 erlang:exit(Pid, Reason)
发送退出信号将导致调用 terminate
回调函数,这是预期的行为。无论进程是否链接到其父进程,在解释退出信号时似乎有所不同。
当您启动 gen_server
时,这是一个简单的过程,因此,erlang:exit/1
or erlang:exit/2
按预期工作。
- If Pid is not trapping exits, Pid itself exits with exit reason Reason.
- If Pid is trapping exits, the exit signal is transformed into a message {'EXIT', From, Reason} and delivered to the message queue of Pid.
所以,目前您的代码陷阱 'EXIT'
信号是因为这封邮件像任何其他邮件一样发送到邮箱并匹配 handle_info/2
通配符模式。
如果您想了解更多相关信息,可以阅读此代码中描述的 gen_server
source code and see how it works. You can also find your problem。
简答:
如果您从 gen_server
actor 内部调用 exit/2
函数,它会根据文档按预期运行,并且会调用 terminate/2
回调。
长答案:
当您从 shell 发送退出消息时,退出元组的 Parent
值设置为 shell 进程 ID,另一方面,当您启动 gen_server
进程形式 shell 它的 Parent
值设置为它自己的进程 ID,而不是 shell 进程 ID,因此当它收到退出消息时它与第二个不匹配decode_msg/8
函数中接收块的子句,因此不调用 terminate/6
函数,最后匹配调用 handle_msg/5
函数的下一个子句。
建议:
为了通过向 gen_server
进程发送退出消息来调用 terminate/3
回调,您可以将退出消息捕获在 handle_info/2
中,然后 return 中停止元组如下:
init([]) ->
process_flag(trap_exit, true),
{ok, #state{}}.
handle_info({'EXIT', _From, Reason}, State) ->
io:format("Got exit message: ~p~n", []),
{stop, Reason, State}.
terminate(Reason, State) ->
io:format("Terminating with reason: ~p~n", [Reason]),
%% do cleanup ...
ok.
gen_server
关于 Module:terminate
回调的文档说:
Even if the gen_server process is not part of a supervision tree, this function is called if it receives an 'EXIT' message from its parent. Reason is the same as in the 'EXIT' message.
这是我的 handle_info
和 terminate
函数:
handle_info(UnknownMessage, State) ->
io:format("Got unknown message: ~p~n", [UnknownMessage]),
{noreply, State}.
terminate(Reason, State) ->
io:format("Terminating with reason: ~p~n", [Reason]).
我使用 gen_server:start
启动此服务器。我假设当我调用 erlang:exit(Pid, fuckoff)
时,它应该调用 terminate
回调函数。但它显示:
Got unknown message: {'EXIT',<0.33.0>,fuckoff}
表示正在调用handle_info
。但是当我调用 gen_server:stop
时,一切都按照文档中提到的那样工作。我正在从 shell 呼叫我的 gen_server
。你能澄清一下吗?
[更新]
Here是gen_server
中decode_msg
函数的源代码。如果它收到任何 'EXIT' 消息,它应该调用 terminate
函数:
decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, Hib) ->
case Msg of
{system, From, Req} ->
sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
[Name, State, Mod, Time], Hib);
{'EXIT', Parent, Reason} ->
terminate(Reason, Name, Msg, Mod, State, Debug);
_Msg when Debug =:= [] ->
handle_msg(Msg, Parent, Name, State, Mod);
_Msg ->
Debug1 = sys:handle_debug(Debug, fun print_event/3,
Name, {in, Msg}),
handle_msg(Msg, Parent, Name, State, Mod, Debug1)
end.
在我的例子中它没有调用 terminate
函数。
[更新]
当我使用 gen_server:start_link()
启动 gen_server
时,使用 erlang:exit(Pid, Reason)
发送退出信号将导致调用 terminate
回调函数,这是预期的行为。无论进程是否链接到其父进程,在解释退出信号时似乎有所不同。
当您启动 gen_server
时,这是一个简单的过程,因此,erlang:exit/1
or erlang:exit/2
按预期工作。
- If Pid is not trapping exits, Pid itself exits with exit reason Reason.
- If Pid is trapping exits, the exit signal is transformed into a message {'EXIT', From, Reason} and delivered to the message queue of Pid.
所以,目前您的代码陷阱 'EXIT'
信号是因为这封邮件像任何其他邮件一样发送到邮箱并匹配 handle_info/2
通配符模式。
如果您想了解更多相关信息,可以阅读此代码中描述的 gen_server
source code and see how it works. You can also find your problem。
简答:
如果您从 gen_server
actor 内部调用 exit/2
函数,它会根据文档按预期运行,并且会调用 terminate/2
回调。
长答案:
当您从 shell 发送退出消息时,退出元组的 Parent
值设置为 shell 进程 ID,另一方面,当您启动 gen_server
进程形式 shell 它的 Parent
值设置为它自己的进程 ID,而不是 shell 进程 ID,因此当它收到退出消息时它与第二个不匹配decode_msg/8
函数中接收块的子句,因此不调用 terminate/6
函数,最后匹配调用 handle_msg/5
函数的下一个子句。
建议:
为了通过向 gen_server
进程发送退出消息来调用 terminate/3
回调,您可以将退出消息捕获在 handle_info/2
中,然后 return 中停止元组如下:
init([]) ->
process_flag(trap_exit, true),
{ok, #state{}}.
handle_info({'EXIT', _From, Reason}, State) ->
io:format("Got exit message: ~p~n", []),
{stop, Reason, State}.
terminate(Reason, State) ->
io:format("Terminating with reason: ~p~n", [Reason]),
%% do cleanup ...
ok.