⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 release_handler_1.erl

📁 OTP是开放电信平台的简称
💻 ERL
📖 第 1 页 / 共 2 页
字号:
%% ``The contents of this file are subject to the Erlang Public License,%% Version 1.1, (the "License"); you may not use this file except in%% compliance with the License. You should have received a copy of the%% Erlang Public License along with this software. If not, it can be%% retrieved via the world wide web at http://www.erlang.org/.%% %% Software distributed under the License is distributed on an "AS IS"%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See%% the License for the specific language governing rights and limitations%% under the License.%% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %%     $Id$%%-module(release_handler_1).%% External exports-export([eval_script/3, eval_script/4, check_script/2]).-export([get_vsn/1]). %% exported because used in a test case-record(eval_state, {bins = [], stopped = [], suspended = [], apps = [],		     libdirs, unpurged = [], vsns = [], newlibs = [],		     opts = []}).%%-----------------------------------------------------------------%% bins      = [{Mod, Binary, FileName}]%% stopped   = [{Mod, [pid()]}] - list of stopped pids for each module%% suspended = [{Mod, [pid()]}] - list of suspended pids for each module%% apps      = [app_spec()] - list of all apps in the new release%% libdirs   = [{Lib, LibVsn, LibDir}] - Maps Lib to Vsn and Directory%% unpurged  = [{Mod, soft_purge | brutal_purge}]%% vsns      = [{Mod, OldVsn, NewVsn}] - remember the old vsn of a mod%%                  before it is removed/a new vsn is loaded; the new vsn%%                  is kept in case of a downgrade, where the code_change%%                  function receives the vsn of the module to downgrade%%                  *to*.%% newlibs   = [{Lib, Dir}] - list of all new libs; used to change%%                            the code path%% opts      = [{Tag, Value}] - list of options%%-----------------------------------------------------------------%%%-----------------------------------------------------------------%%% This is a low-level release handler.%%%-----------------------------------------------------------------check_script(Script, LibDirs) ->    case catch check_old_processes(Script) of	ok ->	    {Before, _After} = split_instructions(Script),	    case catch lists:foldl(fun(Instruction, EvalState1) ->					   eval(Instruction, EvalState1)				   end,				   #eval_state{libdirs = LibDirs},				   Before) of		EvalState2 when is_record(EvalState2, eval_state) -> ok;		{error, Error} -> {error, Error};		Other -> {error, Other}	    end;	{error, Mod} ->	    {error, {old_processes, Mod}}    end.eval_script(Script, Apps, LibDirs) ->    eval_script(Script, Apps, LibDirs, []).eval_script(Script, Apps, LibDirs, Opts) ->    case catch check_old_processes(Script) of	ok ->	    {Before, After} = split_instructions(Script),	    case catch lists:foldl(fun(Instruction, EvalState1) ->					   eval(Instruction, EvalState1)				   end,				   #eval_state{apps = Apps, 					       libdirs = LibDirs,					       opts = Opts},				   Before) of		EvalState2 when is_record(EvalState2, eval_state) ->		    case catch lists:foldl(fun(Instruction, EvalState3) ->						   eval(Instruction, EvalState3)					   end,					   EvalState2,					   After) of			EvalState4 when is_record(EvalState4, eval_state) ->			    {ok, EvalState4#eval_state.unpurged};			restart_new_emulator ->			    restart_new_emulator;			Error ->			    {'EXIT', Error}		    end;		{error, Error} -> {error, Error};		Other -> {error, Other}	    end;	{error, Mod} ->	    {error, {old_processes, Mod}}    end.%%%-----------------------------------------------------------------%%% Internal functions%%%-----------------------------------------------------------------split_instructions(Script) ->    split_instructions(Script, []).split_instructions([point_of_no_return | T], Before) ->    {lists:reverse(Before), [point_of_no_return | T]};split_instructions([H | T], Before) ->    split_instructions(T, [H | Before]);split_instructions([], Before) ->    {[], lists:reverse(Before)}.%%-----------------------------------------------------------------%% Func: check_old_processes/1%% Args: Script = [instruction()]%% Purpose: Check if there is any process that runs an old version%%          of a module that should be soft_purged, (i.e. not purged%%          at all if there is any such process).  Returns {error, Mod}%%          if so, ok otherwise.%% Returns: ok | {error, Mod}%%          Mod = atom()  %%-----------------------------------------------------------------check_old_processes(Script) ->    lists:foreach(fun({load, {Mod, soft_purge, _PostPurgeMethod}}) ->			  check_old_code(Mod);		     ({remove, {Mod, soft_purge, _PostPurgeMethod}}) ->			  check_old_code(Mod);		     (_) -> ok		  end,		  Script).check_old_code(Mod) ->    lists:foreach(fun(Pid) ->			  case erlang:check_process_code(Pid, Mod) of			      false -> ok;			      true -> throw({error, Mod})			  end		  end,		  erlang:processes()).%%-----------------------------------------------------------------%% An unpurged module is a module for which there exist an old%% version of the code.  This should only be the case if there are%% processes running the old version of the code.%%%% This functions evaluates each instruction.  Note that the%% instructions here are low-level instructions.  e.g. lelle's%% old synchronized_change would be translated to%%   {load_object_code, Modules},%%   {suspend, Modules}, [{load, Module}],%%   {resume, Modules}, {purge, Modules}%% Or, for example, if we want to do advanced external code change %% on two modules that depend on each other, by killing them and%% then restaring them, we could do:%%   {load_object_code, [Mod1, Mod2]},%%   % delete old version%%   {remove, {Mod1, brutal_purge}}, {remove, {Mod2, brutal_purge}},%%   % now, some procs migth be running prev current (now old) version%%   % kill them, and load new version%%   {load, {Mod1, brutal_purge}}, {load, {Mod2, brutal_purge}}%%   % now, there is one version of the code (new, current)%%%% NOTE: All load_object_code must be first in the script,%%       a point_of_no_return must be present (if load_object_code%%       is present).%%%% {load_object_code, {Lib, LibVsn, [Mod]} %%    read the files as binarys. do not make code out of them%% {load, {Module, PrePurgeMethod, PostPurgeMethod}}%%    Module must have been load_object_code:ed.  make code out of it%%    old procs && soft_purge => no new release%%    old procs && brutal_purge => old procs killed%%    The new old code will be gc:ed later on, if PostPurgeMethod =%%    soft_purge.  If it is brutal_purge, the code is purged when%%    the release is made permanent.%% {remove, {Module, PrePurgeMethod, PostPurgeMethod}}%%    make current version old.  no current left.%%    old procs && soft_purge => no new release%%    old procs && brutal_purge => old procs killed%%    The new old code will be gc:ed later on, if PostPurgeMethod =%%    soft_purge.  If it is brutal_purge, the code is purged when%%    the release is made permanent.%% {purge, Modules}%%    kill all procs running old code, delete old code%% {suspend, [Module | {Module, Timeout}]}%%    If a process doesn't repsond - never mind.  It will be killed%%    later on (if a purge is performed).%%    Hmm, we must do something smart here... we should probably kill it,%%    but we cant, because its supervisor will restart it directly!  Maybe%%    we should keep a list of those, call supervisor:terminate_child()%%    when all others are suspended, and call sup:restart_child() when the%%    others are resumed.%% {code_change, [{Module, Extra}]}%% {code_change, Mode, [{Module, Extra}]}  Mode = up | down%%    Send code_change only to suspended procs running this code%% {resume, [Module]}%%    resume all previously suspended processes%% {stop, [Module]}%%    stop all procs running this code%% {start, [Module]}%%    starts the procs that were previously stopped for this code.%%    Note that this will start processes in exactly the same place%%    in the suptree where there were procs previously.%% {sync_nodes, Id, [Node]}%% {sync_nodes, Id, {M, F, A}}%%    Synchronizes with the Nodes (or apply(M,F,A) == Nodes).  All Nodes%%    must also exectue the same line.  Waits for all these nodes to get%%    to this line.%% point_of_no_return%% restart_new_emulator%% {stop_application, Appl}     - Impl with apply%% {unload_application, Appl}   - Impl with {remove..}%% {load_application, Appl}     - Impl with {load..}%% {start_application, Appl}    - Impl with apply%%-----------------------------------------------------------------eval({load_object_code, {Lib, LibVsn, Modules}}, EvalState) ->    case lists:keysearch(Lib, 1, EvalState#eval_state.libdirs) of	{value, {Lib, LibVsn, LibDir}} ->	    Ebin = filename:join(LibDir, "ebin"),	    Ext = code:objfile_extension(),	    {NewBins, NewVsns} = 		lists:foldl(fun(Mod, {Bins, Vsns}) ->				    File = lists:concat([Mod, Ext]),				    FName = filename:join(Ebin, File),				    case erl_prim_loader:get_file(FName) of					{ok, Bin, FName2} ->					    NVsns = add_new_vsn(Mod, FName2, Vsns),					    {[{Mod, Bin, FName2} | Bins],NVsns};					error ->					    throw({error, {no_such_file,FName}})				    end			    end,			    {EvalState#eval_state.bins,			     EvalState#eval_state.vsns},			    Modules),	    NewLibs = [{Lib, Ebin} | EvalState#eval_state.newlibs],	    EvalState#eval_state{bins = NewBins,				 newlibs = NewLibs,				 vsns = NewVsns};	{value, {Lib, LibVsn2, _LibDir}} ->	    throw({error, {bad_lib_vsn, Lib, LibVsn2}})    end;eval(point_of_no_return, EvalState) ->    Libs = case get_opt(update_paths, EvalState, false) of	       false ->		   EvalState#eval_state.newlibs; % [{Lib, Path}]	       true ->		   lists:map(fun({Lib, _LibVsn, LibDir}) ->				     Ebin= filename:join(LibDir,"ebin"),				     {Lib, Ebin}			     end,			     EvalState#eval_state.libdirs)	   end,    lists:foreach(fun({Lib, Path}) -> code:replace_path(Lib, Path) end,		  Libs),    EvalState;eval({load, {Mod, _PrePurgeMethod, PostPurgeMethod}}, EvalState) ->    Bins = EvalState#eval_state.bins,    {value, {_Mod, Bin, File}} = lists:keysearch(Mod, 1, Bins),    % load_binary kills all procs running old code    % if soft_purge, we know that there are no such procs now    Vsns = EvalState#eval_state.vsns,    NewVsns = add_old_vsn(Mod, Vsns),    code:load_binary(Mod, File, Bin),    % Now, the prev current is old.  There might be procs    % running it.  Find them.    Unpurged = do_soft_purge(Mod,PostPurgeMethod,EvalState#eval_state.unpurged),    EvalState#eval_state{bins = lists:keydelete(Mod, 1, Bins),			 unpurged = Unpurged,			 vsns = NewVsns};eval({remove, {Mod, _PrePurgeMethod, PostPurgeMethod}}, EvalState) ->    % purge kills all procs running old code    % if soft_purge, we know that there are no such procs now    Vsns = EvalState#eval_state.vsns,    NewVsns = add_old_vsn(Mod, Vsns),    code:purge(Mod),    code:delete(Mod),    % Now, the prev current is old.  There might be procs    % running it.  Find them.    Unpurged =	case code:soft_purge(Mod) of	    true -> EvalState#eval_state.unpurged;	    false -> [{Mod, PostPurgeMethod} | EvalState#eval_state.unpurged]	end,%%    Bins = EvalState#eval_state.bins,%%    EvalState#eval_state{bins = lists:keydelete(Mod, 1, Bins),    EvalState#eval_state{unpurged = Unpurged, vsns = NewVsns};eval({purge, Modules}, EvalState) ->    % Now, if there are any processes still executing old code, OR    % if some new processes started after suspend but before load,    % these are killed.    lists:foreach(fun(Mod) -> code:purge(Mod) end, Modules),    EvalState;eval({suspend, Modules}, EvalState) ->    Procs = get_supervised_procs(),    NewSuspended =	lists:foldl(fun(ModSpec, Suspended) ->			    {Module, Def} = case ModSpec of 						{Mod, ModTimeout} ->						    {Mod, ModTimeout};						Mod ->						    {Mod, default}					    end,			    Timeout = get_opt(suspend_timeout, EvalState, Def),			    Pids = suspend(Module, Procs, Timeout),			    [{Module, Pids} | Suspended]		    end,		    EvalState#eval_state.suspended,		    Modules),    EvalState#eval_state{suspended = NewSuspended};eval({resume, Modules}, EvalState) ->    NewSuspended =	lists:foldl(fun(Mod, Suspended) ->			    lists:filter(fun({Mod2, Pids}) when Mod2 == Mod ->						 resume(Pids),						 false;					    (_) ->						 true					 end,					 Suspended)		    end,		    EvalState#eval_state.suspended,		    Modules),    EvalState#eval_state{suspended = NewSuspended};eval({code_change, Modules}, EvalState) ->    eval({code_change, up, Modules}, EvalState);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -