常駐している Erlang プロセスのコードを入れ替える事ができる条件
常駐している Erlang プロセスのコードを無停止で動的に入れ替えるには、設計の段階で、入れ替えが発生すると思われる箇所を、プロセスとは異なる別モジュールに切り離しておく必要がある。
常駐している Erlang プロセスのコードの入れ替えに失敗する例
まず、下記のようなモジュールを用意する。
-module(test). -export([start/0, talk/1]). -export([loop/0]). start() -> spawn_link(fun loop/0). talk(Pid) -> Pid ! hello, ok. loop() -> receive ReceiveMessage -> ok end, io:fwrite("~p~n", [get_message(ReceiveMessage)]), loop(). get_message(hello) -> hi; get_message(Other) -> Other.
ErlangVM を起動して動作確認を行なう。
% erl 1> Pid = test:start(). <0.33.0> 2> test:talk(Pid). hi ok
この状態で、get_message/1 を下記のように書き換える。
get_message(hello) -> hello; get_message(Other) -> Other.
ErlangVM でコードの入れ替えを試してみる。
3> l(test). {module,test} 4> test:talk(Pid). hi ok
おや?入れ替わっていない。もう一度、ファイルの保存とコンパイルを行ない、ロードし直してみる。
5> l(test). {module,test} 6> test:talk(Pid). ok
あれ?何も帰ってこない?この状態で、i() でプロセスの状態を確認するとプロセスが落ちていた。試しに終了シグナルをトラップしてみた所、killed を受信していた。
ちなみに、talk/1 はプロセスから参照されていない関数である為、入れ替えは可能だが、何度も書き換えてロードすると、やはりプロセスは死んでしまった。
何か入れ替えの手順が抜けているのかな?識者の意見求む。
常駐している Erlang プロセスのコードの入れ替えに成功する例
失敗する例のテストモジュールから、入れ替えの対象となる get_message/1 を切り離す。
loop() -> receive ReceiveMessage -> ok end, io:fwrite("~p~n", [test2:get_message(ReceiveMessage)]), loop().
-module(test2). -export([get_message/1]). get_message(hello) -> hi; get_message(Other) -> Other.
ErlangVM を起動して動作確認を行なう。
% erl 1> Pid = test:start(). <0.33.0> 2> test:talk(Pid). hi ok
この状態で、test2:get_message/1 を下記のように書き換える。
get_message(hello) -> hello; get_message(Other) -> Other.
ErlangVM でコードの入れ替えを試してみる。
3> l(test2). {module,test2} 4> test:talk(Pid). hello ok
無事、入れ替えに成功。test2 は、常駐しているプロセスとは無関係である為、何度ロードし直してもプロセスが落ちる事はない。
まとめ
- 常駐している Erlang プロセスのコードは、入れ替え出来ない。
- 常駐している Erlang プロセスのコードが含まれるモジュールを入れ替えると、プロセスが死ぬ事がある。(深追いしていない。code_server の Source を読めば解る?)
- 常駐している Erlang プロセスのコードが依存しているモジュールのコードは入れ替え出来る。
OTP に従っていれば、プロセスを作成・常駐する箇所は共通モジュール化されている為、一般化できないアプリケーション固有の処理は、別モジュールに自然と切り出される。未熟だと自覚している者こそ OTP に従って間違いの無いプログラムを書きましょう。という事かな。