📄 release_handler_1.erl
字号:
eval({code_change, Mode, Modules}, EvalState) -> Suspended = EvalState#eval_state.suspended, Vsns = EvalState#eval_state.vsns, Timeout = get_opt(code_change_timeout, EvalState, default), lists:foreach(fun({Mod, Extra}) -> Vsn = case lists:keysearch(Mod, 1, Vsns) of {value, {Mod, OldVsn, _NewVsn}} when Mode == up -> OldVsn; {value, {Mod, _OldVsn, NewVsn}} when Mode == down -> {down, NewVsn}; _ when Mode == up -> undefined; _ -> {down, undefined} end, case lists:keysearch(Mod, 1, Suspended) of {value, {_Mod, Pids}} -> change_code(Pids, Mod, Vsn, Extra, Timeout); _ -> ok end end, Modules), EvalState;eval({stop, Modules}, EvalState) -> Procs = get_supervised_procs(), NewStopped = lists:foldl(fun(Mod, Stopped) -> Procs2 = stop(Mod, Procs), [{Mod, Procs2} | Stopped] end, EvalState#eval_state.stopped, Modules), EvalState#eval_state{stopped = NewStopped};eval({start, Modules}, EvalState) -> NewStopped = lists:foldl(fun(Mod, Stopped) -> lists:filter(fun({Mod2, Procs}) when Mod2 == Mod -> start(Procs), false; (_) -> true end, Stopped) end, EvalState#eval_state.stopped, Modules), EvalState#eval_state{stopped = NewStopped};eval({sync_nodes, Id, {M, F, A}}, EvalState) -> sync_nodes(Id, apply(M, F, A)), EvalState;eval({sync_nodes, Id, Nodes}, EvalState) -> sync_nodes(Id, Nodes), EvalState;eval({apply, {M, F, A}}, EvalState) -> apply(M, F, A), EvalState;eval(restart_new_emulator, _EvalState) -> throw(restart_new_emulator).get_opt(Tag, EvalState, Default) -> case lists:keysearch(Tag, 1, EvalState#eval_state.opts) of {value, {_Tag, Value}} -> Value; false -> Default end.%%-----------------------------------------------------------------%% This is a first approximation. Unfortunately, we might end up%% with the situation that after this suspendation, some new%% processes start *before* we have loaded the new code, and these%% will execute the old code. These processes could be terminated%% later on (if the prev current version is purged). The same%% goes for processes that didn't respond to the suspend message.%%-----------------------------------------------------------------suspend(Mod, Procs, Timeout) -> lists:zf(fun({_Sup, _Name, Pid, Mods}) -> case lists:member(Mod, Mods) of true -> case catch sys_suspend(Pid, Timeout) of ok -> {true, Pid}; _ -> % If the proc hangs, make sure to % resume it when it gets suspended! catch sys:resume(Pid), false end; false -> false end end, Procs).sys_suspend(Pid, default) -> sys:suspend(Pid);sys_suspend(Pid, Timeout) -> sys:suspend(Pid, Timeout).resume(Pids) -> lists:foreach(fun(Pid) -> catch sys:resume(Pid) end, Pids).change_code(Pids, Mod, Vsn, Extra, Timeout) -> Fun = fun(Pid) -> case Timeout of default -> ok = sys:change_code(Pid, Mod, Vsn, Extra); _Else -> ok = sys:change_code(Pid, Mod, Vsn, Extra, Timeout) end end, lists:foreach(Fun, Pids).stop(Mod, Procs) -> lists:zf(fun({undefined, _Name, _Pid, _Mods}) -> false; ({Sup, Name, _Pid, Mods}) -> case lists:member(Mod, Mods) of true -> case catch supervisor:terminate_child( Sup, Name) of ok -> {true, {Sup, Name}}; _ -> false end; false -> false end end, Procs).start(Procs) -> lists:foreach(fun({Sup, Name}) -> catch supervisor:restart_child(Sup, Name) end, Procs).%%-----------------------------------------------------------------%% Func: get_supervised_procs/0%% Purpose: This is the magic function. It finds all process in%% the system and which modules they execute as a call_back or%% process module.%% This is achieved by asking the main supervisor for the%% applications for all children and their modules%% (recursively).%% NOTE: If a supervisor is suspended, it isn't possible to call%% which_children. Code change on a supervisor should be%% done in another way; the only code in a supervisor is%% code for starting children. Therefore, to change a%% supervisor module, we should load the new version, and then%% delete the old. Then we should perform the start changes%% manually, by adding/deleting children.%% Returns: [{SuperPid, ChildName, ChildPid, Mods}]%%-----------------------------------------------------------------%% OTP-3452. For each application the first item contains the pid%% of the top supervisor, and the name of the supervisor call-back module. %%-----------------------------------------------------------------get_supervised_procs() -> lists:foldl( fun(Application, Procs) -> case application_controller:get_master(Application) of Pid when is_pid(Pid) -> {Root, _AppMod} = application_master:get_child(Pid), case get_supervisor_module(Root) of {ok, SupMod} -> get_procs(supervisor:which_children(Root), Root) ++ [{undefined, undefined, Root, [SupMod]} | Procs]; {error, _} -> error_logger:error_msg("release_handler: " "cannot find top " "supervisor for " "application ~w~n", [Application]), get_procs(supervisor:which_children(Root), Root) ++ Procs end; _ -> Procs end end, [], lists:map(fun({Application, _Name, _Vsn}) -> Application end, application:which_applications())).get_procs([{Name, Pid, worker, dynamic} | T], Sup) when is_pid(Pid) -> Mods = get_dynamic_mods(Name), [{Sup, Name, Pid, Mods} | get_procs(T, Sup)];get_procs([{Name, Pid, worker, Mods} | T], Sup) when is_pid(Pid), is_list(Mods) -> [{Sup, Name, Pid, Mods} | get_procs(T, Sup)];get_procs([{Name, Pid, supervisor, Mods} | T], Sup) when is_pid(Pid) -> [{Sup, Name, Pid, Mods} | get_procs(T, Sup)] ++ get_procs(supervisor:which_children(Pid), Pid);get_procs([_H | T], Sup) -> get_procs(T, Sup);get_procs(_, _Sup) -> [].get_dynamic_mods(Pid) -> {ok,Res} = gen:call(Pid, self(), get_modules), Res.%% XXXX%% Note: The following is a terrible hack done in order to resolve the%% problem stated in ticket OTP-3452.%% XXXX NOTE WELL: This record is from supervisor.erl. Also the record%% name is really `state'. -record(supervisor_state, {name, strategy, children = [], dynamics = [], intensity, period, restarts = [], module, args}).%% Return the name of the call-back module that implements the%% (top) supervisor SupPid.%% Returns: {ok, Module} | {error,undefined}%%get_supervisor_module(SupPid) -> case catch get_supervisor_module1(SupPid) of {ok, Module} when is_atom(Module) -> {ok, Module}; _Other -> io:format("~w: reason: ~w~n", [SupPid, _Other]), {error, undefined} end.get_supervisor_module1(SupPid) -> {status, _Pid, {module, _Mod}, [_PDict, _SysState, _Parent, _Dbg, Misc]} = sys:get_status(SupPid), [_Name, State, _Type, _Time] = Misc, %% Cannot use #supervisor_state{module = Module} = State. {ok, element(#supervisor_state.module, State)}.%%-----------------------------------------------------------------%% Func: do_soft_purge/3%% Args: Mod = atom()%% PostPurgeMethod = soft_purge | brutal_purge%% Unpurged = [{Mod, PostPurgeMethod}]%% Purpose: Check if there are any processes left running this code.%% If so, make sure Mod is a member in the returned list.%% Otherwise, make sure Mod isn't a member in the returned%% list.%% Returns: An updated list of unpurged modules.%%-----------------------------------------------------------------do_soft_purge(Mod, PostPurgeMethod, Unpurged) -> IsNoOldProcsLeft = code:soft_purge(Mod), case lists:keymember(Mod, 1, Unpurged) of true when IsNoOldProcsLeft == true -> lists:keydelete(Mod, 1, Unpurged); true -> Unpurged; false when IsNoOldProcsLeft == true -> Unpurged; false -> [{Mod, PostPurgeMethod} | Unpurged] end.%%-----------------------------------------------------------------%% Func: sync_nodes/2%% Args: Id = term()%% Nodes = [atom()]%% Purpose: Synchronizes with all nodes.%%-----------------------------------------------------------------sync_nodes(Id, Nodes) -> NNodes = lists:delete(node(), Nodes), lists:foreach(fun(Node) -> {release_handler, Node} ! {sync_nodes, Id, node()} end, NNodes), lists:foreach(fun(Node) -> receive {sync_nodes, Id, Node} -> ok; {nodedown, Node} -> throw({sync_error, {nodedown, Node}}) end end, NNodes).add_old_vsn(Mod, Vsns) -> case lists:keysearch(Mod, 1, Vsns) of {value, {Mod, undefined, NewVsn}} -> OldVsn = get_vsn(code:which(Mod)), lists:keyreplace(Mod, 1, Vsns, {Mod, OldVsn, NewVsn}); {value, {Mod, _OldVsn, _NewVsn}} -> Vsns; false -> OldVsn = get_vsn(code:which(Mod)), [{Mod, OldVsn, undefined} | Vsns] end.add_new_vsn(Mod, File, Vsns) -> NewVsn = get_vsn(File), case lists:keysearch(Mod, 1, Vsns) of {value, {Mod, OldVsn, undefined}} -> lists:keyreplace(Mod, 1, Vsns, {Mod, OldVsn, NewVsn}); false -> [{Mod, undefined, NewVsn} | Vsns] end.%%-----------------------------------------------------------------%% Func: get_vsn/1%% Args: File = string()%% Purpose: Finds the version attribute of a module.%% Returns: Vsn%% Vsn = term()%%-----------------------------------------------------------------get_vsn(File) -> {ok, {_Mod, Vsn}} = beam_lib:version(File), case misc_supp:is_string(Vsn) of true -> Vsn; false -> %% If -vsn(Vsn) defines a term which is not a %% string, the value is returned here as [Vsn]. case Vsn of [VsnTerm] -> VsnTerm; _ -> Vsn end end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -