概要
このページでは、リンクされたプロセスが終了した時にどのように処理されるかについてまとめている。
リンクされた 生 プロセスが終了するときの挙動一覧
リンクされたプロセスが終了される時の挙動についての説明は 本:プログラミングErlang を見るとわかりやすい。
下の表はプログラミングErlangを参考に少し修正している。
(trap_exitで対比できるようにtrap_exitがtrueの時に終了シグナルnormalが飛んできた時を終了シグナルXの場合と分けて書いている。)
trap_exit | 終了シグナル | プロセスの生死 | 動作 |
---|---|---|---|
true | normal | 続行する |
{'EXIT', Pid, normal} をメールボックスに追加する |
true | kill | 死ぬ | 終了シグナル killed をリンクセットにブロードキャストする |
true | X | 続行する |
{'EXIT', Pid, X} をメールボックスに追加する |
false | normal | 続行する | 通常終了シグナルは無視される |
false | kill | 死ぬ | 終了シグナル killed をリンクセットにブロードキャストする |
false | X | 死ぬ | 終了シグナル X をリンクセットにブロードキャストする |
exit(Why)
と exit(self(), Why)
の違い
リンクしているプロセスが受け取るメッセージの終了理由が kill
と killed
で異なる。
exit(kill)
の場合
リンクしているプロセスは kill
を受け取る
> process_flag(trap_exit, true).
false
> spawn_link(fun() -> exit(kill) end).
<0.35.0>
> flush().
Shell got {'EXIT',<0.35.0>,kill}
ok
exit(self(), kill)
の場合
リンクしているプロセスは killed
を受け取る
> process_flag(trap_exit, true).
false
> spawn_link(fun() -> exit(self(), kill) end).
<0.35.0>
> flush().
Shell got {'EXIT',<0.35.0>,killed}
ok
関数の詳細なドキュメント
上の関数の挙動の違いは公式ドキュメントを読むと書かれている。
( kill
が killed
に変わるということが exit/2
にしか書かれていないことより。)
また、プログラミングErlangのドキュメントも載せておく。
exit/1
-spec exit(Reason) -> no_return() when
Reason :: term().
公式ドキュメント(和訳)
終了理由 Reason
で呼び出しプロセスの実行を停止させる。 Reason
はどんな項でも良い。この関数の評価はプロセスを終了させるため、戻り値はない。
プログラミングErlang
現在のプロセスを要因 Reason
で終了させる。この文を実行している節が catch
文のスコープの中にない場合は、現在リンクされているすべてのプロセスに対して引数 Reason
を伴う終了シグナルをブロードキャストする。
(プログラミングErlangより。 Why
を Reason
に置き換えている。)
exit/2
-spec exit(Pid, Reason) -> true when
Pid :: pid() | port(),
Reason :: term().
公式ドキュメント(和訳)
Pid
で識別されるプロセスまたはポートに終了理由 Reason
で終了信号を送信する。
Reason
が normal
か kill
以外の任意の項である場合は、次の動作が適用される。
Pid
が終了を捕捉していない場合は、 Pid
自体が終了理由 Reason
で終了する。 Pid
が終了を捕捉している場合は、終了シグナルがメッセージ {'EXIT', From, Reason}
に変換され、 Pid
のメッセージキューに追加される。 From
は終了シグナルを送ったプロセスのpidである。 process_flag/2
も参照。
Reason
がアトム normal
の場合、 Pid
は終了しない。終了を補足している場合、終了シグナルはメッセージ {'EXIT', From, normal}
に変換されメッセージキューに追加される。
Reason
がアトム kill
の場合、つまり exit(Pid, kill)
が呼ばれた場合、補足されない終了シグナルが送られ、終了理由 killed
で無条件に終了する。
プログラミングErlang
要因が Why
の終了シグナルをプロセス Pid
に送信する。
gen_serverを停止させるときの挙動一覧
リンクされたgen_serverについて、 exit
時の状態、 exit
の送り主によって「何も呼ばれずに死ん」だり、「 terminate/2
が呼ばれ」たり、「 handle_info/2
で補足し」たりするので、ここでまとめておく。
ここで Srv
はgen_serverのプロセスID( pid()
)である。
trap_exit | 終了処理 | 送り主 | 動作 | リンクプロセスへの終了シグナル | ERROR REPORTの出力 |
---|---|---|---|---|---|
false | exit(Srv, normal) |
親プロセス | 死なない | - | なし |
false | exit(Srv, kill) |
親プロセス | 死ぬ | killed |
なし |
false | exit(Srv, X) |
親プロセス | 死ぬ | X |
なし |
false | Srv ! {'EXIT', self(), normal} |
親プロセス |
terminate/2 の呼び出し |
normal |
なし |
false | Srv ! {'EXIT', self(), kill} |
親プロセス |
terminate/2 の呼び出し |
kill |
あり |
false | Srv ! {'EXIT', self(), X} |
親プロセス |
terminate/2 の呼び出し |
X |
あり |
true | exit(Srv, normal) |
親プロセス |
terminate/2 の呼び出し |
normal |
なし |
true | exit(Srv, kill) |
親プロセス | 死ぬ | killed |
なし |
true | exit(Srv, X) |
親プロセス |
terminate/2 の呼び出し |
X |
あり |
true | Srv ! {'EXIT', self(), normal} |
親プロセス |
terminate/2 の呼び出し |
normal |
なし |
true | Srv ! {'EXIT', self(), kill} |
親プロセス |
terminate/2 の呼び出し |
kill |
あり |
true | Srv ! {'EXIT', self(), X} |
親プロセス |
terminate/2 の呼び出し |
X |
あり |
false | exit(Srv, normal) |
親以外のプロセス | 死なない | - | なし |
false | exit(Srv, kill) |
親以外のプロセス | 死ぬ | killed |
なし |
false | exit(Srv, X) |
親以外のプロセス | 死ぬ | X |
なし |
false | Srv ! {'EXIT', self(), normal} |
親以外のプロセス |
handle_info/2 の呼び出し |
- | なし |
false | Srv ! {'EXIT', self(), kill} |
親以外のプロセス |
handle_info/2 の呼び出し |
- | なし |
false | Srv ! {'EXIT', self(), X} |
親以外のプロセス |
handle_info/2 の呼び出し |
- | なし |
true | exit(Srv, normal) |
親以外のプロセス |
handle_info/2 の呼び出し |
- | なし |
true | exit(Srv, kill) |
親以外のプロセス | 死ぬ | killed |
なし |
true | exit(Srv, X) |
親以外のプロセス |
handle_info/2 の呼び出し |
- | なし |
true | Srv ! {'EXIT', self(), normal} |
親以外のプロセス |
handle_info/2 の呼び出し |
- | なし |
true | Srv ! {'EXIT', self(), kill} |
親以外のプロセス |
handle_info/2 の呼び出し |
- | なし |
true | Srv ! {'EXIT', self(), X} |
親以外のプロセス |
handle_info/2 の呼び出し |
- | なし |
gen_server:stop/1,3
gen_server:stop/1,3
はErlang/OTP 18で追加された関数である。
この関数も停止処理に使うものということでここに公式ドキュメントを和訳しておく。
公式ドキュメントは以下のURLにある。
http://www.erlang.org/doc/man/gen_server.html#stop-1
http://www.erlang.org/doc/man/gen_server.html#stop-3
spec
-spec stop(ServerRef) -> ok when
ServerRef :: Name | {Name, Node} | {global, GlobalName} | {via, Mdule, ViaName} | pid(),
Node :: atom(),
GlobalName :: term(),
ViaName :: term().
-spec stop(ServerRef, Reason, Timeout) -> ok when
ServerRef :: Name | {Name, Node} | {global, GlobalName} | {via, Mdule, ViaName} | pid(),
Node :: atom(),
GlobalName :: term(),
ViaName :: term(),
Reason :: term(),
Timeout :: pos_integer() | infinity.
関数の説明(和訳)
与えられた Reason
で汎用サーバに終了させる命令を送り、終了を待つ。gen_serverは終了する前に Module:terminate/2
を呼ぶ。
サーバが期待される理由で終了した場合、関数は ok
を返す。 normal
、 shutdown
、 {shutdown, Term}
以外の理由の場合、 error_logger:format/2
を使ってerror reportが発生する。デフォルトの Reason
は normal
である。
Timeout
は0以上の整数であり、サーバが終了するのを待つまでのミリ秒単位で指定する。または、アトム infinity
を指定すると永遠に待ち続ける。デフォルトの値は infinity
である。サーバが指定した時間で終わらなかった場合は、 timeout
例外が発生する。
プロセスが存在しない場合は、 noproc
例外が発生する。
link/1
の挙動( PidOrPort
が存在しない場合の挙動)
リンク時の状態によって、リンク先のプロセス PidOrPort
がない場合の挙動が異なるので、ここに公式ドキュメントを和訳しておく。
公式ドキュメント(和訳)
既に呼び出しプロセスと別のプロセス(またはポート) PidOrPort
との間にリンクがなければ、そのリンクを作る。プロセスは自身へのリンクを作成しようとした時は何もしない。 true
を返す。
PidOrPort
が存在しない場合、BIFの振る舞いは 呼び出しプロセスが終了を補足しているか どうかによる( process_flag/2
を参照):
-
呼び出しプロセスが終了を補足していない、かつ、
PidOrPort
のチェックがチープな場合(つまり、PidOrPort
がローカルにある場合)、link/1
は理由noproc
で失敗 する。 -
そうでない場合は、呼び出しプロセスが終了を補足している場合、かつ/または、
PidOrPort
がリモートにある場合、link/1
はtrue
を返すが、理由noproc
を伴う終了シグナルが呼び出しプロセスに送られる。