Description
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.