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.