simple_one_for_one の中で simple_one_for_one のプロセスを動的に上げた場合、綺麗に終了する方法はいくつかありますが、 monitor を使うのが自分は好みです。
monitor
monitor はプロセスを監視する仕組みです。supervisor を使うと再起動してくれるじゃないかと思いがちですが、何か不都合があったら 関連する全てのプロセスを終了したい ということがたまにあります。
one_for_all でいいのではと思いがちですが、順番に起動していきたい、つまり依存関係がある場合に monitor を使います。
monitor は demonitor とセットで使います。supervisor:start_child/2 で上げて戻ってきた Pid を監視します。
そして terminate_child/2 する直前に demonitor します。demonitor しないと {'DOWN', MonitorRef, Type, Object, Info} が送られてきてしまいます。
{'DOWN', MonitorRef, Type, Object, Info}
monitor はとても便利です。好きなプロセスを監視できます。 monitor に Pid を渡してあげるだけです。そのプロセスが死ぬときに {'DOWN', MonitorRef, Type, Object, Info} を monitor Ref を持っているプロセスに送ってきてくれます。
ほとんどの場合は Type は process で、Object は Pid で、Info は Reason です。
例
まずは UDP のソケットを動的に上げる仕組みを作るとします。
gen_server + gen_udp でそれを supervisor で simple_one_for_one にセットします。
よくあるパターンとして、パケットの書き換えに状態を必要とする場合があります。
暗号の鍵で IV を使うとかがいい例でしょうか。
その場合は復号/暗号処理用のプロセスを立ち上げます。ただほとんどの場合このプロセスはライブラリとして外に切り出しており supervisor もそのライブラリに含まれていることが多いでしょう。
この場合は process の監視を monitor を使うと良いです。つまり gen_server + gen_udp なプロセスが init/1 する際に、ライブラリの暗号プロセス(自分専用)を立ち上げて monitor して #state{monitor_ref = ...} として保存しておくべきでしょう。
gen_server + gen_udp が死んだとき
まず terminate 部分で demonitor して terminate_child/2 して殺しましょう。この場合死ぬタイミングによっては monitor_ref が存在しない場合もあるので注意しましょう。
別ライブラリの monitor してるプロセスが死んだとき
想定外のエラーの場合、Erlang ではクラッシュさせて殺すのが通例です。
monitor していれば 'DOWN' で上がってきますので、あとは handle_info でハンドリングして、{stop, {shutdown, Reason}} で終了させてしまいましょう。
まとめ
monitor/demonitor を使えるようになってやっと初心者卒業という印象を持っています。simple_one_for_one と monitor を使って色々やってみましょう。
Top comments (0)