-
-
Save applideveloper/d5cf0fd0447473293e076bb6edb4a299 to your computer and use it in GitHub Desktop.
ホットコードローディング時のプロセスクラッシュについて ref: http://qiita.com/sile/items/697f80db992819056127
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-module(hoge). | |
-export([gen_external_fun/0, gen_local_fun/0, gen_anonymouns_fun/0]). | |
-export([hello/0]). | |
gen_external_fun() -> % 公開関数への参照を返す | |
fun hoge:hello/0. % これだけが完全修飾形式での参照 (= 複数回のコードローディングを跨いでも安全) | |
gen_local_fun() -> % ローカル関数への参照を返す | |
fun hello/0. | |
gen_anonymouns_fun() -> % 無名関数への参照を返す | |
fun () -> hoge:hello() end. | |
hello() -> | |
?HELLO. % マクロの値はコンパイル時に指定する |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
> c(hoge, [{d, 'HELLO', hello}]). % コンパイル | |
> (hoge:gen_external_fun())(). | |
hello | |
> (hoge:gen_local_fun())(). | |
hello | |
(hoge:gen_anonymouns_fun())(). | |
hello |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%% 1. 'fun Module:Function/Arity'形式の場合 | |
> erlang:fun_info(hoge:gen_external_fun()). | |
[{module,hoge}, % erlang:fun_info/1で返される情報は、ほとんど見た目上のそれと同じ | |
{name,hello}, | |
{arity,0}, | |
{env,[]}, | |
{type,external}] % type=external | |
%% 2. 'fun Functoin/Arity'形式の場合 | |
> erlang:fun_info(hoge:gen_local_fun()). % 返される情報が前者に比べてだいぶ増えている | |
[{pid,<0.47.0>}, | |
{module,hoge}, % 上の関数は、このモジュールの現在のインスタンスを参照している | |
{new_index,0}, % 参照先モジュールインスタンスの関数テーブル内でのインデックス | |
{new_uniq,<<32,234,39,54,23,31,234,108,60,180,205,165,64, | |
8,222,180>>}, % モージュールインスタンスを識別するID (大雑把にいえば) | |
{index,0}, | |
{uniq,17256761}, | |
{name,'-gen_local_fun/0-fun-0-'}, | |
{arity,0}, | |
{env,[]}, | |
{type,local}] % type=local | |
%% 3. 'fun (Args) -> Body end'形式の場合 | |
> erlang:fun_info(hoge:gen_anonymouns_fun()). | |
[{pid,<0.47.0>}, | |
{module,hoge}, % 以下の三つの値の意味は、一つ上の場合と同様 | |
{new_index,1}, % 関数テーブル内でのインデックスが一つ増えている | |
{new_uniq,<<32,234,39,54,23,31,234,108,60,180,205,165,64, | |
8,222,180>>}, | |
{index,1}, | |
{uniq,17256761}, | |
{name,'-gen_anonymouns_fun/0-fun-0-'}, | |
{arity,0}, | |
{env,[]}, | |
{type,local}] % type=local |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
> HogeFun = fun hogefuga:piyo/0. % 存在しないモジュールの存在しない関数への参照を生成 | |
#Fun<hogefuga.piyo.0> | |
> HogeFun(). % 実際の呼び出し時に、実体を解決出来ずにエラーとなる | |
** exception error: undefined function hogefuga:piyo/0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%% 1. 完全修飾形式での参照の場合 | |
% まだoldバージョンは存在しない | |
> erlang:check_old_code(hoge). | |
false | |
% 自プロセスがold版を実行していないか確認 | |
> erlang:check_process_code(self(), hoge). | |
false % oldのhogeは使っていない | |
% 完全修飾形式での参照を保持してみる | |
> Fun1 = hoge:gen_external_fun(). | |
% 中身を変更してモジュールの再コンパイル&ロード | |
> c(hoge, [{d, 'HELLO', hello_world}]). | |
> erlang:check_old_code(hoge). | |
true % old版が存在するようになった | |
> erlang:check_process_code(self(), hoge). | |
false % 自プロセスはまだold版への参照を保持していない | |
% 二回以上のロードを跨いても特に問題なし | |
> c(hoge, [{d, 'HELLO', hello}]). | |
%% 2. ローカル形式での参照の場合 (無名関数の場合も同様なのでそちらは省略) | |
% ローカル形式での関数への参照を取得 | |
> Fun2 = hoge:gen_local_fun(). | |
> erlang:check_process_code(self(), hoge). | |
false % まだold版モジュールインスタンスへの参照は存在しない | |
% 再コンパイル&ロード | |
> c(hoge, [{d, 'HELLO', hello_world}]). | |
> erlang:check_process_code(self(), hoge). | |
true % old版への参照発生! (Fun2が古いモジュールインスタンスへの参照を保持しているため) | |
% 再コンパイル&ロード | |
> c(hoge, [{d, 'HELLO', hello}]). | |
*** ERROR: Shell process terminated! *** % 二回のコード更新を跨げずにプロセスクラッシュ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-module(fuga). | |
-export([spawn_external_loop/0, spawn_local_loop/0, spawn_anonymous_external_loop/0]). | |
-export([external_loop/0, local_loop/0]). % プロセスのメインループ用 | |
spawn_external_loop() -> % external_loop/0を実行するプロセスを起動 | |
%% NOTE: ループの度に、その時点での最新のモジュールインスタンスのコードが実行される | |
spawn_monitor(?MODULE, external_loop, []). | |
spawn_local_loop() -> % local_loop/0を実行するプロセスを起動 | |
%% NOTE: 参照(実行)するモジュールインスタンスは、この時点で固定化される | |
spawn_monitor(?MODULE, local_loop, []). | |
spawn_anonymous_external_loop() -> % 無名関数経由でexternal_loop/0を実行するプロセスを起動 | |
%% NOTE: | |
%% - 無名関数自体は特定のモジュールインスタンスを参照する | |
%% - ただし、起動後にすぐに external_loop/0 に処理が移り、無名関数への参照は捨てられるので、 | |
%% 実質的には spawn_external_loop/0 とほぼ同様の挙動となる | |
spawn_monitor(fun () -> ?MODULE:external_loop() end). | |
external_loop() -> | |
timer:sleep(?WAIT), | |
?MODULE:external_loop(). % 完全修飾形式で次のループを呼び出す (コード更新があれば最新のものが使用される) | |
local_loop() -> | |
timer:sleep(?WAIT), | |
local_loop(). % ローカル呼び出し (コード更新があっても現在の実行中のバージョンを使い続ける) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%% コンパイル | |
> c(fuga, [{d, 'WAIT', 1}]). | |
%% プロセス起動 | |
2> fuga:spawn_external_loop(). | |
{<0.40.0>,#Ref<0.0.0.78>} | |
3> fuga:spawn_local_loop(). % このプロセスだけローカル呼び出しでループしている | |
{<0.42.0>,#Ref<0.0.0.83>} | |
4> fuga:spawn_anonymous_external_loop(). | |
{<0.44.0>,#Ref<0.0.0.88>} | |
%% oldを参照しているプロセスがないかの確認: 現時点では全部currentのみを参照している | |
5> erlang:check_process_code(pid(0,40,0), fuga). | |
false | |
6> erlang:check_process_code(pid(0,42,0), fuga). | |
false | |
7> erlang:check_process_code(pid(0,44,0), fuga). | |
false | |
%% コード更新: 一回目 | |
> c(fuga, [{d, 'WAIT', 10}]). | |
%% old確認 | |
9> erlang:check_process_code(pid(0,40,0), fuga). | |
false | |
10> erlang:check_process_code(pid(0,42,0), fuga). % fuga:spawn_local_loop/0だけがoldへの参照を保持 | |
true | |
11> erlang:check_process_code(pid(0,44,0), fuga). | |
false | |
%% コード更新: 二回目 | |
c(fuga, [{d, 'WAIT', 1}]). | |
> flush(). % fuga:spawn_local_loop/0 で起動したプロセスのダウン通知メッセージが届いている | |
Shell got {'DOWN',#Ref<0.0.0.83>,process,<0.42.0>,killed} | |
ok | |
> erlang:is_process_alive(pid(0,40,0)). | |
true | |
> erlang:is_process_alive(pid(0,42,0)). | |
false % down | |
> erlang:is_process_alive(pid(0,44,0)). | |
true |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
> List = [1,2,3]. | |
> UnsafeFun = fun () -> lists:length(List) end. % 無名関数なのでモジュールインスタンスへの参照が発生する | |
> SafeFun = {fun lists:length/1, [List]}. % 完全修飾参照と追加引数をタプルで別々に保持すればOK | |
> my_apply(UnsafeFun, []). | |
3. | |
> my_apply(SafeFun, []). | |
3. | |
%% my_apply/2の定義 | |
my_apply({Fun, ExtraArgs}, Args) -> apply(Fun, ExtraArgs++Args); | |
my_apply(Fun) , Args) -> apply(Fun, Args). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-module(piyo). | |
-export([wait_loop/0, recv_loop/0]). | |
wait_loop() -> | |
%% 一時間スリープしてから、次のループを実行する | |
%% => 一時間(sleep)の間に二回このモジュールの更新が行われた場合は、プロセスクラッシュ | |
timer:sleep(60 * 60 * 1000), | |
?MODULE:wait_loop(). | |
recv_loop() -> | |
receive | |
Msg -> | |
do_something(Msg), % 何らかの処理を実行 | |
%% メッセージを受信し、それに対応する処理が完了したら、次のループを実行する | |
%% => 受信を挟まずに二回このモジュールの更新が行われた場合は、プロセスクラッシュ | |
?MODULE:recv_loop() | |
end. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pfun:lambda(fun () -> hello end, []). % pfun:lambda/2で無名関数を囲む |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment