このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

Erlang実験室:例外のcatchと終了シグナル

Erlangの例外は、単にメッセージが謎めいているだけではなくて、他のプログラミング言語とはだいぶ趣<おもむき>が異なるので、理解して正しく使うのはなかなか大変です。そのことはまたの機会に、ということで、このエントリーでは、キャッチされなかった例外がどうなるかを書いておきます。

Erlangのcatchオペレータ(言語構文に含まれる特殊形式だけど、「オペレータ」と呼ぶみたい)は、例外を補足捕捉して通常の値に変換します。Erlang/OTPリリース10からは、catchオペレータよりずっと洗練されたtry構文があります。これらの例外補足捕捉機構でハンドルされなかった例外は、(Erlang内の)プロセスを終了させます。

プロセスが終了すると、そのプロセスとリンクされているすべてのプロセスに終了シグナルが送られます。終了シグナルに乗って運ばれるデータを終了ターム(exit term)と呼ぶのですが、例外の種類*1ごとに終了タームがどうなるかまとめてみました。

例外の生成 catchによる値 終了ターム
throw(Term) Term {{nocatch, Term}, Stack}
exit(Term) {'EXIT' Term} Term
erlang:error(Term) {'EXIT' {Term, Stack}} {Term, Stack}

Stackはシンボリックなスタックトレースです(「分かりにくいと評判のErlangエラーのまとめ」参照)。error例外は、多くの場合、プログラマerlang:error/1 を呼ぶのではなくてランタイムシステムが発行します。

次は実験用のサンプルです。


%% exit_test.erl

-module(exit_test).
-compile(export_all).

start() ->
spawn(fun parent_main/0).

parent_main() ->
process_flag(trap_exit, true),
Pid = spawn_link(fun child_loop/0),
parent_loop(Pid).

parent_loop(Pid) ->
receive
stop ->
io:fwrite("Parent: byebye.~n"),
exit(normal);
{'EXIT', Pid, Why} ->
io:fwrite("Parent: child exited. :~p~n", [Why]),
NewPid = spawn_link(fun child_loop/0),
parent_loop(NewPid);
Message ->
Pid ! Message, % forward to the child
parent_loop(Pid)
end.

child_loop() ->
receive
{error, Term} ->
io:fwrite("Child: error received.~n"),
erlang:error(Term),
child_loop();
{exit, Term} ->
io:fwrite("Child: exit received.~n"),
exit(Term),
child_loop();
{throw, Term} ->
io:fwrite("Child: throw received.~n"),
throw(Term),
child_loop();
Other ->
io:fwrite("Child: unknown message:~p~n", [Other]),
child_loop()
end.

*1:Erlangでは、例外の種類を例外クラスと呼ぶのだけど、うかつに「クラス」とか書くと、無意味な脊髄反射をする人がいそうなので書かない。