gen_fsm
httpd の source を見てたら Supervisor Behaviour を使っていて…Supervisor Behaviour を読んでいたら、supervision tree の考え方と実装方法を理解しなきゃ駄目っぽい気がしたので、OTP Design Principles の Overview に目を通してみた。
で、以前、gen_server で sample を作ったので、今回は、gen_fsm を試しに使ってみた。
source
-module(code_lock). -behaviour(gen_fsm). -export([start_link/1, stop/0]). -export([button/1, get_state/0]). -export([init/1, locked/2, open/2]). -export([handle_event/3, terminate/3]). -export([handle_sync_event/4, handle_info/3, code_change/4]). start_link(Code) -> gen_fsm:start_link({local, code_lock}, code_lock, Code, []). stop() -> gen_fsm:send_all_state_event(code_lock, stop). button(String) -> gen_fsm:send_event(code_lock, {button, String}). get_state() -> gen_fsm:sync_send_all_state_event(code_lock, get_state). init(Code) -> {ok, locked, {[], Code}}. locked({button, String}, {SoFar, Code}) -> case SoFar ++ String of Code -> io:fwrite("open!!~n"), {next_state, open, {[], Code}, 3000}; Incomplete when length(Incomplete)<length(Code) -> {next_state, locked, {Incomplete, Code}}; _Wrong -> {next_state, locked, {[], Code}} end. open(timeout, StateData) -> io:fwrite("close!!~n"), {next_state, locked, StateData}. handle_event(stop, _StateName, StateData) -> {stop, normal, StateData}. terminate(normal, _StateName, _StateData) -> ok. handle_sync_event(get_state, _From, StateName, StateData) -> {reply, StateName, StateName, StateData}. handle_info(_Info, StateName, StateData) -> {next_state, StateName, StateData}. code_change(_OldVsn, StateName, StateData, _Extra) -> {ok, StateName, StateData}.
開始
start_link/1 で gen_fsm:start_link/4 を呼ぶ。
gen_fsm:start_link/4 は、gen_server:start_link/4 と説明が重複するので省略。
id:cooldaemon:20070625 参照の事。
init(Code) -> {ok, locked, {[], Code}}.
init/1 は、gen_fsm:start_link/4 から呼ばれる。
{ok, StateName, StateData} を返すようにする。
イベントを送る
button(String) -> gen_fsm:send_event(code_lock, {button, String}).
gen_fsm:send_event/2 は、StateName(Event, StateData) を非同期で呼ぶ。
同期で呼び出して戻り値を受け取りたい場合は、gen_fsm:sync_send_event/2 や gen_fsm:sync_send_event/3 を使う。
button イベントが発生した際、状態が locked であれば呼ばれる関数は、下記のように書く。
locked({button, String}, {SoFar, Code}) -> ...
戻り値として、{next_state, NewStateName, NewStateData} を返すと状態が NewStateName に変わる。
{next_state, NewStateName, NewStateData, MSec} を返すと、MSec ミリ秒後に状態が変わる。
その場合は、StateName(timeout, StateData) が呼ばれる。
状態に左右されないイベントを送る
非同期の場合は、gen_fsm:send_all_state_event/2 を使い、同期の場合は、gen_fsm:sync_send_all_state_event/2 を使う。
Supervision Tree 配下ではなく、Stand Alone で動作している場合は、gen_fsm:send_all_state_event/2 を利用して、stop を実装できる。
停止
stop() -> gen_fsm:send_all_state_event(code_lock, stop). handle_event(stop, _StateName, StateData) -> {stop, normal, StateData}. terminate(normal, _StateName, _StateData) -> ok.
gen_server の説明と重複するので省略。
その他
相変わらず突っ込み大歓迎。