プロセスの終了とシグナルの送付
Getting Started With Erlangの「4.2 Error Handling」について。
プロセスの終了
処理がループせず終了した場合、または、exit(Reason)が実行された場合、プロセスは終了します。
- 処理が終了した場合とexit(Reason)のReasonがnormalのアトムである場合、正常終了と見なされます。それ以外は異常終了となります。
- 実行時エラーが発生した場合、その原因を引数としてexit(Reason)が実行され異常終了となります。
シグナルの送付
プロセスが終了する際に、link(Other_Pid)によって関連づけられたプロセスが存在する場合、そのプロセスに対して「シグナル」が送付されます。
- シグナルは「終了するプロセスのプロセスID」や「終了した理由」といった情報を含みます。
- プロセスがシグナルを受信した際のデフォルトの動作が規定されており、シグナルを受け取った関連するプロセスは以下の通り振る舞います。
- 通常終了シグナルを受信した場合、無視
- 異常終了シグナルを順した場合、関連プロセスも一緒に終了
- 上記デフォルトの動作はカスタマイズ可能です。(後述)
これを利用して、プロセスでの処理の成否を監視し、エラーが発生した場合関連するトランザクションの実行プロセスをすべて停止する、といったことができます。
以下は確認のためのサンプルです。
- kitten:start(Name)でサーバープロセスと、関連するプロセス3つを起動。
- 関連プロセスは10秒ごとにメッセージを出力する。
- サーバープロセスは送付されたメッセージに応じて以下の終了処理を行う。
- sleepで正常終了(exit(normal))
- killで異常終了(exit(abnormal))
- errorで実行時例外となり異常終了
-module(kitten). -export([start/1, run_prepare/2, run_child/1]). % ねこサーバー。 % プロセスには"kitten"の名前で登録される。 start(Name) -> PID = spawn( kitten, run_prepare, [Name, [kuro, shiro, tora]] ), register(kitten, PID). % 前準備。 % 関連するサーバーとしてこねこサーバーを起動する。 run_prepare( Name, [] ) -> run( Name ); run_prepare( Name, [H|R] ) -> PID = spawn( kitten, run_child, [H] ), link( PID ), % こねこサーバーとこのプロセスをリンクする。 run_prepare( Name, R ). run( Name ) -> receive sleep -> exit(normal); % sleepで正常終了 kill -> exit(abnormal); % killで異常終了 error -> 10/0 % 実行時エラー end. % こねこサーバー % 10秒ごとにミーミー鳴く。 run_child(Name) -> receive after 10000 -> io:format( "~w : mii..~n", [Name] ), run_child(Name) end.
サーバーを起動し、sleepで正常終了してみます。正常終了なのでリンクされたプロセスは停止しません。
1> kitten:start(mii). true 2> tora : mii.. 2> shiro : mii.. 2> kuro : mii.. 2> kitten ! sleep. sleep 3> kuro : mii.. 3> shiro : mii.. 3> tora : mii.. 3>
killやerrorで異常終了した場合は、リンクされたプロセスも同時に停止します。
1> kitten:start(mii). true 2> tora : mii.. 2> shiro : mii.. 2> kuro : mii.. 2> kitten ! kill. kill 3> kitten:start(mii). true 4> tora : mii.. 4> shiro : mii.. 4> kuro : mii.. 4> kitten ! error. error =ERROR REPORT==== 16-Nov-2007::18:53:09 === Error in process <0.37.0> with exit value: {badarith,[{kitten,run,1}]} 5>
シグナル処理のカスタマイズ
プロセスがシグナルを受信した際の処理はカスタマイズできます。手順は次の通り。(ただし、実際にはあんまり使いません、とか書かれていたような気がする・・・・。)
- 受信側のプロセスでprocess_flag(trap_exit, true)を実行する。
- これにより、デフォルトの動作がキャンセルされ、シグナルがメッセージキューに追加されるようになります。
- receiveでシグナルに対応する処理を書く。
- プロセス終了時のシグナルは「{'EXIT',FromPID,Reason}」のタプルでキューに積まれます。
サンプルです。
-module(kitten2). -export([start/1, run_prepare/2, run_child_prepare/1]). % ねこサーバー。 % プロセスには"kitten"の名前で登録される。 start(Name) -> PID = spawn( kitten2, run_prepare, [Name, [kuro, shiro, tora]] ), register(kitten, PID). % 前準備。 % 関連するサーバーとしてこねこサーバーを起動する。 run_prepare( Name, [] ) -> run( Name ); run_prepare( Name, [H|R] ) -> PID = spawn( kitten2, run_child_prepare, [H] ), link( PID ), % こねこサーバーとこのプロセスをリンクする。 run_prepare( Name, R ). run( Name ) -> receive sleep -> exit(normal); % sleepで正常終了 kill -> exit(abnormal); % killで異常終了 error -> 10/0 % 実行時エラー end. % こねこサーバー % 10秒ごとにミーミー鳴く。 run_child_prepare(Name) -> % linkされたプロセスのexitで送付されたシグナルをこのプロセスで受け取るようにする。 process_flag(trap_exit, true), run_child(Name). run_child(Name) -> receive {'EXIT', _, normal} -> % linkされたプロセスで exit( normal ) が実行されたときここに来る。 io:format( "~w : sleep.~n", [Name] ); {'EXIT', _, abnormal} -> % linkされたプロセスで exit( abnormal ) が実行されたときここに来る。 io:format( "~w : kill~n", [Name] ); {'EXIT', _, Reason} -> % linkされたプロセスで exit( その他 ) が実行されたときここに来る。 io:format( "~w : error.[~w]~n", [Name, Reason] ) after 10000 -> io:format( "~w : mii..~n", [Name] ), run_child(Name) end.
実行してみます。
7> kitten2:start(mii). true 8> tora : mii.. 8> shiro : mii.. 8> kuro : mii.. 8> kitten ! sleep. sleepkuro : sleep. tora : sleep. shiro : sleep. 9> kitten2:start(mii). true 10> tora : mii.. 10> shiro : mii.. 10> kuro : mii.. 10> kitten ! kill. killkuro : kill tora : kill shiro : kill 11> kitten2:start(mii). true 12> tora : mii.. 12> shiro : mii.. 12> kuro : mii.. 12> kitten ! error. error =ERROR REPORT==== 16-Nov-2007::19:06:15 === Error in process <0.64.0> with exit value: {badarith,[{kitten2,run,1}]} kuro : error.[{badarith,[{kitten2,run,1}]}] tora : error.[{badarith,[{kitten2,run,1}]}] shiro : error.[{badarith,[{kitten2,run,1}]}] 13>
ふむ。