This module provides a standard way of writing Finite State Machine (FSM) processes. All FSMs written as gen_fsms share a common set of interface functions. The generic parts of the FSM contains functions for debugging, for handling the termination of the parent process, and for presentation of illustrative error information if something goes wrong in the process.
The state of the FSM is defined by two parameters, the
StateName
and the StateData
. For each
StateName
, there must be a corresponding function
exported from the call-back module. When an event is received,
and the current state of the FSM is StateName
,
Module:StateName(Event, StateData)
is called. This
function should return the next state, which is the next
StateName
.
It is also possible to define a function
Module:handle_event(Event, StateName, StateData)
to take
care of events which should always be handled, regardless of
their state. This function is called when
gen_fsm:send_all_state_event/2
is used to generate an
event.
Events can be handled synchronously as well. This means that the caller waits for a reply to the event.
The relationship between the generic interface functions (and received messages) and the callback functions can be illustrated as follows:
Callback module gen_fsm ---------------- ------- gen_fsm:start_link -----> start a new fsm process Module:init/1 <----- looping gen_fsm:send_event -----> Module:StateName/2 <----- gen_fsm:sync_send_event -----> Module:StateName/3 <----- gen_fsm:send_all_state_event -----> Module:handle_event/3 <----- gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4 <----- Module:handle_info/3 <----- other message received. Module:terminate/3 <----- clean up before termination.
Trapping of exits, if required, must be done explicitly. |
An instance of the gen_fsm
behaviour can be debugged by
using the module sys
.
start(Module, StartArgs, Options) -> StartRet
start_link(Module, StartArgs, Options) -> StartRet
start(Name, Module, StartArgs, Options) -> StartRet
start_link(Name, Module, StartArgs, Options) -> StartRet
Name = {local, atom()} | {global, atom()}
Module = atom()
StartArgs = term()
Options = [Opt]
Opt = {debug, [Dbg]} | {timeout, Time}
Dbg = trace | log | statistics | {log_to_file, FileName} |
{install, {Func, FuncState}}
StartRet = {ok, Pid} | ignore | {error, Reason}
Pid = pid()
Reason = {already_started, Pid} | term()
Starts an FSM process. An anonymous process is started if
Name
is not specified. This process can only be
called by using the returned Pid
identifier.
A
process which is started with start
does not care
about the parent, which means that the parent is not handled
explicitly in the generic process part. If started in this
manner, this function must not be used if the FSM
is a worker in a supervision tree.
A process started with start_link
is initially
linked to the caller - the parent - and will terminate
whenever the parent process terminates, and with the same
reason as the parent. If started in this manner, this
function should be used if the FSM is a worker in a
supervision tree.
The function Module:init(StartArgs)
is called (see
below).
Time
specifies how long time, in milliseconds, the
server is allowed to initialize itself.
The debug options are described in sys(3)
.
send_event(ProcessRef,Event) -> void()
ProcessRef = Name | {Name, Node} | {global, Name} |
pid()
Name = atom()
Node = atom()
Event = term()
Sends an event asynchronously to the FSM process. In the
callback module, the function StateName/2
is called,
where StateName
is the name of the current state.
send_all_state_event(ProcessRef,Event) -> void()
ProcessRef = Name | {Name, Node} | {global, Name} |
pid()
Name = atom()
Node = atom()
Event = term()
An event, which can be handled in all states, is sent
asynchronously to the FSM process. In the callback module,
handle_event/3
is called.
sync_send_event(ProcessRef,Event) -> Reply
sync_send_event(ProcessRef,Event, Timeout) -> Reply
ProcessRef = Name | {Name, Node} | {global, Name} |
pid()
Name = atom()
Node = atom()
Event = term()
Timeout = int() > 0 | infinity
Reply = term()
Sends an event synchronously to the FSM process and waits
for the answer. In the callback module, the function
StateName/3
is called, where StateName
is the
name of the current state.
Timeout
should be set to some reasonable value. The
special value infinity
can be used if the user has no
idea how long the request is supposed to take. The default
is 5000.
If Timeout
has an integer value and if no response
has been delivered within Timeout
milliseconds, the
client will terminate with reason {timeout, {gen_fsm,
sync_send_event, [ProcessRef, Event, Timeout]}}
.
If the server should crash during the
request and the client is linked to the server and the
client is trapping exits, (phew) the exit message
is read out from the clients receive queue and then
this function call fails with the exit reason that was read.
This is a remnant from when monitors did not exist and
links was the only way to supervise the request, and the
behaviour may change in a future release. In this release,
unfortuneately, under certain circumstances
(e.g. ProcessRef = {Name, Node}
,
Node
crashes during call) the exit message cannot
be read out. Note that if the server crashes in between calls,
the client must take care of the exit message anyway.
sync_send_all_state_event(ProcessRef,Event) ->
Reply
sync_send_all_state_event(ProcessRef,Event,Timeout) ->
Reply
ProcessRef = Name | {Name, Node} | {global, Name} |
pid()
Name = atom()
Node = atom()
Event = term()
Timeout = int() > 0 | infinity
Reply = term()
An event, which can be handled in all states, is sent
synchronously to the FSM process. In the callback module,
handle_event/4
is called.
Timeout
should be set to some reasonable value. The
special value infinity
can be used if the user has no
idea how long the request is supposed to take. The default
is 5000.
If Timeout
has an integer value and no response has
been delivered within Timeout
milliseconds, the
client will terminate with reason {timeout, {gen_fsm,
sync_send_all_state_event, [ProcessRef, Event,
Timeout]}}
.
To = {pid(), Tag}
Tag = term()
Reply = term()
If a reply cannot be returned immediately - as the
return value of Module:StateName/3
or
Module:handle_sync_event/4
- this function can be
used to make an explicit reply. To
has the same value
as the From
argument in these functions.
The following functions
should be exported from a gen_fsm
callback module.
Module:init(StartArgs) -> Return
StartArgs = term()
StateName = atom()
StateData = term()
Timeout = int() > 0 | infinity
StopReason = term()
Return = {ok, StateName, StateData} | {ok, StateName,
StateData, Timeout} | ignore | {stop, StopReason}
This function initializes the FSM process and returns the
initial state. The Timeout
variable specifies that
the process shall wait for Timeout
milliseconds for
the first message. If no message has arrived within the
specified time, Module:StateName(timeout, StateData)
is called.
The StartArgs
argument supplied to the init/1
function is the same as the argument supplied to the
gen_fsm:start
functions.
If the process should trap exits, this has to be explicitly
expressed here with process_flag(trap_exit, true)
.
The representation of the FSM StateData
is an
implementation specific detail which has to be decided by
the designer of the FSM. It can be any Erlang term.
StateData
will be visible as an argument to all
callback functions. To change something in StateData
,
a new value is returned from the callback function using the
terms described below.
If the initializing procedure fails, the reason is supplied
as StopReason
with the {stop, StopReason}
return value.
This function can return ignore
in order to inform
the parent, especially if it is a supervisor, that the FSM,
as an example, has not started in accordance with the
configuration data.
Module:StateName(Event, StateData) -> Return
Event = term()
StateData = term()
Return = {next_state, NextStateName, NextStateData} | {next_state,
NextStateName, NextStateData, Timeout} | {stop, Reason,
NewStateData}
NextStateName = atom()
NextStateData = term()
Reason = normal | shutdown | term()
Handles events in the state StateName
. The
Timeout
variable is as in Module:init/1
above.
Whenever the function gen_fsm:send_event
is called,
this function is called to handle the event. If the FSM
times out, this function is also called with Event =
timeout
.
Event
is the same term as supplied in the above
client call.
If the FSM decides to terminate, this function should
return {stop, Reason, NewStateData}
, and the function
Module:terminate(Reason, StateName, NewStateData)
is
called. If Reason
is something other than
normal
or shutdown
, the FSM is assumed to have
terminated with a runtime failure. In this case, a lot of
information about the failure is reported. The atom
normal
causes a normal termination while
shutdown
causes an abnormal, but faultless,
termination of the process.
Module:StateName(Event, From, StateData) -> Return
Event = term()
From = {pid(), Tag}
StateData = term()
Return = {next_state, NextStateName,
NextStateData} | {next_state, NextStateName, NextStateData,
Timeout} | {reply, Reply, NextStateName, NextStateData} |
{reply, Reply, NextStateName, NextStateData, Timeout} | {stop,
Reason, NewStateData} | {stop, Reason, Reply, NewStateData}
NextStateName = atom()
NextStateData = term()
Reply = term()
Reason = normal | shutdown | term()
Handles synchronous events in the state StateName
.
The Timeout
variable is as in Module:init/1
above.
Whenever the function gen_fsm:sync_send_event/2,3
is
called, this function is called to handle the event.
Event
is the same as the term supplied with the
above client call.
The FSM decides if a reply is sent to the caller directly
({reply, ...}
), indirectly ({next_state,
...}
), or if the FSM has to terminate ({stop,
...}
) as a result of the request. If {next_state,
...}
is returned, a reply can be sent to the caller
using the reply/2
function.
If the FSM decides to terminate, this function returns
{stop, Reason, NewStateData}
or {stop, Reason,
Reply, NewStateData}
, and the function
Module:terminate(Reason, StateName, NewStateData)
is
called. If Reason
is something other than
normal
or shutdown
, the FSM is assumed to have
terminated with a runtime failure. In this case, a lot of
information about the failure is reported. The atom
normal
causes a normal termination while
shutdown
causes an abnormal, but faultless,
termination of the process.
Module:handle_event(Event, StateName, StateData) ->
Return
Event = term()
StateName = atom()
StateData = term()
Handles events generated with the function
gen_fsm:send_all_state_event/2
.
The Return
value is the same as for
Module:StateName/2
.
Module:handle_sync_event(Event, From, StateName,
StateData) -> Return
Event = term()
From = {pid(), Tag}
StateName = atom()
StateData = term()
Handles events generated with the function
gen_fsm:sync_send_all_state_event/2,3
.
The Return
value is the same as for
Module:StateName/3
.
Module:handle_info(Info, StateName, StateData) -> Return
Info = term()
StateName = atom()
StateData = term()
This function receives all messages sent to this process
which are not generated by gen_fsm:send_event/2
,
gen_fsm:send_all_state_event/2
,
gen_fsm:sync_send_event/2,3
, or
gen_fsm:sync_send_all_state_event/2,3
. Typical
messages handled here include:
{'EXIT', Pid, Reason}
{nodedown, Node}
nodedown
message is handled here.
Msg
Fsm ! Msg
are also handled here.
Communication with the FSM should always go through the interface functions described above. |
The
Return
value is the same as for
Module:StateName/2
.
Module:terminate(Reason, StateName, StateData) -> void()
Reason = term()
StateName = atom()
StateData = term()
This callback function is called whenever the FSM is
about to terminate. Either one of the above callback
functions have returned {stop, StopReason, ...}
, in
which case Reason
is equal to StopReason
; or
some other fault has been caught. Reason
is any term
which describes the termination reason. If the FSM traps
exits, the terminate
function is called if the
FSM's parent (normally a supervisor) dies or orders the
FSM to die. If the FSM does not trap exits, it dies
immediately if the parent dies.
With this function, the FSM can clean up before the process terminates. It can, for example, de-allocate external resources.
The termination reason cannot be
changed here. The FSM will terminate due toReason
regardless of what was returned from this function.
Module:code_change(OldVsn, StateName, StateData, Extra) ->
{ok, NewState, NewStateData}
OldVsn = undefined | term()
StateName = atom()
StateData = term()
Extra = term()
NewStateName = atom()
NewStateData = term()
This function is called when a code change is performed,
which implies that the internal data structures of the FSM
have changed. The function is supposed to convert the old
state to the new one. OldVsn
is the vsn
attribute of the old version of the module. If no such
attribute was defined, the atom undefined
is
sent. Extra
is an optional term, typically defined in
the release upgrade script.
The gen_fsm
behaviour
generates the following system events, handled by the sys
module:
{in, Msg}
when a message is received.
{out, Msg, To, StateName}
when a message is sent.
return
when an event handling callback function
returns.
sys(3)