9章あたり
並行プログラミングにおけるエラーを理解するには以下の3つの概念を理解する
- リンクは2つのプロセスの間のエラー伝播経路を定義するもの
- 2つのプロセスがリンクされている場合に一方のプロセスが死ぬと、終了シグナルがもう一方のプロセスに送られる。
- あるプロセスに現在リンクされているプロセスの集合は、そのプロセスのリンクセット(link set)と呼ばれる。
- 終了シグナルは、プロセスが死ぬときに生成される。
- このシグナルは死ぬプロセスのリンクセットのプロセスすべてにブロードキャストされる。
- 終了シグナルにはプロセスが死んだ理由を示す引数も入っている。
- 死因はexit(Reason)プリミティブを呼び出して明示的に設定することもできるが、
何も設定しなくてもエラーが発生すると設定される。
- 終了シグナルを捕捉するよう設定されたプロセスがシステムプロセスと呼ばれる
- normal以外の終了シグナルを受け取った通常プロセスは、システムプロセスである場合を除いて死んでしまう。
- BIFのprocess_flag(trap_exit, true) を呼び出すと、通常のプロセスを終了の捕捉が可能なシステムプロセスに変更できる。
- システムプロセスがプロセスPidから終了シグナルとしてWhyを受け取ると、
その終了シグナルは{'EXIT', Pid, Why} というメッセージに変換されてシステムプロセスのメールボックスに置かれる。
終了を捕捉するためのほとんどのプログラムは以下の3つの単純なイディオムを使っている
spawnを使って並列プロセスを作るプロセスがあるとする
Pid = spawn(fun() -> ... end)
これ以外は何もない。
生成したプロセスがクラッシュしても、現在のプロセスは動作し続ける。
Pid = spawn_link(fun() -> ... end)
spawn_link を使って並列プロセスを作り、かつ終了を捕捉しないようしておけば
生成したプロセスが異常終了すると現在のプロセスもクラッシュしてくれる。
この場合はspawn_link とtrap_exits を使って次のように書く
...
process_flag(trap_exit, true),
Pid = spawn_link(fun() -> ... end),
...
loop(...).
loop(State) ->
receive
{'EXIT', SomePid, Reason} ->
%% エラー処理
loop(State1);
...
end
loopを評価しているプロセスは終了を捕捉するが、リンクされているプロセスが死んでも自分は死なない。
このプロセスは死のうとするプロセスからの終了シグナルをすべて受け取り、
プロセスの異常終了を検出したときに必要な処置をすることができる。
プロセスが終了するときに何らかの処理を行いたい場合
・lib_misc.erl
on_exit(Pid, Fun) ->
spawn(fun() ->
process_flag(trap_exit, true),
link(Pid),
receive
{'EXIT', Pid, Why} ->
Fun(Why)
end
end).
・Eshell Pidにアトムを送信すると、リストでないのでプロセスが死にon_exitハンドラが呼ばれる
1> F = fun() ->
receive
X -> list_to_atome(X)
end
end.
#Fun<erl_eval.20.118419387>
2> Pid = spawn(F).
<0.82.0>
3> lib_misc:on_exit(Pid, fun(Why) ->
io:format(" ~p died with:~p~n",[Pid, Why])
end).
<0.85.0>
4> Pid ! hello.
hello
<0.82.0> died with:{{shell_undef,list_to_atome,1,[]},
[{shell,shell_undef,2,[{file,"shell.erl"},{line,1061}]},
{erl_eval,local_func,6,
[{file,"erl_eval.erl"},{line,562}]}]}