gen_X 系モジュールの code_change を appup とは関係なく呼ぶ
gen_server や gen_fsm 等が保持する State を、サーバ停止せずに(正確には suspend するけど)入れ替える方法のメモ。
OTP から逸脱している感があるけれど、Release Handling は無視の方向で(w;
ちなみに、下記の内容を試す際は、こちら をどうぞ。
まずは、下記のようなテスト用の gen_server を準備。
-module(code_change_test). -behaviour(gen_server). -export([start/0, stop/0]). -export([state/0]). -export([ init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3 ]). start() -> gen_server:start({local, ?MODULE}, ?MODULE, [], []). stop() -> gen_server:cast(?MODULE, stop). state() -> gen_server:call(?MODULE, state). init(_Args) -> {ok, ver1}. handle_call(state, _From, State) -> io:fwrite("State=~p~n", [State]), {reply, State, State}; handle_call(_Message, _From, State) -> {reply, ok, State}. handle_cast(stop, State) -> {stop, normal, State}; handle_cast(_Message, State) -> {noreply, State}. handle_info(_Info, State) -> {noreply, State}. terminate(normal, _State) -> ok; terminate(_, _) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}.
code_change_test の動作確認。
$ erl 1> c(code_change_test). {ok,code_change_test} 2> {ok, Pid} = code_change_test:start(). {ok,<0.38.0>} 3> code_change_test:state(). State=ver1 ver1
次に、code_change_test の Code を下記のように書き換える。
-module(code_change_test). -behaviour(gen_server). -export([start/0, stop/0]). -export([state/0]). -export([ init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3 ]). start() -> gen_server:start({local, ?MODULE}, ?MODULE, [], []). stop() -> gen_server:cast(?MODULE, stop). state() -> gen_server:call(?MODULE, state). init(_Args) -> {ok, {ver2, [foo, bar]}}. handle_call(state, _From, {ver2, Args}=State) -> io:fwrite("State=~p~n", [Args]), {reply, Args, State}; handle_call(_Message, _From, State) -> {reply, ok, State}. handle_cast(stop, State) -> {stop, normal, State}; handle_cast(_Message, State) -> {noreply, State}. handle_info(_Info, State) -> {noreply, State}. terminate(normal, _State) -> ok; terminate(_, _) -> ok. code_change(OldVsn, State, Extra) -> io:fwrite( lists:foldl( fun (Name, Format) -> Format ++ Name ++ "=~p~n" end, "code_change~n", ["OldVsn", "State", "Extra"] ), [OldVsn, State, Extra] ), {ok, {ver2, [foo, bar]}}.
code_change_test の Code と State の形式を入れ替える。
4> c(code_change_test). {ok,code_change_test} 5> sys:suspend(Pid). ok 6> sys:change_code(Pid, code_change_test, 1, {}). code_change OldVsn=1 State=ver1 Extra={} ok 7> sys:resume(Pid). ok
入れ替わったか確認。
8> code_change_test:state(). State=[foo,bar] [foo,bar]
suspend 中に受け取ったメッセージはスタックされ、resume された瞬間に一気に実行される。