gen_statem provides a generic state machine behaviour
that for new code replaces its predecessor
gen_fsm
since Erlang/OTP 20.0. The gen_fsm behaviour remains
in OTP "as is".
Note
If you are new to gen_statem and want an overview
of concepts and operation the section
gen_statem Behaviour
located in the User's Guide
OTP Design Principles
is recommended to read before this reference manual,
possibly after the Description section you are reading here.
This reference manual contains type descriptions generated from
types in the gen_statem source code, so they are correct.
However, the generated descriptions also reflect the type hierarchy,
which sometimes makes it hard to get a good overview.
If so, see the section
gen_statem Behaviour
in the
OTP Design Principles
User's Guide.
gen_statem has got the same features that
gen_fsm
had and adds some really useful:
- Co-located state code
- Arbitrary term state
- Event postponing
- Self-generated events
- State time-out
- Multiple generic named time-outs
- Absolute time-out time
- Automatic state enter calls
-
Reply from other state than the request, sys traceable
- Multiple sys traceable replies
- Changing the callback module
Two
callback modes
are supported:
-
One for finite-state machines
(gen_fsm like),
which requires the state to be an atom and uses that state as
the name of the current callback function.
-
One that allows the state to be any term and
that uses one callback function for all states.
The callback model(s) for gen_statem differs from
the one for gen_fsm,
but it is still fairly easy to
rewrite from
gen_fsm to gen_statem.
A generic state machine server process (gen_statem) implemented
using this module has a standard set of interface functions
and includes functionality for tracing and error reporting.
It also fits into an OTP supervision tree. For more information, see
OTP Design Principles.
A gen_statem assumes all specific parts to be located in a
callback module exporting a predefined set of functions.
The relationship between the behavior functions and the callback
functions is as follows:
gen_statem module Callback module
----------------- ---------------
gen_statem:start
gen_statem:start_monitor
gen_statem:start_link -----> Module:init/1
Server start or code change
-----> Module:callback_mode/0
gen_statem:stop -----> Module:terminate/3
gen_statem:call
gen_statem:cast
gen_statem:send_request
erlang:send
erlang:'!' -----> Module:StateName/3
Module:handle_event/4
- -----> Module:terminate/3
- -----> Module:code_change/4
Events are of different
types,
so the callback functions can know the origin of an event
and how to respond.
If a callback function fails or returns a bad value,
the gen_statem terminates, unless otherwise stated.
However, an exception of class
throw
is not regarded as an error but as a valid return
from all callback functions.
The state callback for a specific
state
in a gen_statem is the callback function that is called
for all events in this state. It is selected depending on which
callback mode
that the callback module defines with the callback function
Module:callback_mode/0.
When the
callback mode
is state_functions, the state must be an atom and
is used as the state callback name; see
Module:StateName/3.
This co-locates all code for a specific state
in one function as the gen_statem engine
branches depending on state name.
Note the fact that the callback function
Module:terminate/3
makes the state name terminate unusable in this mode.
When the
callback mode
is handle_event_function, the state can be any term
and the state callback name is
Module:handle_event/4.
This makes it easy to branch depending on state or event as you desire.
Be careful about which events you handle in which
states so that you do not accidentally postpone an event
forever creating an infinite busy loop.
When gen_statem receives a process message it is
converted into an event and the
state callback
is called with the event as two arguments: type and content.
When the
state callback
has processed the event it returns to gen_statem
which does a state transition.
If this state transition is to a different state,
that is: NextState =/= State, it is a state change.
The
state callback
may return
transition actions
for gen_statem
to execute during the state transition,
for example to reply to a
gen_statem:call/2,3.
One of the possible transition actions
is to postpone the current event.
Then it is not retried in the current state.
The gen_statem engine keeps a queue of events
divided into the postponed events
and the events still to process.
After a state change the queue restarts
with the postponed events.
The gen_statem event queue model is sufficient
to emulate the normal process message queue with selective receive.
Postponing an event corresponds to not matching it
in a receive statement, and changing states corresponds
to entering a new receive statement.
The
state callback
can insert events using the
transition actions
next_event
and such an event is inserted in the event queue
as the next to call the
state callback
with.
That is, as if it is the oldest incoming event.
A dedicated
event_type()
internal can be used for such events making them impossible
to mistake for external events.
Inserting an event replaces the trick of calling your own
state handling functions that you often would have to
resort to in, for example,
gen_fsm
to force processing an inserted event before others.
The gen_statem engine can automatically
make a specialized call to the
state callback
whenever a new state is entered; see
state_enter().
This is for writing code common to all state entries.
Another way to do it is to explicitly insert an event
at the state transition,
and/or to use a dedicated state transition function,
but that is something you will have to remember
at every state transition to the state(s) that need it.
Note
If you in gen_statem, for example, postpone
an event in one state and then call another state callback
of yours, you have not done a state change
and hence the postponed event is not retried,
which is logical but can be confusing.
For the details of a state transition, see type
transition_option().
A gen_statem handles system messages as described in
sys.
The sys module can be used for debugging a gen_statem.
Notice that a gen_statem does not trap exit signals
automatically, this must be explicitly initiated in
the callback module (by calling
process_flag(trap_exit, true).
Unless otherwise stated, all functions in this module fail if
the specified gen_statem does not exist or
if bad arguments are specified.
The gen_statem process can go into hibernation; see
proc_lib:hibernate/3.
It is done when a
state callback
or
Module:init/1
specifies hibernate in the returned
Actions
list. This feature can be useful to reclaim process heap memory
while the server is expected to be idle for a long time.
However, use this feature with care,
as hibernation can be too costly
to use after every event; see
erlang:hibernate/3.
There is also a server start option
{hibernate_after, Timeout}
for
start/3,4,
start_monitor/3,4,
start_link/3,4 or
enter_loop/4,5,6,
that may be used to automatically hibernate the server.
If the gen_statem process terminates, e.g. as a result of a
function in the callback module returning {stop,Reason}, an exit
signal with this Reason is sent to linked processes and ports. See
Processes in the Reference Manual for details regarding error
handling using exit signals.
Note
For some important information about distributed signals, see the
Blocking Signaling Over Distribution section in the
Processes chapter of the Erlang Reference Manual.
Blocking signaling can, for example, cause call timeouts in
gen_statem to be significantly delayed.