Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?

プログラミングErlang - 並行プログラミングにおけるエラー

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を評価しているプロセスは終了を捕捉するが、リンクされているプロセスが死んでも自分は死なない。
このプロセスは死のうとするプロセスからの終了シグナルをすべて受け取り、
プロセスの異常終了を検出したときに必要な処置をすることができる。

on_exitハンドラ

プロセスが終了するときに何らかの処理を行いたい場合

・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}]}]}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment