|
|
@ -5,6 +5,8 @@ |
|
|
|
|
|
|
|
-include_lib("kernel/include/logger.hrl"). |
|
|
|
|
|
|
|
-import(maps, [iterator/1, next/1]). |
|
|
|
|
|
|
|
-export([ |
|
|
|
%% API for gen_emm |
|
|
|
start/0, start/1, start/2, start_link/0, start_link/1, start_link/2 |
|
|
@ -235,7 +237,7 @@ add_sup_epm(EpmSrv, EpmHandler, Args) -> |
|
|
|
|
|
|
|
-spec del_epm(serverRef(), epmHandler(), term()) -> term(). |
|
|
|
del_epm(EpmSrv, EpmHandler, Args) -> |
|
|
|
epmRpc(EpmSrv, {'$deleteEpm', 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}) -> |
|
|
@ -329,7 +331,7 @@ wakeupFromHib(Parent, ServerName, HibernateAfterTimeout, EpmHers, Debug) -> |
|
|
|
receiveIng(Parent, ServerName, HibernateAfterTimeout, EpmHers, Debug, IsHib) -> |
|
|
|
receive |
|
|
|
{system, From, Req} -> |
|
|
|
sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug, [ServerName, HibernateAfterTimeout, EpmHers, IsHib], IsHib); |
|
|
|
sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug, {ServerName, HibernateAfterTimeout, EpmHers, IsHib}, IsHib); |
|
|
|
{'EXIT', Parent, Reason} -> |
|
|
|
terminate_server(Reason, Parent, ServerName, EpmHers); |
|
|
|
{'$epm_call', From, Request} -> |
|
|
@ -356,8 +358,8 @@ epmCallMsg(Parent, ServerName, HibernateAfterTimeout, EpmHers, Debug, From, Requ |
|
|
|
{Reply, NewEpmHers, IsHib} = doAddSupEpm(EpmHers, EpmHandler, Args, EpmSup), |
|
|
|
reply(From, Reply), |
|
|
|
loopEntry(Parent, ServerName, HibernateAfterTimeout, NewEpmHers, NewDebug, IsHib); |
|
|
|
{'$deleteEpm', EpmHandler, Args} -> |
|
|
|
{Reply, NewEpmHers} = doDeleteEpm(EpmHers, EpmHandler, Args), |
|
|
|
{'$delEpm', EpmHandler, Args} -> |
|
|
|
{Reply, NewEpmHers} = doDelEpm(EpmHers, EpmHandler, Args), |
|
|
|
reply(From, Reply), |
|
|
|
receiveIng(Parent, ServerName, HibernateAfterTimeout, NewEpmHers, NewDebug, false); |
|
|
|
{'$swapEpm', EpmId1, Args1, EpmId2, Args2} -> |
|
|
@ -392,21 +394,22 @@ handleMsg(Parent, ServerName, HibernateAfterTimeout, EpmHers, Debug, Msg) -> |
|
|
|
NewDebug = ?SYS_DEBUG(Debug, ServerName, {in, Msg}), |
|
|
|
case Msg of |
|
|
|
{'EXIT', From, Reason} -> |
|
|
|
MSL1 = handle_exit(From, Reason, MSL, ServerName), |
|
|
|
loop(Parent, ServerName, MSL1, HibernateAfterTimeout, Debug, false); |
|
|
|
%% IMY-todo 下面修正一下相关的数据 |
|
|
|
NewEpmHers = epmStopOne(EpmHers, From), |
|
|
|
receiveIng(Parent, ServerName, HibernateAfterTimeout, NewEpmHers, NewDebug, false); |
|
|
|
{_From, Tag, stop} -> |
|
|
|
catch terminate_server(normal, Parent, MSL, ServerName), |
|
|
|
reply(Tag, ok); |
|
|
|
try terminate_server(normal, Parent, EpmHers, ServerName) |
|
|
|
after |
|
|
|
reply(Tag, ok) |
|
|
|
end; |
|
|
|
{_From, Tag, get_modules} -> |
|
|
|
reply(Tag, get_modules(MSL)), |
|
|
|
loop(Parent, ServerName, MSL, HibernateAfterTimeout, Debug, false); |
|
|
|
reply(Tag, get_modules(EpmHers)), |
|
|
|
receiveIng(Parent, ServerName, HibernateAfterTimeout, EpmHers, NewDebug, false); |
|
|
|
_ -> |
|
|
|
{Hib, MSL1} = doNotify(EpmHers, handleInfo, Msg, false), |
|
|
|
loopEntry(Parent, ServerName, MSL1, HibernateAfterTimeout, NewDebug, Hib) |
|
|
|
{NewEpmHers, IsHib} = doNotify(EpmHers, handleInfo, EpmHers, false), |
|
|
|
loopEntry(Parent, ServerName, HibernateAfterTimeout, NewEpmHers, NewDebug, IsHib) |
|
|
|
end. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
|
|
|
%% EPM inner fun |
|
|
|
addNewEpm(InitRet, EpmHers, Module, EpmId, EpmSup) -> |
|
|
@ -477,9 +480,9 @@ doSwapEpm(EpmHers, EpmId1, Args1, EpmMId, Args2) -> |
|
|
|
end. |
|
|
|
|
|
|
|
doSwapSupEpm(EpmHers, EpmId1, Args1, EpmMId, Args2, EpmSup) -> |
|
|
|
case EpmHers(EpmId1) of |
|
|
|
#{EpmId1 := EpmHer} -> |
|
|
|
State2 = epmTerminate(EpmHer, Args1, swapped, {swapped, EpmMId, EpmSup}), |
|
|
|
case EpmHers of |
|
|
|
#{EpmId1 := #epmHer{epmSup = OldEpmSup} = EpmHer} -> |
|
|
|
State2 = epmTerminate(EpmHer, Args1, swapped, {swapped, EpmMId, OldEpmSup}), |
|
|
|
NewEpmHers = maps:remove(EpmId1, EpmHers), |
|
|
|
doAddSupEpm(NewEpmHers, EpmMId, {Args2, State2}, EpmSup); |
|
|
|
undefined -> |
|
|
@ -499,25 +502,26 @@ allNotify(Iterator, Func, Event, From, TemEpmHers, IsHib) -> |
|
|
|
{TemEpmHers, IsHib} |
|
|
|
end. |
|
|
|
|
|
|
|
doEpmHandle(EpmHers, EpmHandler, Func, Event, From) -> |
|
|
|
doEpmHandle(EpmHers, EpmId, Func, Event, From) -> |
|
|
|
case EpmHers of |
|
|
|
#{EpmHandler := #epmHer{epmM = EpmM, epmS = EpmS} = EpmHer} -> |
|
|
|
#{EpmId := #epmHer{epmM = EpmM, epmS = EpmS} = EpmHer} -> |
|
|
|
try EpmM:Func(Event, EpmS) of |
|
|
|
Result -> |
|
|
|
handleEpmCR(Result, EpmHers, EpmHer, Event, From) |
|
|
|
handleEpmCR(Result, EpmHers, EpmId, EpmHer, Event, From) |
|
|
|
catch |
|
|
|
throw:Ret -> |
|
|
|
handleEpmCR(Ret, EpmHers, EpmHer, Event, From); |
|
|
|
handleEpmCR(Ret, EpmHers, EpmId, EpmHer, Event, From); |
|
|
|
C:R -> |
|
|
|
epmTerminate(EpmHer, {error, {C, R, ?STACKTRACE()}}, Event, crash), |
|
|
|
NewEpmHers = maps:remove(EpmHandler, EpmHer), |
|
|
|
NewEpmHers = maps:remove(EpmId, EpmHer), |
|
|
|
{NewEpmHers, false} |
|
|
|
end; |
|
|
|
_ -> |
|
|
|
try_reply(From, {error, bad_module}), |
|
|
|
{EpmHers, false} |
|
|
|
end. |
|
|
|
|
|
|
|
doDeleteEpm(EpmHers, EpmHandler, Args) -> |
|
|
|
doDelEpm(EpmHers, EpmHandler, Args) -> |
|
|
|
case EpmHers of |
|
|
|
#{EpmHandler := EpmHer} -> |
|
|
|
epmTerminate(EpmHer, Args, delete, normal), |
|
|
@ -527,7 +531,7 @@ doDeleteEpm(EpmHers, EpmHandler, Args) -> |
|
|
|
end. |
|
|
|
|
|
|
|
%% handleEpmCallbackRet |
|
|
|
handleEpmCR(Result, EpmHers, #epmHer{epmId = EpmId} = EpmHer, Event, From) -> |
|
|
|
handleEpmCR(Result, EpmHers, EpmId, EpmHer, Event, From) -> |
|
|
|
case Result of |
|
|
|
kpS -> |
|
|
|
{EpmHers, false}; |
|
|
@ -650,9 +654,9 @@ epm_log(#{label := {gen_emm, no_handle_info}, module := Module, message := Msg}) |
|
|
|
"** Unhandled message: ~tp~n", [Module, Msg]}. |
|
|
|
|
|
|
|
epmStopAll(EpmHers) -> |
|
|
|
allStop(maps:iterator(EpmHers)). |
|
|
|
forStopAll(maps:iterator(EpmHers)). |
|
|
|
|
|
|
|
allStop(Iterator) -> |
|
|
|
forStopAll(Iterator) -> |
|
|
|
case maps:next(Iterator) of |
|
|
|
{_K, V, NextIterator} -> |
|
|
|
epmTerminate(V, stop, 'receive', shutdown), |
|
|
@ -662,7 +666,7 @@ allStop(Iterator) -> |
|
|
|
EpmSup -> |
|
|
|
unlink(EpmSup) |
|
|
|
end, |
|
|
|
allStop(NextIterator); |
|
|
|
forStopAll(NextIterator); |
|
|
|
none -> |
|
|
|
ok |
|
|
|
end. |
|
|
@ -696,8 +700,20 @@ epmTerminate(#epmHer{epmM = EpmM, epmS = State} = EpmHer, Args, LastIn, Reason) |
|
|
|
end. |
|
|
|
|
|
|
|
reply({To, Ref}, Msg) -> |
|
|
|
To ! {Ref, Msg}, |
|
|
|
ok. |
|
|
|
try To ! {Ref, Msg}, |
|
|
|
ok |
|
|
|
catch _:_ -> |
|
|
|
ok |
|
|
|
end. |
|
|
|
|
|
|
|
try_reply(false, _Msg) -> |
|
|
|
ignore; |
|
|
|
try_reply({To, Ref}, Msg) -> |
|
|
|
try To ! {Ref, Msg}, |
|
|
|
ok |
|
|
|
catch _:_ -> |
|
|
|
ok |
|
|
|
end. |
|
|
|
|
|
|
|
terminate_server(Reason, Parent, ServerName, EpmHers) -> |
|
|
|
stop_handlers(EpmHers, ServerName), |
|
|
@ -721,65 +737,74 @@ do_unlink(Parent, MSL) -> |
|
|
|
end, |
|
|
|
MSL). |
|
|
|
|
|
|
|
|
|
|
|
%%----------------------------------------------------------------- |
|
|
|
%% Callback functions for system messages handling. |
|
|
|
%%----------------------------------------------------------------- |
|
|
|
system_continue(Parent, Debug, [ServerName, HibernateAfterTimeout, EpmHers, Hib]) -> |
|
|
|
loopEntry(Parent, ServerName, HibernateAfterTimeout, EpmHers, Debug, Hib). |
|
|
|
system_continue(Parent, Debug, {ServerName, HibernateAfterTimeout, EpmHers, IsHib}) -> |
|
|
|
loopEntry(Parent, ServerName, HibernateAfterTimeout, EpmHers, Debug, IsHib). |
|
|
|
|
|
|
|
-spec system_terminate(_, _, _, [_]) -> no_return(). |
|
|
|
system_terminate(Reason, Parent, _Debug, [ServerName, _HibernateAfterTimeout, EpmHers, _Hib]) -> |
|
|
|
-spec system_terminate(_, _, _, _) -> no_return(). |
|
|
|
system_terminate(Reason, Parent, _Debug, {ServerName, _HibernateAfterTimeout, EpmHers, _IsHib}) -> |
|
|
|
terminate_server(Reason, Parent, ServerName, EpmHers). |
|
|
|
|
|
|
|
%%----------------------------------------------------------------- |
|
|
|
%% Module here is sent in the system msg change_code. It specifies |
|
|
|
%% which module should be changed. |
|
|
|
%%----------------------------------------------------------------- |
|
|
|
system_code_change([ServerName, HibernateAfterTimeout, EpmHers, Hib], Module, OldVsn, Extra) -> |
|
|
|
MSL1 = lists:zf(fun(H) when H#handler.module =:= Module -> |
|
|
|
{ok, NewState} = |
|
|
|
Module:code_change(OldVsn, |
|
|
|
H#handler.state, Extra), |
|
|
|
{true, H#handler{state = NewState}}; |
|
|
|
(_) -> true |
|
|
|
end, |
|
|
|
MSL), |
|
|
|
{ok, [ServerName, MSL1, HibernateAfterTimeout, Hib]}. |
|
|
|
|
|
|
|
system_get_state([_ServerName, MSL, _HibernateAfterTimeout, _Hib]) -> |
|
|
|
{ok, [{Mod, Id, State} || #handler{module = Mod, id = Id, state = State} <- MSL]}. |
|
|
|
|
|
|
|
system_replace_state(StateFun, [ServerName, MSL, HibernateAfterTimeout, Hib]) -> |
|
|
|
{NMSL, NStates} = |
|
|
|
lists:unzip([begin |
|
|
|
Cur = {Mod, Id, State}, |
|
|
|
try |
|
|
|
NState = {Mod, Id, NS} = StateFun(Cur), |
|
|
|
{HS#handler{state = NS}, NState} |
|
|
|
catch |
|
|
|
_:_ -> |
|
|
|
{HS, Cur} |
|
|
|
end |
|
|
|
end || #handler{module = Mod, id = Id, state = State} = HS <- MSL]), |
|
|
|
{ok, NStates, [ServerName, NMSL, HibernateAfterTimeout, Hib]}. |
|
|
|
system_code_change({ServerName, HibernateAfterTimeout, EpmHers, IsHib}, Module, OldVsn, Extra) -> |
|
|
|
NewEpmHers = forCodeChange(maps:iterator(EpmHers), Module, OldVsn, Extra, EpmHers), |
|
|
|
{ok, {ServerName, HibernateAfterTimeout, NewEpmHers, IsHib}}. |
|
|
|
|
|
|
|
forCodeChange(Iterator, CModule, OldVsn, Extra, TemEpmHers) -> |
|
|
|
case maps:next(Iterator) of |
|
|
|
{K, #epmHer{epmM = Module, epmS = EpmS} = V, NextIterator} when Module =:= CModule -> |
|
|
|
{ok, NewEpmS} = Module:code_change(OldVsn, EpmS, Extra), |
|
|
|
forCodeChange(NextIterator, CModule, OldVsn, Extra, TemEpmHers#{K := V#epmHer{epmS = NewEpmS}}); |
|
|
|
{_, _, NextIterator} -> |
|
|
|
forCodeChange(NextIterator, CModule, OldVsn, Extra, TemEpmHers); |
|
|
|
none -> |
|
|
|
TemEpmHers |
|
|
|
end. |
|
|
|
|
|
|
|
system_get_state({_ServerName, _HibernateAfterTimeout, EpmHers, _Hib}) -> |
|
|
|
{ok, forGetState(maps:iterator(EpmHers), [])}. |
|
|
|
|
|
|
|
forGetState(Iterator, Acc) -> |
|
|
|
case maps:next(Iterator) of |
|
|
|
{_K, #epmHer{epmId = EpmId, epmM = Module, epmS = EpmS}, NextIterator} -> |
|
|
|
forGetState(NextIterator, [{Module, EpmId, EpmS} | Acc]); |
|
|
|
none -> |
|
|
|
Acc |
|
|
|
end. |
|
|
|
|
|
|
|
system_replace_state(StateFun, {ServerName, HibernateAfterTimeout, EpmHers, IsHib}) -> |
|
|
|
{NewEpmHers, NStates} = forReplaceState(maps:iterator(EpmHers), StateFun, EpmHers, []), |
|
|
|
{ok, NStates, {ServerName, HibernateAfterTimeout, NewEpmHers, IsHib}}. |
|
|
|
|
|
|
|
forReplaceState(Iterator, StateFun, TemEpmHers, NStates) -> |
|
|
|
case maps:next(Iterator) of |
|
|
|
{K, #epmHer{epmId = EpmId, epmM = Module, epmS = EpmS} = V, NextIterator} -> |
|
|
|
NState = {_, _, NewEpmS} = StateFun({Module, EpmId, EpmS}), |
|
|
|
forCodeChange(NextIterator, TemEpmHers#{K := V#epmHer{epmS = NewEpmS}}, [NState | NStates]); |
|
|
|
none -> |
|
|
|
{TemEpmHers, NStates} |
|
|
|
end. |
|
|
|
|
|
|
|
%%----------------------------------------------------------------- |
|
|
|
%% Format debug messages. Print them as the call-back module sees |
|
|
|
%% them, not as the real erlang messages. Use trace for that. |
|
|
|
%%----------------------------------------------------------------- |
|
|
|
print_event(Dev, {in, Msg}, Name) -> |
|
|
|
print_event(Dev, Msg, Name) -> |
|
|
|
case Msg of |
|
|
|
{notify, Event} -> |
|
|
|
io:format(Dev, "*DBG* ~tp got event ~tp~n", [Name, Event]); |
|
|
|
{_, _, {call, Handler, Query}} -> |
|
|
|
io:format(Dev, "*DBG* ~tp(~tp) got call ~tp~n", |
|
|
|
[Name, Handler, Query]); |
|
|
|
{call, From, Request} -> |
|
|
|
io:format(Dev, "*DBG* ~tp(~tp) got call ~tp from ~tp ~n", [Name, Request, From]); |
|
|
|
{info, CmdOrEmpHandler, Event} -> |
|
|
|
io:format(Dev, "*DBG* ~tp got info ~tp~n", [CmdOrEmpHandler, Event]); |
|
|
|
{in, Msg} -> |
|
|
|
io:format(Dev, "*DBG* ~tp got in ~tp~n", [Name, Msg]); |
|
|
|
_ -> |
|
|
|
io:format(Dev, "*DBG* ~tp got ~tp~n", [Name, Msg]) |
|
|
|
end; |
|
|
|
print_event(Dev, Dbg, Name) -> |
|
|
|
io:format(Dev, "*DBG* ~tp : ~tp~n", [Name, Dbg]). |
|
|
|
io:format(Dev, "*DBG* ~tp : ~tp~n", [Name, Msg]) |
|
|
|
end. |
|
|
|
|
|
|
|
%% format_log/1 is the report callback used by Logger handler |
|
|
|
%% error_logger only. It is kept for backwards compatibility with |
|
|
@ -937,14 +962,6 @@ single(false) -> "". |
|
|
|
mod(latin1) -> ""; |
|
|
|
mod(_) -> "t". |
|
|
|
|
|
|
|
handler(Handler) when not Handler#handler.id -> |
|
|
|
Handler#handler.module; |
|
|
|
handler(Handler) -> |
|
|
|
{Handler#handler.module, Handler#handler.id}. |
|
|
|
|
|
|
|
the_handlers(MSL) -> |
|
|
|
[handler(Handler) || Handler <- MSL]. |
|
|
|
|
|
|
|
%% stop_handlers(MSL, ServerName) -> [] |
|
|
|
|
|
|
|
stop_handlers([Handler | T], SName) -> |
|
|
@ -957,19 +974,35 @@ stop_handlers([], _) -> |
|
|
|
|
|
|
|
%% Message from the release_handler. |
|
|
|
%% The list of modules got to be a set, i.e. no duplicate elements! |
|
|
|
get_modules(MSL) -> |
|
|
|
Mods = [Handler#handler.module || Handler <- MSL], |
|
|
|
ordsets:to_list(ordsets:from_list(Mods)). |
|
|
|
get_modules(EpmHers) -> |
|
|
|
allMods(maps:iterator(EpmHers), []). |
|
|
|
|
|
|
|
allMods(Iterator, Acc) -> |
|
|
|
case maps:next(Iterator) of |
|
|
|
{_K, V, NextIterator} -> |
|
|
|
allMods(NextIterator, [element(#epmHer.epmM, V) | Acc]); |
|
|
|
none -> |
|
|
|
lists:usort(Acc) |
|
|
|
end. |
|
|
|
|
|
|
|
%%----------------------------------------------------------------- |
|
|
|
%% Status information |
|
|
|
%%----------------------------------------------------------------- |
|
|
|
format_status(Opt, StatusData) -> |
|
|
|
[PDict, SysState, Parent, _Debug, [ServerName, _HibernateAfterTimeout, EpmHers, _Hib]] = StatusData, |
|
|
|
[PDict, SysState, Parent, _Debug, {ServerName, _HibernateAfterTimeout, EpmHers, _IsHib}] = StatusData, |
|
|
|
Header = gen:format_status_header("Status for gen_emm handler", ServerName), |
|
|
|
FmtMSL = [MS#handler{state = format_status(Opt, Mod, PDict, State)} || #handler{module = Mod, state = State} = MS <- MSL], |
|
|
|
FmtMSL = allStateStatus(maps:iterator(EpmHers), Opt, PDict, []), |
|
|
|
[{header, Header}, {data, [{"Status", SysState}, {"Parent", Parent}]}, {items, {"Installed handlers", FmtMSL}}]. |
|
|
|
|
|
|
|
allStateStatus(Iterator, Opt, PDict, EpmHers) -> |
|
|
|
case maps:next(Iterator) of |
|
|
|
{_K, #epmHer{epmM = Module, epmS = EpmS} = V, NextIterator} -> |
|
|
|
NewEpmS = format_status(Opt, Module, PDict, EpmS), |
|
|
|
allStateStatus(NextIterator, [V#epmHer{epmS = NewEpmS} | EpmHers]); |
|
|
|
none -> |
|
|
|
EpmHers |
|
|
|
end. |
|
|
|
|
|
|
|
format_status(Opt, Mod, PDict, State) -> |
|
|
|
case erlang:function_exported(Mod, format_status, 2) of |
|
|
|
true -> |
|
|
|