無題の備忘録

IT技術について調べたことや学んだこと、試したこと記録するブログです。Erlang、ネットワーク、 セキュリティ、Linux関係のものが多いです。

Erlang の監視ツリーで gen_server を停止する

Erlang -- gen_server Behaviour にある監視ツリーに含まれている gen_server の停止を試してみたいと思います。

スーパーバイザーとそれによって監視されるワーカーのサンプルコードは、前回の記事( Erlang の supervisor ビヘイビアを学ぶ - 無題の備忘録 )の sample_sup (supervisor ) モジュールと value_server ( gen_server ) モジュールを使います。

ワーカーのサンプルコードとして value_server を使いますが、下記のように2点変更します。

  • まず、init関数に process_flag(trap_exit, true),を追加します。これは、スーパーバイザーからの停止命令( shutdown メッセージ) を補足できるようにするためです。
  • 次に、terminate/2 関数を追加します。この関数にクリーンアップ処理を実装します。この例では、io:format で単に文字列をErlangシェルに表示します。

上記の2点を変更すると、value_server モジュールは下記のようなサンプルコードになります。

-module(value_server).
-behaviour(gen_server).

-export([start_link/1, value/1, pid/1]).
-export([init/1, handle_call/3, handle_cast/2, terminate/2]).

start_link({Name, Value}) ->
    gen_server:start_link({local, Name}, ?MODULE, Value, []).

value(Name) ->
    gen_server:call(Name, value).
pid(Name) ->
    gen_server:call(Name, pid).

init(Value) ->
    process_flag(trap_exit, true),
    {ok, Value}.

handle_call(value, _From, Value) ->
    {reply, Value, Value};
handle_call(pid, _From, Value) ->
    {reply, self(), Value}.
handle_cast(_Request, Value) ->
    {noreply, Value}.

terminate(Reason, Value) ->
    io:format("~p terminate reason:~p with value:~p.~n", [self(), Reason, Value]),
    ok.

下記は使用例です。

> c(sample_sup).
{ok,sample_sup}
> c(value_server).
{ok,value_server}
> {ok, Sup} = sample_sup:start_link().
{ok,<0.91.0>}
> value_server:value(name_a).
value_a
> PidA = value_server:pid(name_a).
<0.92.0>
> is_process_alive(PidA).
true
> supervisor:terminate_child(Sup, name_a).
<0.92.0> terminate reason:shutdown with value:value_a.
ok
> is_process_alive(PidA).
false

sample_sup:start_link() によってスーパーバイザーを起動します。すると、name_a という名前の子プロセスが起動されます。

value_server:value(name_a) は、name_a に保持している値を問い合わせるリクエストを送信しています。

value_server:pid(name_a) によって、ワーカーの Pid を確認しています。

下記の関数によって、name_a の子プロセスを終了しています。すると、value_server の terminate/2 関数に記述した処理が実行されていることがわかります。

> supervisor:terminate_child(Sup, name_a).
<0.92.0> terminate reason:shutdown with value:value_a.

最後は、下記の関数で name_a という名前の子プロセスが生きているか調べて、終了されていることを確認しました。

> is_process_alive(PidA).
false