各种有用的erlang行为
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

2841 lines
122 KiB

-module(gen_ipc).
-compile(inline).
-compile({inline_size, 128}).
-include_lib("kernel/include/logger.hrl").
-include("genGbh.hrl").
-import(maps, [iterator/1, next/1]).
-import(gen_call, [gcall/3, gcall/4, greply/2, try_greply/2]).
-export([
%% API for gen_server or gen_statem behaviour
start/3, start/4, start_link/3, start_link/4
, start_monitor/3, start_monitor/4
, stop/1, stop/3
, cast/2, send/2
, abcast/2, abcast/3
, call/2, call/3
, send_request/2, send_request/4
, wait_response/2, receive_response/2, check_response/2
, wait_response/3, receive_response/3, check_response/3
, reqids_new/0, reqids_size/1
, reqids_add/3, reqids_to_list/1
, multi_call/2, multi_call/3, multi_call/4
, enter_loop/4, enter_loop/5, enter_loop/6
, reply/1, reply/2
%% API for gen_event behaviour
, send_request/3
, info_notify/2, call_notify/2
, epm_call/3, epm_call/4, epm_info/3
, add_epm/3, del_epm/3, add_sup_epm/3
, swap_epm/3, swap_sup_epm/3
, which_epm/1
%% gen callbacks
, init_it/6
%% sys callbacks
, system_continue/3
, system_terminate/4
, system_code_change/4
, system_get_state/1
, system_replace_state/2
, format_status/2
%% Internal callbacks
, wakeupFromHib/12
%% logger callback
, format_log/1
, format_log/2
, epm_log/1
, print_event/3
]).
% 简写备注**********************************
% isPostpone isPos
% isHibernate isHib
% nextEvent nextE
% nextStatus nextS
% keepStatus kpS
% keepStatusState kpS_S
% repeatStatus reS
% repeatStatusState reS_S
% *****************************************
% %% timeout相关宏定义
% -define(REL_TIMEOUT(T), ((is_integer(T) andalso (T) >= 0) orelse (T) =:= infinity)).
% -define(ABS_TIMEOUT(T), (is_integer(T) orelse (T) =:= infinity)).
-define(STACKTRACE(), element(2, erlang:process_info(self(), current_stacktrace))).
-define(CB_FORM_ENTER, 1). %% 从enter 回调返回
-define(CB_FORM_AFTER, 2). %% 从after 回调返回
-define(CB_FORM_EVENT, 3). %% 从event 回调返回
%%%==========================================================================
%%% Interface functions.
%%%==========================================================================
%% gcall 发送消息来源进程格式类型
-type from() :: {To :: pid(), Tag :: term()}.
-type request_id() :: gen:request_id().
-type request_id_collection() :: gen:request_id_collection().
-type response_timeout() :: timeout() | {abs, integer()}.
%% 事件类型
-type eventType() :: externalEventType() | timeoutEventType() | {'onevent', Subtype :: term()}.
-type externalEventType() :: {'call', From :: from()} | 'cast' | 'info'.
-type timeoutEventType() :: GTimeoutName :: term() | 'eTimeout' | 'sTimeout'. %% 前面的GTimeoutName这个是通用超时标识名
%% 是否捕捉信号 gen_event管理进程需要设置该参数为true
-type isTrapExit() :: boolean().
%% 是否允许进入enter 回调
-type isEnter() :: boolean().
%% 如果为 "true" 则推迟当前事件,并在状态更改时重试(=/=)
-type isPos() :: boolean().
%% 如果为 "true" 则使服务器休眠而不是进入接收状态
-type isHib() :: boolean().
%% 定时器相关
-type timeouts() :: Time :: timeout() | integer().
-type timeoutOption() :: {abs, Abs :: boolean()}.
% gen_event模式下回调模块的标识
-type epmHandler() :: atom() | {atom(), term()}.
%% 在状态更改期间:
%% NextStatus and NewData are set.
%% 按照出现的顺序处理 actions()列表
%% 这些action() 按包含列表中的出现顺序执行。设置选项的选项将覆盖任何以前的选项,因此每种类型的最后一种将获胜。
%% 如果设置了enter 则进入enter回调
%% 如果设置了doAfter 则进入after回调
%% 如果 "postpone" 为 "true",则推迟当前事件。
%% 如果设置了“超时”,则开始状态超时。 零超时事件将会插入到待处理事件的前面 先执行
%% 如果有postponed 事件则 事件执行顺序为 超时添加和更新 + 零超时 + 当前事件 + 反序的Postpone事件 + LeftEvent
%% 处理待处理的事件,或者如果没有待处理的事件,则服务器进入接收或休眠状态(当“hibernate”为“ true”时)
-type initAction() ::
{trap_exit, Bool :: isTrapExit()} | % 设置是否捕捉信息 主要用于gen_event模式下
eventAction().
-type eventAction() ::
{'doAfter', Args :: term()} | % 设置执行某事件后是否回调 handleAfter
{'isPos', Bool :: isPos()} | % 设置推迟选项
{'nextE', EventType :: eventType(), EventContent :: term()} | % 插入事件作为下一个处理
commonAction().
-type afterAction() ::
{'nextE', EventType :: eventType(), EventContent :: term()} | % 插入事件作为下一个处理
commonAction().
-type enterAction() ::
{'isPos', false} | % 虽然enter action 不能设置postpone 但是可以取消之前event的设置
commonAction().
-type commonAction() ::
{'isEnter', Bool :: isEnter()} |
{'isHib', Bool :: isHib()} |
timeoutAction() |
replyAction().
-type timeoutAction() ::
timeoutNewAction() |
timeoutCancelAction() |
timeoutUpdateAction().
-type timeoutNewAction() ::
{'eTimeout', Time :: timeouts(), EventContent :: term()} | % Set the event_timeout option
{'eTimeout', Time :: timeouts(), EventContent :: term(), Options :: ([timeoutOption()])} | % Set the event_timeout option
{'sTimeout', Time :: timeouts(), EventContent :: term()} | % Set the status_timeout option
{'sTimeout', Time :: timeouts(), EventContent :: term(), Options :: ([timeoutOption()])} | % Set the status_timeout option
{'gTimeout', Name :: term(), Time :: timeouts(), EventContent :: term()} | % Set the generic_timeout option
{'gTimeout', Name :: term(), Time :: timeouts(), EventContent :: term(), Options :: ([timeoutOption()])}. % Set the generic_timeout option
-type timeoutCancelAction() ::
'c_eTimeout' | % 不可能也不需要更新此超时,因为任何其他事件都会自动取消它。
'c_sTimeout' |
{'c_gTimeout', Name :: term()}.
-type timeoutUpdateAction() ::
{'u_eTimeout', EventContent :: term()} | % 不可能也不需要取消此超时,因为任何其他事件都会自动取消它。
{'u_sTimeout', EventContent :: term()} |
{'u_gTimeout', Name :: term(), EventContent :: term()}.
-type actions(ActionType) ::
ActionType |
[ActionType, ...].
-type replyAction() ::
{'reply', From :: from(), Reply :: term()}.
-type eventCallbackResult() ::
{'reply', Reply :: term(), NewState :: term()} | % 用作gen_server模式时快速响应进入消息接收
{'sreply', Reply :: term(), NextStatus :: term(), NewState :: term()} | % 用作gen_ipc模式便捷式返回reply 而不用把reply放在actions列表中
{'noreply', NewState :: term()} | % 用作gen_server模式时快速响应进入消息接收
{'reply', Reply :: term(), NewState :: term(), Options :: hibernate | {doAfter, Args :: term()}} | % 用作gen_server模式时快速响应进入消息接收
{'sreply', Reply :: term(), NextStatus :: term(), NewState :: term(), Actions :: actions(eventAction())} | % 用作gen_ipc模式便捷式返回reply 而不用把reply放在actions列表中
{'noreply', NewState :: term(), Options :: hibernate | {doAfter, Args :: term()}} | % 用作gen_server模式时快速响应进入循环
{'nextS', NextStatus :: term(), NewState :: term()} | % {next_status,NextS,NewData,[]}
{'nextS', NextStatus :: term(), NewState :: term(), Actions :: actions(eventAction())} | % Status transition, maybe to the same status
commonCallbackResult(eventAction()).
-type afterCallbackResult() ::
{'nextS', NextStatus :: term(), NewState :: term()} | % {next_status,NextS,NewData,[]}
{'nextS', NextStatus :: term(), NewState :: term(), Actions :: actions(afterAction())} | % Status transition, maybe to the same status
{'noreply', NewState :: term()} | % 用作gen_server模式时快速响应进入消息接收
{'noreply', NewState :: term(), Options :: hibernate} | % 用作gen_server模式时快速响应进入消息接收
commonCallbackResult(afterAction()).
-type enterCallbackResult() ::
commonCallbackResult(enterAction()).
-type commonCallbackResult(ActionType) ::
{'kpS', NewState :: term()} | % {keep_status,NewData,[]}
{'kpS', NewState :: term(), Actions :: actions(ActionType)} | % Keep status, change data
'kpS_S' | % {keep_status_and_data,[]}
{'kpS_S', Actions :: [ActionType]} | % Keep status and data -> only actions
{'reS', NewState :: term()} | % {repeat_status,NewData,[]}
{'reS', NewState :: term(), Actions :: actions(ActionType)} | % Repeat status, change data
'reS_S' | % {repeat_status_and_data,[]}
{'reS_S', Actions :: actions(ActionType)} | % Repeat status and data -> only actions
'stop' | % {stop,normal}
{'stop', Reason :: term()} | % Stop the server
{'stop', Reason :: term(), NewState :: term()} | % Stop the server
{'stopReply', Reason :: term(), Replies :: replyAction() | [replyAction(), ...] | term()} | % Reply then stop the server
{'stopReply', Reason :: term(), Replies :: replyAction() | [replyAction(), ...] | term(), NewState :: term()}. % Reply then stop the server
%% 状态机的初始化功能函数
%% 如果要模拟gen_server init返回定时时间 可以在Actions返回定时动作
%% 如果要把改进程当做gen_event管理进程需要在actions列表包含 {trap_exit, true} 设置该进程捕捉异常
-callback init(Args :: term()) ->
'ignore' |
{'stop', Reason :: term()} |
{'ok', State :: term()} |
{'ok', Status :: term(), State :: term()} |
{'ok', Status :: term(), State :: term(), Actions :: actions(initAction())}.
%% 当 enter call 回调函数
-callback handleEnter(OldStatus :: term(), CurStatus :: term(), State :: term()) ->
enterCallbackResult().
%% 当 init返回actions包含 doAfter 的时候会在 enter调用后 调用该函数 或者
%% 在事件回调函数返回后 enter调用后调用该函数
%% 该回调函数相当于 gen_server 的 handle_continue回调 但是在综合模式时 也可以生效
-callback handleAfter(AfterArgs :: term(), Status :: term(), State :: term()) ->
afterCallbackResult().
%% call 所以状态的回调函数
-callback handleCall(EventContent :: term(), Status :: term(), State :: term(), From :: {pid(), Tag :: term()}) ->
eventCallbackResult().
%% cast 回调函数
-callback handleCast(EventContent :: term(), Status :: term(), State :: term()) ->
eventCallbackResult().
%% info 回调函数
-callback handleInfo(EventContent :: term(), Status :: term(), State :: term()) ->
eventCallbackResult().
%% error 回调函数 守护模式下 进程捕捉到错误可以回调次函数
-callback handleError(Error :: term(), State :: term()) ->
eventCallbackResult().
%% 内部事件 Onevent 包括actions 设置的定时器超时产生的事件 和 nextE产生的超时事件 但是不是 call cast info 回调函数 以及其他自定义定时事件 的回调函数
%% 并且这里需要注意 其他erlang:start_timer生成超时事件发送的消息 不能和gen_ipc定时器关键字重合 有可能会导致一些问题
-callback handleOnevent(EventType :: term(), EventContent :: term(), Status :: term(), State :: term()) ->
eventCallbackResult().
%% 在gen_event模式下 扩展了下面三个回调函数 考虑场景是:
%% 比如游戏里的公会 有时候一个公会一个进程来管理可能开销比较高 只用一个进程来管理所以公会有可能一个进程处理不过来
%% 这个时候可以考虑用gen_ipc来分组管理 一个gen_ipc进程管理 N 个公会 但是管理进程需要做一些数据保存什么 或者定时 就可能会用到下面的这些函数
%% gen_event模式时 notify 有可能需要回调该管理进程的该函数
-callback handleEpmEvent(EventContent :: term(), Status :: term(), State :: term()) ->
eventCallbackResult().
%% gen_event模式时 call请求 有可能需要回调该管理进程的该函数
-callback handleEpmCall(EventContent :: term(), Status :: term(), State :: term()) ->
eventCallbackResult().
%% gen_event模式时 info消息 有可能需要回调该管理进程的该函数
-callback handleEpmInfo(EventContent :: term(), Status :: term(), State :: term()) ->
eventCallbackResult().
%% 在服务器终止之前进行清理。
-callback terminate(Reason :: 'normal' | 'shutdown' | {'shutdown', term()} | term(), Status :: term(), State :: term()) ->
any().
%% 代码更新回调函数
-callback code_change(OldVsn :: term() | {'down', term()}, OldStatus :: term(), OldState :: term(), Extra :: term()) ->
{ok, NewStatus :: term(), NewData :: term()} |
(Reason :: term()).
%% 以一种通常被精简的方式来格式化回调模块状态。
%% 对于StatusOption =:= 'normal',首选返回 term 是[{data,[{"Status",FormattedStatus}]}]
%% 对于StatusOption =:= 'terminate',它只是FormattedStatus
-callback formatStatus(StatusOption, [PDict | term()]) ->
Status :: term() when
StatusOption :: 'normal' | 'terminate',
PDict :: [{Key :: term(), Value :: term()}].
-optional_callbacks([
formatStatus/2
, terminate/3
, code_change/4
, handleEnter/3
, handleAfter/3
, handleError/2
, handleOnevent/4
, handleEpmEvent/3
, handleEpmCall/3
, handleEpmInfo/3
]).
-record(epmHer, {
epmId = undefined :: term(),
epmM :: atom(),
epmSup = undefined :: 'undefined' | pid(),
epmS :: term()
}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% start stop API start %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-type serverName() ::
{'local', atom()} |
{'global', GlobalName :: term()} |
{'via', RegMod :: module(), Name :: term()}.
-type serverRef() ::
pid() |
(LocalName :: atom()) |
{Name :: atom(), Node :: atom()} |
{'global', GlobalName :: term()} |
{'via', RegMod :: module(), ViaName :: term()}.
-type startOpt() ::
daemon |
{'timeout', Time :: timeout()} |
{'spawn_opt', [proc_lib:spawn_option()]} |
enterLoopOpt().
-type enterLoopOpt() ::
{'debug', Debugs :: [sys:debug_option()]} |
{'hibernate_after', HibernateAfterTimeout :: timeout()}.
-type startRet() ::
'ignore' |
{'ok', pid()} |
{'ok', {pid(), reference()}} |
{'error', term()}.
-spec start(Module :: module(), Args :: term(), Opts :: [startOpt()]) -> startRet().
start(Module, Args, Opts) ->
gen:start(?MODULE, nolink, Module, Args, Opts).
-spec start(ServerName :: serverName(), Module :: module(), Args :: term(), Opts :: [startOpt()]) -> startRet().
start(ServerName, Module, Args, Opts) ->
gen:start(?MODULE, nolink, ServerName, Module, Args, Opts).
-spec start_link(Module :: module(), Args :: term(), Opts :: [startOpt()]) -> startRet().
start_link(Module, Args, Opts) ->
gen:start(?MODULE, link, Module, Args, Opts).
-spec start_link(ServerName :: serverName(), Module :: module(), Args :: term(), Opts :: [startOpt()]) -> startRet().
start_link(ServerName, Module, Args, Opts) ->
gen:start(?MODULE, link, ServerName, Module, Args, Opts).
%% Start and monitor
-spec start_monitor(Module :: module(), Args :: term(), Opts :: [startOpt()]) -> startRet().
start_monitor(Module, Args, Opts) ->
gen:start(?MODULE, monitor, Module, Args, Opts).
-spec start_monitor(ServerName :: serverName(), Module :: module(), Args :: term(), Opts :: [startOpt()]) -> startRet().
start_monitor(ServerName, Module, Args, Opts) ->
gen:start(?MODULE, monitor, ServerName, Module, Args, Opts).
-spec stop(ServerRef :: serverRef()) -> ok.
stop(ServerRef) ->
gen:stop(ServerRef).
-spec stop(ServerRef :: serverRef(), Reason :: term(), Timeout :: timeout()) -> ok.
stop(ServerRef, Reason, Timeout) ->
gen:stop(ServerRef, Reason, Timeout).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% start stop API end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% gen callbacks start %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
doModuleInit(Module, Args) ->
try
Module:init(Args)
catch
throw:Ret -> Ret;
Class:Reason:Stacktrace -> {'EXIT', Class, Reason, Stacktrace}
end.
init_it(Starter, self, ServerRef, Module, Args, Opts) ->
init_it(Starter, self(), ServerRef, Module, Args, Opts);
init_it(Starter, Parent, ServerRef, Module, Args, Opts) ->
Name = gen:name(ServerRef),
Debug = gen:debug_options(Name, Opts),
GbhOpts = #gbhOpts{daemon = not lists:member(notDaemon, Opts)},
HibernateAfterTimeout = gen:hibernate_after(Opts),
case doModuleInit(Module, Args) of
{ok, State} ->
proc_lib:init_ack(Starter, {ok, self()}),
loopEntry(Parent, Debug, Module, Name, GbhOpts, HibernateAfterTimeout, undefined, State, []);
{ok, Status, State} ->
proc_lib:init_ack(Starter, {ok, self()}),
loopEntry(Parent, Debug, Module, Name, GbhOpts, HibernateAfterTimeout, Status, State, []);
{ok, Status, State, Actions} ->
proc_lib:init_ack(Starter, {ok, self()}),
loopEntry(Parent, Debug, Module, Name, GbhOpts, HibernateAfterTimeout, Status, State, listify(Actions));
{stop, Reason} ->
gen:unregister_name(ServerRef),
proc_lib:init_ack(Starter, {error, Reason}),
exit(Reason);
ignore ->
gen:unregister_name(ServerRef),
proc_lib:init_ack(Starter, ignore),
exit(normal);
{'EXIT', Class, Reason, Stacktrace} ->
gen:unregister_name(ServerRef),
proc_lib:init_ack(Starter, {error, Reason}),
error_info(Class, Reason, Stacktrace, Parent, Name, Module, HibernateAfterTimeout, fasle, #{}, [], #{}, '$un_init', '$un_init', Debug, []),
erlang:raise(Class, Reason, Stacktrace);
_Ret ->
gen:unregister_name(ServerRef),
Error = {'init_bad_return', _Ret},
proc_lib:init_ack(Starter, {error, Error}),
error_info(error, Error, ?STACKTRACE(), Parent, Name, Module, HibernateAfterTimeout, fasle, #{}, [], #{}, '$un_init', '$un_init', Debug, []),
exit(Error)
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% gen callbacks end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 进入循环 调用该进程必须使用proc_lib启动 且已经初始化状态和数据 包括注册名称
%% 可以通过使用与从init / 1返回的参数相同的参数,而不是通过start / 3,4或start_link / 3,4启动状态机,而将当前由proc_lib启动的进程转换为状态机。
-spec enter_loop(Module :: module(), Status :: term(), State :: term(), Opts :: [enterLoopOpt()]) -> no_return().
enter_loop(Module, Status, State, Opts) ->
enter_loop(Module, Status, State, Opts, self(), []).
-spec enter_loop(Module :: module(), Status :: term(), State :: term(), Opts :: [enterLoopOpt()], ServerOrActions :: serverName() | pid() | actions(eventAction())) -> no_return().
enter_loop(Module, Status, State, Opts, ServerOrActions) ->
if
is_list(ServerOrActions) ->
enter_loop(Module, Status, State, Opts, self(), ServerOrActions);
true ->
enter_loop(Module, Status, State, Opts, ServerOrActions, [])
end.
-spec enter_loop(Module :: module(), Status :: term(), State :: term(), Opts :: [enterLoopOpt()], Server :: serverName() | pid(), Actions :: actions(eventAction())) -> no_return().
enter_loop(Module, Status, State, Opts, ServerName, Actions) ->
is_atom(Module) orelse error({atom, Module}),
Parent = gen:get_parent(),
Name = gen:get_proc_name(ServerName),
Debug = gen:debug_options(Name, Opts),
GbhOpts = #gbhOpts{daemon = not lists:member(notDaemon, Opts)},
HibernateAfterTimeout = gen:hibernate_after(Opts),
loopEntry(Parent, Debug, Module, Name, GbhOpts, HibernateAfterTimeout, Status, State, Actions).
%% 这里的 init_it/6 和 enter_loop/5,6,7 函数汇聚
loopEntry(Parent, Debug, Module, Name, GbhOpts, HibernateAfterTimeout, CurStatus, CurState, Actions) ->
%% 如果该进程用于 gen_event 或者该进程需要捕捉退出信号 和 捕捉supervisor进程树的退出信息 则需要设置 process_flag(trap_exit, true) 需要在Actions返回 {trap_exit, true}
MewActions =
case lists:keyfind(trap_exit, 1, Actions) of
false ->
Actions;
{trap_exit, true} ->
process_flag(trap_exit, true),
lists:keydelete(trap_exit, 1, Actions);
_ ->
lists:keydelete(trap_exit, 1, Actions)
end,
?SYS_DEBUG(Debug, Name, {enter, CurStatus}),
%% 强制执行{postpone,false}以确保我们的假事件被丢弃
LastActions = MewActions ++ [{isPos, false}],
parseEventAL(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, false, #{}, [], #{}, CurStatus, CurState, CurStatus, Debug, [{onevent, init_status}], true, LastActions, ?CB_FORM_EVENT).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% sys callbacks start %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
system_continue(Parent, Debug, {Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, IsHib}) ->
if
IsHib ->
proc_lib:hibernate(?MODULE, wakeupFromHib, [Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug]);
true ->
receiveIng(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, false)
end.
system_terminate(Reason, Parent, Debug, {Parent, Name, Module, _GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, _IsHib}) ->
terminate(exit, Reason, ?STACKTRACE(), Parent, Name, Module, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, []).
system_code_change({Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, IsHib}, _Mod, OldVsn, Extra) ->
case
try Module:code_change(OldVsn, CurStatus, CurState, Extra)
catch
throw:Result -> Result;
_C:_R:_S -> {_C, _R, _S}
end
of
{ok, NewStatus, NewState} ->
{ok, {Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, NewStatus, NewState, IsHib}};
Error ->
Error
end.
system_get_state({_Parent, _Name, _Module, _GbhOpts, _HibernateAfterTimeout, _IsEnter, _EpmHers, _Postponed, _Timers, CurStatus, CurState, _IsHib}) ->
{ok, {CurStatus, CurState}}.
system_replace_state(StatusFun, {Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, IsHib}) ->
{NewStatus, NewState} = StatusFun(CurStatus, CurState),
{ok, {NewStatus, NewState}, {Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, NewStatus, NewState, IsHib}}.
format_status(Opt, [PDict, SysStatus, Parent, Debug, {Parent, Name, Module, _GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, _IsHib}]) ->
Header = gen:format_status_header("Status for gen_ipc", Name),
Log = sys:get_log(Debug),
[
{header, Header},
{data,
[
{"Status", SysStatus},
{"Parent", Parent},
{"Time-outs", listTimeouts(Timers)},
{"Logged Events", Log},
{"Postponed", Postponed}
]
} |
case format_status(Opt, PDict, Parent, Name, Module, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState) of
L when is_list(L) -> L;
T -> [T]
end
].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% sys callbacks end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% API helpers start %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec call(ServerRef :: serverRef(), Request :: term()) -> Reply :: term().
call(ServerRef, Request) ->
try gcall(ServerRef, '$gen_call', Request) of
{ok, Reply} ->
Reply
catch Class:Reason ->
erlang:raise(Class, {Reason, {?MODULE, call, [ServerRef, Request]}}, ?STACKTRACE())
end.
-spec call(ServerRef :: serverRef(), Request :: term(), Timeout :: timeout()) -> Reply :: term().
call(ServerRef, Request, Timeout) ->
try gcall(ServerRef, '$gen_call', Request, Timeout) of
{ok, Reply} ->
Reply
catch Class:Reason ->
erlang:raise(Class, {Reason, {?MODULE, call, [ServerRef, Request]}}, ?STACKTRACE())
end.
%%% -----------------------------------------------------------------
%%% Make a call to servers at several nodes.
%%% Returns: {[Replies],[BadNodes]}
%%% A Timeout can be given
%%%
%%% A middleman process is used in case late answers arrives after
%%% the timeout. If they would be allowed to glog the callers message
%%% queue, it would probably become confused. Late answers will
%%% now arrive to the terminated middleman and so be discarded.
%%% -----------------------------------------------------------------
-spec multi_call(
Name :: atom(),
Request :: term()
) ->
{Replies ::
[{Node :: node(), Reply :: term()}],
BadNodes :: [node()]
}.
%%
multi_call(Name, Request)
when is_atom(Name) ->
multi_call([node() | nodes()], Name, Request, infinity).
-spec multi_call(
Nodes :: [node()],
Name :: atom(),
Request :: term()
) ->
{Replies ::
[{Node :: node(), Reply :: term()}],
BadNodes :: [node()]
}.
%%
multi_call(Nodes, Name, Request)
when is_list(Nodes), is_atom(Name) ->
multi_call(Nodes, Name, Request, infinity).
-spec multi_call(
Nodes :: [node()],
Name :: atom(),
Request :: term(),
Timeout :: timeout()
) ->
{Replies ::
[{Node :: node(), Reply :: term()}],
BadNodes :: [node()]
}.
-define(
is_timeout(X), ( (X) =:= infinity orelse ( is_integer(X) andalso (X) >= 0 ) )).
multi_call(Nodes, Name, Request, Timeout)
when is_list(Nodes), is_atom(Name), ?is_timeout(Timeout) ->
Alias = alias(),
try
Timer = if Timeout == infinity -> undefined;
true -> erlang:start_timer(Timeout, self(), Alias)
end,
Reqs = mc_send(Nodes, Name, Alias, Request, Timer, []),
mc_recv(Reqs, Alias, Timer, [], [])
after
_ = unalias(Alias)
end.
-dialyzer({no_improper_lists, mc_send/6}).
mc_send([], _Name, _Alias, _Request, _Timer, Reqs) ->
Reqs;
mc_send([Node|Nodes], Name, Alias, Request, Timer, Reqs) when is_atom(Node) ->
NN = {Name, Node},
Mon = try
erlang:monitor(process, NN, [{tag, Alias}])
catch
error:badarg ->
%% Node not alive...
M = make_ref(),
Alias ! {Alias, M, process, NN, noconnection},
M
end,
try
%% We use 'noconnect' since it is no point in bringing up a new
%% connection if it was not brought up by the monitor signal...
_ = erlang:send(NN,
{'$gen_call', {self(), [[alias|Alias]|Mon]}, Request},
[noconnect]),
ok
catch
_:_ ->
ok
end,
mc_send(Nodes, Name, Alias, Request, Timer, [[Node|Mon]|Reqs]);
mc_send(_BadNodes, _Name, Alias, _Request, Timer, Reqs) ->
%% Cleanup then fail...
unalias(Alias),
mc_cancel_timer(Timer, Alias),
_ = mc_recv_tmo(Reqs, Alias, [], []),
error(badarg).
mc_recv([], Alias, Timer, Replies, BadNodes) ->
mc_cancel_timer(Timer, Alias),
unalias(Alias),
{Replies, BadNodes};
mc_recv([[Node|Mon] | RestReqs] = Reqs, Alias, Timer, Replies, BadNodes) ->
receive
{[[alias|Alias]|Mon], Reply} ->
erlang:demonitor(Mon, [flush]),
mc_recv(RestReqs, Alias, Timer, [{Node,Reply}|Replies], BadNodes);
{Alias, Mon, process, _, _} ->
mc_recv(RestReqs, Alias, Timer, Replies, [Node|BadNodes]);
{timeout, Timer, Alias} ->
unalias(Alias),
mc_recv_tmo(Reqs, Alias, Replies, BadNodes)
end.
mc_recv_tmo([], _Alias, Replies, BadNodes) ->
{Replies, BadNodes};
mc_recv_tmo([[Node|Mon] | RestReqs], Alias, Replies, BadNodes) ->
erlang:demonitor(Mon),
receive
{[[alias|Alias]|Mon], Reply} ->
mc_recv_tmo(RestReqs, Alias, [{Node,Reply}|Replies], BadNodes);
{Alias, Mon, process, _, _} ->
mc_recv_tmo(RestReqs, Alias, Replies, [Node|BadNodes])
after
0 ->
mc_recv_tmo(RestReqs, Alias, Replies, [Node|BadNodes])
end.
mc_cancel_timer(undefined, _Alias) ->
ok;
mc_cancel_timer(Timer, Alias) ->
case erlang:cancel_timer(Timer) of
false ->
receive
{timeout, Timer, Alias} ->
ok
end;
_ ->
ok
end.
-spec cast(ServerRef :: serverRef(), Msg :: term()) -> ok.
cast({global, Name}, Msg) ->
try global:send(Name, {'$gen_cast', Msg}),
ok
catch _:_ -> ok
end;
cast({via, RegMod, Name}, Msg) ->
try RegMod:send(Name, {'$gen_cast', Msg}),
ok
catch _:_ -> ok
end;
cast({Name, Node} = Dest, Msg) when is_atom(Name), is_atom(Node) ->
try erlang:send(Dest, {'$gen_cast', Msg}),
ok
catch _:_ -> ok
end;
cast(Dest, Msg) ->
try erlang:send(Dest, {'$gen_cast', Msg}),
ok
catch _:_ -> ok
end.
-spec send(ServerRef :: serverRef(), Msg :: term()) -> ok.
send({global, Name}, Msg) ->
try global:send(Name, Msg),
ok
catch _:_ -> ok
end;
send({via, RegMod, Name}, Msg) ->
try RegMod:send(Name, Msg),
ok
catch _:_ -> ok
end;
send({Name, Node} = Dest, Msg) when is_atom(Name), is_atom(Node) ->
try erlang:send(Dest, Msg),
ok
catch _:_ -> ok
end;
send(Dest, Msg) ->
try erlang:send(Dest, Msg),
ok
catch _:_ -> ok
end.
%% 异步广播,不返回任何内容,只是发送“ n”祈祷
abcast(Name, Msg) when is_atom(Name) ->
doAbcast([node() | nodes()], Name, Msg).
abcast(Nodes, Name, Msg) when is_list(Nodes), is_atom(Name) ->
doAbcast(Nodes, Name, Msg).
doAbcast(Nodes, Name, Msg) ->
[
begin
try erlang:send({Name, Node}, {'$gen_cast', Msg}),
ok
catch
_:_ -> ok
end
end || Node <- Nodes
],
ok.
%% gen_event send_request/3
-spec send_request(ServerRef :: serverRef(), epmHandler(), term()) -> request_id().
send_request(Name, Handler, Query) ->
gen:send_request(Name, self(), {'$epmCall', Handler, Query}).
%% -----------------------------------------------------------------
%% Send a request to a generic server and return a Key which should be
%% used with wait_response/2 or check_response/2 to fetch the
%% result of the request.
-spec send_request(ServerRef::serverRef(), Request::term()) ->
ReqId::request_id().
send_request(ServerRef, Request) ->
try
gen:send_request(ServerRef, '$gen_call', Request)
catch
error:badarg ->
error(badarg, [ServerRef, Request])
end.
-spec send_request(ServerRef::serverRef(),
Request::term(),
Label::term(),
ReqIdCollection::request_id_collection()) ->
NewReqIdCollection::request_id_collection().
send_request(ServerRef, Request, Label, ReqIdCol) ->
try
gen:send_request(ServerRef, '$gen_call', Request, Label, ReqIdCol)
catch
error:badarg ->
error(badarg, [ServerRef, Request, Label, ReqIdCol])
end.
-spec wait_response(ReqId, WaitTime) -> Result when
ReqId :: request_id(),
WaitTime :: response_timeout(),
Response :: {reply, Reply::term()}
| {error, {Reason::term(), serverRef()}},
Result :: Response | 'timeout'.
wait_response(ReqId, WaitTime) ->
try
gen:wait_response(ReqId, WaitTime)
catch
error:badarg ->
error(badarg, [ReqId, WaitTime])
end.
-spec wait_response(ReqIdCollection, WaitTime, Delete) -> Result when
ReqIdCollection :: request_id_collection(),
WaitTime :: response_timeout(),
Delete :: boolean(),
Response :: {reply, Reply::term()} |
{error, {Reason::term(), serverRef()}},
Result :: {Response,
Label::term(),
NewReqIdCollection::request_id_collection()} |
'no_request' |
'timeout'.
wait_response(ReqIdCol, WaitTime, Delete) ->
try
gen:wait_response(ReqIdCol, WaitTime, Delete)
catch
error:badarg ->
error(badarg, [ReqIdCol, WaitTime, Delete])
end.
-spec receive_response(ReqId, Timeout) -> Result when
ReqId :: request_id(),
Timeout :: response_timeout(),
Response :: {reply, Reply::term()} |
{error, {Reason::term(), serverRef()}},
Result :: Response | 'timeout'.
receive_response(ReqId, Timeout) ->
try
gen:receive_response(ReqId, Timeout)
catch
error:badarg ->
error(badarg, [ReqId, Timeout])
end.
-spec receive_response(ReqIdCollection, Timeout, Delete) -> Result when
ReqIdCollection :: request_id_collection(),
Timeout :: response_timeout(),
Delete :: boolean(),
Response :: {reply, Reply::term()} |
{error, {Reason::term(), serverRef()}},
Result :: {Response,
Label::term(),
NewReqIdCollection::request_id_collection()} |
'no_request' |
'timeout'.
receive_response(ReqIdCol, Timeout, Delete) ->
try
gen:receive_response(ReqIdCol, Timeout, Delete)
catch
error:badarg ->
error(badarg, [ReqIdCol, Timeout, Delete])
end.
-spec check_response(Msg, ReqId) -> Result when
Msg :: term(),
ReqId :: request_id(),
Response :: {reply, Reply::term()} |
{error, {Reason::term(), serverRef()}},
Result :: Response | 'no_reply'.
check_response(Msg, ReqId) ->
try
gen:check_response(Msg, ReqId)
catch
error:badarg ->
error(badarg, [Msg, ReqId])
end.
-spec check_response(Msg, ReqIdCollection, Delete) -> Result when
Msg :: term(),
ReqIdCollection :: request_id_collection(),
Delete :: boolean(),
Response :: {reply, Reply::term()} |
{error, {Reason::term(), serverRef()}},
Result :: {Response,
Label::term(),
NewReqIdCollection::request_id_collection()} |
'no_request' |
'no_reply'.
check_response(Msg, ReqIdCol, Delete) ->
try
gen:check_response(Msg, ReqIdCol, Delete)
catch
error:badarg ->
error(badarg, [Msg, ReqIdCol, Delete])
end.
-spec reqids_new() ->
NewReqIdCollection::request_id_collection().
reqids_new() ->
gen:reqids_new().
-spec reqids_size(ReqIdCollection::request_id_collection()) ->
non_neg_integer().
reqids_size(ReqIdCollection) ->
try
gen:reqids_size(ReqIdCollection)
catch
error:badarg -> error(badarg, [ReqIdCollection])
end.
-spec reqids_add(ReqId::request_id(), Label::term(),
ReqIdCollection::request_id_collection()) ->
NewReqIdCollection::request_id_collection().
reqids_add(ReqId, Label, ReqIdCollection) ->
try
gen:reqids_add(ReqId, Label, ReqIdCollection)
catch
error:badarg -> error(badarg, [ReqId, Label, ReqIdCollection])
end.
-spec reqids_to_list(ReqIdCollection::request_id_collection()) ->
[{ReqId::request_id(), Label::term()}].
reqids_to_list(ReqIdCollection) ->
try
gen:reqids_to_list(ReqIdCollection)
catch
error:badarg -> error(badarg, [ReqIdCollection])
end.
%% Reply from a status machine callback to whom awaits in call/2
-spec reply([replyAction(), ...] | replyAction()) -> ok.
reply({reply, {To, Tag}, Reply}) ->
try To ! {Tag, Reply},
ok
catch _:_ ->
ok
end;
reply(Replies) when is_list(Replies) ->
[greply(From, Reply) || {reply, From, Reply} <- Replies],
ok.
-compile({inline, [reply/2]}).
-spec reply(From :: from(), Reply :: term()) -> ok.
reply(From, Reply) ->
greply(From, Reply).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% API helpers end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% gen_event start %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
epmRequest({global, Name}, Msg) ->
try global:send(Name, Msg),
ok
catch _:_ -> ok
end;
epmRequest({via, RegMod, Name}, Msg) ->
try RegMod:send(Name, Msg),
ok
catch _:_ -> ok
end;
epmRequest(EpmSrv, Cmd) ->
EpmSrv ! Cmd,
ok.
-spec epm_info(serverRef(), epmHandler(), term()) -> term().
epm_info(EpmSrv, EpmHandler, Msg) ->
epmRequest(EpmSrv, {'$epm_info', EpmHandler, Msg}).
-spec info_notify(serverRef(), term()) -> 'ok'.
info_notify(EpmSrv, Event) ->
epmRequest(EpmSrv, {'$epm_info', '$infoNotify', Event}).
epmRpc(EpmSrv, Cmd) ->
try gcall(EpmSrv, '$epm_call', Cmd, infinity) of
{ok, Reply} ->
Reply
catch Class:Reason ->
erlang:raise(Class, {Reason, {?MODULE, call, [EpmSrv, Cmd, infinity]}}, ?STACKTRACE())
end.
epmRpc(EpmSrv, Cmd, Timeout) ->
try gcall(EpmSrv, '$epm_call', Cmd, Timeout) of
{ok, Reply} ->
Reply
catch Class:Reason ->
erlang:raise(Class, {Reason, {?MODULE, call, [EpmSrv, Cmd, Timeout]}}, ?STACKTRACE())
end.
-spec call_notify(serverRef(), term()) -> 'ok'.
call_notify(EpmSrv, Event) ->
epmRpc(EpmSrv, {'$syncNotify', Event}).
-spec epm_call(serverRef(), epmHandler(), term()) -> term().
epm_call(EpmSrv, EpmHandler, Query) ->
epmRpc(EpmSrv, {'$epmCall', EpmHandler, Query}).
-spec epm_call(serverRef(), epmHandler(), term(), timeout()) -> term().
epm_call(EpmSrv, EpmHandler, Query, Timeout) ->
epmRpc(EpmSrv, {'$epmCall', EpmHandler, Query}, Timeout).
-spec add_epm(serverRef(), epmHandler(), term()) -> term().
add_epm(EpmSrv, EpmHandler, Args) ->
epmRpc(EpmSrv, {'$addEpm', EpmHandler, Args}).
-spec add_sup_epm(serverRef(), epmHandler(), term()) -> term().
add_sup_epm(EpmSrv, EpmHandler, Args) ->
epmRpc(EpmSrv, {'$addSupEpm', EpmHandler, Args, self()}).
-spec del_epm(serverRef(), epmHandler(), term()) -> term().
del_epm(EpmSrv, EpmHandler, Args) ->
epmRpc(EpmSrv, {'$delEpm', EpmHandler, Args}).
-spec swap_epm(serverRef(), {epmHandler(), term()}, {epmHandler(), term()}) -> 'ok' | {'error', term()}.
swap_epm(EpmSrv, {H1, A1}, {H2, A2}) ->
epmRpc(EpmSrv, {'$swapEpm', H1, A1, H2, A2}).
-spec swap_sup_epm(serverRef(), {epmHandler(), term()}, {epmHandler(), term()}) -> 'ok' | {'error', term()}.
swap_sup_epm(EpmSrv, {H1, A1}, {H2, A2}) ->
epmRpc(EpmSrv, {'$swapSupEpm', H1, A1, H2, A2, self()}).
-spec which_epm(serverRef()) -> [epmHandler()].
which_epm(EpmSrv) ->
epmRpc(EpmSrv, '$which_handlers').
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% EPM inner fun
addNewEpm(InitRet, EpmHers, Module, EpmId, EpmSup) ->
case InitRet of
{ok, State} ->
EpmHer = #epmHer{epmId = EpmId, epmM = Module, epmS = State, epmSup = EpmSup},
{ok, EpmHers#{EpmId => EpmHer}, false};
{ok, State, hibernate} ->
EpmHer = #epmHer{epmId = EpmId, epmM = Module, epmS = State, epmSup = EpmSup},
{ok, EpmHers#{EpmId => EpmHer}, true};
Other ->
{Other, EpmHers, false}
end.
doAddEpm(EpmHers, {Module, _SubId} = EpmId, Args, EpmSup) ->
case EpmHers of
#{EpmId := _EpmHer} ->
{{error, existed}, EpmHers, false};
_ ->
try Module:init(Args) of
Result ->
addNewEpm(Result, EpmHers, Module, EpmId, EpmSup)
catch
throw:Ret ->
addNewEpm(Ret, EpmHers, Module, EpmId, EpmSup);
C:R:S ->
{{error, {C, R, S}}, EpmHers, false}
end
end;
doAddEpm(EpmHers, Module, Args, EpmSup) ->
case EpmHers of
#{Module := _EpmHer} ->
{{error, existed}, EpmHers, false};
_ ->
try Module:init(Args) of
Result ->
addNewEpm(Result, EpmHers, Module, Module, EpmSup)
catch
throw:Ret ->
addNewEpm(Ret, EpmHers, Module, Module, EpmSup);
C:R:S ->
{{error, {C, R, S}}, EpmHers, false}
end
end.
doAddSupEpm(EpmHers, EpmHandler, Args, EpmSup) ->
case doAddEpm(EpmHers, EpmHandler, Args, EpmSup) of
{ok, _, _} = Result ->
link(EpmSup),
Result;
Ret ->
Ret
end.
doSwapEpm(EpmHers, EpmId1, Args1, EpmMId, Args2) ->
case EpmHers of
#{EpmId1 := #epmHer{epmSup = EpmSup} = EpmHer} ->
State2 = epmTerminate(EpmHer, Args1, swapped, {swapped, EpmMId, EpmSup}),
NewEpmHers = maps:remove(EpmId1, EpmHers),
case EpmSup of
false ->
doAddEpm(NewEpmHers, EpmMId, {Args2, State2}, undefined);
_ ->
doAddSupEpm(NewEpmHers, EpmMId, {Args2, State2}, EpmSup)
end;
undefined ->
doAddEpm(EpmHers, EpmMId, {Args2, undefined}, undefined)
end.
doSwapSupEpm(EpmHers, EpmId1, Args1, EpmMId, Args2, EpmSup) ->
case EpmHers of
#{EpmId1 := EpmHer} ->
State2 = epmTerminate(EpmHer, Args1, swapped, {swapped, EpmMId, EpmSup}),
NewEpmHers = maps:remove(EpmId1, EpmHers),
doAddSupEpm(NewEpmHers, EpmMId, {Args2, State2}, EpmSup);
undefined ->
doAddSupEpm(EpmHers, EpmMId, {Args2, undefined}, EpmSup)
end.
doNotify(EpmHers, Func, Event, _Form) ->
allNotify(iterator(EpmHers), Func, Event, false, EpmHers, false).
allNotify(Iterator, Func, Event, From, TemEpmHers, IsHib) ->
case next(Iterator) of
{K, _V, NextIterator} ->
{NewEpmHers, NewIsHib} = doEpmHandle(TemEpmHers, K, Func, Event, From),
allNotify(NextIterator, Func, Event, From, NewEpmHers, IsHib orelse NewIsHib);
_ ->
{TemEpmHers, IsHib}
end.
doEpmHandle(EpmHers, EpmHandler, Func, Event, From) ->
case EpmHers of
#{EpmHandler := #epmHer{epmM = EpmM, epmS = EpmS} = EpmHer} ->
try EpmM:Func(Event, EpmS) of
Result ->
handleEpmCR(Result, EpmHers, EpmHer, Event, From)
catch
throw:Ret ->
handleEpmCR(Ret, EpmHers, EpmHer, Event, From);
C:R:S ->
epmTerminate(EpmHer, {error, {C, R, S}}, Event, crash),
NewEpmHers = maps:remove(EpmHandler, EpmHers),
{NewEpmHers, false}
end;
_ ->
try_greply(From, {error, bad_module}),
{EpmHers, false}
end.
doDelEpm(EpmHers, EpmHandler, Args) ->
case EpmHers of
#{EpmHandler := EpmHer} ->
epmTerminate(EpmHer, Args, delete, normal),
{ok, maps:remove(EpmHandler, EpmHers)};
undefined ->
{{error, module_not_found}, EpmHers}
end.
epmTerminate(#epmHer{epmM = EpmM, epmS = State} = EpmHer, Args, LastIn, Reason) ->
case erlang:function_exported(EpmM, terminate, 2) of
true ->
Res = (catch EpmM:terminate(Args, State)),
reportTerminate(EpmHer, Reason, Args, LastIn, Res),
Res;
false ->
reportTerminate(EpmHer, Reason, Args, LastIn, ok),
ok
end.
reportTerminate(EpmHer, crash, {error, Why}, LastIn, _) ->
reportTerminate2(EpmHer, Why, LastIn);
%% How == normal | shutdown | {swapped, NewHandler, NewSupervisor}
reportTerminate(EpmHer, How, _, LastIn, _) ->
reportTerminate2(EpmHer, How, LastIn).
reportTerminate2(#epmHer{epmSup = EpmSup, epmId = EpmId, epmS = State} = EpmHer, Reason, LastIn) ->
report_error(EpmHer, Reason, State, LastIn),
case EpmSup of
undefined ->
ok;
_ ->
EpmSup ! {gen_event_EXIT, EpmId, Reason},
ok
end.
report_error(_EpmHer, normal, _, _) -> ok;
report_error(_EpmHer, shutdown, _, _) -> ok;
report_error(_EpmHer, {swapped, _, _}, _, _) -> ok;
report_error(#epmHer{epmId = EpmId, epmM = EpmM}, Reason, State, LastIn) ->
?LOG_ERROR(
#{
label => {gen_ipc, epm_terminate},
handler => {EpmId, EpmM},
name => undefined,
last_message => LastIn,
state => State,
reason => Reason
},
#{
domain => [otp],
report_cb => fun gen_ipc:epm_log/1,
error_logger => #{tag => error}
}).
epm_log(#{label := {gen_ipc, epm_terminate}, handler := Handler, name := SName, last_message := LastIn, state := State, reason := Reason}) ->
Reason1 =
case Reason of
{'EXIT', {undef, [{M, F, A, L} | MFAs]}} ->
case code:is_loaded(M) of
false ->
{'module could not be loaded', [{M, F, A, L} | MFAs]};
_ ->
case erlang:function_exported(M, F, length(A)) of
true ->
{undef, [{M, F, A, L} | MFAs]};
false ->
{'function not exported', [{M, F, A, L} | MFAs]}
end
end;
{'EXIT', Why} ->
Why;
_ ->
Reason
end,
{"** gen_ipc emp handler ~p crashed.~n"
"** Was installed in ~tp~n"
"** Last event was: ~tp~n"
"** When handler state == ~tp~n"
"** Reason == ~tp~n", [Handler, SName, LastIn, State, Reason1]}.
epmStopAll(EpmHers) ->
allStop(iterator(EpmHers)).
allStop(Iterator) ->
case next(Iterator) of
{_K, V, NextIterator} ->
epmTerminate(V, stop, 'receive', shutdown),
case element(#epmHer.epmSup, V) of
undefined ->
ignore;
EpmSup ->
unlink(EpmSup)
end,
allStop(NextIterator);
none ->
ok
end.
epmStopOne(ExitEmpSup, EpmHers) ->
forStopOne(iterator(EpmHers), ExitEmpSup, EpmHers).
forStopOne(Iterator, ExitEmpSup, TemEpmHers) ->
case next(Iterator) of
{K, V, NextIterator} ->
case element(#epmHer.epmSup, V) =:= ExitEmpSup of
true ->
epmTerminate(V, stop, 'receive', shutdown),
forStopOne(NextIterator, ExitEmpSup, maps:remove(K, TemEpmHers));
_ ->
forStopOne(NextIterator, ExitEmpSup, TemEpmHers)
end;
none ->
TemEpmHers
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% gen_event end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
listify(Item) when is_list(Item) ->
Item;
listify(Item) ->
[Item].
%%%==========================================================================
%%% Internal callbacks
wakeupFromHib(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug) ->
%% 这是一条新消息,唤醒了我们,因此我们必须立即收到它
receiveIng(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, true).
%%%==========================================================================
%% Entry point for system_continue/3
reLoopEntry(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, IsHib) ->
if
IsHib ->
proc_lib:hibernate(?MODULE, wakeupFromHib, [Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug]);
true ->
receiveIng(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, false)
end.
%% 接收新的消息
receiveIng(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, IsHib) ->
receive
Msg ->
case Msg of
{'$gen_call', From, Request} ->
matchCallMsg(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, From, Request);
{'$gen_cast', Cast} ->
matchCastMsg(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, Cast);
{timeout, TimerRef, TimeoutType} ->
case Timers of
#{TimeoutType := {TimerRef, TimeoutMsg}} ->
NewTimers = maps:remove(TimeoutType, Timers),
matchTimeoutMsg(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, NewTimers, CurStatus, CurState, Debug, TimeoutType, TimeoutMsg);
_ ->
matchInfoMsg(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, Msg)
end;
{system, PidFrom, Request} ->
%% 不返回但尾递归调用 system_continue/3
sys:handle_system_msg(Request, PidFrom, Parent, ?MODULE, Debug, {Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, IsHib}, IsHib);
{'EXIT', PidFrom, Reason} ->
case Parent =:= PidFrom of
true ->
terminate(exit, Reason, ?STACKTRACE(), Parent, Name, Module, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, []);
_ ->
NewEpmHers = epmStopOne(PidFrom, EpmHers),
matchInfoMsg(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, NewEpmHers, Postponed, Timers, CurStatus, CurState, Debug, Msg)
end;
{'$epm_call', From, Request} ->
matchEpmCallMsg(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, From, Request);
{'$epm_info', CmdOrEmpHandler, Event} ->
matchEpmInfoMsg(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, CmdOrEmpHandler, Event);
_ ->
matchInfoMsg(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, Msg)
end
after
HibernateAfterTimeout ->
proc_lib:hibernate(?MODULE, wakeupFromHib, [Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug])
end.
matchCallMsg(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, From, Request) ->
CurEvent = {{call, From}, Request},
?SYS_DEBUG(Debug, Name, {in, CurEvent, CurStatus}),
try Module:handleCall(Request, CurStatus, CurState, From) of
Result ->
handleEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, [CurEvent], Result, ?CB_FORM_EVENT, From)
catch
throw:Result ->
handleEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, [CurEvent], Result, ?CB_FORM_EVENT, From);
Class:Reason:Strace ->
try_greply(From, {error, {inner_error, {Class, Reason, Strace}}}),
innerError(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, [CurEvent], Class, Reason, Strace)
end.
matchCastMsg(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, Cast) ->
CurEvent = {cast, Cast},
?SYS_DEBUG(Debug, Name, {in, CurEvent, CurStatus}),
try Module:handleCast(Cast, CurStatus, CurState) of
Result ->
handleEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, [CurEvent], Result, ?CB_FORM_EVENT, false)
catch
throw:Result ->
handleEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, [CurEvent], Result, ?CB_FORM_EVENT, false);
Class:Reason:Strace ->
innerError(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, [CurEvent], Class, Reason, Strace)
end.
matchInfoMsg(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, Msg) ->
CurEvent = {info, Msg},
?SYS_DEBUG(Debug, Name, {in, CurEvent, CurStatus}),
try Module:handleInfo(Msg, CurStatus, CurState) of
Result ->
handleEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, [CurEvent], Result, ?CB_FORM_EVENT, false)
catch
throw:Result ->
handleEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, [CurEvent], Result, ?CB_FORM_EVENT, false);
Class:Reason:Strace ->
innerError(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, [CurEvent], Class, Reason, Strace)
end.
matchTimeoutMsg(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, TimeoutType, TimeoutMsg) ->
CurEvent = {TimeoutType, TimeoutMsg},
?SYS_DEBUG(Debug, Name, {in, CurEvent, CurStatus}),
try Module:handleOnevent(TimeoutType, TimeoutMsg, CurStatus, CurState) of
Result ->
handleEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, [CurEvent], Result, ?CB_FORM_EVENT, false)
catch
throw:Result ->
handleEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, [CurEvent], Result, ?CB_FORM_EVENT, false);
Class:Reason:Strace ->
innerError(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, [CurEvent], Class, Reason, Strace)
end.
matchEpmCallMsg(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, From, Request) ->
?SYS_DEBUG(Debug, Name, {in, Request, CurStatus}),
case Request of
'$which_handlers' ->
reply(From, EpmHers);
{'$addEpm', EpmHandler, Args} ->
{Reply, NewEpmHers, IsHib} = doAddEpm(EpmHers, EpmHandler, Args, undefined),
reply(From, Reply),
reLoopEntry(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, NewEpmHers, Postponed, Timers, CurStatus, CurState, Debug, IsHib);
{'$addSupEpm', EpmHandler, Args, EpmSup} ->
{Reply, NewEpmHers, IsHib} = doAddSupEpm(EpmHers, EpmHandler, Args, EpmSup),
reply(From, Reply),
reLoopEntry(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, NewEpmHers, Postponed, Timers, CurStatus, CurState, Debug, IsHib);
{'$delEpm', EpmHandler, Args} ->
{Reply, NewEpmHers} = doDelEpm(EpmHers, EpmHandler, Args),
reply(From, Reply),
receiveIng(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, NewEpmHers, Postponed, Timers, CurStatus, CurState, Debug, false);
{'$swapEpm', EpmId1, Args1, EpmId2, Args2} ->
{Reply, NewEpmHers, IsHib} = doSwapEpm(EpmHers, EpmId1, Args1, EpmId2, Args2),
reply(From, Reply),
reLoopEntry(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, NewEpmHers, Postponed, Timers, CurStatus, CurState, Debug, IsHib);
{'$swapSupEpm', EpmId1, Args1, EpmId2, Args2, SupPid} ->
{Reply, NewEpmHers, IsHib} = doSwapSupEpm(EpmHers, EpmId1, Args1, EpmId2, Args2, SupPid),
reply(From, Reply),
reLoopEntry(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, NewEpmHers, Postponed, Timers, CurStatus, CurState, Debug, IsHib);
{'$syncNotify', Event} ->
{NewEpmHers, IsHib} = doNotify(EpmHers, handleEvent, Event, false),
reply(From, ok),
startEpmCall(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, NewEpmHers, Postponed, Timers, CurStatus, CurState, Debug, handleEpmEvent, Request, IsHib);
{'$epmCall', EpmHandler, Query} ->
{NewEpmHers, IsHib} = doEpmHandle(EpmHers, EpmHandler, handleCall, Query, From),
startEpmCall(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, NewEpmHers, Postponed, Timers, CurStatus, CurState, Debug, handleEpmCall, Request, IsHib)
end.
matchEpmInfoMsg(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, CmdOrEmpHandler, Event) ->
?SYS_DEBUG(Debug, Name, {in, {CmdOrEmpHandler, Event}, CurStatus}),
case CmdOrEmpHandler of
'$infoNotify' ->
{NewEpmHers, IsHib} = doNotify(EpmHers, handleEvent, Event, false),
startEpmCall(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, NewEpmHers, Postponed, Timers, CurStatus, CurState, Debug, handleEpmEvent, Event, IsHib);
EpmHandler ->
{NewEpmHers, IsHib} = doEpmHandle(EpmHers, EpmHandler, handleInfo, Event, false),
startEpmCall(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, NewEpmHers, Postponed, Timers, CurStatus, CurState, Debug, handleEpmInfo, Event, IsHib)
end.
startEpmCall(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, CallbackFun, Event, IsHib) ->
case erlang:function_exported(Module, CallbackFun, 3) of
true ->
?SYS_DEBUG(Debug, Name, {in, Event, CurStatus}),
try Module:CallbackFun(Event, CurStatus, CurState) of
Result ->
handleEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, [Event], Result, ?CB_FORM_EVENT, false)
catch
throw:Ret ->
handleEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, [Event], Ret, ?CB_FORM_EVENT, false);
Class:Reason:Strace ->
innerError(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, [Event], Class, Reason, Strace)
end;
_ ->
reLoopEntry(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, IsHib)
end.
startEnterCall(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, PrevStatus, CurState, CurStatus, Debug, LeftEvents, NextEs, IsPos, IsHib, DoAfter) ->
try Module:handleEnter(PrevStatus, CurStatus, CurState) of
Result ->
handleEnterCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, PrevStatus, CurState, CurStatus, Debug, LeftEvents, NextEs, IsPos, IsHib, DoAfter, Result)
catch
throw:Result ->
handleEnterCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, PrevStatus, CurState, CurStatus, Debug, LeftEvents, NextEs, IsPos, IsHib, DoAfter, Result);
Class:Reason:Strace ->
innerError(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents, Class, Reason, Strace)
end.
startAfterCall(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents, Args) ->
try Module:handleAfter(Args, CurStatus, CurState) of
Result ->
handleEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents, Result, ?CB_FORM_AFTER, false)
catch
throw:Result ->
handleEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents, Result, ?CB_FORM_AFTER, false);
Class:Reason:Strace ->
innerError(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, {doAfter, Args}, Class, Reason, Strace)
end.
startEventCall(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents, {Type, Content}) ->
case Type of
'cast' ->
try Module:handleCast(Content, CurStatus, CurState) of
Result ->
handleEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents, Result, ?CB_FORM_EVENT, false)
catch
throw:Ret ->
handleEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents, Ret, ?CB_FORM_EVENT, false);
Class:Reason:Strace ->
innerError(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, {Type, Content}, Class, Reason, Strace)
end;
'info' ->
try Module:handleInfo(Content, CurStatus, CurState) of
Result ->
handleEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents, Result, ?CB_FORM_EVENT, false)
catch
throw:Ret ->
handleEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents, Ret, ?CB_FORM_EVENT, false);
Class:Reason:Strace ->
innerError(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, {Type, Content}, Class, Reason, Strace)
end;
{'call', From} ->
try Module:handleCall(Content, CurStatus, CurState, From) of
Result ->
handleEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents, Result, ?CB_FORM_EVENT, From)
catch
throw:Ret ->
handleEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents, Ret, ?CB_FORM_EVENT, From);
Class:Reason:Strace ->
try_greply(From, {error, {inner_error, {Class, Reason, Strace}}}),
innerError(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, {Type, Content}, Class, Reason, Strace)
end;
_ ->
try Module:handleOnevent(Type, Content, CurStatus, CurState) of
Result ->
handleEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents, Result, ?CB_FORM_EVENT, false)
catch
throw:Ret ->
handleEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents, Ret, ?CB_FORM_EVENT, false);
Class:Reason:Strace ->
innerError(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, {Type, Content}, Class, Reason, Strace)
end
end.
%% handleEpmCallbackRet
handleEpmCR(Result, EpmHers, #epmHer{epmId = EpmId} = EpmHer, Event, From) ->
case Result of
kpS ->
{EpmHers, false};
{noreply, NewEpmS} ->
MewEpmHer = setelement(#epmHer.epmS, EpmHer, NewEpmS),
{EpmHers#{EpmId := MewEpmHer}, false};
{noreply, NewEpmS, hibernate} ->
MewEpmHer = setelement(#epmHer.epmS, EpmHer, NewEpmS),
{EpmHers#{EpmId := MewEpmHer}, true};
{swapEpm, NewEpmS, Args1, EpmMId, Args2} ->
#epmHer{epmId = OldEpmMId, epmSup = EpmSup} = MewEpmHer = setelement(#epmHer.epmS, EpmHer, NewEpmS),
State = epmTerminate(MewEpmHer, Args1, swapped, {swapped, OldEpmMId, EpmSup}),
TemEpmHers = maps:remove(EpmId, EpmHers),
{_, NewEpmHers, IsHib} =
case EpmSup of
undefined ->
doAddEpm(TemEpmHers, EpmMId, {Args2, State}, undefined);
_ ->
doAddSupEpm(TemEpmHers, EpmMId, {Args2, State}, EpmSup)
end,
{NewEpmHers, IsHib};
{swapEpm, Reply, NewEpmS, Args1, EpmMId, Args2} ->
reply(From, Reply),
#epmHer{epmId = OldEpmMId, epmSup = EpmSup} = MewEpmHer = setelement(#epmHer.epmS, EpmHer, NewEpmS),
State = epmTerminate(MewEpmHer, Args1, swapped, {swapped, OldEpmMId, EpmSup}),
TemEpmHers = maps:remove(EpmId, EpmHers),
{_, NewEpmHers, IsHib} =
case EpmSup of
undefined ->
doAddEpm(TemEpmHers, EpmMId, {Args2, State}, undefined);
_ ->
doAddSupEpm(TemEpmHers, EpmMId, {Args2, State}, EpmSup)
end,
{NewEpmHers, IsHib};
removeEpm ->
epmTerminate(EpmHer, removeEpm, remove, normal),
{maps:remove(EpmId, EpmHers), false};
{removeEpm, Reply} ->
reply(From, Reply),
epmTerminate(EpmHer, removeEpm, remove, normal),
{maps:remove(EpmId, EpmHers), false};
{reply, Reply} ->
reply(From, Reply),
{EpmHers, false};
{reply, Reply, NewEpmS} ->
reply(From, Reply),
MewEpmHer = setelement(#epmHer.epmS, EpmHer, NewEpmS),
{EpmHers#{EpmId := MewEpmHer}, false};
{reply, Reply, NewEpmS, hibernate} ->
reply(From, Reply),
MewEpmHer = setelement(#epmHer.epmS, EpmHer, NewEpmS),
{EpmHers#{EpmId := MewEpmHer}, true};
Other ->
epmTerminate(EpmHer, {error, Other}, Event, crash),
{maps:remove(EpmId, EpmHers), false}
end.
%% handleEnterCallbackRet
handleEnterCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, PrevStatus, CurState, CurStatus, Debug, LeftEvents, NextEs, IsPos, IsHib, DoAfter, Result) ->
case Result of
{kpS, NewState} ->
dealEnterCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, PrevStatus, NewState, CurStatus, Debug, LeftEvents, NextEs, IsPos, IsHib, DoAfter, false);
{kpS, NewState, Actions} ->
parseEnterAL(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, PrevStatus, NewState, CurStatus, Debug, LeftEvents, NextEs, IsPos, IsHib, DoAfter, false, listify(Actions));
kpS_S ->
dealEnterCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, PrevStatus, CurState, CurStatus, Debug, LeftEvents, NextEs, IsPos, IsHib, DoAfter, false);
{kpS_S, Actions} ->
parseEnterAL(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, PrevStatus, CurState, CurStatus, Debug, LeftEvents, NextEs, IsPos, IsHib, DoAfter, false, listify(Actions));
{reS, NewState} ->
dealEnterCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, PrevStatus, NewState, CurStatus, Debug, LeftEvents, NextEs, IsPos, IsHib, DoAfter, true);
{reS, NewState, Actions} ->
parseEnterAL(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, PrevStatus, NewState, CurStatus, Debug, LeftEvents, NextEs, IsPos, IsHib, DoAfter, true, listify(Actions));
reS_S ->
dealEnterCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, PrevStatus, CurState, CurStatus, Debug, LeftEvents, NextEs, IsPos, IsHib, DoAfter, true);
{reS_S, Actions} ->
parseEnterAL(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, PrevStatus, CurState, CurStatus, Debug, LeftEvents, NextEs, IsPos, IsHib, DoAfter, true, listify(Actions));
stop ->
terminate(exit, normal, ?STACKTRACE(), Parent, Name, Module, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents);
{stop, Reason} ->
terminate(exit, Reason, ?STACKTRACE(), Parent, Name, Module, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents);
{stop, Reason, NewState} ->
terminate(exit, Reason, ?STACKTRACE(), Parent, Name, Module, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, NewState, Debug, LeftEvents);
{stopReply, Reason, Replies} ->
?SYS_DEBUG(Debug, Name, {out, Replies}),
try
terminate(exit, Reason, ?STACKTRACE(), Parent, Name, Module, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents)
after
case Replies of
{reply, RFrom, Reply} ->
reply(RFrom, Reply);
_ ->
[reply(RFrom, Reply) || {reply, RFrom, Reply} <- Replies],
ok
end
end;
{stopReply, Reason, Replies, NewState} ->
?SYS_DEBUG(Debug, Name, {out, Replies}),
try
terminate(exit, Reason, ?STACKTRACE(), Parent, Name, Module, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, NewState, Debug, LeftEvents)
after
case Replies of
{reply, RFrom, Reply} ->
reply(RFrom, Reply);
_ ->
[reply(RFrom, Reply) || {reply, RFrom, Reply} <- Replies],
ok
end
end;
_ ->
innerError(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents, error, {bad_handleEnterCR, Result}, ?STACKTRACE())
end.
handleEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents, Result, CallbackForm, From) ->
case Result of
{noreply, NewState} ->
receiveIng(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, NewState, Debug, false);
{noreply, NewState, Option} ->
case Option of
hibernate ->
reLoopEntry(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, NewState, Debug, true);
{doAfter, Args} ->
startAfterCall(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, NewState, Debug, [], Args);
_Ret ->
innerError(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents, error, {bad_noreply, _Ret}, ?STACKTRACE())
end;
{reply, Reply, NewState} ->
reply(From, Reply),
?SYS_DEBUG(Debug, Name, {out, Reply, From}),
receiveIng(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, NewState, Debug, false);
{reply, Reply, NewState, Option} ->
reply(From, Reply),
?SYS_DEBUG(Debug, Name, {out, Reply, From}),
case Option of
hibernate ->
reLoopEntry(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, NewState, Debug, true);
{doAfter, Args} ->
startAfterCall(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, NewState, Debug, [], Args);
_Ret ->
innerError(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents, error, {bad_reply, _Ret}, ?STACKTRACE())
end;
{sreply, Reply, NewStatus, NewState} ->
reply(From, Reply),
?SYS_DEBUG(Debug, Name, {out, Reply, From}),
dealEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, NewState, NewStatus, Debug, LeftEvents, NewStatus =/= CurStatus);
{sreply, Reply, NewStatus, NewState, Actions} ->
reply(From, Reply),
?SYS_DEBUG(Debug, Name, {out, Reply, From}),
parseEventAL(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, NewState, NewStatus, Debug, LeftEvents, NewStatus =/= CurStatus, listify(Actions), CallbackForm);
{nextS, NewStatus, NewState} ->
dealEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, NewState, NewStatus, Debug, LeftEvents, NewStatus =/= CurStatus);
{nextS, NewStatus, NewState, Actions} ->
parseEventAL(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, NewState, NewStatus, Debug, LeftEvents, NewStatus =/= CurStatus, listify(Actions), CallbackForm);
{kpS, NewState} ->
dealEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, NewState, CurStatus, Debug, LeftEvents, false);
{kpS, NewState, Actions} ->
parseEventAL(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, NewState, CurStatus, Debug, LeftEvents, false, listify(Actions), CallbackForm);
kpS_S ->
dealEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, CurStatus, Debug, LeftEvents, false);
{kpS_S, Actions} ->
parseEventAL(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, CurStatus, Debug, LeftEvents, false, listify(Actions), CallbackForm);
{reS, NewState} ->
dealEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, NewState, CurStatus, Debug, LeftEvents, true);
{reS, NewState, Actions} ->
parseEventAL(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, NewState, CurStatus, Debug, LeftEvents, true, listify(Actions), CallbackForm);
reS_S ->
dealEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, CurStatus, Debug, LeftEvents, true);
{reS_S, Actions} ->
parseEventAL(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, CurStatus, Debug, LeftEvents, true, listify(Actions), CallbackForm);
stop ->
terminate(exit, normal, ?STACKTRACE(), Parent, Name, Module, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents);
{stop, Reason} ->
terminate(exit, Reason, ?STACKTRACE(), Parent, Name, Module, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents);
{stop, Reason, NewState} ->
terminate(exit, Reason, ?STACKTRACE(), Parent, Name, Module, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, NewState, Debug, LeftEvents);
{stopReply, Reason, Replies} ->
?SYS_DEBUG(Debug, Name, {out, Replies}),
try
terminate(exit, Reason, ?STACKTRACE(), Parent, Name, Module, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents)
after
case Replies of
{reply, RFrom, Reply} ->
reply(RFrom, Reply);
_ when is_list(Replies) ->
[reply(RFrom, Reply) || {reply, RFrom, Reply} <- Replies],
ok;
_ ->
_ = reply(From, Replies)
end
end;
{stopReply, Reason, Replies, NewState} ->
?SYS_DEBUG(Debug, Name, {out, Replies}),
try
terminate(exit, Reason, ?STACKTRACE(), Parent, Name, Module, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, NewState, Debug, LeftEvents)
after
case Replies of
{reply, RFrom, Reply} ->
_ = reply(RFrom, Reply);
_ when is_list(Replies) ->
[reply(RFrom, Reply) || {reply, RFrom, Reply} <- Replies],
ok;
_ ->
_ = reply(From, Replies)
end
end;
_ ->
innerError(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents, error, {bad_handleEventCR, Result}, ?STACKTRACE())
end.
dealEnterCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, NewStatus, Debug, LeftEvents, NextEs, IsPos, IsHib, DoAfter, IsCallEnter) ->
NewTimers = cancelESTimeout(CurStatus =:= NewStatus, Timers),
case IsEnter andalso IsCallEnter of
true ->
startEnterCall(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, NewTimers, CurStatus, CurState, NewStatus, Debug, LeftEvents, NextEs, IsPos, IsHib, DoAfter);
false ->
performTransitions(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, NewTimers, CurStatus, CurState, NewStatus, Debug, LeftEvents, NextEs, IsPos, IsHib, DoAfter)
end.
%% dealEventCallbackRet
dealEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, NewStatus, Debug, LeftEvents, IsCallEnter) ->
NewTimers = cancelESTimeout(CurStatus =:= NewStatus, Timers),
case IsEnter andalso IsCallEnter of
true ->
startEnterCall(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, NewTimers, CurStatus, CurState, NewStatus, Debug, LeftEvents, [], false, false, false);
false ->
performTransitions(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, NewTimers, CurStatus, CurState, NewStatus, Debug, LeftEvents, [], false, false, false)
end.
%% 处理enter callback 动作列表
%% parseEnterActionsList
parseEnterAL(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, NewStatus, Debug, LeftEvents, NextEs, IsPos, IsCallEnter, IsHib, DoAfter, Actions) ->
NewTimers = cancelESTimeout(CurStatus =:= NewStatus, Timers),
%% enter 调用不能改成状态 actions 不能返回 IsPos = true 但是可以取消之前的推迟 设置IsPos = false 不能设置 doafter 不能插入事件
case Actions of
[] ->
case IsEnter andalso IsCallEnter of
true ->
startEnterCall(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, NewTimers, CurStatus, CurState, NewStatus, Debug, LeftEvents, NextEs, IsPos, IsHib, DoAfter);
_ ->
performTransitions(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, NewTimers, CurStatus, CurState, NewStatus, Debug, LeftEvents, NextEs, IsPos, IsHib, DoAfter)
end;
_ ->
case doParseAL(Actions, ?CB_FORM_ENTER, Name, IsEnter, NewTimers, Debug, IsPos, IsHib, DoAfter, NextEs) of
{error, ErrorContent} ->
innerError(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, Actions, error, ErrorContent, ?STACKTRACE());
{NewIsEnter, NNewTimers, Debug, NewIsPos, NewIsHib, DoAfter, NewNextEs} ->
case NewIsEnter andalso IsCallEnter of
true ->
startEnterCall(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, NewIsEnter, EpmHers, Postponed, NNewTimers, CurStatus, CurState, NewStatus, Debug, LeftEvents, NewNextEs, NewIsPos, NewIsHib, DoAfter);
_ ->
performTransitions(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, NewIsEnter, EpmHers, Postponed, NNewTimers, CurStatus, CurState, NewStatus, Debug, LeftEvents, NewNextEs, NewIsPos, NewIsHib, DoAfter)
end
end
end.
%% 处理非 enter 或者after callback 返回的动作列表
%% parseEventActionsList
parseEventAL(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, NewStatus, Debug, LeftEvents, IsCallEnter, Actions, CallbackForm) ->
NewTimers = cancelESTimeout(CurStatus =:= NewStatus, Timers),
case Actions of
[] ->
case IsEnter andalso IsCallEnter of
true ->
startEnterCall(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, NewTimers, CurStatus, CurState, NewStatus, Debug, LeftEvents, [], false, false, false);
_ ->
performTransitions(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, NewTimers, CurStatus, CurState, NewStatus, Debug, LeftEvents, [], false, false, false)
end;
_ ->
case doParseAL(Actions, CallbackForm, Name, IsEnter, NewTimers, Debug, false, false, false, []) of
{error, ErrorContent} ->
innerError(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, Actions, error, ErrorContent, ?STACKTRACE());
{NewIsEnter, NNewTimers, Debug, NewIsPos, NewIsHib, MewDoAfter, NewNextEs} ->
case NewIsEnter andalso IsCallEnter of
true ->
startEnterCall(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, NNewTimers, CurStatus, CurState, NewStatus, Debug, LeftEvents, NewNextEs, NewIsPos, NewIsHib, MewDoAfter);
_ ->
performTransitions(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, NNewTimers, CurStatus, CurState, NewStatus, Debug, LeftEvents, NewNextEs, NewIsPos, NewIsHib, MewDoAfter)
end
end
end.
%% loopParseActionsList
doParseAL([], _CallbackForm, _Name, IsEnter, Times, Debug, IsPos, IsHib, DoAfter, NextEs) ->
{IsEnter, Times, Debug, IsPos, IsHib, DoAfter, NextEs};
doParseAL([OneAction | LeftActions], CallbackForm, Name, IsEnter, Timers, Debug, IsPos, IsHib, DoAfter, NextEs) ->
case OneAction of
{reply, From, Reply} ->
reply(From, Reply),
?SYS_DEBUG(Debug, Name, {out, Reply, From}),
doParseAL(LeftActions, CallbackForm, Name, IsEnter, Timers, Debug, IsPos, IsHib, DoAfter, NextEs);
{eTimeout, Time, TimeoutMsg} ->
case Time of
infinity ->
NewTimers = doCancelTimer(eTimeout, Timers),
doParseAL(LeftActions, CallbackForm, Name, IsEnter, NewTimers, Debug, IsPos, IsHib, DoAfter, NextEs);
_ ->
TimerRef = erlang:start_timer(Time, self(), eTimeout),
?SYS_DEBUG(Debug, Name, {start_timer, {eTimeout, Time, TimeoutMsg, []}}),
NewTimers = doRegisterTimer(eTimeout, TimerRef, TimeoutMsg, Timers),
doParseAL(LeftActions, CallbackForm, Name, IsEnter, NewTimers, Debug, IsPos, IsHib, DoAfter, NextEs)
end;
{sTimeout, Time, TimeoutMsg} ->
case Time of
infinity ->
NewTimers = doCancelTimer(sTimeout, Timers),
doParseAL(LeftActions, CallbackForm, Name, IsEnter, NewTimers, Debug, IsPos, IsHib, DoAfter, NextEs);
_ ->
TimerRef = erlang:start_timer(Time, self(), sTimeout),
?SYS_DEBUG(Debug, Name, {start_timer, {sTimeout, Time, TimeoutMsg, []}}),
NewTimers = doRegisterTimer(sTimeout, TimerRef, TimeoutMsg, Timers),
doParseAL(LeftActions, CallbackForm, Name, IsEnter, NewTimers, Debug, IsPos, IsHib, DoAfter, NextEs)
end;
{gTimeout, TimeoutName, Time, TimeoutMsg} ->
case Time of
infinity ->
NewTimers = doCancelTimer(TimeoutName, Timers),
doParseAL(LeftActions, CallbackForm, Name, IsEnter, NewTimers, Debug, IsPos, IsHib, DoAfter, NextEs);
_ ->
TimerRef = erlang:start_timer(Time, self(), TimeoutName),
?SYS_DEBUG(Debug, Name, {start_timer, {TimeoutName, Time, TimeoutMsg, []}}),
NewTimers = doRegisterTimer(TimeoutName, TimerRef, TimeoutMsg, Timers),
doParseAL(LeftActions, CallbackForm, Name, IsEnter, NewTimers, Debug, IsPos, IsHib, DoAfter, NextEs)
end;
{eTimeout, Time, TimeoutMsg, Options} ->
case Time of
infinity ->
NewTimers = doCancelTimer(eTimeout, Timers),
doParseAL(LeftActions, CallbackForm, Name, IsEnter, NewTimers, Debug, IsPos, IsHib, DoAfter, NextEs);
_ ->
TimerRef = erlang:start_timer(Time, self(), eTimeout, Options),
?SYS_DEBUG(Debug, Name, {start_timer, {eTimeout, Time, TimeoutMsg, Options}}),
NewTimers = doRegisterTimer(eTimeout, TimerRef, TimeoutMsg, Timers),
doParseAL(LeftActions, CallbackForm, Name, IsEnter, NewTimers, Debug, IsPos, IsHib, DoAfter, NextEs)
end;
{sTimeout, Time, TimeoutMsg, Options} ->
case Time of
infinity ->
NewTimers = doCancelTimer(sTimeout, Timers),
doParseAL(LeftActions, CallbackForm, Name, IsEnter, NewTimers, Debug, IsPos, IsHib, DoAfter, NextEs);
_ ->
TimerRef = erlang:start_timer(Time, self(), sTimeout, Options),
?SYS_DEBUG(Debug, Name, {start_timer, {sTimeout, Time, TimeoutMsg, Options}}),
NewTimers = doRegisterTimer(sTimeout, TimerRef, TimeoutMsg, Timers),
doParseAL(LeftActions, CallbackForm, Name, IsEnter, NewTimers, Debug, IsPos, IsHib, DoAfter, NextEs)
end;
{gTimeout, TimeoutName, Time, TimeoutMsg, Options} ->
case Time of
infinity ->
NewTimers = doCancelTimer(TimeoutName, Timers),
doParseAL(LeftActions, CallbackForm, Name, IsEnter, NewTimers, Debug, IsPos, IsHib, DoAfter, NextEs);
_ ->
TimerRef = erlang:start_timer(Time, self(), TimeoutName, Options),
?SYS_DEBUG(Debug, Name, {start_timer, {TimeoutName, Time, TimeoutMsg, Options}}),
NewTimers = doRegisterTimer(TimeoutName, TimerRef, TimeoutMsg, Timers),
doParseAL(LeftActions, CallbackForm, Name, IsEnter, NewTimers, Debug, IsPos, IsHib, DoAfter, NextEs)
end;
{u_eTimeout, NewTimeoutMsg} ->
NewTimers = doUpdateTimer(eTimeout, NewTimeoutMsg, Timers),
doParseAL(LeftActions, CallbackForm, Name, IsEnter, NewTimers, Debug, IsPos, IsHib, DoAfter, NextEs);
{u_sTimeout, NewTimeoutMsg} ->
NewTimers = doUpdateTimer(sTimeout, NewTimeoutMsg, Timers),
doParseAL(LeftActions, CallbackForm, Name, IsEnter, NewTimers, Debug, IsPos, IsHib, DoAfter, NextEs);
{u_gTimeout, TimeoutName, NewTimeoutMsg} ->
NewTimers = doUpdateTimer(TimeoutName, NewTimeoutMsg, Timers),
doParseAL(LeftActions, CallbackForm, Name, IsEnter, NewTimers, Debug, IsPos, IsHib, DoAfter, NextEs);
c_eTimeout ->
NewTimers = doCancelTimer(eTimeout, Timers),
doParseAL(LeftActions, CallbackForm, Name, IsEnter, NewTimers, Debug, IsPos, IsHib, DoAfter, NextEs);
c_sTimeout ->
NewTimers = doCancelTimer(sTimeout, Timers),
doParseAL(LeftActions, CallbackForm, Name, IsEnter, NewTimers, Debug, IsPos, IsHib, DoAfter, NextEs);
{c_gTimeout, TimeoutName} ->
NewTimers = doCancelTimer(TimeoutName, Timers),
doParseAL(LeftActions, CallbackForm, Name, IsEnter, NewTimers, Debug, IsPos, IsHib, DoAfter, NextEs);
{isEnter, NewIsEnter} ->
?SYS_DEBUG(Debug, Name, {change_isEnter, NewIsEnter}),
doParseAL(LeftActions, CallbackForm, Name, Timers, NewIsEnter, Debug, IsPos, IsHib, DoAfter, NextEs);
{isHib, NewIsHib} ->
doParseAL(LeftActions, CallbackForm, Name, IsEnter, Timers, Debug, IsPos, NewIsHib, DoAfter, NextEs);
{isPos, NewIsPos} when (not NewIsPos orelse CallbackForm == ?CB_FORM_EVENT) ->
doParseAL(LeftActions, CallbackForm, Name, IsEnter, Timers, Debug, NewIsPos, IsHib, DoAfter, NextEs);
{doAfter, Args} when CallbackForm == ?CB_FORM_EVENT ->
doParseAL(LeftActions, CallbackForm, Name, IsEnter, Timers, Debug, IsPos, IsHib, {true, Args}, NextEs);
{nextE, Type, Content} when CallbackForm == ?CB_FORM_EVENT orelse CallbackForm == ?CB_FORM_AFTER ->
%% 处理next_event动作
doParseAL(LeftActions, CallbackForm, Name, IsEnter, Timers, Debug, IsPos, IsHib, DoAfter, [{Type, Content} | NextEs]);
_ ->
{error, {bad_ActionType, OneAction}}
end.
% checkTimeOptions({TimeoutType, Time, TimeoutMsg, Options} = NewTV) ->
% case Options of
% [{abs, true}] when ?ABS_TIMEOUT(Time) ->
% NewTV;
% [{abs, false}] when ?REL_TIMEOUT(Time) ->
% {TimeoutType, Time, TimeoutMsg};
% [] when ?REL_TIMEOUT(Time) ->
% {TimeoutType, Time, TimeoutMsg};
% _ ->
% %% 如果将来 start_timer opt扩展了 这里的代码也要修改
% error_timeout_opt
% end.
%% 进行状态转换
performTransitions(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, NewStatus, Debug, AllLeftEvents, NextEs, IsPos, IsHib, DoAfter) ->
%% 已收集所有选项,并缓冲next_events。执行实际状态转换。如果推迟则将当前事件移至推迟
%% 此时 NextEs的顺序与最开始出现的顺序相反. 后面执行的顺序 当前新增事件 + 反序的Postpone事件 + LeftEvents
case AllLeftEvents of
[] ->
CurEvent = undefined,
LeftEvents = [];
_ ->
[CurEvent | LeftEvents] = AllLeftEvents
end,
?SYS_DEBUG(Debug, Name, case IsPos of true -> {postpone, CurEvent, CurStatus, NewStatus}; _ -> {consume, CurEvent, CurStatus, NewStatus} end),
if
CurStatus =:= NewStatus ->
%% Cancel event timeout
if
IsPos ->
LastLeftEvents =
case NextEs of
[] ->
LeftEvents;
[Es1] ->
[Es1 | LeftEvents];
[Es2, Es1] ->
[Es1, Es2 | LeftEvents];
_ ->
lists:reverse(NextEs, LeftEvents)
end,
performEvents(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, [CurEvent | Postponed], Timers, NewStatus, CurState, Debug, LastLeftEvents, IsHib, DoAfter);
true ->
LastLeftEvents =
case NextEs of
[] ->
LeftEvents;
[Es1] ->
[Es1 | LeftEvents];
[Es2, Es1] ->
[Es1, Es2 | LeftEvents];
_ ->
lists:reverse(NextEs, LeftEvents)
end,
performEvents(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, NewStatus, CurState, Debug, LastLeftEvents, IsHib, DoAfter)
end;
true ->
%% 状态发生改变 重试推迟的事件
if
IsPos ->
NewLeftEvents =
case Postponed of
[] ->
[CurEvent | LeftEvents];
[E1] ->
[E1, CurEvent | LeftEvents];
[E2, E1] ->
[E1, E2, CurEvent | LeftEvents];
_ ->
lists:reverse(Postponed, [CurEvent | LeftEvents])
end,
LastLeftEvents =
case NextEs of
[] ->
NewLeftEvents;
[Es1] ->
[Es1 | NewLeftEvents];
[Es2, Es1] ->
[Es1, Es2 | NewLeftEvents];
_ ->
lists:reverse(NextEs, NewLeftEvents)
end,
performEvents(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, [], Timers, NewStatus, CurState, Debug, LastLeftEvents, IsHib, DoAfter);
true ->
NewLeftEvents =
case Postponed of
[] ->
LeftEvents;
[E1] ->
[E1 | LeftEvents];
[E2, E1] ->
[E1, E2 | LeftEvents];
_ ->
lists:reverse(Postponed, LeftEvents)
end,
LastLeftEvents =
case NextEs of
[] ->
NewLeftEvents;
[Es1] ->
[Es1 | NewLeftEvents];
[Es2, Es1] ->
[Es1, Es2 | NewLeftEvents];
_ ->
lists:reverse(NextEs, NewLeftEvents)
end,
performEvents(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, [], Timers, NewStatus, CurState, Debug, LastLeftEvents, IsHib, DoAfter)
end
end.
%% 状态转换已完成,如果有排队事件,则继续循环,否则获取新事件
performEvents(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents, IsHib, DoAfter) ->
% io:format("loop_done: status_data = ~p ~n postponed = ~p LeftEvents = ~p ~n timers = ~p.~n", [S#status.status_data,,S#status.postponed,LeftEvents,S#status.timers]),
case DoAfter of
{true, Args} ->
%% 这里 IsHib设置会被丢弃 按照gen_server中的设计 continue 和 hiernate是互斥的
startAfterCall(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents, Args);
_ ->
case LeftEvents of
[] ->
reLoopEntry(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, IsHib);
[Event | _Events] ->
%% 循环直到没有排队事件
if
IsHib ->
%% _ = garbage_collect(),
erts_internal:garbage_collect(major);
true ->
ignore
end,
startEventCall(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents, Event)
end
end.
%% 取消事件超时 和 状态超时
cancelESTimeout(true, Timers) ->
%% Cancel event timeout
case Timers of
#{eTimeout := {TimerRef, _TimeoutMsg}} ->
cancelTimer(eTimeout, TimerRef, Timers);
_ ->
Timers
end;
cancelESTimeout(false, Timers) ->
%% 取消 status and event timeout
case Timers of
#{sTimeout := {STimerRef, _STimeoutMsg}} ->
TemTimer = cancelTimer(sTimeout, STimerRef, Timers),
case TemTimer of
#{eTimeout := {ETimerRef, _ETimeoutMsg}} ->
cancelTimer(eTimeout, ETimerRef, TemTimer);
_ ->
TemTimer
end;
_ ->
case Timers of
#{eTimeout := {ETimerRef, _ETimeoutMsg}} ->
cancelTimer(eTimeout, ETimerRef, Timers);
_ ->
Timers
end
end.
doRegisterTimer(TimeoutType, NewTimerRef, TimeoutMsg, Timers) ->
case Timers of
#{TimeoutType := {OldTimerRef, _OldTimeMsg}} ->
justCancelTimer(TimeoutType, OldTimerRef),
Timers#{TimeoutType := {NewTimerRef, TimeoutMsg}};
_ ->
Timers#{TimeoutType => {NewTimerRef, TimeoutMsg}}
end.
doCancelTimer(TimeoutType, Timers) ->
case Timers of
#{TimeoutType := {TimerRef, _TimeoutMsg}} ->
cancelTimer(TimeoutType, TimerRef, Timers);
_ ->
Timers
end.
doUpdateTimer(TimeoutType, Timers, TimeoutMsg) ->
case Timers of
#{TimeoutType := {TimerRef, _OldTimeoutMsg}} ->
Timers#{TimeoutType := {TimerRef, TimeoutMsg}};
_ ->
Timers
end.
justCancelTimer(TimeoutType, TimerRef) ->
case erlang:cancel_timer(TimerRef) of
false ->
%% 找不到计时器,我们还没有看到超时消息
receive
{timeout, TimerRef, TimeoutType} ->
%% 丢弃该超时消息
ok
after 0 ->
ok
end;
_ ->
%% Timer 已经运行了
ok
end.
cancelTimer(TimeoutType, TimerRef, Timers) ->
case erlang:cancel_timer(TimerRef) of
false ->
%% 找不到计时器,我们还没有看到超时消息
receive
{timeout, TimerRef, TimeoutType} ->
%% 丢弃该超时消息
ok
after 0 ->
ok
end;
_ ->
%% Timer 已经运行了
ok
end,
maps:remove(TimeoutType, Timers).
%% 排队立即超时事件(超时0事件)
%% 自事件超时0起,事件得到特殊处理
%% 任何收到的事件都会取消事件超时,
%% 因此,如果在事件超时0事件之前存在入队事件-事件超时被取消,因此没有事件。
%% 其他(status_timeout和{timeout,Name})超时0个事件
%% 在事件计时器超时0事件之后发生的事件被认为是
%% 属于在事件计时器之后启动的计时器
%% 已触发超时0事件,因此它们不会取消事件计时器。
%% mergeTimeoutEvents([], _Status, _CycleData, Debug, Events) ->
%% {Events, Debug};
%% mergeTimeoutEvents([{eTimeout, _} = TimeoutEvent | TimeoutEvents], Status, CycleData, Debug, []) ->
%% %% 由于队列中没有其他事件,因此添加该事件零超时事件
%% ?SYS_DEBUG(Debug, CycleData, {insert_timeout, TimeoutEvent, Status}),
%% mergeTimeoutEvents(TimeoutEvents, Status, CycleData, Debug, [TimeoutEvent]);
%% mergeTimeoutEvents([{eTimeout, _} | TimeoutEvents], Status, CycleData, Debug, Events) ->
%% %% 忽略,因为队列中还有其他事件,因此它们取消了事件超时0。
%% mergeTimeoutEvents(TimeoutEvents, Status, CycleData, Debug, Events);
%% mergeTimeoutEvents([TimeoutEvent | TimeoutEvents], Status, CycleData, Debug, Events) ->
%% %% Just prepend all others
%% ?SYS_DEBUG(Debug, CycleData, {insert_timeout, TimeoutEvent, Status}),
%% mergeTimeoutEvents(TimeoutEvents, Status, CycleData, Debug, [TimeoutEvent | Events]).
%% Return a list of all pending timeouts
listTimeouts(Timers) ->
{maps:size(Timers), allTimer(iterator(Timers), [])}.
allTimer(Iterator, Acc) ->
case next(Iterator) of
{TimeoutType, {_TimerRef, TimeoutMsg}, NextIterator} ->
allTimer(NextIterator, [{TimeoutType, TimeoutMsg} | Acc]);
none ->
Acc
end.
%%---------------------------------------------------------------------------
innerError(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, MsgEvent, Class, Reason, Stacktrace) ->
case GbhOpts of
#gbhOpts{daemon = true} ->
error_msg({innerError, {Class, Reason, Stacktrace}}, Parent, Name, Module, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, MsgEvent),
case erlang:function_exported(Module, handleError, 2) of
true ->
try Module:handleError({Class, Reason, Stacktrace}, CurState) of
Result ->
handleEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, [MsgEvent], Result, ?CB_FORM_EVENT, false)
catch
throw:Result ->
handleEventCR(Parent, Name, Module, GbhOpts, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, [MsgEvent], Result, ?CB_FORM_EVENT, false);
IClass:IReason:IStrace ->
error_msg({handleError, {IClass, IReason, IStrace}}, Parent, Name, Module, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, {Class, Reason, Stacktrace}),
kpS
end;
false ->
kpS
end;
_ ->
terminate(Class, Reason, Stacktrace, Parent, Name, Module, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, MsgEvent)
end.
terminate(Class, Reason, Stacktrace, Parent, Name, Module, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents) ->
epmStopAll(EpmHers),
%% 这里停止所有的epm 但是并没有更新 epmHers为#{} 目前感觉没必要清理掉
case erlang:function_exported(Module, terminate, 3) of
true ->
try Module:terminate(Reason, CurStatus, CurState) of
_ -> ok
catch
throw:_ -> ok;
Class:Reason:Strace ->
error_info(Class, Reason, Strace, Parent, Name, Module, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents),
erlang:raise(Class, Reason, Strace)
end;
false ->
ok
end,
case Reason of
normal ->
?SYS_DEBUG(Debug, Name, {terminate, Reason, CurStatus});
shutdown ->
?SYS_DEBUG(Debug, Name, {terminate, Reason, CurStatus});
{shutdown, _} ->
?SYS_DEBUG(Debug, Name, {terminate, Reason, CurStatus});
_ ->
error_info(Class, Reason, Stacktrace, Parent, Name, Module, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents)
end,
case Stacktrace of
[] ->
erlang:Class(Reason);
_ ->
erlang:raise(Class, Reason, Stacktrace)
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% debug 日志 Start%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
error_info(Class, Reason, Stacktrace, Parent, Name, Module, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents) ->
Log = sys:get_log(Debug),
?LOG_ERROR(
#{
label => {gen_ipc, terminate},
name => Name,
module => Module,
queue => LeftEvents,
postponed => Postponed,
isEnter => IsEnter,
status => format_status(terminate, get(), Parent, Name, Module, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState),
timeouts => listTimeouts(Timers),
log => Log,
reason => {Class, Reason, Stacktrace},
client_info => cliStacktrace(LeftEvents)
},
#{
domain => [otp],
report_cb => fun gen_ipc:format_log/2,
error_logger => #{tag => error, report_cb => fun gen_ipc:format_log/1}}).
error_msg(Reason, Parent, Name, Module, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState, Debug, LeftEvents) ->
Log = sys:get_log(Debug),
?LOG_ERROR(
#{
label => {gen_ipc, inner_error},
name => Name,
module => Module,
queue => LeftEvents,
postponed => Postponed,
isEnter => IsEnter,
status => format_status(inner_error, get(), Parent, Name, Module, HibernateAfterTimeout, IsEnter, EpmHers, Postponed, Timers, CurStatus, CurState),
timeouts => listTimeouts(Timers),
log => Log,
reason => Reason,
client_info => cliStacktrace(LeftEvents)
},
#{
domain => [otp],
report_cb => fun gen_ipc:format_log/2,
error_logger => #{tag => error, report_cb => fun gen_ipc:format_log/1}}).
cliStacktrace([]) ->
undefined;
cliStacktrace([{{call, {Pid, _Tag}}, _Req} | _]) when is_pid(Pid) ->
if
node(Pid) =:= node() ->
case process_info(Pid, [current_stacktrace, registered_name]) of
undefined ->
{Pid, dead};
[{current_stacktrace, Stacktrace}, {registered_name, []}] ->
{Pid, {Pid, Stacktrace}};
[{current_stacktrace, Stacktrace}, {registered_name, Name}] ->
{Pid, {Name, Stacktrace}}
end;
true ->
{Pid, remote}
end;
cliStacktrace([_ | _]) ->
undefined;
cliStacktrace(_) ->
undefined.
%% format_log/1 is the report callback used by Logger handler
%% error_logger only. It is kept for backwards compatibility with
%% legacy error_logger event handlers. This function must always
%% return {Format,Args} compatible with the arguments in this module's
%% calls to error_logger prior to OTP-21.0.
format_log(Report) ->
Depth = error_logger:get_format_depth(),
FormatOpts = #{
chars_limit => unlimited,
depth => Depth,
single_line => false,
encoding => utf8
},
format_log_multi(limit_report(Report, Depth), FormatOpts).
limit_report(Report, unlimited) ->
Report;
limit_report(
#{
label := {gen_ipc, terminate},
queue := Q,
postponed := Postponed,
module := Module,
status := FmtData,
timeouts := Timeouts,
log := Log,
reason := {Class, Reason, Stacktrace},
client_info := ClientInfo
} = Report,
Depth) ->
Report#{
queue =>
case Q of
[Event | Events] ->
[io_lib:limit_term(Event, Depth) | io_lib:limit_term(Events, Depth)];
_ ->
[]
end,
postponed =>
case Postponed of
[] -> [];
_ -> io_lib:limit_term(Postponed, Depth)
end,
modules => io_lib:limit_term(Module, Depth),
status => io_lib:limit_term(FmtData, Depth),
timeouts =>
case Timeouts of
{0, _} -> Timeouts;
_ -> io_lib:limit_term(Timeouts, Depth)
end,
log =>
case Log of
[] -> [];
_ -> [io_lib:limit_term(T, Depth) || T <- Log]
end,
reason =>
{Class, io_lib:limit_term(Reason, Depth), io_lib:limit_term(Stacktrace, Depth)},
client_info => limit_client_info(ClientInfo, Depth)
};
limit_report(
#{
label := {gen_ipc, inner_error},
queue := Q,
postponed := Postponed,
module := Module,
status := FmtData,
timeouts := Timeouts,
log := Log,
reason := {Class, Reason, Stacktrace},
client_info := ClientInfo
} = Report,
Depth) ->
Report#{
queue =>
case Q of
[Event | Events] ->
[io_lib:limit_term(Event, Depth) | io_lib:limit_term(Events, Depth)];
_ ->
[]
end,
postponed =>
case Postponed of
[] -> [];
_ -> io_lib:limit_term(Postponed, Depth)
end,
modules => io_lib:limit_term(Module, Depth),
status => io_lib:limit_term(FmtData, Depth),
timeouts =>
case Timeouts of
{0, _} -> Timeouts;
_ -> io_lib:limit_term(Timeouts, Depth)
end,
log =>
case Log of
[] -> [];
_ -> [io_lib:limit_term(T, Depth) || T <- Log]
end,
reason =>
{Class, io_lib:limit_term(Reason, Depth), io_lib:limit_term(Stacktrace, Depth)},
client_info => limit_client_info(ClientInfo, Depth)
}.
limit_client_info({Pid, {Name, Stacktrace}}, Depth) ->
{Pid, {Name, io_lib:limit_term(Stacktrace, Depth)}};
limit_client_info(Client, _Depth) ->
Client.
%% format_log/2 is the report callback for any Logger handler, except
%% error_logger.
format_log(Report, FormatOpts0) ->
Default = #{
chars_limit => unlimited,
depth => unlimited,
single_line => false,
encoding => utf8
},
FormatOpts = maps:merge(Default, FormatOpts0),
IoOpts =
case FormatOpts of
#{chars_limit := unlimited} -> [];
#{chars_limit := Limit} -> [{chars_limit, Limit}]
end,
{Format, Args} = format_log_single(Report, FormatOpts),
io_lib:format(Format, Args, IoOpts).
format_log_single(
#{
label := {gen_ipc, terminate},
name := Name,
queue := Q,
%% postponed
%% isEnter
status := FmtData,
%% timeouts
log := Log,
reason := {Class, Reason, Stacktrace},
client_info := ClientInfo
},
#{single_line := true, depth := Depth} = FormatOpts) ->
P = p(FormatOpts),
{FixedReason, FixedStacktrace} = fix_reason(Class, Reason, Stacktrace),
{ClientFmt, ClientArgs} = format_client_log_single(ClientInfo, P, Depth),
Format =
lists:append(
["State machine ", P, " terminating. Reason: ", P,
case FixedStacktrace of
[] -> "";
_ -> ". Stack: " ++ P
end,
case Q of
[] -> "";
_ -> ". Last event: " ++ P
end,
". State: ", P,
case Log of
[] -> "";
_ -> ". Log: " ++ P
end,
"."]
),
Args0 =
[Name, FixedReason] ++
case FixedStacktrace of
[] -> [];
_ -> [FixedStacktrace]
end ++
case Q of
[] -> [];
[Event | _] -> [Event]
end ++
[FmtData] ++
case Log of
[] -> [];
_ -> [Log]
end,
Args =
case Depth of
unlimited ->
Args0;
_ ->
lists:flatmap(fun(A) -> [A, Depth] end, Args0)
end,
{Format ++ ClientFmt, Args ++ ClientArgs};
format_log_single(
#{
label := {gen_ipc, inner_error},
name := Name,
queue := Q,
%% postponed
%% isEnter
status := FmtData,
%% timeouts
log := Log,
reason := {Class, Reason, Stacktrace},
client_info := ClientInfo
},
#{single_line := true, depth := Depth} = FormatOpts) ->
P = p(FormatOpts),
{FixedReason, FixedStacktrace} = fix_reason(Class, Reason, Stacktrace),
{ClientFmt, ClientArgs} = format_client_log_single(ClientInfo, P, Depth),
Format =
lists:append(
["State machine ", P, " inner_error. Reason: ", P,
case FixedStacktrace of
[] -> "";
_ -> ". Stack: " ++ P
end,
case Q of
[] -> "";
_ -> ". Last event: " ++ P
end,
". State: ", P,
case Log of
[] -> "";
_ -> ". Log: " ++ P
end,
"."]
),
Args0 =
[Name, FixedReason] ++
case FixedStacktrace of
[] -> [];
_ -> [FixedStacktrace]
end ++
case Q of
[] -> [];
[Event | _] -> [Event]
end ++
[FmtData] ++
case Log of
[] -> [];
_ -> [Log]
end,
Args =
case Depth of
unlimited ->
Args0;
_ ->
lists:flatmap(fun(A) -> [A, Depth] end, Args0)
end,
{Format ++ ClientFmt, Args ++ ClientArgs};
format_log_single(Report, FormatOpts) ->
format_log_multi(Report, FormatOpts).
format_log_multi(
#{
label := {gen_ipc, terminate},
name := Name,
queue := Q,
postponed := Postponed,
module := Module,
isEnter := StateEnter,
status := FmtData,
timeouts := Timeouts,
log := Log,
reason := {Class, Reason, Stacktrace},
client_info := ClientInfo
},
#{depth := Depth} = FormatOpts) ->
P = p(FormatOpts),
{FixedReason, FixedStacktrace} = fix_reason(Class, Reason, Stacktrace),
{ClientFmt, ClientArgs} = format_client_log(ClientInfo, P, Depth),
CBMode =
case StateEnter of
true ->
[Module, 'isEnter=true'];
false ->
Module
end,
Format =
lists:append(
["** gen_ipc State machine ", P, " inner_error~n",
case Q of
[] -> "";
_ -> "** Last event = " ++ P ++ "~n"
end,
"** When server status = ", P, "~n",
"** Reason for termination = ", P, ":", P, "~n",
"** Callback modules = ", P, "~n",
"** Callback mode = ", P, "~n",
case Q of
[_, _ | _] -> "** Queued = " ++ P ++ "~n";
_ -> ""
end,
case Postponed of
[] -> "";
_ -> "** Postponed = " ++ P ++ "~n"
end,
case FixedStacktrace of
[] -> "";
_ -> "** Stacktrace =~n** " ++ P ++ "~n"
end,
case Timeouts of
{0, _} -> "";
_ -> "** Time-outs: " ++ P ++ "~n"
end,
case Log of
[] -> "";
_ -> "** Log =~n** " ++ P ++ "~n"
end]),
Args0 =
[Name |
case Q of
[] -> [];
[Event | _] -> [Event]
end] ++
[FmtData,
Class, FixedReason,
Module,
CBMode] ++
case Q of
[_ | [_ | _] = Events] -> [Events];
_ -> []
end ++
case Postponed of
[] -> [];
_ -> [Postponed]
end ++
case FixedStacktrace of
[] -> [];
_ -> [FixedStacktrace]
end ++
case Timeouts of
{0, _} -> [];
_ -> [Timeouts]
end ++
case Log of
[] -> [];
_ -> [Log]
end,
Args =
case Depth of
unlimited ->
Args0;
_ ->
lists:flatmap(fun(A) -> [A, Depth] end, Args0)
end,
{Format ++ ClientFmt, Args ++ ClientArgs};
format_log_multi(
#{
label := {gen_ipc, terminate},
name := Name,
queue := Q,
postponed := Postponed,
module := Module,
isEnter := StateEnter,
status := FmtData,
timeouts := Timeouts,
log := Log,
reason := {Class, Reason, Stacktrace},
client_info := ClientInfo
},
#{depth := Depth} = FormatOpts) ->
P = p(FormatOpts),
{FixedReason, FixedStacktrace} = fix_reason(Class, Reason, Stacktrace),
{ClientFmt, ClientArgs} = format_client_log(ClientInfo, P, Depth),
CBMode =
case StateEnter of
true ->
[Module, 'isEnter=true'];
false ->
Module
end,
Format =
lists:append(
["** gen_ipc State machine ", P, " terminating~n",
case Q of
[] -> "";
_ -> "** Last event = " ++ P ++ "~n"
end,
"** When server status = ", P, "~n",
"** Reason for termination = ", P, ":", P, "~n",
"** Callback modules = ", P, "~n",
"** Callback mode = ", P, "~n",
case Q of
[_, _ | _] -> "** Queued = " ++ P ++ "~n";
_ -> ""
end,
case Postponed of
[] -> "";
_ -> "** Postponed = " ++ P ++ "~n"
end,
case FixedStacktrace of
[] -> "";
_ -> "** Stacktrace =~n** " ++ P ++ "~n"
end,
case Timeouts of
{0, _} -> "";
_ -> "** Time-outs: " ++ P ++ "~n"
end,
case Log of
[] -> "";
_ -> "** Log =~n** " ++ P ++ "~n"
end]),
Args0 =
[Name |
case Q of
[] -> [];
[Event | _] -> [Event]
end] ++
[FmtData,
Class, FixedReason,
Module,
CBMode] ++
case Q of
[_ | [_ | _] = Events] -> [Events];
_ -> []
end ++
case Postponed of
[] -> [];
_ -> [Postponed]
end ++
case FixedStacktrace of
[] -> [];
_ -> [FixedStacktrace]
end ++
case Timeouts of
{0, _} -> [];
_ -> [Timeouts]
end ++
case Log of
[] -> [];
_ -> [Log]
end,
Args =
case Depth of
unlimited ->
Args0;
_ ->
lists:flatmap(fun(A) -> [A, Depth] end, Args0)
end,
{Format ++ ClientFmt, Args ++ ClientArgs}.
fix_reason(Class, Reason, Stacktrace) ->
case Stacktrace of
[{M, F, Args, _} | ST]
when Class =:= error, Reason =:= undef ->
case code:is_loaded(M) of
false ->
{{'module could not be loaded', M}, ST};
_ ->
Arity =
if
is_list(Args) ->
length(Args);
is_integer(Args) ->
Args
end,
case erlang:function_exported(M, F, Arity) of
true ->
{Reason, Stacktrace};
false ->
{{'function not exported', {M, F, Arity}}, ST}
end
end;
_ -> {Reason, Stacktrace}
end.
format_client_log_single(undefined, _, _) ->
{"", []};
format_client_log_single({Pid, dead}, _, _) ->
{" Client ~0p is dead.", [Pid]};
format_client_log_single({Pid, remote}, _, _) ->
{" Client ~0p is remote on node ~0p.", [Pid, node(Pid)]};
format_client_log_single({_Pid, {Name, Stacktrace0}}, P, Depth) ->
%% Minimize the stacktrace a bit for single line reports. This is
%% hopefully enough to point out the position.
Stacktrace = lists:sublist(Stacktrace0, 4),
Format = lists:append([" Client ", P, " stacktrace: ", P, "."]),
Args =
case Depth of
unlimited ->
[Name, Stacktrace];
_ ->
[Name, Depth, Stacktrace, Depth]
end,
{Format, Args}.
format_client_log(undefined, _, _) ->
{"", []};
format_client_log({Pid, dead}, _, _) ->
{"** Client ~p is dead~n", [Pid]};
format_client_log({Pid, remote}, _, _) ->
{"** Client ~p is remote on node ~p~n", [Pid, node(Pid)]};
format_client_log({_Pid, {Name, Stacktrace}}, P, Depth) ->
Format = lists:append(["** Client ", P, " stacktrace~n** ", P, "~n"]),
Args =
case Depth of
unlimited ->
[Name, Stacktrace];
_ ->
[Name, Depth, Stacktrace, Depth]
end,
{Format, Args}.
p(#{single_line := Single, depth := Depth, encoding := Enc}) ->
"~" ++ single(Single) ++ mod(Enc) ++ p(Depth);
p(unlimited) ->
"p";
p(_Depth) ->
"P".
single(true) -> "0";
single(false) -> "".
mod(latin1) -> "";
mod(_) -> "t".
%% Call Module:format_status/2 or return a default value
format_status(Opt, PDict, _Parent, _Name, Module, _HibernateAfterTimeout, _IsEnter, _EpmHers, _Postponed, _Timers, CurStatus, CurState) ->
case erlang:function_exported(Module, formatStatus, 2) of
true ->
try Module:formatStatus(Opt, [PDict, CurStatus, CurState])
catch
throw:Result -> Result;
_:_ ->
format_status_default(Opt, {{CurStatus, CurState}, atom_to_list(Module) ++ ":formatStatus/2 crashed"})
end;
false ->
format_status_default(Opt, {CurStatus, CurState})
end.
%% The default Module:format_status/3
format_status_default(Opt, State_Data) ->
case Opt of
terminate ->
State_Data;
_ ->
[{data, [{"State", State_Data}]}]
end.
print_event(Dev, SystemEvent, Name) ->
case SystemEvent of
{in, Event, Status} ->
io:format(
Dev, "*DBG* ~tp receive ~ts in Status ~tp~n",
[Name, event_string(Event), Status]);
{code_change, Event, Status} ->
io:format(
Dev, "*DBG* ~tp receive ~ts after code change in Status ~tp~n",
[Name, event_string(Event), Status]);
{out, Reply, {To, _Tag}} ->
io:format(
Dev, "*DBG* ~tp send ~tp to ~tw~n",
[Name, Reply, To]);
{out, Replys} ->
io:format(
Dev, "*DBG* ~tp sends to: ~tp~n",
[Name, Replys]);
{enter, Status} ->
io:format(
Dev, "*DBG* ~tp enter in Status ~tp~n",
[Name, Status]);
{start_timer, Action} ->
io:format(
Dev, "*DBG* ~tp start_timer ~tp ~n",
[Name, Action]);
{insert_timeout, Event, Status} ->
io:format(
Dev, "*DBG* ~tp insert_timeout ~tp in Status ~tp~n",
[Name, Event, Status]);
{terminate, Reason, Status} ->
io:format(
Dev, "*DBG* ~tp terminate ~tp in Status ~tp~n",
[Name, Reason, Status]);
{change_isEnter, IsEnter} ->
io:format(
Dev, "*DBG* ~tp change_isEnter to ~tp ~n",
[Name, IsEnter]);
{Tag, Event, Status, NextStatus}
when Tag =:= postpone; Tag =:= consume ->
StateString =
case NextStatus of
Status ->
io_lib:format("~tp", [Status]);
_ ->
io_lib:format("~tp => ~tp", [Status, NextStatus])
end,
io:format(
Dev, "*DBG* ~tp ~tw ~ts in state ~ts~n",
[Name, Tag, event_string(Event), StateString]);
_NotMatch ->
io:format(
Dev, "*DBG* ~tp NotMatch ~tp ~n",
[Name, _NotMatch])
end.
event_string(Event) ->
case Event of
{{call, {Pid, _Tag}}, Request} ->
io_lib:format("call ~tp from ~tw", [Request, Pid]);
{EventType, EventContent} ->
io_lib:format("~tw ~tp", [EventType, EventContent])
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% debug End %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%