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.