Erlang generic standard behaviours -- gen_server terminate
gen_server 主体 module 已经分析完了(http://www.cnblogs.com/--00/p/4271982.html),接着,分析下gen_server 中的terminate .首先分析一个问题, 这个问题是之前在weibo 上和别人讨论过的一个问题: Why will a rpc:call started gen_server process terminate with normal reason?
注:被call 的gen_server 进程 trap_exit 为true .
rpc call
首先, 得先去看看rpc call 是怎样的行为流程.
call(N,M,F,A,infinity) when node() =:= N -> %% Optimize local call
local_call(M,F,A);
call(N,M,F,A,infinity) ->
do_call(N, {call,M,F,A,group_leader()}, infinity);
call(N,M,F,A,Timeout) when is_integer(Timeout), Timeout >= 0 ->
do_call(N, {call,M,F,A,group_leader()}, Timeout).
以上, 会对local node 的call 进行optimize 处理, 对于remote node 来说, 继续调用do_call 函数
do_call(Node, Request, infinity) ->
rpc_check(catch gen_server:call({?NAME,Node}, Request, infinity));
do_call(Node, Request, Timeout) ->
Tag = make_ref(),
{Receiver,Mref} =
erlang:spawn_monitor(
fun() ->
%% Middleman process. Should be unsensitive to regular
%% exit signals.
process_flag(trap_exit, true),
Result = gen_server:call({?NAME,Node}, Request, Timeout),
exit({self(),Tag,Result})
end),
receive
{'DOWN',Mref,_,_,{Receiver,Tag,Result}} ->
rpc_check(Result);
{'DOWN',Mref,_,_,Reason} ->
%% The middleman code failed. Or someone did
%% exit(_, kill) on the middleman process => Reason==killed
rpc_check_t({'EXIT',Reason})
end.
do_call 的时候会对Timeout 为infinity 做直接调用gen_server:call 处理;而对于Timeout 不为infinity的情况,使用Middleware process(和gen_server module 中的multi_call 相似),不论gen_server:call 调用(L11)的返回结果是什么,Middleware process 都会使用exit(L12).
在此,如果rpc:call 的执行参数是 TargetNode,GenServerMod, start_link, Args, Middleware process 就是GenServerMod 进程的Parent,而在调用结束之后,Middleware process exit了.至此,得到的信息:
1, 调用GenServerMod:start_link(Args) 的进程会在执行结束后exit;
2, 调用进程是GenServerMod的Parent
gen_server terminate
首先,需要对terminate 进行区分, 一个是GenServerMod 的callback 方法terminate, 一个是gen_server module中的函数, 此处主要是对gen_server module 中的terminate 函数进行分析.
然后,检索gen_server module 代码中, 哪些地方哪些情况下调用了terminate 函数:
1, define terminate func
%%% ---------------------------------------------------
%%% Terminate the server.
%%% --------------------------------------------------- -spec terminate(_, _, _, _, _, _) -> no_return().
terminate(Reason, Name, Msg, Mod, State, Debug) ->
terminate(Reason, Reason, Name, Msg, Mod, State, Debug). -spec terminate(_, _, _, _, _, _, _) -> no_return().
terminate(ExitReason, ReportReason, Name, Msg, Mod, State, Debug) ->
Reply = try_terminate(Mod, ExitReason, State),
case Reply of
{'EXIT', ExitReason1, ReportReason1} ->
FmtState = format_status(terminate, Mod, get(), State),
error_info(ReportReason1, Name, Msg, FmtState, Debug),
exit(ExitReason1);
_ ->
case ExitReason of
normal ->
exit(normal);
shutdown ->
exit(shutdown);
{shutdown,_}=Shutdown ->
exit(Shutdown);
_ ->
FmtState = format_status(terminate, Mod, get(), State),
error_info(ReportReason, Name, Msg, FmtState, Debug),
exit(ExitReason)
end
end.
terminate func 会先调用try_terminate(即GenServerMod:terminate),然后report 错误信息,最后exit .
2, handle_msg 调用terminate
handle_msg 函数中,当遇到'EXIT' 退出,GenServerMod callback 方法返回'stop'时,都会调用terminate函数. 如:
{ok, {stop, Reason, NState}} ->
terminate(Reason, Name, Msg, Mod, NState, []);
{'EXIT', ExitReason, ReportReason} ->
terminate(ExitReason, ReportReason, Name, Msg, Mod, State, []);
3, receive Parent 'EXIT' message
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.
在L6,当收到Parent 'EXIT' 的消息时, gen_server module 会调用terminate 函数, 使GenServerMod 以相同于 Parent 的Reason 退出.
以上两种情况,gen_server module 都会调用terminate 函数, 继而回调GenServerMod 的terminate callback, 最后使GenServerMod 进程退出.
但是,为什么trap_exit 为false 或者是 start 的时候就不会发生此种情况? (因为进程不会收到L6处的消息)
link VS monitor
既然都已经说到了trap_exit了,而且之前的gen_server module 和 gen 分析中,都看到了大量的start_link和erlang:monitor,就简单说下Erlang 中link 和monitor 的却别.
对于一个进程died 这件事, link 和monitor 是两种不同的通知方式. 当使用link 时, 某进程died 之后, 其他与之link 的进程会收到 Exit Signals , 而使用monitor时, 某进程died之后, monitor 它的进程会收到 message 而不是 signals.
回到最开始的问题
这样的话, 这个问题的原因就很清楚了.
how to fix?
1, 使用start 代替 start_link ,并在 spawn_opts 的参数中添加'monitor' option;
2, 直接修改gen_server module 的decode_msg 函数, 去掉{'EXIT', Parent, Reason} branch 的处理. (好像有点粗鲁^^)
参考
http://marcelog.github.io/articles/erlang_link_vs_monitor_difference.html
Erlang generic standard behaviours -- gen_server terminate的更多相关文章
- Erlang generic standard behaviours -- gen_server system msg
这是Erlang generic standard behaviors gen_server 分析的系列的最后一篇,主要分析gen_server module 辅助性的功能函数. 在gen_serve ...
- Erlang generic standard behaviours -- gen_server module
在分析完gen module (http://www.cnblogs.com/--00/p/4271386.html)之后,就可以开始进入gen_server 的主体module 了.gen_serv ...
- Erlang generic standard behaviours -- gen_server hibernate
hibernate 主要用于在内存空闲时,通过整理进程的stack,回收进程的heap 来达到回收内存节省资源的效果. hibernate 可用于OTP 进程以及普通进程, hibernate 的官方 ...
- Erlang generic standard behaviours -- gen_server noblock call
在Erlang 系统中,经常需要gen_server 进程来处理共享性的数据,也就是总希望一个gen_server 进程来为多个普通进程提供某种通用性的服务,这也是gen_server 设计的初衷.但 ...
- Erlang generic standard behaviours -- gen
在分析 gen_server (或者是gen_fsm )之前,首先应该弄明白,gen 这个module . -module(gen). -compile({inline,[get_node/1]}). ...
- Erlang generic standard behaviours -- summary
gen_server 相关的片段分析得也差不多了, 这篇作为一个简要的总结.这一系列相关的分析暂且告一段落(之后如有必要,还会回来的 ^^ ),下一个系列主要是以pool 相关, 包括但不仅限于开源项 ...
- gen_server terminate与trap_exit
不论是新手还是熟手,写gen_server时常会遇到terminate/2,有时执行,有时却不执行的困惑. 比如stackoverflow中的Handling the cleanup of the g ...
- erlang四大behaviour之一gen_server
来源:http://www.cnblogs.com/puputu/articles/1701017.html erlang程序设计里面有个设计原则就是把你的进程构造成树,把共用代码提出来,特定功能 ...
- erlang四大behaviour之一gen_server(转载)
erlang程序设计里面有个设计原则就是把你的进程构造成树,把共用代码提出来,特定功能用自己的module实现,这也就是behaviour了,应用behaviour可以减少与本身事务无关的代码量,设计 ...
随机推荐
- SQL查询 [SCOPE_IDENTITY、IDENT_CURRENT 和 @@IDENTITY的区别(比较)] ---转载
@@IDENTITY (Transact-SQL) 返回最后插入的标识值的系统函数. 备注 在一条 INSERT.SELECT INTO 或大容量复制语句完成后,@@IDENTITY 中包含语句生成的 ...
- ubuntun下安装Fiddler
对于分析网页或者写爬虫的时候经常需要用到抓包工具进行网页数据的抓包.在Windows下可以安装Fiddler来抓包.在ubuntun下不能直接安装Fiddler.需要先安装mono 1 首先安装mon ...
- 运行“cordova build android” - 无法找到属性android:fontVariationSettings和android:ttcIndex
:app:processArm64DebugResourcesC:\Users\xfcao\.gradle\caches\transforms-1\files-1.1\xwalk_core_libra ...
- deviceToken的获取(二)
第一步:申请证书: 第二步:申请app ids,应用名字必须一致.然后再进入进行编辑,使其enable,绿灯. 第三步:申请provisioning profile,生成.mobileprovisio ...
- Swift 闭包的简单学习
OC中已经学习了闭包 在swift里面 该怎么处理 不多说 上代码 //(num:Int) ->Bool是闭包的参数类型 func hasCloserMatch(arr :[Int], valu ...
- Qt事件机制(是动作发生后,一种通知对象的消息,是被动与主动的总和。先处理自己队列中的消息,然后再处理系统消息队列中的消息)
Qt事件机制 Qt程序是事件驱动的, 程序的每个动作都是由幕后某个事件所触发.. Qt事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. Qt事件的类型很多, 常见的qt的事件如下: 键盘事 ...
- Vim 标签定义
一.单个文件: m+标记字符 打上标记,如在开头行按ms(start),标记开头: 如需返回到自己的标记点,按`+标记字符就行: 二.多个文件: m+大写标记字符 如果删除了标签的行,同时也删除了标签 ...
- TCP/IP 协议中的编址
TCP/IP协议的互联网需要用到四个级别的地址:物理地址.逻辑地址.端口地址和特定应用地址 一.物理地址 物理地址称为链路地址,是由接点所在的局域网或广域网为该结点指定的地址. 这种地址的长度和格式随 ...
- 关于spring事务注解
关于事务的注解常用如下1.如果有事务, 那么加入事务, 没有的话新建一个(默认情况下),也就是当我们要开启事务的时候才用,即有数据库有增删改操作@Transactional(rollbackFor=E ...
- STL中流相关的输入输出符和get函数彻底总结:cin、cin.get()、cin.getline()、getline()、gets()等函数的用法
我的总结: [首先:对于流来说,就把流看成一个中转的仓库,对流进行<<运算或>>运算或者get函数的运算都是指,把“流”中的数据“运送”到“内存变量”中去,还是把内存变量中的数 ...