模塊摘要
通用有限狀態機行爲。
描述
用於實現有限狀態機的行爲模塊。使用該模塊實現的通用有限狀態機進程(gen_fsm)將具備一組標準的接口函數,幷包括用於跟蹤和錯誤報告的功能。它也適用於OTP監督樹。有關更多信息,請參閱OTP設計原則。
gen_fsm假定全部特定部分都位於回調模塊中,導出預約義的函數集。行爲函數和回調函數之間的關係能夠說明以下:
gen_fsm module Callback module
-------------- ---------------
gen_fsm:start_link -----> Module:init/1
gen_fsm:send_event -----> Module:StateName/2
gen_fsm:send_all_state_event -----> Module:handle_event/3
gen_fsm:sync_send_event -----> Module:StateName/3
gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
- -----> Module:handle_info/3
- -----> Module:terminate/3
- -----> Module:code_change/4
若是回調函數失敗或返回錯誤值,gen_fsm將終止。
gen_fsm處理系統消息,如sys(3)中所述。該SYS模塊可用於調試gen_fsm。
請注意,gen_fsm不會自動捕獲退出信號,必須在回調模塊中明確啓動。
除非另有說明,不然若是指定的gen_fsm不存在或者給出了錯誤的參數,則此模塊中的全部函數都將失敗。
若是回調函數指定'hibernate'而不是超時值,gen_fsm進程能夠進入休眠狀態(參見erlang(3))。若是預計服務器長時間處於空閒狀態,這可能頗有用。可是,應謹慎使用此功能,由於休眠意味着至少有兩個垃圾收集(在休眠時和喚醒後不久),對於忙的狀態機器並不想在每一次調用之間處理垃圾回收。
導出
start_link(Module, Args, Options) -> Result
start_link(FsmName, Module, Args, Options) -> Result
Types:
FsmName = {local,Name} | {global,GlobalName} | {via,Module,ViaName}
Name = atom()
GlobalName = ViaName = term()
Module = atom()
Args = term()
Options = [Option]
Option = {debug,Dbgs} | {timeout,Time} | {spawn_opt,SOpts}
Dbgs = [Dbg]
Dbg = trace | log | statistics | {log_to_file,FileName} | {install,{Func,FuncState}}
SOpts = [SOpt]
SOpt - 見erlang:spawn_opt/2,3,4,5
Result = {ok,Pid} | ignore | {error,Error}
Pid = pid()
Error = {already_started,Pid} | term()
建立gen_fsm進程做爲監督樹的一部分。該功能應由監督樹直接或間接調用。除其餘外,它將確保gen_fsm連接到監督樹。
gen_fsm進程調用Module:init/1進行初始化。爲確保同步啓動過程,在Module:init/1返回以前,start_link/3,4不會返回。
若是FsmName = {local,Name},則gen_fsm使用register/2在本地註冊爲Name。若是FsmName = {global,GlobalName},則gen_fsm使用global:register_name/2全局註冊爲GlobalName。若是EventMgrName={via,Module,ViaName},則事件管理器註冊用Module表示的註冊表。所述模塊的回調應該導出的函數register_name/2,unregister_name/1,whereis_name/1和send/2,其行爲應與global中的相應函數相同。所以,{via,global,GlobalName}是有效的引用。
若是未提供名稱,則不會註冊gen_fsm。
Module是回調模塊的名稱。
Args是一個任意項,它做爲參數傳遞給Module:init/1。
若是選項{timeout,Time}存在,則容許gen_fsm花費Time毫秒初始化,或者它將被終止,啓動函數將返回 {error,timeout}。
若是存在選項{debug,Dbgs},則將爲Dbgs中的每一個項調用相應的sys函數。見sys(3)。
若是選項{spawn_opt,SOpts}存在,則SOpts將做爲選項列表傳遞給spawn_opt BIF,後者用於生成gen_fsm進程。見erlang(3)。
注意
目前不容許使用spawn選項monitor,但會致使函數失敗,緣由爲badarg。
若是gen_fsm成功建立並初始化,則函數返回{ok,Pid},其中Pid是gen_fsm的pid。若是已存在具備指定FsmName的進程,則該函數返回 {error,{already_started,Pid}},其中Pid是該進程的pid。
若是Module:init/1因Reason而失敗,則函數返回{error,Reason}。若是Module:init/1返回{stop,Reason}或ignore,則進程終止,函數分別返回{error,Reason}或ignore。
start(Module, Args, Options) -> Result
start(FsmName, Module, Args, Options) -> Result
Types:
FsmName = {local,Name} | {global,GlobalName} | {via,Module,ViaName}
Name = atom()
GlobalName = ViaName = term()
Module = atom()
Args = term()
Options = [Option]
Option = {debug,Dbgs} | {timeout,Time} | {spawn_opt,SOpts}
Dbgs = [Dbg]
Dbg = trace | log | statistics | {log_to_file,FileName} | {install,{Func,FuncState}}
SOpts = [term()]
Result = {ok,Pid} | ignore | {error,Error}
Pid = pid()
Error = {already_started,Pid} | term()
建立一個獨立的gen_fsm進程,即gen_fsm,它不是監督樹的一部分,所以沒有監督者。
有關參數和返回值的說明,請參見start_link/3,4。
send_event(FsmRef, Event) -> ok
Types:
FsmRef = Name | {Name,Node} | {global,GlobalName} | {via,Module,ViaName} | pid()
Name = Node = atom()
GlobalName = ViaName = term()
Event = term()
將事件異步發送到gen_fsm FsmRef並當即返回ok。gen_fsm將調用Module:StateName/2來處理事件,其中StateName是gen_fsm的當前狀態的名稱。
FsmRef能夠是:
pid,
Name,若是gen_fsm在本地註冊,
{Name,Node},若是gen_fsm在另外一個節點本地註冊,或者
{global,GlobalName},若是gen_fsm是全局註冊的,
{via,Module,ViaName},若是事件管理器是經過可選進程註冊表註冊的,
Event是一個任意項,做爲Module:StateName/2的參數之一傳遞。
send_all_state_event(FsmRef, Event) -> ok
Types:
FsmRef = Name | {Name,Node} | {global,GlobalName} | {via,Module,ViaName} | pid()
Name = Node = atom()
GlobalName = ViaName = term()
Event = term()
將事件異步發送到gen_fsm FsmRef並當即返回ok。gen_fsm將調用Module:handle_event/3來處理事件。
有關參數的說明,請參閱send_event/2。
send_event和send_all_state_event之間的區別在於使用哪一個回調函數來處理事件。在每一個狀態下發送事件以相同方式被處理,此函數頗有用,由於在每一個狀態名稱函數中只須要一個handle_event子句來處理事件而不是每一個狀態名函數一個子句。
sync_send_event(FsmRef, Event) -> Reply
sync_send_event(FsmRef, Event, Timeout) -> Reply
Types:
FsmRef = Name | {Name,Node} | {global,GlobalName} | {via,Module,ViaName} | pid()
Name = Node = atom()
GlobalName = ViaName = term()
Event = term()
Timeout = int()>0 | infinity
Reply = term()
將事件發送到gen_fsm FsmRef並等待,直到回覆到達或發生超時。gen_fsm將調用Module:StateName/3來處理事件,其中 StateName是gen_fsm的當前狀態的名稱。
有關FsmRef和Event 的說明,請參閱send_event/2。
Timeout是一個大於零的整數,它指定等待回覆的毫秒數,或無限期等待的原子infinity。默認值爲5000.若是在指定時間內未收到回覆,則函數調用失敗。
返回值Reply在Module:StateName/3的返回值中定義。
在OTP R12B/Erlang 5.6中刪除了在鏈接到客戶端時,若是服務器在調用期間掛了,有時會消耗服務器退出消息的古老行爲。
sync_send_all_state_event(FsmRef, Event) -> Reply
sync_send_all_state_event(FsmRef, Event, Timeout) -> Reply
Types:
FsmRef = Name | {Name,Node} | {global,GlobalName} | {via,Module,ViaName} | pid()
Name = Node = atom()
GlobalName = ViaName = term()
Event = term()
Timeout = int()>0 | infinity
Reply = term()
將事件發送到gen_fsm FsmRef並等待,直到回覆到達或發生超時。gen_fsm將調用Module:handle_sync_event/4來處理事件。
有關FsmRef和Event的說明,請參閱send_event/2。有關Timeout和Reply的說明,請參閱sync_send_event/3。
見send_all_state_event/2,討論sync_send_event和sync_send_all_state_event的區別。
reply(Caller, Reply) -> true
Types:
Caller - 見下面
Reply = term()
當沒法在Module:State/3或Module:handle_sync_event/4的返回值中定義回覆時,gen_fsm可使用此函數向客戶端進程顯式發送回覆,當調用sync_send_event/2,3或sync_send_all_state_event/2,3。
Caller必須是提供給回調函數的From參數。Reply是一個任意項,它將做爲sync_send_event/2,3或sync_send_all_state_event/2,3的返回值返回給客戶端。
send_event_after(Time, Event) -> Ref
Types:
Time = integer()
Event = term()
Ref = reference()
在gen_fsm內部發送延遲事件,在time ms後調用此函數。使用cancel_timer/1可用於取消延遲發送的引用,當即返回。
gen_fsm將調用Module:StateName/2來處理事件,其中StateName是傳遞延遲事件時gen_fsm的當前狀態的名稱。
Event是一個任意項,做爲Module:StateName/2的參數之一傳遞。
start_timer(Time, Msg) -> Ref
Types:
Time = integer()
Msg = term()
Ref = reference()
在gen_fsm內部發送超時事件,在Time ms後調用此函數。當即返回可用於使用cancel_timer/1取消計時器的引用。
gen_fsm將調用Module:StateName/2來處理事件,其中StateName是傳遞超時消息時gen_fsm的當前狀態的名稱。
Msg是一個任意的術語,它在超時消息{timeout,Ref,Msg}中傳遞,做爲Module:StateName/2的參數之一。
cancel_timer(Ref) -> RemainingTime | false
Types:
Ref = reference()
RemainingTime = integer()
取消gen_fsm中Ref引用的內部計時器。
Ref是從send_event_after/2 或 start_timer/2返回的引用。
若是計時器已經超時,但事件還沒有發送,則會被取消,就好像它沒有超時同樣,所以今後函數返回後將沒有錯誤的計時器事件。
若是Ref引用活動計時器,則返回以毫秒爲單位的剩餘時間,直到計時器到期爲止,不然返回false。
enter_loop(Module, Options, StateName, StateData)
enter_loop(Module, Options, StateName, StateData, FsmName)
enter_loop(Module, Options, StateName, StateData, Timeout)
enter_loop(Module, Options, StateName, StateData, FsmName, Timeout)
Types:
Module = atom()
Options = [Option]
Option = {debug,Dbgs}
Dbgs = [Dbg]
Dbg = trace | log | statistics | {log_to_file,FileName} | {install,{Func,FuncState}}
StateName = atom()
StateData = term()
FsmName = {local,Name} | {global,GlobalName} | {via,Module,ViaName}
Name = atom()
GlobalName = ViaName = term()
Timeout = int() | infinity
將現有進程轉換爲gen_fsm。不返回,而是調用進程將進入gen_fsm接收receive循環併成爲gen_fsm進程。該進程必須使用proc_lib中的啓動函數被啓動,參看proc_lib(3)。用戶對進程初始化負責,包括註冊名字。
當須要比gen_fsm行爲提供的更復雜的初始化過程時,此函數頗有用。
Module,Options和FsmName與調用start[_link]/3,4時的含義相同。可是,若是指定了FsmName,則必須在調用此函數以前相應地註冊該進程。
StateName,StateData和Timeout與Module:init/1的返回值具備相同的含義 。此外,回調模塊Module不須要導出init/1功能。
失敗:若是調用進程未由proc_lib啓動函數啓動,或者未根據FsmName註冊。
回調函數
應從gen_fsm回調模塊導出如下函數。
在描述中,表達式state name用於表示狀態機的狀態。state data用於表示實現狀態機的Erlang進程的內部狀態。
導出
Module:init(InitArgs) -> {ok,State} | {ok,State,hibernate} | {error,Reason}
Types:
InitArgs = Args | {Args,Term}
Args = Term = term()
State = term()
Reason = term()
每當將新事件處理程序添加到事件管理器時,都會調用此函數來初始化事件處理程序。
若是添加了事件處理程序,因爲調用gen_event:add_handler/3或gen_event:add_sup_handler/3,則InitArgs是這些函數的Args參數。
若是事件處理程序因爲調用gen_event:swap_handler/3或gen_event:swap_sup_handler/3而替換另外一個事件處理程序,或者因爲來自其餘一個回調函數的交換返回元組,則InitArgs是一個元組{Args,Term} 其中Args是函數call/return元組中提供的參數,Term是終止舊事件處理程序的結果,請參閱gen_event:swap_handler/3。
若是成功,該函數應返回{ok,State}或{ok,State,hibernate},其中State是事件處理程序的初始內部狀態。
若是返回{ok,State,hibernate},則事件管理器將進入休眠狀態(經過調用proc_lib:hibernate/3),等待下一個事件發生。
Module:StateName(Event, StateData) -> Result
Types:
Event = timeout | term()
StateData = term()
Result = {next_state,NextStateName,NewStateData}
| {next_state,NextStateName,NewStateData,Timeout}
| {next_state,NextStateName,NewStateData,hibernate}
| {stop,Reason,NewStateData}
NextStateName = atom()
NewStateData = term()
Timeout = int()>0 | infinity
Reason = term()
每一個可能的狀態名稱應該有一個此函數的實例。每當gen_fsm接收到使用gen_fsm:send_event/2發送的事件時,將調用與當前狀態名稱StateName同名的此函數的實例來處理該事件。 若是發生超時,也會調用它。
事件是原子timeout,若是發生超時,Event提供參數給send_event/2。
StateData是gen_fsm的狀態數據。
若是函數返回{next_state,NextStateName,NewStateData},{next_state,NextStateName,NewStateData,Timeout}或{next_state,NextStateName,NewStateData,hibernate},gen_fsm將繼續執行,當前狀態名稱設置爲NextStateName而且可能更新狀態數據NewStateData。 有關Timeout和hibernate的說明,請參閱Module:init/1。
若是函數返回{stop,Reason,NewStateData},gen_fsm將調用Module:terminate(Reason,NewStateData)並終止。
Module:handle_event(Event, StateName, StateData) -> Result
Types:
Event = term()
StateName = atom()
StateData = term()
Result = {next_state,NextStateName,NewStateData}
| {next_state,NextStateName,NewStateData,Timeout}
| {next_state,NextStateName,NewStateData,hibernate}
| {stop,Reason,NewStateData}
NextStateName = atom()
NewStateData = term()
Timeout = int()>0 | infinity
Reason = term()
每當gen_fsm接收到使用gen_fsm:send_all_state_event/2發送的事件時,就會調用此函數來處理該事件。
StateName是gen_fsm的當前狀態名稱。
有關其餘參數和可能的返回值的說明,請參閱Module:StateName/2。
Module:StateName(Event, From, StateData) -> Result
Types:
Event = term()
From = {pid(),Tag}
StateData = term()
Result = {reply,Reply,NextStateName,NewStateData}
| {reply,Reply,NextStateName,NewStateData,Timeout}
| {reply,Reply,NextStateName,NewStateData,hibernate}
| {next_state,NextStateName,NewStateData}
| {next_state,NextStateName,NewStateData,Timeout}
| {next_state,NextStateName,NewStateData,hibernate}
| {stop,Reason,Reply,NewStateData} | {stop,Reason,NewStateData}
Reply = term()
NextStateName = atom()
NewStateData = term()
Timeout = int()>0 | infinity
Reason = normal | term()
每一個可能的狀態名稱應該有一個此函數的實例。每當gen_fsm接收到使用gen_fsm:sync_send_event/2,3發送的事件時,將調用與當前狀態名稱StateName同名的此函數的實例來處理該事件。
Event是提供給sync_send_event的Event參數。
From是一個元組{Pid,Tag},其中Pid是名爲sync_send_event/2,3進程的pid,Tag是惟一標記。
StateData是gen_fsm的狀態數據。
若是函數返回{reply,Reply,NextStateName,NewStateData},{reply,Reply,NextStateName,NewStateData,Timeout}或{reply,Reply,NextStateName,NewStateData,hibernate},則Reply將給回到From做爲sync_send_event/2,3的返回值。而後gen_fsm繼續執行,當前狀態名稱設置爲NextStateName,而且可能更新狀態數據NewStateData。有關Timeout和hibernate的說明,請參閱Module:init/1。
若是函數返回{next_state,NextStateName,NewStateData},{next_state,NextStateName,NewStateData,Timeout}或{next_state,NextStateName,NewStateData,hibernate},gen_fsm將繼續使用NewStateData在NextStateName中執行。必須使用gen_fsm:reply/2明確給出對From的任何回覆。
若是函數返回{stop,Reason,Reply,NewStateData},則回覆將返回From。若是函數返回{stop,Reason,NewStateData},則必須使用gen_fsm:reply/2顯式給出對From的任何回覆。而後gen_fsm將調用Module:terminate(Reason,NewStateData)並終止。
Module:handle_sync_event(Event, From, StateName, StateData) -> Result
Types:
Event = term()
From = {pid(),Tag}
StateName = atom()
StateData = term()
Result = {reply,Reply,NextStateName,NewStateData}
| {reply,Reply,NextStateName,NewStateData,Timeout}
| {reply,Reply,NextStateName,NewStateData,hibernate}
| {next_state,NextStateName,NewStateData}
| {next_state,NextStateName,NewStateData,Timeout}
| {next_state,NextStateName,NewStateData,hibernate}
| {stop,Reason,Reply,NewStateData} | {stop,Reason,NewStateData}
Reply = term()
NextStateName = atom()
NewStateData = term()
Timeout = int()>0 | infinity
Reason = term()
每當gen_fsm接收到使用gen_fsm:sync_send_all_state_event/2,3發送的事件時,將調用此函數來處理該事件。
StateName是gen_fsm的當前狀態名稱。
有關其餘參數和可能的返回值的說明,請參閱Module:StateName/3。
Module:handle_info(Info, StateName, StateData) -> Result
Types:
Info = term()
StateName = atom()
StateData = term()
Result = {next_state,NextStateName,NewStateData}
| {next_state,NextStateName,NewStateData,Timeout}
| {next_state,NextStateName,NewStateData,hibernate}
| {stop,Reason,NewStateData}
NextStateName = atom()
NewStateData = term()
Timeout = int()>0 | infinity
Reason = normal | term()
當gen_fsm接收到除同步或異步事件(或系統消息)以外的任何其餘消息時,將調用此函數。
Info是收到的消息。
有關其餘參數和可能的返回值的說明,請參閱Module:StateName/2。
Module:terminate(Reason, StateName, StateData)
Types:
Reason = normal | shutdown | {shutdown,term()} | term()
StateName = atom()
StateData = term()
當gen_fsm即將終止時,該函數被gen_fsm調用。它應該與Module:init/1相反,並進行任何須要的清理。返回時,gen_fsm以Reason結束。返回值被忽略。
Reason是表示中止緣由的術語,StateName是當前狀態名稱,StateData是gen_fsm的狀態數據。
Reason取決於gen_fsm爲什麼終止。若是是由於另外一個回調函數返回了一箇中止元組{stop,..},則Reason將具備該元組中指定的值。若是是因爲失敗,則Reason是錯誤緣由。
若是gen_fsm是監督樹的一部分而且由其主管命令終止,則若是知足如下條件,則將使用Reason = shutdown調用此函數:
gen_fsm已被設置爲捕獲退出信號,而且監督者的子規範中定義的關閉策略是整數超時值,而不是brutal_kill。
即便gen_fsm不是監督樹的一部分,若是它從其父級接收到「EXIT」消息,也將調用該函數。Reason與「EXIT」消息中的緣由相同。
不然,gen_fsm將當即終止。
請注意,除了正常,關閉或{shutdown,Term}以外的任何其餘緣由,假定gen_fsm因爲錯誤而終止,而且使用error_logger:format/2發出錯誤報告。
Module:code_change(OldVsn, StateName, StateData, Extra) -> {ok, NextStateName, NewStateData}
Types:
OldVsn = Vsn | {down, Vsn}
Vsn = term()
StateName = NextStateName = atom()
StateData = NewStateData = term()
Extra = term()
當gen_fsm在發佈升級/降級期間更新其內部狀態數據時,即在appup文件中給出Change= {advanced,Extra}的指令{update,Module,Change,...}時,將調用此函數。 見OTP設計原則。
在升級的狀況下,OldVsn是Vsn,在降級的狀況下,OldVsn是{down,Vsn}。 Vsn由舊版本的回調模塊Module的vsn屬性定義。 若是未定義此類屬性,則版本是BEAM文件的校驗和。
StateName是當前狀態名稱,StateData是gen_fsm的內部狀態數據。
Extra從更新指令的{advanced,Extra}部分按原樣傳遞。
該函數應返回新的當前狀態名稱和更新的內部數據。
Module:format_status(Opt, [PDict, StateData]) -> Status
Types:
Opt = normal | terminate
PDict = [{Key, Value}]
StateData = term()
Status = term()
請注意,該回調可選,因此回調模塊不須要導出它,這個回調模塊提供一個默認實現,該函數返回回調模塊狀態。
在如下狀況下,gen_fsm進程會調用此函數:
調用sys:get_status/1,2之一以獲取gen_fsm狀態。對於這種狀況,Opt設置爲原子normal。
gen_fsm異常終止並記錄錯誤。對於這種狀況,選項設置爲原子terminate。
此函數可用於自定義這些狀況的gen_fsm狀態的形式和外觀。但願自定義sys:get_status/1,2返回值以及其狀態如何在終止錯誤日誌中出現的回調模塊導出format_status/2的實例,該實例返回描述gen_fsm當前狀態的術語。
PDict是gen_fsm的進程字典的當前值。
StateData是gen_fsm的內部狀態數據。
該函數應返回Status,這是一個定製gen_fsm當前狀態和狀態詳細信息的術語。狀態能夠採起的形式沒有限制,但對於sys:get_status/1,2狀況(當Opt是normal時),Status值的推薦形式爲[{data,[{「StateData」,Term}]其中Term提供gen_fsm狀態數據的相關詳細信息。不須要遵循此建議,但這樣作會使回調模塊狀態與sys:get_status/1,2返回值的其他部分保持一致。
此函數的一個用途是返回緊湊的替代狀態數據表示,以免在日誌文件中打印大的狀態術語。