Erlang のスタンドアローンの gen_server を停止する
Erlang -- gen_server Behaviour にあるスタンドアローンでの gen_server の停止を試してみたいと思います。
gen_server のサンプルコードとして sample_queue.erl というファイル名で下記のコードを作成します。 このコードは、gen_server のビヘイビアを使って、キューの機能を提供します。
in/1関数で gen_server にアイテムを登録して、out/0関数で gen_server からアイテムを取得します。stop/0関数で gen_server を停止します。
-module(sample_queue).
-behaviour(gen_server).
-export([start_link/0, in/1, out/0, stop/0]).
-export([init/1, handle_call/3, handle_cast/2, terminate/2]).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
in(Item) ->
gen_server:call(?MODULE, {in, Item}).
out() ->
gen_server:call(?MODULE, out).
stop() ->
gen_server:cast(?MODULE, stop).
init(_Args) ->
{ok, queue:new()}.
handle_call({in, Item}, _From, Queue) ->
NewQueue = queue:in(Item, Queue),
{reply, ok, NewQueue};
handle_call(out, _From, Queue) ->
case queue:out(Queue) of
{{value, Item}, NewQueue} -> {reply, Item, NewQueue};
{empty, NewQueue} -> {reply, empty, NewQueue}
end.
handle_cast(stop, Queue) ->
{stop, normal, Queue}.
terminate(normal, _State) ->
timer:sleep(1000),
io:format("clean up done.~n"),
ok.
下記は使用例です。下記では、sample_queue モジュールの gen_server を起動し、その後、sample_queue モジュールのAPIを使って、a, b, c というアイテムを登録し、その後はキューのアイテムを空になるまで取り出しています。最後に、gen_server を停止します。
> c(sample_queue).
{ok,sample_queue}
> sample_queue:start_link().
{ok,<0.86.0>}
> sample_queue:in(a).
ok
> sample_queue:in(b).
ok
> sample_queue:in(c).
ok
> sample_queue:out().
a
> sample_queue:out().
b
> sample_queue:out().
c
> sample_queue:out().
empty
> sample_queue:stop().
ok
clean up done.
stop/0関数は gen_server:cast関数を呼び出しており、非同期リクエストです。
stop() ->
gen_server:cast(?MODULE, stop).
そのため、sample_queue:stop() を呼び出すとすぐに ok が返ってきますが、終了処理は続いています。Erlang シェルで okの後に clean up done. が表示されているのはそのためです。
> sample_queue:stop(). ok clean up done.
話は前後しますが、gen_server:cast(?MODULE, stop). の非同期リクエストは、下記のコールバック関数を呼び出し、
handle_cast(stop, Queue) ->
{stop, normal, Queue}.
この関数の返り値を {stop, normal, Queue} とすることで、下記のように terminate(Reason, State) の Reason を normal とした関数にマッチします。後始末が必要な gen_server であればこの teminate/2 関数に後始末の処理を記述します。
terminate(normal, _State) ->
timer:sleep(1000),
io:format("clean up done.~n"),
ok.